一般使用時都不成問題,不過當需要大量寫入資料就會發現一點點的效能差異影響是很大的,
之前寫了一個 MQTT Broker(Server) ,平時自己用都沒問題,
但最近測試大量連線和封包傳送,就發現速度慢了一截,
LOG 的第三方元件我使用 NLOG,
平時有需要,就一筆一筆寫入 Log,
若需要大量寫入,我會修改 NLog 設定,以 Buffer 方式寫入
大概的設定如下所示
一般不使用 Buffer方式
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" autoReload="true" throwExceptions="true"> <targets> <target Name="TraceTarget" xsi:type="File" layout="${level:uppercase=true:padding=-5} ${date:format=yyyy\-MM\-dd\ HH\:mm\:ss\.fffffff} - ${message}" fileName="${basedir}/Log/Trace/Trace.log" archiveFileName="${basedir}/Log/Trace/Trace${date:yyyyMMdd\_HH}.{#}.log" archiveAboveSize="5242880" archiveNumbering="Sequence" concurrentWrites="true" keepFileOpen="false" maxArchiveFiles="100" encoding="UTF-8" /> </targets> <rules> <logger Name="Trace" minlevel="DEBUG" writeTo="TraceTarget" LogWvalue="DEBUG" /> </rules> </nlog>
使用 Buffer 方式
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" autoReload="true" throwExceptions="true"> <targets> <target xsi:type="BufferingWrapper" Name="TraceBufferingWrapper" bufferSize="100"> <target Name="TraceTarget" xsi:type="File" layout="${level:uppercase=true:padding=-5} ${date:format=yyyy\-MM\-dd\ HH\:mm\:ss\.fffffff} - ${message}" fileName="${basedir}/Log/Trace/Trace.log" archiveFileName="${basedir}/Log/Trace/Trace${date:yyyyMMdd\_HH}.{#}.log" archiveAboveSize="5242880" archiveNumbering="Sequence" concurrentWrites="true" keepFileOpen="false" maxArchiveFiles="100" encoding="UTF-8" /> </target> </targets> <rules> <logger Name="Trace" minlevel="DEBUG" writeTo="TraceBufferingWrapper" LogWvalue="DEBUG" /> </rules> </nlog>
雖然這樣的寫法就能提升效能,不過程式中寫 Log 的次數頻繁,內部效能還需要再進一步修正,
利用 ConcurrentQueue 先將要寫的 Log 內容推到 Queue 中,每隔 500 ms 再將 Queue 中的內容取出,並寫入 Log
- private string _logSetFile = @".\LogSetting.xml";
- ConcurrentQueue<string> _logQ = new ConcurrentQueue<string>();
- System.Timers.Timer _timer = new System.Timers.Timer();
- public LogTest()
- {
- if (System.IO.File.Exists(_logSetFile) == false)
- {
- try
- { Logger.CreatDefaultLogSettingFile(_logSetFile); }
- catch (Exception ex)
- { MessageBox.Show(ex.ToString()); return; }
- }
- Logger.Initialze(_logSetFile);
- _timer.Elapsed += LogTimeElapsed;
- _timer.Interval = 500;
- }
- public void StartLogTestWithQ()
- {
- Logger.CreateInstance("LogTestWithQ");
- _timer.Start();
- _sw2.Start();
- for (int i = 1; i <= 10000; i++)
- {
- _logQ.Enqueue("第 " + i.ToString() + " 筆");
- }
- _sw2.Done();
- _logQ.Enqueue("測試完成花費時間 " + _sw2.ElapsedTime().ToString());
- MessageBox.Show("測試完成花費時間 " + _sw2.ElapsedTime().ToString());
- }
- public void StartLogTestWithoutQ()
- {
- Logger.CreateInstance("LogTestWithoutQ");
- _sw2.Start();
- for (int i = 1; i <= 10000; i++)
- {
- Logger.LogMessage("LogTestWithoutQ", "第 " + i.ToString() + " 筆", LogLevel.Debug);
- }
- _sw2.Done();
- Logger.LogMessage("LogTestWithoutQ", "測試完成花費時間 " + _sw2.ElapsedTime().ToString(), LogLevel.Debug);
- MessageBox.Show("測試完成花費時間 " + _sw2.ElapsedTime().ToString());
- }
- /// <summary>
- /// 利用時間計數,一次寫入 Log ,不要占用太多資源
- /// </summary>
- /// <param name="obj"></param>
- /// <param name="e"></param>
- private void LogTimeElapsed(object obj, System.Timers.ElapsedEventArgs e)
- {
- StringBuilder sb = new StringBuilder();
- while (_logQ.Count > 0)
- {
- string result = "";
- if (_logQ.TryDequeue(out result))
- {
- Logger.LogMessage("LogTestWithQ", result, LogLevel.Debug);
- }
- }
- }
經過測試果然需要 Log Queue 搭配 NLog Buffer 的方式速度才會快
測試寫入 10000 筆資料,結果如下
//LogTestWithQ none Buffer (Log File 寫入時間長,但程式執行時間短) DEBUG 2021-07-16 10:52:22.2869563 - 第 1 筆 DEBUG 2021-07-16 10:52:28.7547517 - 測試完成花費時間 0.0014641 //LogTestWithoutQ none Buffer (速度最慢) DEBUG 2021-07-16 10:52:11.8478062 - 第 1 筆 DEBUG 2021-07-16 10:52:16.2525868 - 測試完成花費時間 4.4130297 //LogTestWithQ use Buffer (***速度最快) DEBUG 2021-07-16 11:01:58.9000957 - 第 1 筆 DEBUG 2021-07-16 11:01:58.9549269 - 測試完成花費時間 0.0018626 //LogTestWithoutQ use Buffer DEBUG 2021-07-16 11:01:53.5231884 - 第 1 筆 DEBUG 2021-07-16 11:01:53.6112196 - 測試完成花費時間 0.102756