Замедляет ли функция, которая только вызывает другую функцию? - PullRequest
4 голосов
/ 07 февраля 2012

Я видел какой-то код, похожий на этот

int *func2(int *var) {
    //Do some actual work
    return var;
}

int *func1(int *var) {
    return func2(var);
}

int main() {
    int var;
    var = func1(&var);
    return 0;
}

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

Ответы [ 4 ]

12 голосов
/ 07 февраля 2012

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

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

4 голосов
/ 07 февраля 2012

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

Таким образом, ответ на ваш вопрос: Да, компилятор обычно достаточно умен, чтобы исключить вызов.вам нужно.

Вы также можете использовать ключевое слово inline, чтобы сделать его более явным: (хотя компилятор все еще может игнорировать его)

inline int *func1(int *var) {
    return func2(var);
}
1 голос
/ 07 февраля 2012

Супер быстрый ответ: Да, может быть.

Быстрый ответ: Да, но, как правило, недостаточно, чтобы вы заботились, а иногда и вовсе.

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

  1. Функция наподобие getc, которая просто извлекает следующий байт из буфера, продвигает позицию и возвращает (в общем случае, когда буфер не пуст).
  2. Функция, которая продвигает конечный автомат посредством тривиальной операции и возвращает, например, обработку одного байта символа UTF-8.
  3. Примитивы блокировки / синхронизации. Это особый случай, потому что фактический доступ к атомарной памяти должен доминировать во времени выполнения, что делает накладные расходы незначительными. Но если ваш предполагаемый вариант использования состоит в том, чтобы просто удерживать блокировки для одной тривиальной операции (например, lock(); a++; unlock();), то даже небольшое добавленное время с удерживаемой блокировкой может оказать радикальное влияние на производительность состязания, если блокировка интенсивно утверждается.

Наконец, ответ «что вам следует делать»: пишите код наиболее естественным образом, пока тестирование / измерение не покажут вам проблему с производительностью. Только тогда вы должны подумать об увеличении кода для повышения производительности.

0 голосов
/ 07 февраля 2012

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

...