開発サイトにある Event Viewer アプリケーションのパフォーマンスの問題を調査していると、アルゴリズムに興味深い問題があることに気付きました。次に、単純化されたテスト プロジェクトを作成して、2 つの異なるアルゴリズムをテストしました。このプログラムは基本的に、クラスを使用して Windows イベント ログを取得し、それらのログをクエリ可能なエンティティEventLog
に変換します。EventLogItem
この操作は、2 つの異なるループを使用して実行され、タイミングがとられます。最初の (逆方向の) ループは、リストの最後の項目のインデックスから開始し、項目を変換してからインデックスを減らします。メソッドは次のように定義されます。
private static void TranslateLogsUsingBackwardLoop()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
var originalLogs = EventLog.GetEventLogs();
var translatedLogs = new List<EventLogItem>();
Parallel.ForEach<EventLog>(originalLogs, currentLog =>
{
for (int index = currentLog.Entries.Count - 1; index >= 0; index--)
{
var currentEntry = currentLog.Entries[index];
EventLogItem translatedEntry = new EventLogItem
{
MachineName = currentEntry.MachineName,
LogName = currentLog.LogDisplayName,
CreatedTime = currentEntry.TimeGenerated,
Source = currentEntry.Source,
Message = currentEntry.Message,
Number = currentEntry.Index,
Category = currentEntry.Category,
Type = currentEntry.EntryType,
InstanceID = currentEntry.InstanceId,
User = currentEntry.UserName,
};
lock (translatedLogs)
{
translatedLogs.Add(translatedEntry);
}
}
});
stopwatch.Stop();
Console.WriteLine("{0} logs were translated in {1} using backward loop.", translatedLogs.Count, stopwatch.Elapsed);
}
2 番目の (順方向) ループは、インデックス 0 から開始し、インデックスをインクリメントします。このメソッドは次のように定義されています。
private static void TranslateLogsUsingForwardLoop()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
var originalLogs = EventLog.GetEventLogs();
var translatedLogs = new List<EventLogItem>();
Parallel.ForEach<EventLog>(originalLogs, currentLog =>
{
for (int index = 0; index < currentLog.Entries.Count; index++)
{
var currentEntry = currentLog.Entries[index];
EventLogItem translatedEntry = new EventLogItem
{
MachineName = currentEntry.MachineName,
LogName = currentLog.LogDisplayName,
CreatedTime = currentEntry.TimeGenerated,
Source = currentEntry.Source,
Message = currentEntry.Message,
Number = currentEntry.Index,
Category = currentEntry.Category,
Type = currentEntry.EntryType,
InstanceID = currentEntry.InstanceId,
User = currentEntry.UserName,
};
lock (translatedLogs)
{
translatedLogs.Add(translatedEntry);
}
}
});
stopwatch.Stop();
Console.WriteLine("{0} logs were translated in {1} using forward loop.", translatedLogs.Count, stopwatch.Elapsed);
}
そして主な方法:
static void Main(string[] args)
{
TranslateLogsUsingForwardLoop();
Console.WriteLine();
Thread.Sleep(2000);
TranslateLogsUsingBackwardLoop();
Console.ReadLine();
}
これは私が得たものです(このテストを数回実行しましたが、結果はほぼ同じです):
私がこれをテストしたサーバーは、毎秒イベント ログにログを記録したことに注意してください。そのため、変換されたログの数は同じではありません。では、なぜ後方ループの方が速いのでしょうか? 最初は、逆方向ループ アルゴリズムでcurrentLog.Entries.Count
は が 1 回だけ評価されるためだと考えていましたが、順方向ループではループの反復ごとに計算して比較する必要がありますindex
が、これも正しくないようです。何か案は?