Ответ 1
Это зависит от ваших флагов компиляции. С -combine
и -fwhole-program
, gcc
будет выполнять функцию, вложенную через границы cpp. Я не уверен, что сделает компоновщик, если вы скомпилируете несколько файлов объектов.
Мне известно, что ключевое слово inline
имеет полезные свойства, например. для хранения специализированных шаблонов внутри файла заголовка.
С другой стороны, я часто читал, что inline
почти бесполезен в качестве подсказки для компилятора для фактически встроенных функций.
Кроме того, ключевое слово не может использоваться внутри файла cpp, поскольку компилятор хочет проверить функции, помеченные ключевым словом inline
, когда они вызывают.
Поэтому я немного запутался в "автоматических" встроенных возможностях современных компиляторов (а именно gcc 4.43). Когда я определяю функцию внутри cpp, может ли компилятор встраивать его в любом случае, если он считает, что вложение имеет смысл для функции или я лишу его возможности оптимизации? (Что было бы хорошо для большинства функций, но важно знать, что маленькие называются очень часто)
Это зависит от ваших флагов компиляции. С -combine
и -fwhole-program
, gcc
будет выполнять функцию, вложенную через границы cpp. Я не уверен, что сделает компоновщик, если вы скомпилируете несколько файлов объектов.
В блоке компиляции у компилятора не будет проблем с встроенными функциями (даже если они не помечены как встроенные). По сравнению с единицами компиляции это сложнее, но современные компиляторы могут это сделать.
Использование inline tag
мало влияет на "современные" компиляторы и независимо от того, действительно ли оно выполняет функции (у него лучшая эвристика, чем у человека) (если вы не укажете флаги, чтобы заставить его так или иначе (что обычно плохая идея, поскольку люди плохо принимают это решение)).
Microsoft Visual С++ смог сделать это, по крайней мере, с Visual Studio 2005. Они называют это "Оптимизация всей программы" или "Генерация кода времени соединения". В этой реализации компилятор фактически не создает машинный код, а записывает предварительно обработанный код С++ в объектные файлы. Затем компоновщик объединит весь код в один огромный блок кода и выполнит фактическую компиляцию.
GCC может сделать это, по крайней мере, с версии 4.5 с существенными улучшениями в GCC 4.7. Насколько мне известно, эта функция по-прежнему считается несколько экспериментальной (по крайней мере, поскольку многие дистрибутивы Linux не используют ее). Реализация GCC работает аналогично, сначала записывая предварительно обработанный источник (в его промежуточном языке GIMPLE) в объектные файлы, а затем компилируя все объектные файлы в один объектный файл, который затем передается в компоновщик (это позволяет GCC продолжать работать с существующими линкерами).
Многие крупные проекты на С++ также делают то, что сейчас называется "сборками единства". Вместо того, чтобы передавать сотни отдельных исходных файлов С++ в компилятор, создается один исходный файл, который включает в себя все другие исходные файлы в проекте. Первоначальный замысел заключается в уменьшении времени компиляции (поскольку заголовки и т.д. Не нужно анализировать снова и снова), но в качестве побочного эффекта он будет иметь тот же результат, что и упомянутые выше методы LTO/LTCG: предоставление компилятор отлично просматривает все функции во всех модулях компиляции.
Я прыгаю между впечатлением от моей изобретательности С++ (MSVC 2010) и ее глупости. Некоторый код, который преобразовал формат пикселей через шаблоны, которые были бы разрешены в 5-10 инструкций по сборке, когда они были правильно встроены, раздувается в килобайтах (!) Вызовов вложенных функций. В других случаях он настолько агрессивно, что целые классы исчезают, хотя они содержат нетривиальные функции.
В стандарте ничего не говорится о том, как функция может быть встроена. И компиляторы могут встроить функции, если они имеют доступ к их реализации. Если у вас есть только заголовок с двоичными файлами, это было бы невозможно. Если он находится в том же модуле, компилятор может встроить эту функцию, даже если она находится в файле cpp
.