Цикл намного быстрее цикла for, чем цикл while, тогда, когда оператор изменяется, цикл while работает быстрее.Что просходит? - PullRequest
4 голосов
/ 09 января 2012

Из интереса я проверял, есть ли какая-либо разница между циклом for и циклом while, делающим одно и то же.Что заставляет цикл while работать на моем компьютере примерно на 2-2,5 секунды дольше (AMD Phenom II X6 1090T @ 3,20 ГГц), чем цикл for?Разве они не делают то же самое?Получаете ли вы аналогичные результаты?

Кроме того, когда я заменяю оператор x = null; из циклов просто пустым оператором, цикл while будет значительно быстрее.Что здесь происходит?

Конечно, количество итераций довольно велико, но разве разница не очень значительна?

static void Main(string[] args)
{
    String x;
    const Int64 FIVE_BN = 5000000000;
    Int64 i = 0;

    DateTime start = DateTime.Now;
    for (; FIVE_BN > i; i++)
        x = null; //Replace with only ; in both loops and the for loop is faster
    Console.Out.WriteLine(FIVE_BN.ToString() + " times (for): " + (DateTime.Now - start));

    i = 0;

    start = DateTime.Now;
    while(FIVE_BN > i++)
        x = null; //Replace with only ; in both loops and the for loop is faster
    Console.Out.WriteLine(FIVE_BN.ToString() + " times (while): " + (DateTime.Now - start));

    Console.Read();
    return;
}

Ответы [ 3 ]

11 голосов
/ 09 января 2012

Хотя это полностью микрооптимизация, которая никогда не станет узким местом в производительности. Интересно, что они на самом деле разные, интересно, когда вы извлекаете методы обоих циклов с 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

8 голосов
/ 09 января 2012

Возможно, вы захотите отключить cordbg (и тщательно включить все JIT-оптимизации), чтобы взглянуть на сгенерированный нативный код, чтобы точно понять, почему это происходит ... но зачем? В реальном коде разница не будет значительной, потому что вы будете выполнять реальную работу в цикле.

Микрооптимизация совершенно нереального кода не является плодотворным занятием, ИМО. Даже микрооптимизирующий реальный код обычно обычно не приносит результатов, если только вы не убедились, что это узкое место.

3 голосов
/ 09 января 2012

Для информации - невозможно воспроизвести:

с присвоением:

5000000000 times (for): 00:00:15.0488608
5000000000 times (while): 00:00:12.7107270

только с ;

5000000000 times (for): 00:00:15.0558611
5000000000 times (while): 00:00:12.7297281

(здесь я работаю в режиме выпуска, вне отладчика и т. Д.)

возможно , что это зависит от платформы (я использую 4.0.30319.488, x64) или от процессора (я использую Intel i7 4x2.67 ГГц (плюс HT)), но мое первое предположение было бы как тест выполняется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...