Вызовы функций с оптимизацией констант в C / Stack Overflow

Если у вас есть вызов функции с константами, он не имеет побочных эффектов и не зависит ни от чего, например:

int foo (int a, int b)
{
вернуть a + b;
}

Функция встроена? Или, возможно, функция оценивается во время компиляции, а результат этой оценки вставляется вместо вызова функции?

4

Решение

Я попытался скомпилировать это с помощью довольно старого gcc —

#include <iostream>

int foo(int a,int b)
{
return a+b;
}int main()
{
std::cout << foo(100, 123) ;
}

И главное скомпилировано в это —

LFB1439:
subq    $8, %rsp
.LCFI1:
movl    $223, %esi
movl    $_ZSt4cout, %edi
call    _ZNSolsEi
xorl    %eax, %eax
addq    $8, %rsp
ret

Таким образом, он скомпилировал дополнение во время компиляции, получив 223.

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

7

Другие решения

Не в C ++. Они не будут выполняться во время компиляции просто так — если компилятор волшебным образом не сделает это. Однако это нельзя навязать.

Тем не менее, с C ++ 11, вы можете использовать constexpr чтобы убедиться, что он оценивается во время компиляции, например:

constexpr int get_five() {return 5;}

Таким образом, вы можете переписать вашу функцию как:

constexpr int foo(int a,int b)
{
return a+b;
}

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

Из Википедии:

Если функция или конструктор constexpr вызывается с аргументами, которые
не являются константными выражениями, вызов ведет себя так, как если бы функция была
не constexpr, и полученное значение не является константным выражением.
Аналогично, если выражение в операторе return constexpr
функция не оценивается как постоянное выражение для определенного
вызов, результат не является константным выражением.

Это означает, что foo(1,1) будет постоянным, но:

int i,j;
cin >> i >> j;
foo(i,j) // this is not constant

Ссылка: http://en.wikipedia.org/wiki/C%2B%2B11#constexpr_-_Generalized_constant_expressions

4

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

Даже если такой гарантии нет, вы должны доверять своему компилятору. Они довольно хорошо оптимизируют ваш код. Если вы хотите убедиться, что функция выполняется во время компиляции, вы можете добавить constexpr перегрузка (только C ++ 11):

constexpr int foo(int a,int b){
return a+b;
}

Я попробовал следующий фрагмент:

int add(int a, int b) {
return a + b;
}

int main() {
return add(5, 2);
}

При компиляции с использованием GCC и флага -O3 он компилируется в это:

0x08048300 <+0>:    mov    $0x7,%eax
0x08048305 <+5>:    ret

Следовательно, вы можете видеть, что он выполняется во время компиляции.

2

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

Общие случаи, при которых оптимизирующий компилятор выбирает не встроенные функции:

  • Рекурсивные функции
  • Когда он предпочитает размер по скорости или если встраивание сильно искажает размер конечного двоичного файла

Еще одна проблема, которую следует отметить, заключается в том, что встраивание изменит связь между функциями.

Компиляция следующего кода C на GCC и G ++ с -O3:

int foo(int a, int b) {
return a+b;
}

int main(void)
{
return foo(1, 2);
}

В результате получается следующий код сборки:

00000000004004e0 <main>:
main():
4004e0:       b8 03 00 00 00          mov    $0x3,%eax
4004e5:       c3                      retq
1

Вы можете проверить список сборки, чтобы увидеть, если он встроен, но, как упоминалось ранее, это зависит от компилятора.

0

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

Если вы хотите убедиться, что функция встроена, вы всегда можете объявить ее с помощью ключевого слова inline:

inline int foo(int a,int b){
return a+b;
}

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

0

Возможный сценарий, как эта функция может быть рассчитана во время компиляции:
1) встроенный компилятор foo функция во время одного из этапов оптимизации встраивания.
2) во время постоянное распространение Компилятор фазы оптимизации может «распространять» значения переменных, которые известны во время компиляции, то есть константы и константные выражения.

NB. Вы никогда не будете точно знать, встроена ли функция, пока не увидите код ассемблера вашей программы. Даже если вы используете inline спецификатор. Компилятор может игнорировать этот спецификатор или встроенную функцию без этого спецификатора.

0