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

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

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

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

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

Спасибо

Ответы

Ответ 1

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

Ответ 2

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

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

Подробнее о gcc см. здесь. Не уверен о VС++, хотя.

Ответ 3

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

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

Причина, по которой все еще доступны, заключается в том, что LTO может принимать большое количество оперативной памяти и процессора - у Ive VС++ было несколько минут на компоновку большого проекта на С++. Иногда его не стоит позволять, пока вы не отправитесь. Вы также можете исчерпать адресное пространство с достаточно большим проектом, так как он должен загрузить весь этот байт-код в оперативную память.

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

Ответ 4

Если функция включена, разница не будет.

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

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

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