Объявить функцию без побочных эффектов
C ++ не является функциональным языком программирования. Функции, вызывающие побочные эффекты, являются нормой для функций, регулярно изменяющих переменные-члены. Если компилятор не видит тела функции, он предполагает, что функция вызывает побочные эффекты. Это может привести к упущению возможностей оптимизации.
Упущенная возможность для оптимизации
Рассмотрим следующий код. Здесь функция main (почему-то) дважды вычисляет квадрат общего числа аргументов и возвращает сумму квадратов.
int square(int x); int main(int argc, char *argv[]) { auto x = square(argc); auto y = square(argc); return x+y; }
Здесь компилятор не видит тела функции square
, поэтому он предполагает, что функция может вызывать побочные эффекты. В следующем ассемблерном коде показаны два вызова функции добавления.
Компилятор не смог оптимизировать второй вызов, так как он не может предположить, что квадратная функция не имеет побочных эффектов.
main: push rbp mov ebp, edi push rbx sub rsp, 8 call square(int) mov edi, ebp mov ebx, eax call square(int) add rsp, 8 add eax, ebx pop rbx pop rbp ret
Оптимизация с атрибутом чистой функции
Если бы существовал способ сообщить компилятору, что функция является чистой (т.е. без побочных эффектов), компилятор мог бы использовать эту информацию для оптимизации кода.
Если мы просто добавим атрибут [[gnu::pure]]
к функции square, компилятор будет считать, что функция не вызывает побочных эффектов.
Когда функция объявляется чистой, компилятор также гарантирует, что:
- Функция не обращается к глобальным переменным и не обновляет их.
- Возвращаемое значение функции просто зависит от входных параметров.
[[gnu::pure]] int square(int x); int main(int argc, char *argv[]) { auto x = square(argc); auto y = square(argc); return x+y; }
Это приводит к значительной оптимизации, и компилятор может просто вызвать функцию square
один раз и просто удвоить результат - add eax, eax
. Чистое объявление гарантирует, что несколько вызовов функции с одинаковыми параметрами всегда будут приводить к одному и тому же возвращаемому значению.
main: sub rsp, 8 call square(int) add rsp, 8 add eax, eax ret
На момент написания этой статьи атрибут [[gnu::pure]]
поддерживается компиляторами GCC и Clang.
Учить больше
Еженедельное видео Джейсона Тернера по C ++, которое легло в основу этого сообщения.
Ссылки на обозреватель компилятора для примера, представленного выше: