Разница в встраивании функций компилятором или компоновщиком?
Мне интересно, есть ли разница между встроенными функциями на уровне компоновщика или уровнем компилятора с точки зрения скорости выполнения?
например. если у меня есть все мои функции в .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 файле, но потенциально вызванную из многих мест.