У меня есть приложение WPF, которое много подходит для больших наборов данных, и в настоящее время он использует С# и LINQ для соответствия POCOs и отображения в сетке. Поскольку количество включенных наборов данных увеличилось, а объем данных увеличился, меня попросили рассмотреть проблемы с производительностью. Одно из предположений, которые я тестировал сегодня вечером, заключалось в том, есть ли существенная разница, если мы должны конвертировать часть кода в С++ 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; }
}
Одна вещь, которую я заметил, заключалась в том, что в среднем для простого теста создания списка, а затем для создания под-списка всех объектов с четным идентификатором код С++/CLI был примерно на 8% медленнее на моей машине разработки (64-битная Win8, 8 ГБ оперативной памяти). Например, случай создания и фильтрации объекта С# занимает ~ 7 секунд, тогда как код С++/CLI в среднем составлял ~ 8 секунд. Любопытно, почему это было бы, я использовал ILDASM, чтобы увидеть, что происходит под обложками, и был удивлен, увидев, что код С++/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);
}
Класс С# выше. Класс С++ определяется как:
public ref class CppClassWithMembers
{
public:
long long Id;
System::DateTime CreateDateTime;
System::String^ Name;
CppClassWithMembers()
{
this->CreateDateTime = System::DateTime::Now;
}
};
Когда я извлекаю IL для обоих конструкторов классов, это то, что я получаю. Сначала С#:
.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
И затем С++:
.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
Мой вопрос: почему код С++ использует локальный для хранения значения вызова из DateTime.Now
? Существует ли для С++ причина для этого, или это только то, как они решили реализовать компилятор?
Я уже знаю, что есть много других способов улучшить производительность, и я знаю, что я довольно далеко от кроличьей дыры, как есть, но мне было любопытно узнать, может ли кто-нибудь пролить свет на это. Прошло много времени с тех пор, как я сделал С++, и с появлением Windows 8, и Microsoft вновь сосредоточился на С++, я подумал, что было бы хорошо обновить, и это также было частью моей мотивации для этого упражнения, но разница между двумя выходами компилятора попала мне в глаза.