Хотя в некоторых ситуациях было бы очень удобно использовать встроенные функции,
Есть ли недостатки у встроенных функций?
Вывод:
Судя по всему, в использовании встроенных функций нет ничего плохого.
Но стоит отметить следующие моменты!
Чрезмерное использование встраивания действительно может замедлить работу программ. В зависимости от размера функции ее встраивание может привести к увеличению или уменьшению размера кода. Встраивание очень маленькой функции доступа обычно уменьшает размер кода, в то время как встраивание очень большой функции может значительно увеличить размер кода. На современных процессорах код меньшего размера обычно выполняется быстрее из-за лучшего использования кеша инструкций. - Рекомендации Google
Преимущества встроенных функций в скорости имеют тенденцию уменьшаться по мере увеличения размера функции. В какой-то момент накладные расходы на вызов функции становятся небольшими по сравнению с выполнением тела функции, и выгода теряется - Источник
Есть несколько ситуаций, когда встроенная функция может не работать:
Ключевое слово __inline заставляет функцию быть встроенной, только если вы укажете параметр оптимизации. Если указан параметр optimize, то соблюдение __inline зависит от настройки параметра встроенного оптимизатора. По умолчанию встроенный параметр действует при каждом запуске оптимизатора. Если вы указываете optimize, вы также должны указать опцию noinline, если вы хотите, чтобы ключевое слово __inline игнорировалось. -Источник
«Есть несколько ситуаций, когда встроенная функция может не работать: для функции, возвращающей значения; если существует оператор return. Для функции, не возвращающей никаких значений». Вы также можете просто сказать «встроенные функции могут никогда не работать».
Ваши очки сосредоточены только на производительности. ИМО, более важным моментом является то, что inline позволяет избежать нарушений ODR для автономных функций, определенных в библиотеках только для заголовков. Это использование inline, которое компилятор не может игнорировать. Он по-прежнему может отказаться от фактического встраивания функции, но должен рассматривать ее так, как будто существует только одно определение.





Я в этом сомневаюсь. Даже компилятор автоматически встраивает некоторые функции для оптимизации.
Я бы расширил то, что сказал @Vaibhav, и объявил, что «inline» - это «регистр» 2000-х годов. Помните ключевое слово "зарегистрироваться"? Кто-нибудь использовал его с 1980-х годов?
@PaulTomblin: Стандарт C предусматривает, что несколько единиц перевода могут объявлять функции inline с одним и тем же именем, и рекомендует объединить их в общее определение; в большинстве случаев код, использующий inline, можно заставить работать на компиляторе, который не распознает его через #define inline static, но использование inline на компиляторе, который понимает, что он, как правило, даст лучшие результаты, чем static.
Это может увеличить размер исполняемый файл, и я не думаю компиляторы всегда будут делать они встроены, даже если вы использовали встроенное ключевое слово. (Или это другой путь вокруг, как то, что Вайбхав сказал?...)
Я думаю, что обычно нормально, если функция имеет только 1 или 2 оператора.
Редактировать: Вот что об этом говорится в документе linux CodingStyle:
Chapter 15: The inline disease
There appears to be a common misperception that gcc has a magic "make me faster" speedup option called "inline". While the use of inlines can be appropriate (for example as a means of replacing macros, see Chapter 12), it very often is not. Abundant use of the inline keyword leads to a much bigger kernel, which in turn slows the system as a whole down, due to a bigger icache footprint for the CPU and simply because there is less memory available for the pagecache. Just think about it; a pagecache miss causes a disk seek, which easily takes 5 miliseconds. There are a LOT of cpu cycles that can go into these 5 miliseconds.
A reasonable rule of thumb is to not put inline at functions that have more than 3 lines of code in them. An exception to this rule are the cases where a parameter is known to be a compiletime constant, and as a result of this constantness you know the compiler will be able to optimize most of your function away at compile time. For a good example of this later case, see the kmalloc() inline function.
Often people argue that adding inline to functions that are static and used only once is always a win since there is no space tradeoff. While this is technically correct, gcc is capable of inlining these automatically without help, and the maintenance issue of removing the inline when a second user appears outweighs the potential value of the hint that tells gcc to do something it would have done anyway.
В VC++ он увеличит размер, не уверен в других компиляторах. Но если они все же решат сделать его встроенным, я уверен, что это тоже сделало бы его больше.
под ними я подразумеваю рассматриваемый компилятор.
Да, приращение размера было основным, о котором я слышал ... любопытно узнать, есть ли еще!
Гораздо более важным, чем длина функции, IMHO, является вопрос о том, позволит ли знание аргументов, передаваемых на различных сайтах вызовов, применять оптимизацию конкретно к ним. Есть много ситуаций, когда 99% вызовов определенной функции передают константу [не всегда одну и ту же] конкретному аргументу, и когда знание того, что определенный параметр будет постоянным, позволит компилятору опустить более 90% код, связанный с функцией.
@ BrianR.Bondy: встраивание крошечных функций, которые получают или устанавливают один член, обычно будет иметь размер снижаться двоичного файла. Для настройки и создания call требуется больше инструкций, чем для загрузки или сохранения одной вещи, а возможность оптимизации после встраивания часто означает, что что-то может просто храниться в регистре на протяжении всего цикла вместо повторных вызовов.
Чрезмерное встраивание функций может увеличить размер скомпилированного исполняемого файла, что может негативно повлиять на производительность кеша, но в настоящее время компилятор решает о встраивании функций самостоятельно (в зависимости от многих критериев) и игнорирует ключевое слово inline.
Стоит отметить, что ключевое слово inline на самом деле является просто подсказкой для компилятора. Компилятор может игнорировать встроенный код и просто где-нибудь сгенерировать код для функции.
Основным недостатком встроенных функций является то, что они могут увеличьте размер вашего исполняемого файла (в зависимости от количества экземпляров). Это может быть проблемой на некоторых платформах (например, встроенных системах), особенно если сама функция является рекурсивной.
Я также рекомендовал бы сделать встроенные функции очень маленький - преимущества встроенных функций в скорости имеют тенденцию уменьшаться по мере увеличения размера функции. В какой-то момент накладные расходы на вызов функции становятся небольшими по сравнению с выполнением тела функции, и выгода теряется.
Вы также должны отметить, что встроенное ключевое слово - это всего лишь запрос. Компилятор может решить не встраивать его, точно так же компилятор может сделать встроенную функцию, которую вы не определили как встроенную, если он считает, что компромисс между скоростью и размером того стоит.
Это решение обычно принимается на основе ряда вещей, таких как настройка между оптимизацией по скорости (избегает вызова функции) и оптимизацией по размеру (встраивание может вызвать раздувание кода, поэтому не подходит для больших многократно используемых функций).
с компилятором VC++ вы можете обойти это решение, используя __forceinline
ТАК вообще: Используйте встроенную функцию, если вы действительно хотите иметь функцию в заголовке, но в другом месте есть небольшой смысл, потому что, если вы собираетесь что-то от нее получить, хороший компилятор все равно сделает ее встроенной для вас.
Я согласен с другими сообщениями:
Третий момент - это может вынудить вас раскрыть детали реализации в ваших заголовках, например,
class OtherObject;
class Object {
public:
void someFunc(OtherObject& otherObj) {
otherObj.doIt(); // Yikes requires OtherObj declaration!
}
};
Без встроенного прямого объявления OtherObject было все, что вам нужно. С встроенным вашим заголовок требует определения для OtherObject.
На практике я не верю, что ваша первая пуля верна. Компилятор подходит для встроенного игнорировать, но у него есть опция только в том случае, если определение функции видно на сайте вызова и это более или менее ограничивает вас функциями, локальными для текущего TU.
Встраивание более крупных функций может увеличить размер программы, что приведет к большему количеству промахов кеш инструкций и замедлению ее работы.
Решить, когда функция достаточно мала, чтобы встраивание повысило производительность, довольно сложно. Руководство по стилю C++ от Google рекомендует встраивать только функции из 10 строк или меньше.
(Упрощенно) Пример:
Представьте себе простую программу, которая просто вызывает функцию «X» 5 раз.
Если X маленький и все вызовы встроены: потенциально все инструкции будут предварительно загружены в кэш инструкций с одним доступом к основной памяти - отлично!
Если X большой, скажем, приближается к емкости кэша инструкций:
Встраивание X потенциально может привести к однократной выборке инструкций из памяти для каждого встроенного экземпляра X.
.
Если X не встроен, инструкции могут быть извлечены из памяти при первом вызове X, но потенциально могут оставаться в кеше для последующих вызовов.
Можете ли вы уточнить или предоставить пример (возможно, шаблон использования), показывающий, как большая встроенная функция приводит к большему количеству промахов в кеше? Предположим, у нас есть только большая встроенная функция один, которая часто используется, разве это не уменьшит количество промахов кеша за счет увеличения локальности ссылки?
@nawK: Добавил пример. Когда функция достаточно велика, ваша локализация ссылки (для инструкций) может уменьшиться при встраивании, поскольку каждый встроенный экземпляр является отдельной копией.
Как уже упоминалось, ключевое слово inline - это только подсказка для компилятора. Фактически, большинство современных компиляторов полностью игнорируют этот намек. У компилятора есть собственная эвристика, чтобы решить, встраивать ли функцию, и, откровенно говоря, он не хочет ваших советов, большое вам спасибо.
Если вы действительно, действительно хотите сделать что-то встроенным, если вы действительно профилировали это и посмотрели на разборку, чтобы убедиться, что переопределение эвристики компилятора действительно имеет смысл, тогда это возможно:
Однако ключевое слово inline имеет вторую, допустимую цель - объявление функций в файлах заголовков, но не внутри определения класса. Ключевое слово inline необходимо, чтобы компилятор не создавал несколько определений функции.
Вы, вероятно, имеете в виду «определение» функций в файлах заголовков, а не их объявление.
Определение должно быть доступно, чтобы компилятор мог решить, будет ли он встроить функцию или нет. Intra-TU требует, чтобы определения были видны в файле заголовка, а для этого требуется ключевое слово inline. Таким образом, это возвращает нас к тому, что разработчик должен вызвать.
«Фактически, большинство современных компиляторов полностью игнорируют этот намек». Можете ли вы подтвердить это источниками? В документации clang указано, что это рассматривается как подсказка, но, конечно, не игнорируется полностью. В документации MSVC указано нечто подобное. Ваше утверждение не подходит для двух самых популярных «современных» компиляторов.
@rdb: Сама подсказка, вероятно, бессмысленна, но ключевое слово inline также обещает, что все остальные вызывающие функцию также смогут увидеть определение. Отсутствие необходимости также выдавать отдельное определение функции может говорить в пользу встраивания.
Возникла проблема со встроенной функцией - после того, как вы определили функцию в файле заголовка (что подразумевает встроенную функцию, явную или неявную путем определения тела функции-члена внутри класса), нет простого способа изменить ее, не заставляя пользователей перекомпилировать (в отличие от повторного подключения). Часто это вызывает проблемы, особенно если рассматриваемая функция определена в библиотеке, а заголовок является частью ее интерфейса.
Да, это проблема, которую часто упускают из виду при экспорте встроенных функций из общей библиотеки!
Это также может быть преимуществом, поскольку вашим пользователям не нужно ссылаться на файл .lib. Делает возможными однофайловые библиотеки только для заголовков, которые легко распространять и использовать. Это не хуже, чем библиотеки шаблонов, которые встречаются довольно часто.
Я не знаю, связан ли мой ответ с вопросом, но:
Будьте осторожны с очень в отношении встроенных виртуальных методов! Некоторые компиляторы с ошибками (например, предыдущие версии Visual C++) будут генерировать встроенный код для виртуальных методов, где стандартное поведение заключалось в том, чтобы ничего не делать, а идти вниз по дереву наследования и вызывать соответствующий метод.
Как говорили другие люди, встроенная функция может создать проблему, если код большой. Поскольку каждая инструкция хранится в определенной области памяти, поэтому перегрузка встроенной функции заставляет код запускать больше времени.
есть несколько других ситуаций, когда встроенный может не работать
Среди других проблем со встроенными функциями, которые я видел часто чрезмерно используемыми (я видел встроенные функции из 500 строк), вам следует знать следующее:
создавать нестабильность
#include проникает в клиента. Это может быть очень неприятно, если вы переработаете встроенную функцию и удалите больше не используемый заголовок, на который полагался какой-то клиент.размер исполняемого файла
время исполнения
Стандарт кодирования, в котором я работаю, ограничивает встроенные функции простыми сеттерами / геттерами и, в частности, говорит, что деструкторы не должны быть встроенными, если у вас нет измерений производительности, чтобы показать, что встраивание дает заметное преимущество.
Хорошее резюме. Обратите внимание, что ваши маркеры о том, когда они встроены, применяются компилятором, а когда нет, скорее зависят от компилятора. (MSVC будет делать это иначе, чем gcc, иначе, чем ...)