大規模なデータセット間で多くの照合を行うWPFアプリケーションがあり、現在、C#とLINQを使用してPOCOを照合し、グリッドに表示しています。含まれるデータセットの数が増え、データの量が増えるにつれて、パフォーマンスの問題を調べるように求められました。今晩私がテストしていた仮定の1つは、コードの一部をC++CLIに変換する場合に実質的な違いがあるかどうかでした。List<>
そのために、5,000,000個のアイテムを作成し、簡単なマッチングを行う簡単なテストを作成しました。基本的なオブジェクト構造は次のとおりです。
public class CsClassWithProps
{
public CsClassWithProps()
{
CreateDate = DateTime.Now;
}
public long Id { get; set; }
public string Name { get; set; }
public DateTime CreateDate { get; set; }
}
私が気づいたことの1つは、平均して、リストを作成し、偶数IDを持つすべてのオブジェクトのサブリストを作成するという単純なテストでは、C ++ / CLIコードが開発マシン(64ビットWin8)で約8%遅いことでした。 、8GBのRAM)。たとえば、C#オブジェクトが作成およびフィルタリングされる場合は最大7秒かかりましたが、C ++/CLIコードは平均で最大8秒かかりました。なぜそうなるのか知りたくて、ILDASMを使用して、内部で何が起こっているのかを確認しました。C++/CLIコードにコンストラクターに余分なステップがあることに驚きました。最初にテストコード:
static void CreateCppObjectWithMembers()
{
List<CppClassWithMembers> results = new List<CppClassWithMembers>();
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < Iterations; i++)
{
results.Add(new CppClassWithMembers() { Id = i, Name = string.Format("Name {0}", i) });
}
var halfResults = results.Where(x => x.Id % 2 == 0).ToList();
sw.Stop();
Console.WriteLine("Took {0} total seconds to execute", sw.Elapsed.TotalSeconds);
}
C#クラスは上にあります。C++クラスは次のように定義されます。
public ref class CppClassWithMembers
{
public:
long long Id;
System::DateTime CreateDateTime;
System::String^ Name;
CppClassWithMembers()
{
this->CreateDateTime = System::DateTime::Now;
}
};
両方のクラスのコンストラクターのILを抽出すると、これが得られます。最初にC#:
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 21 (0x15)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: nop
IL_0008: ldarg.0
IL_0009: call valuetype [mscorlib]System.DateTime [mscorlib]System.DateTime::get_Now()
IL_000e: stfld valuetype [mscorlib]System.DateTime CsLibWithMembers.CsClassWithMembers::CreateDate
IL_0013: nop
IL_0014: ret
} // end of method CsClassWithMembers::.ctor
そしてC++:
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 25 (0x19)
.maxstack 2
.locals ([0] valuetype [mscorlib]System.DateTime V_0)
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: call valuetype [mscorlib]System.DateTime [mscorlib]System.DateTime::get_Now()
IL_000b: stloc.0
IL_000c: ldarg.0
IL_000d: ldloc.0
IL_000e: box [mscorlib]System.DateTime
IL_0013: stfld class [mscorlib]System.ValueType modopt([mscorlib]System.DateTime) modopt([mscorlib]System.Runtime.CompilerServices.IsBoxed) CppLibWithMembers.CppClassWithMembers::CreateDateTime
IL_0018: ret
} // end of method CppClassWithMembers::.ctor
私の質問は、C ++コードがローカルを使用してからの呼び出しの値を格納するのはなぜDateTime.Now
ですか?これが発生するC++固有の理由はありますか、それともコンパイラの実装を選択した方法ですか?
パフォーマンスを向上させる方法は他にもたくさんあることはすでに知っています。私はウサギの穴からかなり離れていることを知っていますが、誰かがこれに光を当てることができるかどうか知りたいと思いました。私がC++を実行してから長い時間が経ち、Windows 8の登場と、MicrosoftのC ++への新たな焦点により、更新するのは良いことだと思いました。それもこの演習の動機の一部でしたが、 2つのコンパイラ出力の違いが私の目に留まりました。