Преимущества встроенных функций в C ++? - PullRequest
248 голосов
/ 28 сентября 2008

В чем преимущества / недостатки использования встроенных функций в C ++? Я вижу, что это только увеличивает производительность для кода, который выводит компилятор, но с современными оптимизированными компиляторами, быстрыми процессорами, огромной памятью и т. Д. (Не так, как в 1980 году <где памяти было мало и все должно было уместиться в 100 КБ памяти) преимущества у них сегодня есть? </p>

Ответы [ 14 ]

194 голосов
/ 28 сентября 2008

Преимущества

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

Недостатки

  • Это может сделать ваш код больше (т. Е. Если вы используете inline для нетривиальных функций). Таким образом, это может спровоцировать оптимизацию подкачки и компиляции из компилятора.
  • Это немного нарушает вашу инкапсуляцию, потому что она раскрывает внутреннюю часть обработки вашего объекта (но тогда каждый "закрытый" член тоже). Это означает, что вы не должны использовать встраивание в шаблоне PImpl.
  • Это немного нарушает инкапсуляцию 2: встраивание C ++ разрешается во время компиляции. Это означает, что если вы измените код встроенной функции, вам нужно будет перекомпилировать весь код, используя его, чтобы быть уверенным, что он будет обновлен (по той же причине я избегаю значений по умолчанию для параметров функции)
  • При использовании в заголовке он увеличивает размер вашего заголовочного файла и, таким образом, разбавляет интересную информацию (например, список методов класса) кодом, который не волнует пользователя (по этой причине я объявляю встроенный функционирует внутри класса, но определяет его в заголовке после тела класса, а не внутри тела класса).

Inlining Magic

  • Компилятор может включать или не включать функции, которые вы пометили как встроенные; он также может решить встроить функции, не помеченные как встроенные, во время компиляции или компоновки.
  • Inline работает как копирование / вставка, управляемая компилятором, что сильно отличается от макроса препроцессора: макрос будет принудительно встроен, будет загрязнять все пространства имен и код, не будет легко отлаживаться и будет сделать, даже если компилятор посчитал бы это неэффективным.
  • Каждый метод класса, определенный внутри тела самого класса, рассматривается как «встроенный» (даже если компилятор все еще может решить не встроить его
  • Виртуальные методы не должны быть встроенными. Тем не менее, иногда, когда компилятор может точно знать тип объекта (т. Е. Объект был объявлен и создан внутри одного и того же тела функции), даже виртуальная функция будет встроена, потому что компилятор точно знает тип объекта.
  • Методы / функции шаблонов не всегда встроены (их наличие в заголовке не сделает их автоматически встроенными).
  • Следующим шагом после «inline» является метапрограммирование шаблона. То есть «Встраивая» ваш код во время компиляции, иногда компилятор может определить конечный результат функции ... Таким образом, сложный алгоритм иногда может быть сведен к некому выражению return 42 ;. Это для меня экстремальное встраивание . Это редко случается в реальной жизни, это увеличивает время компиляции, не увеличивает ваш код и делает ваш код быстрее. Но, как и Грааль, не пытайтесь применять его повсюду, потому что большая часть обработки не может быть решена таким образом ... Тем не менее, это круто в любом случае ...
    : - p
138 голосов
/ 28 сентября 2008

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

Имеет ли это существенное значение? Не заметно на современном оборудовании для большинства. Но это может иметь значение, которого достаточно для некоторых людей.

Маркировка чего-либо встроенного не дает вам гарантии, что оно будет встроено. Это просто предложение для компилятора. Иногда это невозможно, например, когда у вас есть виртуальная функция или когда задействована рекурсия. И иногда компилятор просто решает не использовать его.

Я мог видеть ситуацию, подобную этой, имеющую заметную разницу:

inline int aplusb_pow2(int a, int b) {
  return (a + b)*(a + b) ;
}

for(int a = 0; a < 900000; ++a)
    for(int b = 0; b < 900000; ++b)
        aplusb_pow2(a, b);
41 голосов
/ 14 сентября 2011

В архаичных C и C ++ inline похоже на register: предложение (не более, чем предложение) компилятору о возможной оптимизации.

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

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

Функции-члены, определенные внутри класса, по умолчанию являются «встроенными», как и шаблоны (в отличие от глобальных функций).

//fileA.h
inline void afunc()
{ std::cout << "this is afunc" << std::endl; }

//file1.cpp
#include "fileA.h"
void acall()
{ afunc(); }

//main.cpp
#include "fileA.h"
void acall();

int main()
{ 
   afunc(); 
   acall();
}

//output
this is afunc
this is afunc

Обратите внимание на включение файла file..h в два файла .cpp, в результате чего два экземпляра afunc(). Линкер откажется от одного из них. Если не указано inline, компоновщик будет жаловаться.

16 голосов
/ 28 сентября 2008

Inlining - это предложение для компилятора, которое он может игнорировать. Идеально подходит для небольших кусочков кода.

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

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

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

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

12 голосов
/ 14 сентября 2011

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

Преимущества: -

  1. Не требует дополнительных затрат на вызов функции.

  2. Также сохраняются накладные расходы на переменные push / pop в стеке при вызове функции.

  3. Это также экономит накладные расходы на обратный вызов из функции.

  4. Увеличивает локальность ссылок, используя кэш инструкций.

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

Чтобы узнать больше, перейдите по этой ссылке http://tajendrasengar.blogspot.com/2010/03/what-is-inline-function-in-cc.html

6 голосов
/ 07 мая 2010

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

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

4 голосов
/ 14 сентября 2011

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

4 голосов
/ 28 сентября 2008

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

3 голосов
/ 10 декабря 2013

Это не все о производительности. Оба C ++ и C используются для встроенного программирования, сидя на вершине аппаратного обеспечения. Если вы, например, хотите написать обработчик прерываний, вам нужно убедиться, что код может быть выполнен сразу, без замены дополнительных регистров и / или страниц памяти. Вот когда полезен inline. Хорошие компиляторы делают некоторые «вставки» сами, когда нужна скорость, но «встроенные» заставляют их.

3 голосов
/ 28 сентября 2008

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

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

Когда встроить и / или определить макрос для принудительного включения? - Только при наличии продемонстрированного и необходимого доказанного увеличения скорости для критической секции кода, которая, как известно, влияет на общую производительность приложения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...