Преимущества встроенных функций в C++?
Каковы преимущества / недостатки использования встроенных функций в C++? Я вижу, что это только увеличивает производительность для кода, который выводит компилятор, но с современными оптимизированными компиляторами, быстрыми процессорами, огромной памятью и т. Д. (Не так, как в 1980 году<где памяти было мало и все должно было уместиться в 100 КБ памяти), что преимущества у них сегодня есть?
14 ответов
Встроенные функции работают быстрее, потому что вам не нужно вставлять и извлекать такие вещи из стека, как параметры и адрес возврата; однако, это делает ваш двоичный файл немного больше.
Имеет ли это существенное значение? Не заметно на современном оборудовании для большинства. Но это может иметь значение, которого достаточно для некоторых людей.
Маркировка чего-то встроенного не дает вам гарантии, что это будет встроено. Это просто предложение для компилятора. Иногда это невозможно, например, когда у вас есть виртуальная функция или когда задействована рекурсия. И иногда компилятор просто решает не использовать его.
Я мог видеть ситуацию, подобную этой, имеющую заметную разницу:
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);
преимущества
- Вставляя ваш код туда, где он необходим, ваша программа будет тратить меньше времени на вызов функции и возврат частей. Предполагается, что ваш код будет работать быстрее, даже если он будет больше (см. Ниже). Встраивание тривиальных аксессоров может быть примером эффективного встраивания.
- Отметив его как встроенный, вы можете поместить определение функции в заголовочный файл (то есть он может быть включен в несколько модулей компиляции, без жалоб компоновщика)
Недостатки
- Это может сделать ваш код больше (например, если вы используете inline для нетривиальных функций). Таким образом, это может спровоцировать оптимизацию подкачки и компиляции.
- Это немного нарушает вашу инкапсуляцию, потому что она раскрывает внутреннюю часть обработки вашего объекта (но тогда каждый "закрытый" член тоже будет). Это означает, что вы не должны использовать встраивание в шаблоне PImpl.
- Это немного нарушает вашу инкапсуляцию 2: встраивание C++ разрешается во время компиляции. Это означает, что если вы измените код встроенной функции, вам нужно будет перекомпилировать весь код, используя его, чтобы быть уверенным, что он будет обновлен (по той же причине я избегаю значений по умолчанию для параметров функции)
- При использовании в заголовке он увеличивает размер вашего заголовочного файла и, таким образом, разбавляет интересную информацию (например, список методов класса) кодом, который не волнует пользователя (поэтому я объявляю встроенные функции внутри класс, но определит его в заголовке после тела класса, а не внутри тела класса).
Магия
- Компилятор может включать или не включать функции, которые вы пометили как встроенные; он также может принять решение встроить функции, не помеченные как встроенные, во время компиляции или компоновки.
- Inline работает как копирование / вставка, управляемая компилятором, что весьма отличается от макроса препроцессора: макрос будет принудительно встроен, будет загрязнять все пространства имен и код, не будет легко отлаживаться и будет выполняться даже если бы компилятор считал это неэффективным.
- Каждый метод класса, определенный внутри тела самого класса, считается "встроенным" (даже если компилятор все же может решить не встроить его в класс).
- Виртуальные методы не должны быть встроенными. Тем не менее, иногда, когда компилятор может точно знать тип объекта (т. Е. Объект был объявлен и создан внутри одного и того же тела функции), даже виртуальная функция будет встроена, потому что компилятор точно знает тип объекта.
- Шаблонные методы / функции не всегда встроены (их наличие в заголовке не сделает их автоматически встроенными).
- Следующим шагом после "inline" является метапрограммирование шаблона. Т.е., "вставляя" ваш код во время компиляции, иногда компилятор может определить конечный результат функции... Таким образом, сложный алгоритм иногда может быть сведен к
return 42 ;
заявление. Это для меня экстремальное встраивание. Это случается редко в реальной жизни, это увеличивает время компиляции, не увеличивает ваш код и делает ваш код быстрее. Но, как и Грааль, не пытайтесь применять его везде, потому что большая часть обработки не может быть решена таким образом... Тем не менее, это круто в любом случае...
:-п
В архаичных 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
указан, компоновщик будет жаловаться.
Встраивание - это предложение для компилятора, которое он может игнорировать. Это идеально подходит для небольших кусков кода.
Если ваша функция встроенная, она в основном вставляется в код, где выполняется вызов функции, а не фактически вызывает отдельную функцию. Это может помочь со скоростью, так как вам не нужно делать фактический звонок.
Это также помогает процессорам в конвейерной обработке, поскольку им не нужно перезагружать конвейер новыми инструкциями, вызванными вызовом.
Единственным недостатком является возможный увеличенный размер двоичного файла, но, пока функции невелики, это не будет иметь большого значения.
В настоящее время я склонен оставлять такие решения для компиляторов (ну, во всяком случае, умных). Люди, которые их написали, имеют тенденцию иметь гораздо более детальное знание основополагающих архитектур.
Встроенная функция - это метод оптимизации, используемый компиляторами. Можно просто добавить встроенное ключевое слово в прототип функции, чтобы сделать функцию встроенной. Встроенная функция указывает компилятору вставлять полное тело функции везде, где эта функция используется в коде.
Не требует накладных расходов на вызов функции.
Это также экономит накладные расходы на переменные push/pop в стеке при вызове функции.
Это также экономит накладные расходы на обратный вызов из функции.
Это увеличивает местность ссылки, используя кэш инструкций.
После встраивания компилятор может также применить внутрипроцедурную оптимизацию, если указано. Это самое важное, так что теперь компилятор может сосредоточиться на устранении мертвого кода, может уделять больше внимания прогнозированию ветвлений, устранению индукционных переменных и т. Д.
Чтобы узнать больше об этом можно перейти по этой ссылке http://tajendrasengar.blogspot.com/2010/03/what-is-inline-function-in-cc.html
Я хотел бы добавить, что встроенные функции имеют решающее значение при создании разделяемой библиотеки. Без маркировки встроенной функции она будет экспортирована в библиотеку в двоичном виде. Он также будет присутствовать в таблице символов, если экспортируется. С другой стороны, встроенные функции не экспортируются ни в двоичные файлы библиотеки, ни в таблицу символов.
Это может быть критично, когда библиотека предназначена для загрузки во время выполнения. Это может также затронуть бинарно-совместимые библиотеки. В таких случаях не используйте inline.
inline
позволяет поместить определение функции в заголовочный файл и #include
этот заголовочный файл в нескольких исходных файлах без нарушения одного правила определения.
Во время оптимизации многие компиляторы будут использовать встроенные функции, даже если вы их не отметили. Обычно вам нужно помечать функции как встроенные только в том случае, если вы знаете что-то, чего нет у компилятора, так как он может сам принять правильное решение.
Вообще говоря, в наши дни любой современный компилятор, беспокоящийся о вложении чего-либо, является пустой тратой времени. Компилятор должен на самом деле оптимизировать все эти соображения для вас с помощью собственного анализа кода и вашей спецификации флагов оптимизации, передаваемых компилятору. Если вам небезразлична скорость, скажите компилятору оптимизировать скорость. Если вам небезразлично пространство, скажите компилятору оптимизировать пространство. Как упоминалось в другом ответе, приличный компилятор будет даже встроен автоматически, если это действительно имеет смысл.
Кроме того, как заявили другие, использование inline не гарантирует ничего inline. Если вы хотите гарантировать это, вы должны будете определить макрос вместо встроенной функции, чтобы сделать это.
Когда встроить и / или определить макрос для принудительного включения? - Только когда у вас есть доказанное и необходимое доказанное увеличение скорости для критической части кода, которая, как известно, влияет на общую производительность приложения.
Это не все о производительности. Оба C++ и C используются для встроенного программирования, сидя на вершине аппаратного обеспечения. Если вы, например, хотите написать обработчик прерываний, вам нужно убедиться, что код может быть выполнен сразу, без замены дополнительных регистров и / или страниц памяти. Именно тогда inline пригодится. Хорошие компиляторы делают некоторые "вставки" сами, когда нужна скорость, но "встроенные" заставляют их.
Попал в ту же проблему с встраиванием функций в такие библиотеки. Кажется, что встроенные функции не скомпилированы в библиотеку. в результате компоновщик выдает ошибку "неопределенная ссылка", если исполняемый файл хочет использовать встроенную функцию библиотеки. (случилось со мной при компиляции исходного кода Qt с помощью gcc 4.5.
Наш профессор информатики призвал нас никогда не использовать inline в программе на C++. Когда его спросили, почему, он любезно объяснил, что современные компиляторы должны определять, когда использовать inline автоматически.
Так что да, inline может быть техникой оптимизации, которая будет использоваться везде, где это возможно, но, очевидно, это то, что уже сделано для вас, когда есть возможность встроить функцию в любом случае.
Почему бы не сделать все функции встроенными по умолчанию? Потому что это инженерный компромисс. Существует как минимум два типа "оптимизации": ускорение программы и уменьшение размера (объема памяти) программы. Встраивание вообще ускоряет вещи. Он избавляет от накладных расходов на вызов функции, избегая выталкивания и извлечения параметров из стека. Однако это также увеличивает объем памяти программы, поскольку теперь каждый вызов функции должен быть заменен полным кодом функции. Чтобы сделать вещи еще более сложными, помните, что ЦП хранит часто используемые куски памяти в кеше ЦП для сверхбыстрого доступа. Если вы сделаете образ памяти программы достаточно большим, ваша программа не сможет эффективно использовать кеш, а в худшем случае встраивание может замедлить вашу программу. В некоторой степени компилятор может рассчитать компромиссы и может принять более правильные решения, чем вы, просто взглянув на исходный код.
Вывод из другого обсуждения здесь:
Есть ли недостатки с встроенными функциями?
Видимо, нет ничего плохого в использовании встроенных функций.
Но стоит отметить следующие моменты!
Чрезмерное использование встраивания может замедлить работу программ. В зависимости от размера функции, ее вставка может привести к увеличению или уменьшению размера кода. Встраивание очень маленькой функции доступа обычно уменьшает размер кода, тогда как встраивание очень большой функции может значительно увеличить размер кода. На современных процессорах меньший код обычно выполняется быстрее из-за лучшего использования кэша команд. - Google Guidelines
Преимущества скорости встроенных функций имеют тенденцию уменьшаться по мере увеличения размера. В какой-то момент накладные расходы на вызов функции становятся небольшими по сравнению с выполнением тела функции, и выгода теряется - источник
Есть несколько ситуаций, когда встроенная функция может не работать:
- Для функции, возвращающей значения; если существует оператор возврата
- Для функции, не возвращающей никаких значений; если существует оператор loop, switch или goto.
- Если функция рекурсивная. -Источник
__inline
ключевое слово заставляет функцию быть встроенной, только если вы укажете опцию оптимизации. Если указана оптимизация, или нет__inline
Соблюдается, зависит от настройки опции встроенного оптимизатора. По умолчанию встроенный параметр действует при каждом запуске оптимизатора. Если вы укажете оптимизировать, вы также должны указать опцию noinline, если вы хотите__inline
ключевое слово, которое нужно игнорировать. -Источник