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

StackOverflow https://stackoverflow.com/questions/1759300

  •  20-09-2019
  •  | 
  •  

Вопрос

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

Увидев некоторые ответы, некоторые сопутствующие вопросы:

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

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

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

Это было полезно?

Решение

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

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

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

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

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

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

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


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

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

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

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

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

  • Когда компилятор не будет знать, когда сделать функцию/метод «встроенной»?

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

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

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

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

Другие советы

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

Даны два исходных файла, таких как:

  • 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;
    }
    

  • Случай А:

    Скомпилировать:

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

    Выходной сигнал:

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

    Обсуждение:

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

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

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


  • Случай В:

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

    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, приводящий к распечатке для Случай В как inline111: fun() = 111, &fun = 0x402980.


  • Случай С:

    Скомпилировать (примечание -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 является По умолчанию без вариантов оптимизации).Как видно из приведенного здесь вывода, fun() на самом деле это было встроенный расширенный (согласно его определению в том, что конкретный единица перевода), в результате чего получается два другой fun() распечатки.Несмотря на это, все еще существует только один глобально связанный экземпляр fun() (в соответствии с требованиями стандарта), как видно из идентичный &fun распечатка.

Вам все равно необходимо явно встроить свою функцию при специализации шаблона (если специализация находится в файле .h).

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

2) Всегда.См. № 1.

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

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

Если функция определена в .cpp файл, вам следует нет напишите ключевое слово.

Когда компилятор не будет знать, когда сделать функцию/метод «встроенной»?

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

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

Нет, это совершенно не имеет значения.

  • Когда компилятор не будет знать, когда сделать функцию/метод «встроенной»?

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

Прочитав несколько тем, я из любопытства попробовал влияние встроенных функций на код, над которым я только что работал, и в результате я получил измеримое ускорение для 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 и просмотрев ассемблерный код:Компилятор создавал вызовы функций-членов без включенной оптимизации в отлаживать режим.Даже если функция помечена знаком __forceinline _forceinline, встроенный код среды выполнения не создается.

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

При разработке и отладке кода оставляйте inline вне.Это усложняет отладку.

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

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

Когда следует встроить:

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

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

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

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

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

Встроенная функция C++ — это мощная концепция, которая обычно используется с классами.Если функция является встроенной, компилятор помещает копию кода этой функции в каждую точку вызова функции во время компиляции.

Любое изменение встроенной функции может потребовать перекомпиляции всех клиентов функции, поскольку компилятору придется заменить весь код еще раз, иначе он продолжит использовать старую функциональность.

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

Определение функции в определении класса является определением встроенной функции, даже без использования спецификатора inline.

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

#include <iostream>

using namespace std;

inline int Max(int x, int y) { return (x > y)? x : y; }

// Main function for the program
int main() {
   cout << "Max (100,1010): " << Max(100,1010) << endl;

   return 0;
}

для получения дополнительной информации см. здесь.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top