Сюрпризы оптимизации
Друг прислал намедни интересную картинку (Паша - привет!). На первый взгляд - ситуация более чем странная, ведь код должен быть эквивалентным.
Но давайте посмотрим на этот кусочек кода с точки зрения разработчика компилятора.
Конструкция a[i] = a[i] + i + x
представляет собой присваивание выражения a[i] + i + x
в место в памяти, адресуемого выражением a[i]
.
Когда в левой части присваивания и в правой части присваивания стоит выражение a[i]
, оптимизатор может довольно изящно исправить это выражение. Например, используя длинные (SIMD) инструкции или используя какие-нибудь другие техники оптимизации. Просто потому, что модификации переменной локальны, т.е. нет необходимости ходить в другую область памяти и нет побочных эффектов.
Если мы немного изменим код, обратившись к массиву несколько по-другому, то смысл присваивания и действия, выполняемые компилятором заметно изменятся:
a[f(i)] = a[f(i)] + i + x
Хотя ожидаемая семантика выражение с точки зрения обращения к массиву не поменялась, с точки зрения компилятора все изменилось. Нужно дополнить операцию индексации вызовом функции. В этом все еще нет особой разницы по сравнению с предыдущим примером, за одной существенной деталью – у функции могут быть побочные эффекты. И если в развернутой записи (как в FastLoop()) программист сам отвечает за второй вызов f(i)
, то в случае краткой записи (как в SlowLoop()) компилятор не может дать гарантию, что у функции нет побочных эффектов, поэтому преобразует выражение так, чтобы гарантировать целостность всего выражения: a[f(i)] += i + x
преобразуется в
var temp = f(i) a[temp] = a[temp] + i + x
Даже если у нас нет явного вызова функции, а есть только индексация, выражение a[i] += ...
все-равно отличается от выражения a[i] = a[i] + ...
из-за выделения индекса в отдельную сущность.
Это, конечно, обобщенная схема. Детали реализации и причины подобного поведения необходимо выяснять в каждом конкретном случае.
<зануда-моде>
- Некоторые компиляторы умеют проверять наличие побочных эффектов;
- Некоторые компиляторы умеют убирать переменную в подобных ситуациях;
- При некоторых настройках компиляции можно получить разные цифры производительности;
- Микробенчмаркинг – неблагодарное занятие во многих ситуациях.
</зануда-моде>