() }, { "Dictionary Search(Sequential)", new List<(long, long, long)>() }, { "OrderedDictionary Search(Contains)", new List<(long, long, long)>() }, { "OrderedDictionary Search(Sequential)", new List<(long, long, long)>() }, { "Dictionary Add(SerialNum)", new List<(long, long, long)>() },// キーに連番を設定 { "Dictionary Add(SerialNum)(initialCapacity)", new List<(long, long, long)>() },// キーに連番を設定, 初期容量を指定 { "Dic"> () }, { "Dictionary Search(Sequential)", new List<(long, long, long)>() }, { "OrderedDictionary Search(Contains)", new List<(long, long, long)>() }, { "OrderedDictionary Search(Sequential)", new List<(long, long, long)>() }, { "Dictionary Add(SerialNum)", new List<(long, long, long)>() },// キーに連番を設定 { "Dictionary Add(SerialNum)(initialCapacity)", new List<(long, long, long)>() },// キーに連番を設定, 初期容量を指定 { "Dic"> () }, { "Dictionary Search(Sequential)", new List<(long, long, long)>() }, { "OrderedDictionary Search(Contains)", new List<(long, long, long)>() }, { "OrderedDictionary Search(Sequential)", new List<(long, long, long)>() }, { "Dictionary Add(SerialNum)", new List<(long, long, long)>() },// キーに連番を設定 { "Dictionary Add(SerialNum)(initialCapacity)", new List<(long, long, long)>() },// キーに連番を設定, 初期容量を指定 { "Dic">
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace CalculatorApp.Backend.Kensyou.HairetsuSokudo
{
public class HairetsuSokudoHikaku
{
// 計測結果の保存用
private Dictionary<string, List<(long loopCnt, long TimeMs, long MemoryBytes)>> results = new Dictionary<string, List<(long, long, long)>>()
{
{ "Dictionary Search(Contains)", new List<(long, long, long)>() },
{ "Dictionary Search(Sequential)", new List<(long, long, long)>() },
{ "OrderedDictionary Search(Contains)", new List<(long, long, long)>() },
{ "OrderedDictionary Search(Sequential)", new List<(long, long, long)>() },
{ "Dictionary Add(SerialNum)", new List<(long, long, long)>() },// キーに連番を設定
{ "Dictionary Add(SerialNum)(initialCapacity)", new List<(long, long, long)>() },// キーに連番を設定, 初期容量を指定
{ "Dictionary Add(Random)", new List<(long, long, long)>() },// キーに重複しないランダムな値を設定
{ "Dictionary Add(Random)(initialCapacity)", new List<(long, long, long)>() },// キーに重複しないランダムな値を設定, 初期容量を指定
{ "OrderedDictionary Add(SerialNum)", new List<(long, long, long)>() },// キーに連番を設定
{ "OrderedDictionary Add(SerialNum)(initialCapacity)", new List<(long, long, long)>() },// キーに連番を設定, 初期容量を指定
{ "OrderedDictionary Add(Random)", new List<(long, long, long)>() },// キーに重複しないランダムな値を設定
{ "OrderedDictionary Add(Random)(initialCapacity)", new List<(long, long, long)>() },// キーに重複しないランダムな値を設定, 初期容量を指定
{ "Dictionary Remove", new List<(long, long, long)>() },
{ "OrderedDictionary Remove(PhysicalDeletion)", new List<(long, long, long)>() },// 物理削除
{ "OrderedDictionary Remove(LogicalDeletion)", new List<(long, long, long)>() },// 論理削除
{ "OrderedDictionary Clear", new List<(long, long, long)>() },
};
private int loopCnt = 0;
// OrderedDictionary[i]の順序リストの速度検証用
private Dictionary<string, List<MeasurementResult>> dicOrderedDictionaryAccessList
= new Dictionary<string, List<MeasurementResult>>()
{
{"OrderedDictionary" , new List<MeasurementResult>()}
, {"Dictionary" ,new List<MeasurementResult>()}
};
/// <summary>
/// 速度検証開始
/// </summary>
/// <param name="addCount">検証データ量(初期値は10万件)</param>
/// <param name="numberOfRuns">検証回数(初期値は60回)</param>
public void KensyouStart(int addCount = 100000, int numberOfRuns = 60)
{
try
{
System.Diagnostics.Debug.WriteLine("=== 辞書、OrderedDictionaryの速度検証 ===");
// 重複しないキーの生成
HashSet<int> uniqueKeys = new HashSet<int>();
for (int i = 0; i < addCount; i++)
{
uniqueKeys.Add(i);
}
int[] keysArray = uniqueKeys.ToArray(); // 配列に変換
// ランダムキーの生成
Random random = new Random();
HashSet<int> uniqueRandomKeys = new HashSet<int>();
while (uniqueRandomKeys.Count < addCount)
{
uniqueRandomKeys.Add(random.Next());
}
int[] keysRandomArray = uniqueRandomKeys.ToArray(); // 配列に変換
for (loopCnt = 0; loopCnt < numberOfRuns; loopCnt++)
{
// 検索
SearchComparison(addCount, keysArray);
// 追加
AddComparison(addCount, keysArray, keysRandomArray);
// 削除
RemoveComparison(addCount, keysArray);
}
// OrderedDictionary[i]の内部的な線形探索の処理時間を検証(うまくいかなかった)
//MeasureOrderedDictionaryAccessProc();
// 結果出力
PrintResults();
}
catch (Exception ex)
{
LogError(ex, nameof(KensyouStart));
throw;
}
}
/// <summary>
/// 検索処理検証
/// </summary>
/// <param name="addCount"></param>
/// <param name="keysArray"></param>
private void SearchComparison(int addCount, int[] keysArray)
{
try
{
int searchCount = keysArray.Length; // 10万回検索を行う
// 検索に使用するデータ
var orderedDictionary = new OrderedDictionary<int, int>();
foreach (int key in keysArray)
{
orderedDictionary.Add(key, key);
}
// 辞書(Contains)
Dictionary<int, int> dictionary = keysArray.ToDictionary(x => x, x => x);
MeasureTimeAndMemory("Dictionary Search(Contains)", () =>
{
for (int i = 0; i < searchCount; i++)
{
bool found = dictionary.ContainsKey(i);
}
});
// 辞書(Sequential)
MeasureTimeAndMemory("Dictionary Search(Sequential)", () =>
{
for (int i = 0; i < searchCount; i++)
{
var item = dictionary[i];
}
});
// OrderedDictionary(Contains)
MeasureTimeAndMemory("OrderedDictionary Search(Contains)", () =>
{
for (int i = 0; i < searchCount; i++)
{
bool found = orderedDictionary.ContainsKey(i);
}
});
// OrderedDictionary(Sequential)
MeasureTimeAndMemory("OrderedDictionary Search(Sequential)", () =>
{
for (int i = 0; i < searchCount; i++) // 順番通りに検索
{
var item = orderedDictionary[i];
}
});
}
catch (Exception ex)
{
LogError(ex, nameof(SearchComparison));
throw;
}
}
/// <summary>
/// 追加処理検証
/// </summary>
/// <param name="addCount"></param>
/// <param name="keysArray"></param>
private void AddComparison(int addCount, int[] keysArray, int[] keysRandomArray)
{
try
{
// 辞書(SerialNum)
Dictionary<int, string> dictionary = new Dictionary<int, string>();
MeasureTimeAndMemory("Dictionary Add(SerialNum)", () =>
{
for (int i = 0; i < addCount; i++)
{
dictionary.Add(i, $"Value_{i}");
}
});
// 辞書(SerialNum)(initialCapacity) 初期容量を指定
Dictionary<int, string> dictionaryInitialCapacity = new Dictionary<int, string>(addCount);
MeasureTimeAndMemory("Dictionary Add(SerialNum)(initialCapacity)", () =>
{
for (int i = 0; i < addCount; i++)
{
dictionaryInitialCapacity.Add(i, $"Value_{i}");
}
});
// 辞書(Random)
Dictionary<int, string> dictionaryRandom = new Dictionary<int, string>();
MeasureTimeAndMemory("Dictionary Add(Random)", () =>
{
for (int i = 0; i < addCount; i++)
{
dictionaryRandom.Add(keysRandomArray[i], $"Value_{i}");
}
});
// 辞書(Random)(initialCapacity) 初期容量を指定
Dictionary<int, string> dictionaryRandomInitialCapacity = new Dictionary<int, string>(addCount);
MeasureTimeAndMemory("Dictionary Add(Random)(initialCapacity)", () =>
{
for (int i = 0; i < addCount; i++)
{
dictionaryRandomInitialCapacity.Add(keysRandomArray[i], $"Value_{i}");
}
});
// OrderedDictionary(SerialNum)
var orderedDictionarySequential = new OrderedDictionary<int, string>();
MeasureTimeAndMemory("OrderedDictionary Add(SerialNum)", () =>
{
for (int i = 0; i < addCount; i++)
{
orderedDictionarySequential.Add(i, $"Value_{i}");
}
});
// OrderedDictionary(SerialNum)(initialCapacity) 初期容量を指定
var orderedDictionaryInitialCapacity = new OrderedDictionary<int, string>(addCount);
MeasureTimeAndMemory("OrderedDictionary Add(SerialNum)(initialCapacity)", () =>
{
for (int i = 0; i < addCount; i++)
{
orderedDictionaryInitialCapacity.Add(keysArray[i], $"Value_{keysArray[i]}");
}
});
// OrderedDictionary(Random)
var orderedDictionaryRandom = new OrderedDictionary<int, string>();
MeasureTimeAndMemory("OrderedDictionary Add(Random)", () =>
{
for (int i = 0; i < addCount; i++)
{
orderedDictionaryRandom.Add(keysRandomArray[i], $"Value_{i}");
}
});
// OrderedDictionary(Random)(initialCapacity) 初期容量を指定
var orderedDictionaryRandomInitialCapacity = new OrderedDictionary<int, string>(addCount);
MeasureTimeAndMemory("OrderedDictionary Add(Random)(initialCapacity)", () =>
{
for (int i = 0; i < addCount; i++)
{
orderedDictionaryRandomInitialCapacity.Add(keysRandomArray[i], $"Value_{i}");
}
});
}
catch (Exception ex)
{
LogError(ex, nameof(AddComparison));
throw;
}
}
/// <summary>
/// 削除処理検証
/// </summary>
/// <param name="addCount"></param>
/// <param name="keysArray"></param>
private void RemoveComparison(int addCount, int[] keysArray)
{
try
{
int removeCount = keysArray.Length;
Random random = new Random();
// 辞書
Dictionary<int, int> dictionary = keysArray.Take(removeCount).ToDictionary(x => x, x => x);
MeasureTimeAndMemory("Dictionary Remove", () =>
{
for (int i = 0; i < removeCount; i++)
{
int valueToSearch = random.Next(0, removeCount - i);
dictionary.Remove(valueToSearch);
}
});
// OrderedDictionary(PhysicalDeletion)物理削除
var orderedDictionaryPhysicalDeletion = new OrderedDictionary<int, string>();
foreach (int key in keysArray.Take(removeCount))
{
orderedDictionaryPhysicalDeletion.Add(key, $"Value_{key}");
}
MeasureTimeAndMemory("OrderedDictionary Remove(PhysicalDeletion)", () =>
{
for (int i = 0; i < removeCount; i++)
{
orderedDictionaryPhysicalDeletion.Remove(0);
}
});
// OrderedDictionary(LogicalDeletion)論理削除
var orderedDictionaryLogicalDeletion = new OrderedDictionary<int, ItemStatus>();
foreach (int key in keysArray)
{
orderedDictionaryLogicalDeletion.Add(key, new ItemStatus { Value = $"Value_{key}", IsDeleted = false });
}
MeasureTimeAndMemory("OrderedDictionary Remove(LogicalDeletion)", () =>
{
for (int i = 0; i < removeCount; i++)
{
orderedDictionaryLogicalDeletion[i].IsDeleted = true; // フラグを直接変更
}
});
// OrderedDictionary(Clear)
var orderedDictionaryClear = new OrderedDictionary<int, string>();
foreach (int key in keysArray.Take(removeCount))
{
orderedDictionaryClear.Add(key, $"Value_{key}");
}
MeasureTimeAndMemory("OrderedDictionary Clear", () =>
{
orderedDictionaryClear.Clear();
});
}
catch (Exception ex)
{
LogError(ex, nameof(RemoveComparison));
throw;
}
}
/// <summary>
/// OrderedDictionary[i]の内部的な線形探索の処理時間を検証(うまくいかなかった)
/// </summary>
/// <param name="addCount"></param>
private void MeasureOrderedDictionaryAccessProc(int addCount = 10000000)
{
try
{
// 事前準備: 重複しないキーを事前生成し、配列に変換
HashSet<int> uniqueKeys = new HashSet<int>();
for (int i = 0; i < addCount; i++)
{
uniqueKeys.Add(i);
}
int[] keysArray = uniqueKeys.ToArray(); // 配列に変換
// 検索に使用するデータ
var orderedDictionary = new OrderedDictionary<int, int>();
foreach (int key in keysArray)
{
orderedDictionary.Add(key, key);
}
int step = 10000; // 計測間隔
var results = new List<MeasurementResult>();
for (int i = 0; i < addCount; i += step)
{
var stopwatch = new Stopwatch();
stopwatch.Start();
// 1回の計測で10000回繰り返しアクセス
for (int j = 0; j < step; j++)
{
var value = orderedDictionary[i + j];
}
stopwatch.Stop();
double totalElapsedTimeMs = stopwatch.Elapsed.TotalMilliseconds; // 合計時間
double averageTimePerAccess = totalElapsedTimeMs / step; // 1回あたりの平均時間
results.Add(new MeasurementResult() { Count = i.ToString(), AverageTimeMs = averageTimePerAccess.ToString("F10") });
}
dicOrderedDictionaryAccessList["OrderedDictionary"]
.AddRange(results);
}
catch (Exception ex)
{
LogError(ex, nameof(MeasureOrderedDictionaryAccessProc));
throw;
}
}
/// <summary>
/// 処理の開始、および速度測定とメモリ使用量測定
/// </summary>
/// <param name="operationName"></param>
/// <param name="action"></param>
private void MeasureTimeAndMemory(string operationName, Action action)
{
try
{
var stopwatch = new Stopwatch();
long beforeMemory = GC.GetTotalMemory(true); // 実行前のメモリ使用量を取得
stopwatch.Start();
action();
stopwatch.Stop();
long afterMemory = GC.GetTotalMemory(false); // 実行後のメモリ使用量を取得
var result = ((long)loopCnt, stopwatch.ElapsedMilliseconds, afterMemory - beforeMemory);
results[operationName].Add(result);
}
catch (Exception ex)
{
LogError(ex, nameof(MeasureTimeAndMemory));
throw;
}
}
/// <summary>
/// 測定結果出力
/// </summary>
private void PrintResults()
{
try
{
System.Diagnostics.Debug.WriteLine($"各配列やコレクションの平均(初回処理は集計から除外)");
System.Diagnostics.Debug.WriteLine($"名前\\t平均時間 ms\\t平均メモリ使用量 KB");
foreach (var entry in results)
{
if (entry.Value.Count > 1)
{
entry.Value.RemoveAt(0);// 初回は除外
}
string name = entry.Key;
var times = entry.Value.Select(x => x.TimeMs).ToArray();
var memories = entry.Value.Select(x => x.MemoryBytes).ToArray();
long avgTime = (long)times.Average();
double avgMemory = memories.Average();
System.Diagnostics.Debug.WriteLine($"{name}\\t{avgTime} ms\\t\\t{avgMemory:F1} BYTE");
}
// OrderedDictionary[i]の内部的な線形探索の処理時間検証結果出力
foreach (var entry in dicOrderedDictionaryAccessList)
{
string fileName = $"{entry.Key}_{DateTime.Now:yyyyMMdd_HHmmss}.csv";
string downloadsFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Downloads");
string fullPath = Path.Combine(downloadsFolder, fileName);
if (!Directory.Exists(downloadsFolder))
{
Directory.CreateDirectory(downloadsFolder);
}
using (var writer = new StreamWriter(fullPath))
{
writer.WriteLine("Count,AverageTime(ms)");
foreach (var item in entry.Value)
{
writer.WriteLine($"{item.Count},{item.AverageTimeMs:F4}");
}
}
}
}
catch (Exception ex)
{
LogError(ex, nameof(PrintResults));
throw;
}
}
/// <summary>
/// エラーログ出力メソッド
/// </summary>
/// <param name="ex"></param>
/// <param name="methodName"></param>
private void LogError(Exception ex, string methodName)
{
System.Diagnostics.Debug.WriteLine($"[Error in {methodName}] {ex.Message}");
}
}
/// <summary>
/// 論理削除フラグを持つクラス
/// </summary>
public class ItemStatus
{
public string Value { get; set; } // 値
public bool IsDeleted { get; set; } // 論理削除フラグ
}
/// <summary>
/// シーケンシャルアクセス検証用
/// </summary>
public class MeasurementResult
{
public string Count { get; set; }
public string AverageTimeMs { get; set; }
}
}