Страницы

Поиск по вопросам

среда, 24 апреля 2019 г.

Преинкремент и постинкремент

Задали вопрос на учебе, что больше потребляет ресурсов ++i или i++. Самостоятельно инфо не нашёл, прошу подсказать где искать.


Ответ

Согласно спецификации языка, разница между пре- и постинкрементом состоит в том, что при вычислении выражения значением результат является в одном случае старое, а в другом — новое значение инкрементируемой переменной. В случае простого применения в форме x++; (например, такое часто встречается в циклах) возвращаемое значение не используется, и смысл обеих форм строго одинаков, так что они компилируются в одинаковый объектный код.
Даже если оператор инкремента переопределён, разницы нет, т. к. (в отличие от C++) нет возможности иметь различные определения для пре- и постинкрементов.
Откуда растут ноги у этого вопроса? А вот откуда. В древние времена были наивные компиляторы языка C (и, возможно, C++), которые не умели оптимизировать код. И если они компилировали прямолинейно, то код получался таким:
// x++ int temp = x; x = x + 1; // результат = temp; но он не нужен, так что он отбрасывается
и
// ++x; x = x + 1; // результат = x; но он не нужен, так что он отбрасывается
Ваш преподаватель, вероятно, слыхал об этом, и ждёт от вас ответа о том, что необходимо ручной оптимизацией убрать ненужную временную переменную. Он неправ, отстал от реальности на вечность и тянет вас в эпоху трилобитов.
В реальности последние как минимум 10 лет компиляторы умеют делать оптимизации намного лучше людей, и в частности прекрасно умеют выбрасывать никому не нужные временные переменные. Так что разницы в сгенерированном коде просто не будет, что можно видеть даже для C++ здесь.* (Если вы, конечно, не отключите оптимизации.)

Теперь с точки зрения промышленной практики. Если вам нужно ускорить выполнение вашей программы, нанооптимизации наподобие использования преинкремента вместо постинкремента — даже если бы они и давали выигрыш в один такт процессора — никогда не являются нужным средством. Оптимизируйте ваши алгоритмы и структуры данных, переходите от квадратичных алгоритмов к линейным и логарифмическим. Не экономьте копейки, это не окупается.

*Например, из таких двух функций:
volatile int x; // угадайте, что будет, если убрать volatile :-P
int pre(int num) { for (int i = 0; i < num; ++i) x = i; }
int post(int num) { for (int i = 0; i < num; i++) x = i; }
gcc 7.2 с ключом -O3 производит
pre(int): test edi, edi jle .L2 xor eax, eax .L3: mov DWORD PTR x[rip], eax add eax, 1 cmp edi, eax jne .L3 .L2: rep ret post(int): test edi, edi jle .L6 xor eax, eax .L8: mov DWORD PTR x[rip], eax add eax, 1 cmp edi, eax jne .L8 .L6: xor eax, eax ret
А с ключом -Os вообще
pre(int): xor eax, eax .L3: cmp eax, edi jge .L2 mov DWORD PTR x[rip], eax inc eax jmp .L3 .L2: ret post(int): jmp pre(int)

Комментариев нет:

Отправить комментарий