Что такое inline функция

Встраиваемые функции

Блок, относящийся только к системам Microsoft

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

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

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

__inline __inline optfunction-definition

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

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

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

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

Завершение блока, относящегося только к системам Майкрософт

Источник

Встраиваемые функции

Встраиваемые функции

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

Для объявления встраиваемой функции используется ключевое слово inline (или __inline, __forceinline в зависимости от компилятора)

Здесь, для тестирования, использованы атрибуты компилятора gcc, которые форсируют встраивание. Рассмотрим код, который компилируется при использовании inline

И без использования (видим вызов функции CALL в строке 10)

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

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

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

Источник

11.6 – Встраиваемые (inline) функции

Использование функций дает множество преимуществ, в том числе:

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

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

C++ предлагает способ сочетания преимуществ функций со скоростью кода, написанного на месте: встраиваемые (inline) функции. Ключевое слово inline используется для запроса у компилятора обработки вашей функции как встраиваемой функции. Когда компилятор компилирует ваш код, все встраиваемые (inline) функции разворачиваются на месте, то есть вызов функции заменяется копией содержимого самой функции, что устраняет накладные расходы на вызов функции! Обратной стороной является то, что поскольку встраиваемая функция разворачивается на месте для каждого вызова, это может сделать ваш скомпилированный код немного больше, особенно если встраиваемая функция длинная и/или выполняется много вызовов встраиваемой функции.

Рассмотрим следующий фрагмент:

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

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

Лучшая практика

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

Встраиваемые функции не подпадают под правило «одно определение в программе».

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

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

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

Источник

Inline Functions (C++)

A function defined in the body of a class declaration is an inline function.

Example

In the class declaration, the functions were declared without the inline keyword. The inline keyword can be specified in the class declaration; the result is the same.

A given inline member function must be declared the same way in every compilation unit. This constraint causes inline functions to behave as if they were instantiated functions. Additionally, there must be exactly one definition of an inline function.

A class member function defaults to external linkage unless a definition for that function contains the inline specifier. The preceding example shows that you don’t have to declare these functions explicitly with the inline specifier. Using inline in the function definition causes it to be an inline function. However, it’s not allowed to redeclare a function as inline after a call to that function.

The inline and __inline specifiers instruct the compiler to insert a copy of the function body into each place the function is called.

The insertion, called inline expansion or inlining, occurs only if the compiler’s cost-benefit analysis shows it’s worthwhile. Inline expansion minimizes the function-call overhead at the potential cost of larger code size.

Using inline functions can make your program faster because they eliminate the overhead associated with function calls. Functions expanded inline are subject to code optimizations not available to normal functions.

The inline keyword tells the compiler that inline expansion is preferred. However, the compiler can create a separate instance of the function (instantiate) and create standard calling linkages instead of inserting the code inline. Two cases where this behavior can happen are:

Functions that are referred to through a pointer elsewhere in the translation unit.

These reasons may interfere with inlining, as may others, at the discretion of the compiler; you shouldn’t depend on the inline specifier to cause a function to be inlined.

As with normal functions, there’s no defined order for argument evaluation in an inline function. In fact, it could be different from the argument evaluation order when passed using the normal function-call protocol.

The /Ob compiler optimization option helps to determine whether inline function expansion actually occurs.

/LTCG does cross-module inlining whether it’s requested in source code or not.

Example 1

A class’s member functions can be declared inline, either by using the inline keyword or by placing the function definition within the class definition.

Example 2

Microsoft-specific

The function or its caller is compiled with /Ob0 (the default option for debug builds).

The function and the caller use different types of exception handling (C++ exception handling in one, structured exception handling in the other).

The function has a variable argument list.

The function is recursive and doesn’t have #pragma inline_recursion(on) set. With the pragma, recursive functions are inlined to a default depth of 16 calls. To reduce the inlining depth, use inline_depth pragma.

The function is virtual and is called virtually. Direct calls to virtual functions can be inlined.

The program takes the address of the function and the call is made via the pointer to the function. Direct calls to functions that have had their address taken can be inlined.

The function is also marked with the naked __declspec modifier.

The function is compiled by using /Od or /Ob0. No inlining is expected in these cases.

The function is defined externally, in an included library or another translation unit, or is a virtual call target or indirect call target. The compiler can’t identify non-inlined code that it can’t find in the current translation unit.

Recursive functions can be replaced with inline code to a depth specified by the inline_depth pragma, up to a maximum of 16 calls. After that depth, recursive function calls are treated as calls to an instance of the function. The depth to which recursive functions are examined by the inline heuristic can’t exceed 16. The inline_recursion pragma controls the inline expansion of a function currently under expansion. See the Inline-Function Expansion (/Ob) compiler option for related information.

END Microsoft Specific

For more information on using the inline specifier, see:

When to use inline functions

Inline functions are best used for small functions such as accessing private data members. The main purpose of these one- or two-line «accessor» functions is to return state information about objects. Short functions are sensitive to the overhead of function calls. Longer functions spend proportionately less time in the calling and returning sequence and benefit less from inlining.

A Point class can be defined as follows:

Assuming coordinate manipulation is a relatively common operation in a client of such a class, specifying the two accessor functions ( x and y in the preceding example) as inline typically saves the overhead on:

Function calls (including parameter passing and placing the object’s address on the stack)

Preservation of caller’s stack frame

New stack frame setup

Restoring the old stack frame

Inline functions vs. macros

Inline functions are similar to macros, because the function code is expanded at the point of the call at compile time. However, inline functions are parsed by the compiler, and macros are expanded by the preprocessor. As a result, there are several important differences:

Inline functions follow all the protocols of type safety enforced on normal functions.

Inline functions are specified using the same syntax as any other function except that they include the inline keyword in the function declaration.

Expressions passed as arguments to inline functions are evaluated once. In some cases, expressions passed as arguments to macros can be evaluated more than once.

The following example shows a macro that converts lowercase letters to uppercase:

The intent of the expression toupper(getc(stdin)) is that a character should be read from the console device ( stdin ) and, if necessary, converted to uppercase.

Because of the implementation of the macro, getc is executed once to determine whether the character is greater than or equal to «a,» and once to determine whether it’s less than or equal to «z.» If it is in that range, getc is executed again to convert the character to uppercase. It means the program waits for two or three characters when, ideally, it should wait for only one.

Inline functions remedy the problem previously described:

Источник

Встраиваемые функции (C++)

Функция, определенная в теле объявления класса, является встроенной.

Пример

В объявлении класса функции были объявлены без inline ключевого слова. inline Ключевое слово может быть указано в объявлении класса; результат будет таким же.

Заданная встроенная функция-член должна быть объявлена одинаково в каждом блоке компиляции. Из-за этого ограничения встраиваемые функции работают так же, как если бы они были созданными экземплярами функций. Кроме того, должно существовать только одно определение встраиваемой функции.

Функция-член класса по умолчанию имеет внешнюю компоновку, если только определение этой функции не содержит inline спецификатор. В предыдущем примере показано, что вам не нужно явно объявлять эти функции с помощью inline описателя. Использование inline в определении функции приводит к тому, что оно будет встроенной функцией. Однако не допускается повторное объявление функции inline после вызова этой функции.

inline Описатели и __inline указывают компилятору вставить копию тела функции в каждое место вызова функции.

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

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

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

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

Функции, на которые создаются ссылки посредством указателя в любом месте блока трансляции.

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

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

/Ob Параметр оптимизации компилятора позволяет определить, выполняется ли фактическое расширение функции.

/LTCG выполняет Межмодульное встраивание независимо от того, запрошены ли они в исходном коде.

Пример 1

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

Пример 2

Только для систем Майкрософт

Даже при использовании __forceinline компилятор не может встроен код во всех обстоятельствах. Компилятор не может выполнить встраивание функции, если:

Функция или ее вызывающий объект компилируются вместе с /Ob0 (параметром по умолчанию для отладочных сборок).

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

Функция имеет переменное число аргументов.

Функция является рекурсивной и не имеет #pragma inline_recursion(on) набора. С помощью этой директивы выполняется подстановка рекурсивных функций с глубиной по умолчанию, 16 вызовам. Чтобы уменьшить глубину встраивания, используйте inline_depth директиву pragma.

Функция является виртуальной, и для нее используется виртуальный вызов. Прямые вызовы виртуальных функций могут подставляться.

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

Функция также помечается naked __declspec модификатором.

Функция компилируется с помощью/OD или/Ob0. В таких случаях встраивание не ожидается.

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

Рекурсивные функции могут быть заменены встроенным кодом на глубину, заданную inline_depth директивой pragma, до максимум 16 вызовов. Начиная с этой глубины рекурсивные функции обрабатываются как вызовы на экземпляр функции. Глубина, для которой рекурсивные функции проверяются встроенным эвристическим методом, не может превышать 16. inline_recursion Директива pragma управляет встроенным расширением функции, находящегося в данный момент в расширении. Дополнительные сведения см. в описании параметра компилятора для расширения встроенной функции (/OB).

Завершение блока, относящегося только к системам Майкрософт

Дополнительные сведения об использовании inline спецификатора см. в следующих статьях:

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

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

Point Класс можно определить следующим образом:

Предполагается, что обработка координат является относительно распространенной операцией в клиенте такого класса, указывая две функции доступа ( x и y в предыдущем примере), как inline правило, позволяет экономить издержки:

вызовы функций (включая передачу параметров и размещение адреса объекта в стеке);

сохранение кадра стека вызывающего объекта;

Настройка нового кадра стека

передачу возвращаемого значения;

Восстановление старого кадра стека

Встроенные функции и макросы

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

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

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

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

В следующем примере показан макрос, преобразующий строчные буквы в прописные.

Из-за реализации макроса getc выполняется один раз для определения того, что символ больше или равен «a», и один раз, чтобы определить, меньше ли он или равен z. Если он находится в этом диапазоне, getc выполняется еще раз, чтобы преобразовать символ в верхний регистр. Это означает, что программа ожидает два или три символа, когда, в идеале, будет ожидать только один.

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

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *