Хотя это полностью микрооптимизация, которая никогда не станет узким местом в производительности. Интересно, что они на самом деле разные, интересно, когда вы извлекаете методы обоих циклов с VS2010, я получаю следующее:
private static String forLoop(ref Int64 i)
{
String x;
for (; FIVE_BN > i; i++)
x = null; //Replace with only ; in both loops and the for loop is faster
return x;
}
private static void whileloop(ref String x, ref Int64 i)
{
while (FIVE_BN > i++)
x = null; //Replace with only ; in both loops and the for loop is faster
}
И это довольно интересно ... это показывает, что две функции действительно разные.
теперь, когда мы заменяем логику в цикле на ;
, вместо этого мы получаем следующие извлеченные методы:
private static Int64 forLoopShort(Int64 i)
{
for (; FIVE_BN > i; i++)
; //Replace with only ; in both loops and the for loop is faster
return i;
}
private static Int64 whileLoopShort(Int64 i)
{
while (FIVE_BN > i++)
; //Replace with only ; in both loops and the for loop is faster
return i;
}
Что указывает, почему циклы работают в основном с этой конфигурацией.
Чтобы понять, чем они отличаются, когда встроены (а не извлекаются в методы), нам нужно посмотреть, как выглядит оптимизированный код CLR (хотя оптимизатор может фактически устранить любые существенные различия между двумя функциями). для последующего редактирования.
Edit:
CIL выявляет различия:
Цикл For имеет .maxstack 2
, но цикл while имеет .maxstack 4
, в противном случае есть небольшая разница в порядке операций из-за того, что приращение для while
происходит в начале цикла, но for
операция происходит в конце цикла (измените содержимое цикла на Console.WriteLine(i)
и увидите, что цикл While будет печататься с 1, а цикл For будет печататься с 0 (оба выполняют одно и то же число итераций цикла, хотя ).
Когда содержимое цикла равно ;
, оба цикла в CIL на 2 строки короче, а следующие строки удалены (для обоих циклов):
IL_0006: ldnull
IL_0007: stloc.0
Однако когда мы встраиваем релиз, код сильно отличается:
Разница между x = null;
и ;
не имеет значения ни для одного из циклов, поскольку оптимизатор заметил, что значение никогда не меняется на ненулевое.
Разница между оптимизированными циклами for и while заключается в следующем:
CIL for
петля:
IL_0000: ldc.i4.0
IL_0001: conv.i8
IL_0002: stloc.0
IL_0003: br.s IL_000a
IL_0005: ldloc.0
IL_0006: ldc.i4.1
IL_0007: conv.i8
IL_0008: add
IL_0009: stloc.0
IL_000a: ldc.i8 0x12a05f200
IL_0013: ldloc.0
IL_0014: bgt.s IL_0005
IL_0016: ret
И CIL while
Петля:
IL_0000: ldc.i4.0
IL_0001: conv.i8
IL_0002: stloc.0
IL_0003: ldc.i8 0x12a05f200
IL_000c: ldloc.0
IL_000d: dup
IL_000e: ldc.i4.1
IL_000f: conv.i8
IL_0010: add
IL_0011: stloc.0
IL_0012: bgt.s IL_0003
IL_0014: ret
Итак, мы видим, что оптимизированный цикл while быстрее, чем цикл for на 2 операции, однако он использует больше стекового пространства.
Разница между этими двумя, по-видимому, полностью связана с разницей в том, где i++
происходит.
Действительно, это подтверждается созданием нового метода:
private static void forLoopVeryShort()
{
string x;
Int64 i = 0;
for (; FIVE_BN > i++;)
; //Replace with only ; in both loops and the for loop is faster
}
Код CIL для этого метода for
при сборке (в версии или отладке) идентичен коду цикла while
.
В этом твоя разница. Циклы for выполняются точно так же, как и циклы while, когда они работают точно так же. Разница, которую вы заметили, полностью связана с выполнением кода в отладочной, а не в выпускной версии, в сочетании с тем, что JIT не всегда так эффективен, как оптимизатор кода выпуска.
Мне понравился этот вопрос, я кое-что узнал из него; Я надеюсь, что и другие тоже. + 1