С большей вероятностью компилятор встроит функцию в объявление класса с указанным ключевым словом inline?

Недавно я просматривал код коллеги и заметил, что он поставил ключевое слово «inline» перед кучей функций Getter, которые были определены в объявлении класса.

eg

class Foo
{
public:
    inline bool GetBar() const { return m_Bar; }
private:
    bool m_Bar;
};

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

Его ответ был следующим:

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

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

  3. Наличие ключевого слова inline означает, что предупреждения «предупреждать, если не встроены» будут срабатывать (если включено), если указанные функции не встроены по какой-либо причине.

Лично я вообще не согласен с первой причиной. Для меня наличия функций, определенных в объявлении класса, достаточно, чтобы продемонстрировать намерение.

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

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

Спасибо!


person JBeFat    schedule 11.11.2015    source источник
comment
С большей вероятностью компилятор встроит функцию в объявление класса с указанным ключевым словом inline? - нет. Единственная цель inline в C ++ - предотвратить нарушения ODR в результате определения функций в файле заголовка. (в то время как в C он имеет ровно ноль полезных целей. Я знаю, что вы не спрашивали о C, но я подумал упомянуть этот факт в качестве пояснения.)   -  person The Paramagnetic Croissant    schedule 11.11.2015
comment
@TheParamintageCroissant Я подумал, что это может быть так, но мне бы очень хотелось узнать наверняка. Вы точно знаете, что ни один из основных компиляторов (скажем, MSVC, GCC и Clang) не принимает во внимание ключевое слово inline в этом случае?   -  person JBeFat    schedule 11.11.2015
comment
Оба stackoverflow.com/questions/31722473/ и stackoverflow.com/questions/17325911/ может помочь. И лично я гораздо больше о нарушениях отступов или использовании переменных типа i и j для индексов массива.   -  person Craig Estey    schedule 11.11.2015
comment
@JBeFat Я знаю, что GCC не игнорирует ключевое слово полностью; хотя я вижу, что ваш коллега хочет быть уверенным, что функция встроена. Если это так, ему лучше использовать __attribute__((always_inline)), потому что inline не гарантирует встраивания. А еще лучше, ему, вероятно, следует оставить решение компилятору; возможно, после включения оптимизации всего модуля (с использованием -flto).   -  person The Paramagnetic Croissant    schedule 11.11.2015
comment
@GregorMcGregor Простите меня, если моя формулировка не была особенно ясной - я знаю, что функция уже неявно встроена, мой вопрос касался того, влияет ли ключевое слово inline как-либо на решение компилятора встроить функцию.   -  person JBeFat    schedule 11.11.2015
comment
@TheParamintageCroissant Иногда хочется обратного. Я видел что-то встроенное в gcc [без встроенного (в C)] даже с двумя параметрами командной строки -fno-inline- *. Мне пришлось использовать __attribute __ ((never_inline))   -  person Craig Estey    schedule 11.11.2015
comment
@CraigEstey Да, именно так, но в этом посте OP пишет, что автор кода хочет обеспечить встраивание. Конечно, он мог бы захотеть обратного - для чего удаление inline не было бы решением, и ваше предложение относительно этих атрибутов было бы применимо.   -  person The Paramagnetic Croissant    schedule 11.11.2015
comment
Спасибо за ваши комментарии, ребята! На самом деле вопрос не в том, как заставить функции быть встроенными, и я знаю, что ключевое слово inline - это всего лишь предложение. Мне интересно выяснить, обоснован ли его ответ (3 пронумерованных пункта), потому что я не могу найти никаких доказательств в Интернете.   -  person JBeFat    schedule 11.11.2015
comment
@JBeFat inline может быть допустимым. Предположим, у вас есть функция с большим членом, которую компилятор не встроит [и не имеет встроенного ключевого слова]. Но, несмотря на выбор компилятора, автор определяет, что функция должна / должна быть встроена для случая использования. Это действительно так. Я видел 200 лайнеров с always_inline, потому что они используются в 1-2 местах, и автор хотел более чистую альтернативу макросу cpp. Или, если функция-член поднимается вне определения класса, автоматическая / неявная встроенная строка больше не применяется. встроенная строка покажет намерение, будет там полезна. ИМО, просмотр комментариев более полезен - показать намерение и т. Д.?   -  person Craig Estey    schedule 11.11.2015


Ответы (3)


В конкретном примере, который вы упомянули, GetBar неявно встроен, что делает ключевое слово inline излишним. В разделе 7.1.2 P3 стандарта C ++ говорится:

Функция, определенная в определении класса, является встроенной функцией.

То же самое говорится и в документации VC ++:

Функции-члены класса могут быть объявлены встроенными либо с помощью ключевого слова inline, либо путем помещения определения функции в определение класса.

Как правило, вам не нужно использовать ключевое слово inline. Один эксперт по C / C ++ однажды сказал еще в 2002 году:

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

Вы можете найти полную статью здесь. Вы должны прочитать всю статью, чтобы понять, почему ключевое слово было добавлено в C99. Также в статье рассматриваются extern inline функции.

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

Влияет ли ключевое слово inline каким-либо образом на решение компилятора встроить функцию?

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

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

Это неверная причина. Программист - человек. Обычно человеку очень сложно принять лучшее решение, чем MSVC, в отношении того, следует ли встроить функцию или нет. Во-вторых, что должен делать программист, когда вы говорите ему / ей, что функция должна быть встроена? Компилятор выполняет встраивание. Предупреждение само по себе не говорит вам, нужно ли вам что-то с этим делать, и, если это так, что делать.

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

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

Наличие ключевого слова inline означает, что предупреждения «предупреждать, если не встроены» будут срабатывать (если включено), если указанные функции не встроены по какой-либо причине.

Компилятор MSVC выдает предупреждение C4710, когда функция, выбранная для встраивания был не встроенным. Это предупреждение отключено по умолчанию, потому что большинство разработчиков не заботятся (и не должны) обращать внимание на это предупреждение. Однако вы можете включить это предупреждение. Если вы не являетесь исследователем оптимизации компилятора и не хотите разбираться в алгоритме встраивания, используемом MSVC, вам не следует включать эти предупреждения.

person Hadi Brais    schedule 14.11.2015
comment
Спасибо за информативный ответ. Интересно знать, что MSVC действительно принимает во внимание встроенное ключевое слово (хотя я согласен, что это довольно слабая причина для включения ключевого слова). MSVC - это основной компилятор, который мы используем, поэтому я отмечаю это как ответ. Спасибо еще раз! - person JBeFat; 20.11.2015
comment
Ключевое слово inline действует в большинстве компиляторов при условии, что их параметры оптимизации настроены на попытку встроить только явно встроенные функции. GCC, например, принадлежит к этой категории в -O1 режиме. Однако ни в одном из них добавление явного inline к неявно встроенной функции не имеет никакого эффекта. Это верно и для MSVC. - person AnT; 07.07.2018
comment
@AnT Что вы имеете в виду под неявно встроенной функцией? Вы имеете в виду указание опции оптимизации, которая включает встраивание? В любом случае ключевое слово inline always действует в MSVC независимо от того, включена ли оптимизация или нет. - person Hadi Brais; 07.07.2018
comment
Я имею в виду, что функция, определенная внутри определения класса, всегда встроена по правилам языка. Нет необходимости добавлять явное ключевое слово inline к такому определению - оно уже встроено. Кто-то, по-видимому, полагал, что добавление inline к определению класса делает эту функцию даже более встроенной, чем она есть. Но я не знаю ни одного компилятора, где бы это работало таким образом. - person AnT; 07.07.2018
comment
@AnT Спасибо! Ты прав. Я забыл об этом. Функция-член, определенная внутри определения класса, неявно встроена и имеет тот же эффект, что и функция, определенная снаружи с помощью ключевого слова inline. - person Hadi Brais; 07.07.2018

Редактировать 1: Добавлен код встраивания LLVM (другими словами "clang")

Изменить 2: добавить пояснение, как «разрешить» это.

Фактически правильный

Пункт 1, конечно, не требует пояснений.

Пункт 2 - ерунда - все современные компиляторы (по крайней мере, MS, GCC и Clang [aka XCode]) полностью игнорируют inline ключевые слова и принимают решение исключительно на основе критериев частоты / размера (определение «коэффициента раздувания кода» на основе размера * количества раз, поэтому небольшие функции или функции, вызываемые только несколько раз, с большей вероятностью будут встроены - конечно, геттер будет идеальным выбором для всегда встраивания компилятором, поскольку это будет всего две или три инструкции и, скорее всего, короче, чем загрузка this , затем вызывая функцию получения.

Ключевое слово inline вообще не имеет никакого значения [и стандарт C ++ утверждает, что определение внутри класса в любом случае inline].

Пункт 3 - еще один вероятный сценарий, но я бы подумал, что тот факт, что он неявно встроен в его определение, должен дать тот же результат. Некоторое время назад было обсуждение ключевого слова inline и его значения в списке рассылки Clang, и был сделан вывод, что «компилятор обычно знает лучше».

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

Изменить 1:

Код взят из LLVM "InlineCost.cpp":

InlineCost InlineCostAnalysis::getInlineCost(CallSite CS, Function *Callee,
                                             int Threshold) {
  // Cannot inline indirect calls.
  if (!Callee)
    return llvm::InlineCost::getNever();

  // Calls to functions with always-inline attributes should be inlined
  // whenever possible.
  if (CS.hasFnAttr(Attribute::AlwaysInline)) {
    if (isInlineViable(*Callee))
      return llvm::InlineCost::getAlways();
    return llvm::InlineCost::getNever();
  }

  // Never inline functions with conflicting attributes (unless callee has
  // always-inline attribute).
  if (!functionsHaveCompatibleAttributes(CS.getCaller(), Callee,
                                         TTIWP->getTTI(*Callee)))
    return llvm::InlineCost::getNever();

  // Don't inline this call if the caller has the optnone attribute.
  if (CS.getCaller()->hasFnAttribute(Attribute::OptimizeNone))
    return llvm::InlineCost::getNever();

  // Don't inline functions which can be redefined at link-time to mean
  // something else.  Don't inline functions marked noinline or call sites
  // marked noinline.
  if (Callee->mayBeOverridden() ||
      Callee->hasFnAttribute(Attribute::NoInline) || CS.isNoInline())
    return llvm::InlineCost::getNever();

  DEBUG(llvm::dbgs() << "      Analyzing call of " << Callee->getName()
        << "...\n");

  CallAnalyzer CA(TTIWP->getTTI(*Callee), ACT, *Callee, Threshold, CS);
  bool ShouldInline = CA.analyzeCall(CS);

  DEBUG(CA.dump());

  // Check if there was a reason to force inlining or no inlining.
  if (!ShouldInline && CA.getCost() < CA.getThreshold())
    return InlineCost::getNever();
  if (ShouldInline && CA.getCost() >= CA.getThreshold())
    return InlineCost::getAlways();

  return llvm::InlineCost::get(CA.getCost(), CA.getThreshold());
}

Как можно видеть (если немного покопаться в остальной части кода), есть только проверки для встроенных параметров «всегда» и «никогда». Нет для самого встроенного ключевого слова.

[Обратите внимание, что это встроенный код для clang и clang ++ - clang сам по себе не делает ничего особенно умного при генерации кода, это «просто» (расстроивший сотни программистов, потративших сотни лет на этот проект!) Синтаксический анализатор для C и C ++, который переводится в LLVM IR, все полезные и умные вещи выполняются на уровне LLVM - это действительно хороший способ предоставить "многоязычный" каркас компилятора. Я написал компилятор Pascal, и, несмотря на то, что я новичок в работе с компилятором, мой компилятор (который использует LLVM для генерации реального машинного кода) лучше в тестах (сгенерированного кода), чем Free Pascal - все благодаря LLVM, почти нет это моя работа - за исключением некоторого кода для встраивания нескольких часто называемых функций в один конкретный тест]

У меня нет доступа к источникам для компилятора MS (ох!), И мне не нужно загружать gcc только для того, чтобы проверить это. По опыту я знаю, что все три будут встраивать функции без ключевого слова inline, а gcc будет агрессивно встраивать функции, которые, как он может определить, имеют только одного вызывающего (например, большие static вспомогательные функции)

Изменить 2:

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

Еще один анекдот: мне лично нравится писать if-утверждения в виде

if (some stuff goes here)

(пробел между if и круглыми скобками, но не вокруг предмета внутри), но действующий стандарт кодирования гласит:

if( some stuff goes here )

(без пробела между if и скобками, но вокруг предмета внутри)

Я случайно получил что-то вроде этого:

if ( some stuff goes here )

в коде, который был рассмотрен. Я исправил эту строку, но решил ТАКЖЕ исправить 175 других if-операторов с пробелом после if - всего в этом файле было менее 350 if-операторов, поэтому БОЛЕЕ половины из них были неправильными ...

person Mats Petersson    schedule 11.11.2015
comment
Последний раз, когда я проводил тщательное исследование поведения инлайнинга ICC, было несколько версий назад, но я думаю, что это все еще современный компилятор. Документация и фактическое поведение практически не имели друг с другом ничего общего, но оба указывали, что ключевое слово inline действительно влияет на поведение встраивания в узком граничном диапазоне большого размера функции. Исходный пример был слишком маленьким, чтобы ключевое слово имело значение в любом нормальном компиляторе. Но это не значит, что ключевое слово никогда не влияет на решение о встраивании. - person JSF; 11.11.2015
comment
@GregorMcGregor: Я добавил код из LLVM (по состоянию на пятницу прошлой недели, плюс-минус). В настоящее время на моем компьютере нет исходных кодов для других проектов компилятора, поэтому я не могу предоставить доказательства этого. - person Mats Petersson; 12.11.2015
comment
Единственное важное различие, которое я думаю между использованием ключевого слова inline или нет, заключается в том, что встроенные функции с явной пометкой должны быть определены в одной и той же единице перевода (даже если компилятор решает, что эта функция не должна быть встроена), и функции без ключевого слова inline. не к. Конечно, если не встроенная функция не определена в той же единице перевода, компилятор не может пометить ее как встроенную, потому что он не знает ее тела. - person Peregring-lk; 12.11.2015
comment
@ Peregring-lk: Но здесь мы говорим о функциях, объявленных и определенных в самом теле класса. Это автоматически inline, даже если вы не говорите об этом в коде, и не может быть ничем другим. - person Mats Petersson; 12.11.2015
comment
Сообщение от 2018 г .: blog.tartanllama.xyz/inline-hints - присутствие inline, безусловно, может повлиять на решение компилятора встроить (хотя это не единственный фактор). - person davmac; 27.03.2018

Я игнорирую специфическую для C ++ часть вопроса и сосредотачиваюсь только на обычных встроенных функциях (C или C ++) в соответствии с вашим маркером №2:

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

https://blog.tartanllama.xyz/inline-hints/ содержит некоторые подробности с датой Январь 2018. Вкратце: Clang + LLVM и GCC принимают во внимание ключевое слово при принятии решения о встраивании функции. Это не единственное соображение, но оно действительно влияет на решение.

person Conrad Meyer    schedule 16.02.2019