Поскольку этот вопрос касается оператора приращения и разницы в скорости с префиксной / постфиксной нотацией, я опишу вопрос очень тщательно, чтобы Эрик Липперт не обнаружил его и не разозлил меня!
(дополнительная информация и подробности о почему я спрашиваю, можно найти на http://www.codeproject.com/KB/cs/FastLessCSharpIteration.aspx?msg=3899456#xx3899456xx/ )
У меня есть четыре следующих фрагмента кода : -
(1) Отдельно, Префикс:
for (var j = 0; j != jmax;) { total += intArray[j]; ++j; }
(2) Отдельно, Постфикс:
for (var j = 0; j != jmax;) { total += intArray[j]; j++; }
(3) Индексатор, Постфикс:
for (var j = 0; j != jmax;) { total += intArray[j++]; }
(4) Индексатор, Префикс:
for (var j = -1; j != last;) { total += intArray[++j]; } // last = jmax - 1
То, что я пытался сделать, это доказать / опровергнуть, есть ли разница в производительности между префиксной и постфиксной нотацией в этом контексте (т. Е. Локальная переменная, не изменяемая, не изменяемая из другого потока и т. д.), и если да, то почему.
Тестирование скорости показало, что:
(1) и (2) работают с одинаковой скоростью.
(3) и (4) работают с одинаковой скоростью.
(3) / (4) на ~ 27% медленнее, чем (1) / (2).
Поэтому я делаю вывод, что нет преимущества в производительности у выбор префиксной нотации над постфиксной нотацией как таковой. Однако когда фактически используется Результат операции , это приводит к более медленному коду, чем если бы его просто выбросили.
Затем я взглянул на сгенерированный IL с помощью Reflector и обнаружил следующее:
Количество байтов IL одинаково во всех случаях.
.maxstack варьируется от 4 до 6, но я считаю, что он используется только для целей проверки и поэтому не имеет отношения к производительности.
(1) и ( 2) генерировал точно такой же IL, поэтому неудивительно, что время было идентичным. Таким образом, мы можем игнорировать (1).
(3) и (4) сгенерировали очень похожий код - единственное существенное отличие состоит в расположении кода операции дублирования для учета Результат операции . Опять же, нет ничего удивительного в том, что синхронизация идентична.
Итак, я затем сравнил (2) и (3), чтобы выяснить, что может объяснить разницу в скорости:
(2) использует ldloc.0 op дважды (один раз как часть индексатора, а затем позже как часть приращения).
(3) использовал ldloc.0, за которым сразу следовала операция dup.
(и наличие dup
в (3) и (4) нарушает этот шаблон, и поэтому оптимизация не выполняется)
И дополнительно:
Если это правда, то, по крайней мере, для (3), не приведет ли замена dup
другим ldloc.0
к повторному введению этого шаблона?