Компиляторы
February 14, 2021

Сюрпризы оптимизации

Друг прислал намедни интересную картинку (Паша - привет!). На первый взгляд - ситуация более чем странная, ведь код должен быть эквивалентным.

Но давайте посмотрим на этот кусочек кода с точки зрения разработчика компилятора.

Конструкция 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] + ... из-за выделения индекса в отдельную сущность.

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

<зануда-моде>

  • Некоторые компиляторы умеют проверять наличие побочных эффектов;
  • Некоторые компиляторы умеют убирать переменную в подобных ситуациях;
  • При некоторых настройках компиляции можно получить разные цифры производительности;
  • Микробенчмаркинг – неблагодарное занятие во многих ситуациях.

</зануда-моде>