Разница в встроенных функциях компилятором или компоновщиком?

Мне интересно, есть ли разница между встроенными функциями на уровне компоновщика или уровне компилятора с точки зрения скорости выполнения?

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

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

Я полагаю, что это может зависеть от компилятора, и в этом случае меня больше всего интересует Visual C++ (Windows) и gcc (Linux).

спасибо

4 ответов


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


Как правило, к моменту запуска компоновщика ваш источник уже скомпилирован в машинный код. Задача компоновщиков-взять все фрагменты кода и ссылке затем вместе (возможно, фиксируя адреса по пути). В таком случае, нет места для выполнения подстановки.

но не все потеряно. Gcc предоставляет механизм оптимизации времени связи (с использованием опции-flto) при компиляции и связывании. Это заставляет gcc создавать байтовый код, который затем может компилируется и связывается компоновщиком в один исполняемый файл. Поскольку байт-код содержит больше информации, чем оптимизированный машинный код. Компоновщик теперь может выполнять радикальную оптимизацию на все кода. То, что компилятор не может сделать.

посмотреть здесь для получения более подробной информации о gcc. Не уверен в VC++, хотя.


встраивание обычно выполняется в пределах одной единицы перевода (.cpp file). Когда вы вызываете функции в другом файле, они никогда не встроены.

Link Time Optimization (LTO) изменяет это, позволяя встроенному работать между единицами перевода. Он всегда должен быть равен или лучше (иногда очень значительно) регулярной связи с точки зрения эффективности сгенерированного кода.

причина оба варианта все еще доступна что LTO может принять большое объем ОЗУ и процессора – у меня было VC++ занять несколько минут на связывание большого проекта c++ раньше. Иногда это не стоит того, чтобы включить, пока вы не отправите. У вас также может закончиться адресное пространство с достаточно большим проектом, так как он должен загрузить весь байт-код в ОЗУ.

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


Если функция is кликабельны, не было бы никакой разницы.

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

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

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