Я сделал домашнее задание и нашел неоднократные заверения в том, что не имеет никакого значения в производительности независимо от того, объявляете ли вы свои переменные внутри или вне цикла for и фактически компилируете тот же MSIL. Но я все-таки возился с ним и обнаружил, что перемещение деклараций переменных внутри цикла действительно приводит к значительному и стабильному увеличению производительности.
Я написал небольшой тестовый класс консоли для измерения этого эффекта. Я инициализирую статические элементы массива double[]
, а два метода выполняют на нем операции цикла, записывая результаты в статический буфер массива double[]
. Первоначально моими методами были те, с которыми я заметил разницу, а именно вычисление величины комплексного числа. Запустив их для массива элементов длиной 1000000 в 100 раз, я получил последовательно меньшее время выполнения для того, в котором переменные (6 double
variables) находились внутри цикла: например, 32,83 ± 0,64 мс против 43, 24 ± 0,45 мс на пожилой конфигурации с Intel Core 2 Duo @2,66 ГГц. Я попытался выполнить их в другом порядке, но это не повлияло на результаты.
Тогда я понял, что вычисление величины комплексного числа далека от минимального рабочего примера и проверено два гораздо более простых метода:
static void Square1()
{
double x;
for (int i = 0; i < buffer.Length; i++) {
x = items[i];
buffer[i] = x * x;
}
}
static void Square2()
{
for (int i = 0; i < buffer.Length; i++) {
double x;
x = items[i];
buffer[i] = x * x;
}
}
При этом результаты получились иначе: объявление переменной вне цикла показалось более благоприятным: 7.07 ± 0.43 мс для Square1()
v 12.07 ± 0.51 мс для Square2()
.
Я не знаком с ILDASM, но я разобрал эти два метода, и единственная разница, по-видимому, является инициализацией локальных переменных:
.locals init ([0] float64 x,
[1] int32 i,
[2] bool CS$4$0000)
in Square1()
v
.locals init ([0] int32 i,
[1] float64 x,
[2] bool CS$4$0000)
в Square2()
. В соответствии с этим, то, что stloc.1
в одном, это stloc.0
в другом, и наоборот. В более длинном сложном вычислении величины MSIL кодирует даже размер кода, и я видел stloc.s i
в коде внешней декларации, где во внутреннем коде декларации был stloc.0
.
Итак, как это может быть? Я что-то забываю или это реальный эффект? Если это так, это может существенно повлиять на производительность длинных циклов, поэтому я думаю, что это заслуживает обсуждения.
Ваши мысли очень ценятся.
EDIT: Единственное, что я забыл, это проверить его на нескольких компьютерах перед публикацией. Я запустил его на i5 сейчас, а результаты почти идентичны для двух методов. Извиняюсь за то, что вы опубликовали такое вводящее в заблуждение наблюдение.