Я сделал свою домашнюю работу и нашел многократные заверения в том, что не имеет никакого значения в производительности независимо от того, объявляете ли вы свои переменные внутри или вне цикла for и фактически компилируете тот же MSIL. Но я все-таки возился с ним и обнаружил, что перемещение деклараций переменных внутри цикла действительно вызывает значительное и последовательное увеличение производительности.Могут ли переменные, объявленные внутри цикла for, влиять на производительность цикла?
Я написал небольшой тестовый класс консоли для измерения этого эффекта. Я инициализирую статический массив double[]
, и два метода выполняют на нем операции цикла, записывая результаты в статический массив double[]
. Первоначально моими методами были те, с которыми я заметил разницу, а именно вычисление величины комплексного числа. Запустив их для элементов массив длиной 1000000 в 100 раз, я получил последовательно меньшее время выполнения для того, в котором переменные (6 double
) находились внутри цикла: например, 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)
в 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 сейчас, а результаты почти идентичны для двух методов. Приносим извинения за то, что вы опубликовали такое вводящее в заблуждение наблюдение.
Хорошее исследование, вы наверняка заработаете над головой. – NicoRiff
@NicoRiff: Действительно, это очень хорошо написанный вопрос. (К сожалению, хотя я думаю, что ответ тривиален.) – Bathsheba
Я не могу ждать ответа @JonSkeet на этот вопрос – NicoRiff