Рассмотрим следующую простую программу:
using System;
using System.Diagnostics;
class Program
{
private static void Main(string[] args)
{
const int size = 10000000;
var array = new string[size];
var str = new string('a', 100);
var sw = Stopwatch.StartNew();
for (int i = 0; i < size; i++)
{
var str2 = new string('a', 100);
//array[i] = str2; // This is slow
array[i] = str; // This is fast
}
sw.Stop();
Console.WriteLine("Took " + sw.ElapsedMilliseconds + "ms.");
}
}
Если я запустил это, он будет относительно быстрым. Если я раскомментирую "медленную" линию и закомментирую "быструю" линию, она будет более чем на 5 раз медленнее. Обратите внимание, что в обеих ситуациях он инициализирует строку "str2" внутри цикла. Это не оптимизируется в любом случае (это можно проверить, посмотрев на IL или разборку).
В обоих случаях код, похоже, будет работать с одинаковым объемом работы. Ему необходимо выделить/инициализировать строку, а затем назначить ссылку на местоположение массива. Единственная разница заключается в том, является ли эта ссылка локальной var "str" или "str2" .
Почему он делает такую большую разницу в производительности, назначая ссылку на "str" или "str2" ?
Если мы посмотрим на разборку, есть разница:
(fast)
var str2 = new string('a', 100);
0000008e mov r8d,64h
00000094 mov dx,61h
00000098 xor ecx,ecx
0000009a call 000000005E393928
0000009f mov qword ptr [rsp+58h],rax
000000a4 nop
(slow)
var str2 = new string('a', 100);
00000085 mov r8d,64h
0000008b mov dx,61h
0000008f xor ecx,ecx
00000091 call 000000005E383838
00000096 mov qword ptr [rsp+58h],rax
0000009b mov rax,qword ptr [rsp+58h]
000000a0 mov qword ptr [rsp+38h],rax
В "медленной" версии есть две дополнительные операции "mov", где "быстрая" версия имеет только "nop".
Может ли кто-нибудь объяснить, что здесь происходит? Трудно понять, как две дополнительные операции mov могут привести к замедлению > 5x, тем более, что я ожидаю, что большая часть времени должна быть потрачена на инициализацию строки. Спасибо за любые идеи.