Часто люди говорят о вызове функций, вызывающих определенные накладные расходы или неизбежный набор дополнительных проблем и обстоятельств в программе. Можно ли это лучше объяснить и сравнить с аналогичной программой без вызова функции?
Почему возникают накладные расходы при вызове функций?
Ответы (4)
Это зависит от настроек вашего компилятора и от того, как он оптимизирует код. Некоторые функции встроены. Другие нет. Обычно это зависит от того, оптимизируете ли вы размер или скорость.
Как правило, вызов функции вызывает задержку по двум причинам:
Программа должна подключиться к некоторому случайному месту в памяти, где начинается код вашей функции. Для этого ему нужно сохранить текущую позицию курсора в стек, чтобы он знал, куда вернуться. Этот процесс потребляет более одного цикла ЦП.
В зависимости от архитектуры вашего ЦП может существовать конвейер, который извлекает следующие несколько инструкций из памяти в кэш ЦП параллельно с выполнением текущей инструкции. Это сделано для увеличения скорости выполнения. Когда вы вызываете функцию, курсор перехватывает совершенно другой адрес, и все кэшированные инструкции сбрасываются из конвейера. Это вызывает дополнительные задержки.
CALL
в наши дни составляет около одного цикла.
- person Jens; 03.08.2015
Также см. здесь и здесь для обсуждения того, когда встраивание имеет смысл.
Встраивание
В общем, вы можете предложить компилятору только
inline
функцию, но компилятор может решить иначе. Однако Visual Studio предлагает собственное ключевое словоforceinline
. Некоторые функции не могут быть встроены, например. когда они рекурсивны или когда целевая функция не может быть определена во время компиляции (вызовы через таблицы функций, вызовы виртуальных функций в C++).Я предлагаю вам доверять компилятору, следует ли встраивать функцию. Если вы действительно хотите встроить свой код, рассмотрите возможность использования макроса.
Накладные расходы
Накладные расходы памяти минимальны при использовании функций, потому что вы не дублируете код; встроенный код дублируется на сайт вызова. Накладные расходы на производительность в наши дни незначительны, потому что современные архитектуры действительно хорошо прогнозируют и вызывают всего 1-2 такта.
Функции могут быть inlined
, но нормой (в основном) является то, что функции находятся по определенному адресу, и значения, переданные функции, помещаются в стек, а затем результат помещается в стек и возвращается.
Функции, безусловно, могут быть встроенными, если выполняются некоторые условия, но они точно не всегда встроенные. Чаще всего вызов функции приводит к подлинному невстроенному вызову функции. Вызов функции связан с некоторыми дополнительными затратами, такими как
- Подготовка параметров для функции в соответствии с соглашением о вызове функции
- Получение возвращаемого значения функции
- Код пролога и эпилога функции, отвечающий за управление локальной памятью, управление памятью параметров и сохранение значений регистров
- Функция может забивать некоторые регистры ЦП, тем самым нарушая их использование в вызывающем коде и тем самым препятствуя оптимизации.
- Менее удобное для ЦП и виртуальной памяти поведение кода, выполняемого нелинейным образом.
Все это приведет к накладным расходам, которые, вероятно, не существовали бы, если бы тело функции было встроено в вызывающий код.
inline
инг таких функций предоставляет компилятору эти константные значения, что обеспечивает более агрессивную оптимизацию. - person user3528438   schedule 03.08.2015