Миф о программировании? Скорость увеличения и уменьшения до и после

Итак, я ставил отметку о тесте C ++, пока наш старший программист отсутствовал, и самый первый вопрос был примерно таким:

Какой метод будет работать быстрее без оптимизации компилятора?

Метод 1

for(int i = 0; i < 100000; i++) { }

Способ 2

for(int i = 100000; i >= 0; --i) { }

Теперь, если вы похожи на меня и почти всех других разработчиков, с которыми я встречался (за исключением одного), вас заставили поверить, что операторы предварительного приращения и предварительного декремента (++i и --i) по своей сути медленнее, чем пост операторы инкремента и постдекремента. Поэтому, естественно, я выбрал метод 2.

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

Подумав, что это какая-то тщательно продуманная шутка, я решил доказать, что он неправ, и придумал следующее:

#define ITERATIONS  (1000000000)
clock_t start = clock();
for(int64_t i = 0; i < ITERATIONS; i++)
{}
auto first_clock = clock() - start;

start = clock();
for(int64_t j = ITERATIONS; j > 0; --j)
{}
auto last_clock = clock() - start;

К моему большому удивлению:

first_clock = 25333
last_clock =  25277

Во всех смыслах и целях эти методы равны!

Итак, откуда появился этот миф? Было ли это правдой когда-то, а в новых компиляторах стало ложью? Жду интересного обсуждения. :)


person Colin Basnett    schedule 26.08.2013    source источник
comment
Разве вы не хотели сравнивать с i++ ??   -  person πάντα ῥεῖ    schedule 27.08.2013
comment
Я очень разочарован, что ваш компилятор не выбросил сразу оба цикла.   -  person WhozCraig    schedule 27.08.2013
comment
Ошибка копирования и вставки @ g-makulik, исправлено   -  person Colin Basnett    schedule 27.08.2013
comment
Существует разница в производительности при использовании с другими типами, например. с каким-то big_integer. По этой причине в C ++ принято отдавать предпочтение предварительным инкрементам.   -  person nosid    schedule 27.08.2013
comment
Разница в скорости не с целыми числами, а с итераторами.   -  person IdeaHat    schedule 27.08.2013
comment
@WhozCraig Работает в режиме отладки, поэтому никаких оптимизаций.   -  person Colin Basnett    schedule 27.08.2013
comment
IMHO, вам следует взглянуть на сгенерированный код сборки, если вы действительно хотите знать, что происходит и какой из них быстрее. Измерение производительности - неправильный способ, потому что некоторая оптимизация компилятора или даже другой процесс могут повлиять на результаты.   -  person Hugo Corrá    schedule 27.08.2013
comment
@cmbasnett, затем выровняйте игровое поле.   -  person WhozCraig    schedule 27.08.2013
comment
@nosid и MadScienceDreams: даже с другими типами и итераторами неплохой компилятор увидит, что вы не используете временное, и избавится от него, делая разницу в производительности между двумя операторами практически нулевой. Вам следует использовать оператор, который имеет смысл для того, что вы делаете.   -  person Zac Howland    schedule 27.08.2013
comment
На многих процессорах (с оптимизацией) сравнение с нулем может быть быстрее, чем с произвольным значением; сравнение можно комбинировать с декрементом. Публикация и предварительное приращение здесь не имеют большого значения, и нет никакого способа рассуждать о скорости неоптимизированного кода.   -  person Mike Seymour    schedule 27.08.2013
comment
@cmbasnett, пожалуйста, замените «медленнее» на «быстрее» в том, что вас заставили поверить, что операторы предварительного инкремента и предварительного декремента (++ i и --i) по своей сути медленнее, чем операторы постинкремента и постдекремента . Поэтому, естественно, я выбрал Метод 2 ».   -  person    schedule 27.08.2013
comment
@WhozCraig Post / pre не имеет значения, и, вероятно, существует именно для того, чтобы заставить людей думать, что это то, о чем идет речь. Фактически, речь идет о тестировании против ненулевого значения для конца цикла против тестирования против изменения знака. Это вопрос для собеседования, и он предназначен для того, чтобы показать, насколько внимательно программист изучает вопрос.   -  person kfsone    schedule 27.08.2013
comment
@ZacHowland: Это действительно зависит от компилятора. Clang 3.4 удается устранить временное в некоторых ситуациях, но GCC 4.8 почти никогда не справляется.   -  person nosid    schedule 27.08.2013
comment
@kfsone Тогда хороший вопрос, потому что когда все играют по одним и тем же ограничениям (оба / ни одно тестирование на ноль и т.д.), результаты довольно очевидны. Похоже, голос опыта на вашей стороне. Должен записать это для моего файла iview.   -  person WhozCraig    schedule 27.08.2013
comment
Следуя за kfsone, исследуйте несколько наборов инструкций, один / некоторые имеют декремент и переходят, если ноль, одна инструкция. У многих есть математические операторы (сложение / вычитание), которые изменяют флаги, и есть нулевой флаг, поэтому вы можете выполнить вычитание с последующим прыжком, если ноль, где при подсчете вы увеличиваете сравнение с некоторым числом, а затем переходите, если больше или равно или ноль или что-то еще, и это сравнение может включать загрузку регистра с этой константой, которая требует еще одной инструкции / времени. Таким образом, для некоторых наборов инструкций нет реальной разницы, но для других разница есть.   -  person old_timer    schedule 27.08.2013
comment
Предварительное приращение обычно выполняется быстрее для нецелочисленных типов данных (например, std::vector <...>::iterator). Кроме того, я никогда не видел никакого преимущества в производительности для интегральных (т. Е. int) типов данных.   -  person Andon M. Coleman    schedule 27.08.2013
comment
Вопрос C ++, а не вопрос C ++.   -  person Oktalist    schedule 20.11.2014