Когда я должен написать ключевое слово "inline" для функции/метода?

когда я должен написать ключевое слово inline для функции/метода в C++?

после просмотра некоторых ответов, некоторые связанные вопросы:

  • когда я не напишите ключевое слово "inline" для функции/метода в C++?

  • когда компилятор не будет знать, когда сделать функцию / метод "inline"?

  • имеет ли значение, если приложение многопоточный когда один пишет 'inline' для функции / метода?

12 ответов


Ох, одна из моих любимых мозолей.

inline больше нравится static или extern чем директива, сообщающая компилятору встроить ваши функции. extern, static, inline директивы связывания, использовали почти исключительно линкер, а не компилятор.

говорят, что inline указывает компилятору, что вы считаете, что функция должна быть встроена. Возможно, это было верно в 1998 году, но десять лет спустя компилятор не нуждается в таких подсказках. Не упоминать люди обычно ошибаются, когда дело доходит до оптимизации кода, поэтому большинство компиляторов игнорируют "подсказку".

  • static - имя переменной/функции не может использоваться в других единицах перевода. Компоновщику необходимо убедиться, что он случайно не использует статически определенную переменную/функцию из другой единицы перевода.

  • extern - используйте это имя переменной / функции в этом блоке перевода, но не жалуйтесь, если это не определено. Компоновщик отсортирует его и убедится, что весь код, который пытался использовать какой-то символ extern, имеет свой адрес.

  • inline - эта функция будет определена в нескольких единицах перевода, не беспокойтесь об этом. Компоновщику необходимо убедиться, что все единицы перевода используют один экземпляр переменной/функции.

Примечание: как правило, объявление шаблонов inline is бессмысленно, поскольку они имеют семантику связи inline уже. Однако,explicit специализация и создание экземпляров шаблонов требуются inline использовать.


конкретные ответы на ваши вопросы:

  • когда я должен написать ключевое слово "inline" для функции/метода в C++?

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

  • когда я не должен писать ключевое слово "inline" для функции/метода в C++?

    не добавляйте inline только потому, что вы думаете, что ваш код будет работать быстрее, если компилятор inlines он.

  • когда компилятор не будет знать, когда сделать функцию / метод "inline"?

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

    как в сторону, чтобы предотвратить встраивание в GCC, используйте __attribute__(( noinline )), а в Visual Studio используйте __declspec(noinline).

  • имеет ли значение, является ли приложение многопоточным, когда он пишет "inline" для функции/метода?

    многопоточность никак не влияет на встраивание.


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

учитывая два исходных файла, такие как:

  • inline111.cpp:

    #include <iostream>
    
    void bar();
    
    inline int fun() {
      return 111;
    }
    
    int main() {
      std::cout << "inline111: fun() = " << fun() << ", &fun = " << (void*) &fun;
      bar();
    }
    
  • inline222.cpp:

    #include <iostream>
    
    inline int fun() {
      return 222;
    }
    
    void bar() {
      std::cout << "inline222: fun() = " << fun() << ", &fun = " << (void*) &fun;
    }
    

  • случае A:

    Compile:

    g++ -std=c++11 inline111.cpp inline222.cpp
    

    выход:

    inline111: fun() = 111, &fun = 0x4029a0
    inline222: fun() = 111, &fun = 0x4029a0
    

    Обсуждение:

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

    2. Линкер не жалуется на Одно Правило Определения, as fun() объявлен inline. Однако, потому что inline111.cpp является первой единицей перевода (которая фактически вызывает fun()) обработанный компилятором, компилятор создает экземпляр fun() по его первый вызов-встреча в inline111.cpp. Если компилятор решит не расширения fun() на его призыв где-либо еще в вашей программе (например с inline222.cpp), вызов fun() всегда будет связан с его экземпляром, созданным из inline111.cpp (вызов fun() внутри inline222.cpp может также создать экземпляр в этой единице перевода,но он останется несвязанным). Действительно, это видно из тождественного &fun = 0x4029a0 распечатки.

    3. наконец, несмотря на inline предложение компилятору на самом деле расширить ОДН-vkladyw fun(), это игнорировать ваше предложение полностью, что понятно, потому что fun() = 111 в обеих строках.


  • Случай B:

    Compile (обратите внимание на обратный порядок):

    g++ -std=c++11 inline222.cpp inline111.cpp
    

    выход:

    inline111: fun() = 222, &fun = 0x402980
    inline222: fun() = 222, &fun = 0x402980
    

    Обсуждение:

    1. этот случай утверждает то, что обсуждалось в Случае.

    2. обратите внимание на важный момент, что если вы прокомментируете фактический вызов fun() на inline222.cpp (например комментарий cout-заявлением inline222.cpp полностью) тогда, несмотря на порядок составления ваших переводческих единиц,fun() будет создан экземпляр при первой встрече вызова в inline111.cpp, в результате чего в печать для Случае Б as inline111: fun() = 111, &fun = 0x402980.


  • Дело C:

    Compile (обратите внимание-O2):

    g++ -std=c++11 -O2 inline222.cpp inline111.cpp
    

    или

    g++ -std=c++11 -O2 inline111.cpp inline222.cpp
    

    выход:

    inline111: fun() = 111, &fun = 0x402900
    inline222: fun() = 222, &fun = 0x402900
    

    Обсуждение:

    1. как описано здесь, -O2 оптимизация поощряет компилятор к на самом деле расширить функции, которые могут быть встроены (обратите внимание также, что -fno-inline и по умолчанию без параметров оптимизации). Как видно из outprint здесь fun() на самом деле был встроенный расширенный (согласно его определению в том особенности единица перевода), в результате чего два разные fun() распечатки. Несмотря на это, есть еще единственный глобально связанный экземпляр fun() (как требуется стандартом), как видно из одинаковых &fun распечатать.

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


1) в настоящее время, практически никогда. Если это хорошая идея встроить функцию, компилятор сделает это без вашей помощи.

2) всегда. См. #1.

(отредактировано, чтобы отразить, что вы разбили свой вопрос на два вопроса...)


когда я не должен писать ключевое слово "inline" для функции/метода в C++?

Если функция определена в вы должны не написать ключевое слово.

когда компилятор не будет знать, когда сделать функцию / метод "inline"?

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

имеет ли значение, является ли приложение многопоточным, когда он пишет "inline" для функции/метода?

нет, это не важно.


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

компилятор обычно делает хорошую работу по обнаружению + оптимизации таких вещей.


  • когда компилятор не будет знать, когда сделать функцию / метод "inline"?

Это зависит от используемого компилятора. Не слепо верьте, что в настоящее время компиляторы знают лучше, чем люди, как встроить, и вы никогда не должны использовать его по соображениям производительности, потому что это директива связи, а не подсказка оптимизации. Хотя я согласен с тем, что идеологически эти аргументы правильны, столкновение с реальностью может быть другим.

после прочтения несколько потоков вокруг я попробовал из любопытства эффекты встроенного кода, который я просто работаю, и результаты заключались в том, что я получил измеримое ускорение для GCC и не ускорялся для компилятора Intel.

(более подробно: математическое моделирование с несколькими критическими функциями, определенными вне класса, GCC 4.6.3 (g++ -O3), ICC 13.1.0 (icpc-O3); добавление встроенных в критические точки, вызванное +6% ускорением с кодом GCC).

поэтому, если вы квалифицируете GCC 4.6 как современный компилятор, результатом является то, что встроенный директива по-прежнему имеет значение, если вы пишете задачи с интенсивным процессором и знаете, где именно находится узкое место.


gcc по умолчанию не вставляет никаких функций при компиляции без оптимизация включена. Я не знаю о visual studio-deft_code

Я проверил это для Visual Studio 9 (15.00.30729.01) путем компиляции с /FAcs и просмотра кода сборки: Компилятор производил вызовы функций-членов без оптимизации, включенной в debug режим. Даже если функция помечена как __forceinline, нет встроенного кода выполнения произведенный.


вы хотите поместить его в самом начале, перед типом возврата. Но большинство компиляторов игнорируют его. Если он определен и имеет меньший блок кода, большинство компиляторов все равно считают его встроенным.


при разработке и отладке кода, оставить inline выход. Это усложняет отладку.

основная причина их добавления - помочь оптимизировать сгенерированный код. Как правило, это увеличивает пространство кода для скорости, но иногда inline сохраняет пространство кода и время выполнения.

тратить такого рода мысли об оптимизации производительности до завершения алгоритма преждевременная оптимизация.


когда следует inline:

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

2.Функция должна быть небольшой, часто вызываемой и делать встроенную действительно выгодна,так как согласно правилу 80-20, попробуйте сделать те функции встроенными, которые оказывают большое влияние на производительность программы.

Как мы знаем, что inline-это просто запрос к компилятору, похожему на register и это будет стоить вам при размере кода объекта.


Если вы не пишете библиотеку или не имеете особых причин, вы можете забыть о inline и использовать link-оптимизация времени. Он удаляет требование о том, что определение функции должно быть в заголовке, чтобы оно рассматривалось для вставки между единицами компиляции, что именно то, что inline позволяет.

(см. есть ли причина, почему не использовать оптимизацию времени ссылки?)