Согласно тому, что я прочитал, компилятор не обязан заменять вызов функции встроенной функции своим телом, но сделает это, если сможет. Это заставило меня задуматься - почему у нас есть встроенное слово, если это так? Почему бы не сделать все функции встроенными по умолчанию и позволить компилятору выяснить, может ли он заменить вызовы телом функции или нет?
c++
language-design
optimization
compilation
EpsilonVector
источник
источник
Ответы:
inline
из C; это не было новым для C ++.Есть ключевые слова C (
register
иinline
), которые были разработаны, чтобы позволить программисту помочь в оптимизации кода. В настоящее время они обычно игнорируются, поскольку компиляторы могут лучше выполнять присваивание регистров и решать, когда вставлять функции (фактически, компилятор может либо встроить, либо не встроить функцию в разное время). Генерация кода на современных процессорах гораздо сложнее, чем на более детерминированных, которые были обычны, когда Ричи изобретал C.Что означает это слово в C ++, так это то, что оно может иметь несколько идентичных определений и должно быть определено в каждой единице перевода, которая его использует. (Другими словами, вам нужно убедиться, что он может быть встроен.) Вы можете иметь
inline
функцию в заголовке без проблем, и функции-члены, определенные в определении класса, автоматически становятся эффективнымиinline
.источник
inline
.inline
сначала это было стандартизировано в C ++, хотя оно уже было доступно в качестве расширения поставщика в C .... да, кажется, оно было добавлено в стандарт C в C99.inline
в C99 и позже отличаются от правил дляinline
в C ++.Первоначально
inline
был очень сильный намек на то, что вызовы функции должны быть встроены.Но единственное гарантированный эффект
inline
состоит в том, что функция может быть определена (эффективно идентично) в нескольких единицах перевода, например, вы поместите определение в заголовочный файл.В настоящее время некоторые компиляторы очень заинтересованы в подсказке, например, g ++. И некоторые компиляторы относятся к этому менее серьезно, например Visual C ++. Но все должны соблюдать гарантии.
К сожалению, эти два значения - подсказка по оптимизации и то, что мы могли бы назвать опровергаемым определением на уровне компоновщика, - принадлежат одному и тому же ключевому слову, поскольку это означает, что вы не можете иметь одно без другого.
Также прискорбно, что
inline
(или лучше отдельное ключевое слово о отбрасываемом определении) не может быть применено к данным .Потребность в сбрасываемых данных на уровне компоновщика возросла, так как модули только с заголовками стали более популярными. Например, многие вспомогательные библиотеки Boost доступны только для заголовков.
Однако для данных вы можете применить небольшую хитрость с шаблонами. Определите его в некотором шаблоне класса, предоставьте
typedef
параметр шаблонаvoid
(или любой другой). Это связано с тем, что правило единого определения делает специальное исключение для шаблонов.Примечания:
¹
inline
переменные будут поддерживаться в C ++ 17 .источник
inline
.Почему бы не сделать все функции встроенными по умолчанию? Потому что это инженерный компромисс. Существует как минимум два типа «оптимизации»: ускорение программы и уменьшение размера (объема памяти) программы. Встраивание вообще ускоряет вещи. Это избавляет от накладных расходов на вызов функции, избегая выталкивания и извлечения параметров из стека. Однако это также увеличивает объем памяти программы, поскольку теперь каждый вызов функции должен заменяться полным кодом функции. Чтобы сделать вещи еще более сложными, помните, что ЦП хранит часто используемые куски памяти в кеше ЦП для сверхбыстрого доступа. Если вы сделаете образ памяти программы достаточно большим, ваша программа не сможет эффективно использовать кеш, а в худшем случае встраивание может замедлить вашу программу.
источник
Чтобы понять «inline», вам нужно понять историю и то, на что была похожа жизнь 20 (и 30) лет назад.
Мы писали код на компьютерах, у которых было мало памяти, поэтому компилятор не мог обрабатывать весь код, составляющий программу, за один раз. Компилятор также был очень медленным, так что вам не нужно было перекомпилировать код, который не изменился - более 24 часов (на компьютере, который стоит более дорогой машины), чтобы перекомпилировать весь код, было нормальным для нескольких проектов, которые я работал над.
Поэтому каждый файл кода был отдельно скомпилирован в объектные файлы. Каждый объектный файл начинался со списка всех содержащихся в нем функций вместе с «адресом» функции. У объектного файла также был список всех функций, которые он вызывал в других объектных файлах, вместе с расположением вызова.
Линкер сначала прочитать все объектные файлы и создать список всех функций , которые они экспортируются вместе с файлом они были в и там адрес. Затем он перечитает все объектные файлы, выведет их в программный файл, обновляя при этом все «внешние» вызовы функций адресом функции.
Компоновщик не изменял и не оптимизировал машинный код, созданный компилятором, никоим образом, кроме как для исправления ссылок на вызовы внешних функций. Компоновщик был частью операционной системы и предшествовал большинству компиляторов. Когда люди писали новый компилятор, им нужно было, чтобы он работал с текущими компоновщиками и имел возможность ссылаться на текущие объектные файлы, иначе системные вызовы не могли быть сделаны.
Компилятор видел только код в файле «.c» или «.cpp», который он компилировал вместе со всеми включенными заголовочными файлами. Поэтому он не мог выполнить какую-либо оптимизацию на основе кода в других файлах «.c» или «.cpp».
Ключевое слово «inline» позволило определить тело функции (метода) в заголовочном файле, что позволило компилятору использовать код функции при компиляции кода, который ее вызывает. Например, скажем, у вас есть класс коллекции, определенный в другом файле .cpp, этот класс будет иметь метод «isEmpty», содержащий одну строку кода, и результирующая программа получит большое ускорение, если вместо вызова функции вызов функции был заменен этой строкой.
В то время ключевое слово «inline» рассматривалось как «дешевый и простой» способ, позволяющий инкапсулировать данные, избегая при этом затрат на вызовы функций, без этого многие программисты имели бы доступ только к закрытым полям объекта. (Макросы, где гораздо хуже «встраивать» код, чем обычно в то время.)
В наши дни «компоновщики» выполняют большую часть оптимизации кода и, как правило, пишутся некой командой как компилятор. Компилятор часто просто проверяет правильность кода и «сжимает» его, оставляя большую часть задачи создания машинного кода компоновщику.
источник
Давайте посмотрим, что говорится в стандарте (выделены важные части жирным шрифтом):
Итак, если вы хотите быть уверены, вы должны прочитать документацию вашего компилятора.
Встраивать все - плохая идея, так как это может привести к большому количеству дублированного машинного кода ...
Так что вы должны знать:
источник
inline
, повторяя знание, поскольку ОП, кажется, упускает часть, и это основная причина, по которой он не понимает. Чтобы получить очко, ему нужно понять, что за этим стоит ...Позвольте мне дать вам вескую причину для использования встроенного ключевого слова.
Во встроенной системе, такой как принтер билетов или аналогичная меньшая система. Процессор очень ограничен, и вызов функции (подготовка параметров функции в стеке, вызов, извлечение параметров из стека и возврат ответа и т. Д.) Может занять несколько мс, кроме самой функции.
Допустим, время вызова составляет около 60 мс (только для вызова, а не фактической функции), и вы должны сделать 50 итераций (циклические или итеративные вызовы в дереве).
Время для перемещения вперед и назад после вызова этой функции займет 60 * 50 = 3000 (3 секунды).
Если у вас есть память, вы обязательно сделаете inline, чтобы сэкономить 3 секунды.
Таким образом, inline в основном используются, когда вам нужна скорость выполнения. В некоторых проектах, с которыми я был связан, время вызова было больше, чем время выполнения, классическая ситуация, когда использовать inline.
источник