我有一个 WPF 应用程序,它可以跨大型数据集进行大量匹配,目前它使用 C# 和 LINQ 来匹配 POCO 并在网格中显示。随着包含的数据集数量的增加和数据量的增加,我被要求研究性能问题。我今晚测试的一个假设是,如果我们将一些代码转换为 C++ CLI,是否存在实质性差异。为此,我编写了一个简单的测试来创建一个List<>
包含 5,000,000 个项目的 a,然后进行一些简单的匹配。基本的对象结构是:
public class CsClassWithProps
{
public CsClassWithProps()
{
CreateDate = DateTime.Now;
}
public long Id { get; set; }
public string Name { get; set; }
public DateTime CreateDate { get; set; }
}
我注意到的一件事是,平均而言,对于创建列表然后构建具有偶数 ID 的所有对象的子列表的简单测试,C++/CLI 代码在我的开发机器(64 位 Win8 , 8GB 内存)。例如,创建和过滤 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++ 代码使用本地来存储调用的值 from DateTime.Now
?发生这种情况是否有特定于 C++ 的原因,或者仅仅是他们选择实现编译器的方式?
我已经知道还有许多其他方法可以提高性能,而且我知道我离兔子洞还很远,但我很想知道是否有人可以对此有所了解。自从我做 C++ 以来已经很久了,随着 Windows 8 的出现,以及微软对 C++ 的重新关注,我认为刷新一下会很好,这也是我进行这个练习的动机的一部分,但是两个编译器输出之间的差异引起了我的注意。