Когда использовать подставляемую функцию и если не использовать его?

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

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

173
задан Benjamin 30 December 2013 в 19:01
поделиться

11 ответов

Избежать затрат на вызов функции - это только половина дела.

do:

  • используйте inline вместо #define
  • очень маленькие функции являются хорошими кандидатами на inline : более быстрый код и меньшие исполняемые файлы (подробнее шансы остаться в кеше кода)
  • функция мала и вызывается очень часто

не:

  • большие функции: приводит к большим исполняемым файлам,
    • встроить существующую функцию
    • изменить встроенную функцию или сделать встроенную функцию не встроенной: предыдущая версия библиотеки вызывает старую реализацию

при разработке библиотеки, чтобы сделать класс расширяемым в будущем вам следует:

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

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

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

Ссылки:


РЕДАКТИРОВАТЬ: Бьярн Страуструп, C ++ Язык программирования:

Функция может быть определена как встроенная . Например:

inline int fac(int n)
{
  return (n < 2) ? 1 : n * fac(n-1);
}

Спецификатор inline - это подсказка компилятору, что он должен попытаться сгенерировать код для вызова fac () inline вместо того, чтобы устанавливать код для функция один раз, а затем вызов через обычный механизм вызова функции. Умный компилятор может сгенерировать константу 720 для вызова fac (6) . Возможность взаимно рекурсивных встроенных функций, встроенных функций, которые рекурсивны или не рекурсивны в зависимости от ввода, и т. Д. Делает невозможным гарантировать, что каждый вызов встроенной функции фактически встроен. Степень продуманности компилятора не может быть законодательно закреплена, поэтому один компилятор может генерировать 720 , другой 6 * fac (5) , а третий - вызов без встроенного кода fac (6) .

Чтобы сделать встраивание возможным при отсутствии необычно умных средств компиляции и компоновки, определение - а не просто объявление - встроенной функции должно быть в области видимости (§9.2). Встроенный спецификатор не влияет на семантику функции. В частности, встроенная функция по-прежнему имеет уникальный адрес и, следовательно, имеет статические переменные (§7.1.2) встроенной функции.

EDIT2: ISO-IEC 14882-1998, 7.1.2 Спецификаторы функций

Объявление функции (8.3.5, 9.3, 11.4) со встроенным спецификатором объявляет встроенную функцию. Встроенный спецификатор указывает реализации, что встроенная подстановка тела функции в точке вызова предпочтительнее обычного механизма вызова функции. Реализация не требуется для выполнения этой встроенной замены в точке вызова; однако, даже если эта встроенная подстановка опущена, другие правила для встроенных функций, определенных в 7.1.2, все равно должны соблюдаться.

198
ответ дан 23 November 2019 в 20:36
поделиться

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

Когда встроенный метод передается в исходный файл и больше не встраивается, весь проект должен быть перекомпонован (по крайней мере, таков мой опыт). А также при преобразовании методов во встроенные.

0
ответ дан 23 November 2019 в 20:36
поделиться

inline has very little to do with optimization. inline is an instruction to the compiler not to produce an error if the function given definition occurs multiple times in the program and a promise that the definition will occur in every translation that it is used and everywhere it does appear it will have exactly the same definition.

Given the above rules, inline is suitable for short functions whose body doesn't necessitate including extra dependencies over what just a declaration would need. Every time the defintion is encountered it must be parsed and code for its body may be generated so it implies some compiler overhead over a function defined only once in a single source file.

A compiler may inline (i.e. replace a call to the function with code that performs that action of that function) any function call that it chooses. It used to be the case that it "obviously" couldn't inline a function that wasn't declared in the same translation unit as the call but with the increasing use of link time optimization even this isn't true now. Equally true is the fact that functions marked inline may not be inlined.

56
ответ дан 23 November 2019 в 20:36
поделиться

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

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

Однако вы можете быть удивлены - многие компиляторы C ++ автоматически встраивают небольшие функции для вас - и они также могут игнорировать ваш запрос на встраивание.

10
ответ дан 23 November 2019 в 20:36
поделиться

Преждевременная оптимизация - корень всех зол!

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

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

Повторное предположение - напишите встроенные однострочные функции и подумайте о других позже.

4
ответ дан 23 November 2019 в 20:36
поделиться

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

вы можете узнать больше о встроенных функциях в c ++ faq

2
ответ дан 23 November 2019 в 20:36
поделиться

Лучший способ узнать это - профилировать свою программу и отмечать небольшие функции, которые вызываются много раз и прожигают циклы ЦП как inline . Ключевое слово здесь "small" - как только накладные расходы на вызов функции незначительны по сравнению с временем, потраченным на функцию, бессмысленно встраивать их.

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

5
ответ дан 23 November 2019 в 20:36
поделиться

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

void IncreaseCount() { freeInstancesCnt++; }

Читатель сразу знает полную семантику кода.

1
ответ дан 23 November 2019 в 20:36
поделиться

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

0
ответ дан 23 November 2019 в 20:36
поделиться

Лучшим способом было бы изучить и сравнить сгенерированные инструкции для встроенных и не встроенных. Однако всегда можно безопасно опустить inline . Использование в строке может привести к нежелательным неприятностям.

0
ответ дан 23 November 2019 в 20:36
поделиться

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

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

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

  • Короткие процедуры, вызываемые в замкнутом цикле.
  • Очень простые средства доступа (получение / установка) и функции-оболочки.
  • Код шаблона в заголовке файлы, к сожалению, автоматически получают встроенную подсказку.
  • Сокращенный код, который используется как макрос. (Например, min () / max ())
  • Краткие математические процедуры.
0
ответ дан 23 November 2019 в 20:36
поделиться