встроенное ключевое слово против встраиваемой концепции

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

Читая ниже, различите inline ключевое слово и «встраивание» концепции.

Вот мой вывод:

Концепция встраивания

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

Ключевое слово inline

Восприятие А

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

Я оспариваю это по следующим причинам:

  1. Более крупные и рекурсивные функции не встраиваются, и компилятор игнорирует ключевое слово inline.
  2. Меньшие функции автоматически встраиваются оптимизатором независимо от того, упоминается ключевое слово inline или нет.

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

Восприятие B

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

Единственное детерминированное использование inline - поддержание правила единого определения.

т.е. если функция объявлена ​​с inline, то только требуются следующие вещи:

  1. Даже если его тело находится в нескольких единицах перевода (например, включает этот заголовок в несколько файлов .cpp), компилятор сгенерирует только одно определение и избежит ошибки компоновщика нескольких символов. (Примечание: если тела этой функции отличаются, то это неопределенное поведение.)
  2. Тело функции inline должно быть видимым / доступным для всех единиц перевода, которые его используют. Другими словами, объявление inline функции в .h и определение в любом .cpp файле приведет к «неопределенной ошибке компоновщика символов» для других .cpp файлов.

Вердикт

Восприятие «А» полностью неправильное, а восприятие «Б» полностью правильное.

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


person iammilind    schedule 20.11.2014    source источник
comment
вы можете использовать static inline и поместить его в заголовок. Это заставит компилятор использовать эту функцию в каждой единице перевода отдельно.   -  person Luka Rahne    schedule 20.11.2014
comment
@LukaRahne: Или вы можете просто использовать inline и поместить его в заголовок, без странной ситуации наличия нескольких версий функции (с разными адресами и отдельными копиями статических переменных).   -  person Mike Seymour    schedule 20.11.2014
comment
@LukaRahne static важно сделать эти функции, определенные в разных ЕП, на самом деле совершенно разными функциями. В противном случае они все равно должны были бы быть идентичными.   -  person Deduplicator    schedule 20.11.2014
comment
Неправильно - это немного сурово, вам не кажется. Перспектива A - это стандартное объяснение, данное программистам на C, изучающим C ++. В этом нет ничего плохого, это просто неполно. Здесь не упоминается, как, например, меняются правила, касающиеся множественных деклараций и определений. Совместите обе точки зрения, и вы получите более полную картину.   -  person Elias Van Ootegem    schedule 20.11.2014
comment
В некоторых компиляторах ключевое слово inline напоминает компилятору о необходимости встроить функцию в режим отладки или когда оптимизация отключена. Для более высоких уровней оптимизации компилятор может встраивать небольшие функции независимо от того, используется ключевое слово inline или нет. Помните, что ключевое слово inline также можно использовать с автономными функциями. У меня эта концепция работает над компилятором IAR EWARM.   -  person Thomas Matthews    schedule 20.11.2014
comment
Есть проблема с основной предпосылкой этого вопроса, которая состоит в том, что определение / стандарт языка - и, следовательно, любой ответ, основанный на игре по языковому праву - не имеет абсолютно никакого прямого отношения к тому, что на самом деле делает компилятор . например Меньшие функции автоматически встраиваются оптимизатором - ›какой оптимизатор? Что делать, если я использую TCC или -O0? Что, если эта функция еще не завершена на моей платформе? Что, если я использую пессимизирующий компилятор (думаю, это уже обсуждалось здесь ранее)? Язык этого не определяет; восприятие A полностью зависит от реализации.   -  person Leushenko    schedule 20.11.2014
comment
Я думаю, что это довольно хорошее чтение по встраиванию: drdobbs.com/inline-redux/184403879   -  person Fred Larson    schedule 20.11.2014
comment
Восприятие A неверно только в том, что inline не гарантирует какой-либо эффект по стандарту, но компиляторы могут гарантировать любой эффект, который они хотят. Поэтому неправильно говорить, что inline не влияет на встраивание, только то, что не гарантируется какой-либо эффект - но все же может!   -  person cdhowie    schedule 21.11.2014
comment
@MooingDuck: есть вопрос; это просто явно не отмечено знаком вопроса. В самом последнем предложении говорится, что он хочет знать, верен этот вердикт или нет. Это окольный способ спросить, я пришел к такому выводу; это правильно?   -  person Cornstalks    schedule 21.11.2014


Ответы (2)


Я не был уверен в вашем утверждении:

Меньшие функции автоматически "встраиваются" оптимизатором независимо от того, упоминается он или нет ... Совершенно ясно, что пользователь не имеет никакого контроля над функцией "встраивание" с использованием ключевого слова inline.

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

Я просмотрел репозиторий Github, чтобы найти Clang и LLVM. (Спасибо, программное обеспечение с открытым исходным кодом!) Я обнаружил, что ключевое слово inline действительно повышает вероятность встраивания функции в Clang / LLVM.

Поиск

Поиск слова inline в репозитории Clang приводит к спецификатору токена kw_inline. Похоже, что Clang использует умную систему на основе макросов для создания лексического анализатора и других функций, связанных с ключевыми словами, так что прямых упоминаний типа if (tokenString == "inline") return kw_inline не обнаружено. Но здесь в >, мы видим, что kw_inline вызывает вызов DeclSpec::setFunctionSpecInline().

case tok::kw_inline:
  isInvalid = DS.setFunctionSpecInline(Loc, PrevSpec, DiagID);
  break;

Внутри этой функции мы установить бит и выдать предупреждение, если это дубликат inline:

if (FS_inline_specified) {
  DiagID = diag::warn_duplicate_declspec;
  PrevSpec = "inline";
  return true;
}
FS_inline_specified = true;
FS_inlineLoc = Loc;
return false;

При поиске FS_inline_specified в другом месте мы видим, что это единственный бит в битовом поле и используется в функции получения, isInlineSpecified():

bool isInlineSpecified() const {
  return FS_inline_specified | FS_forceinline_specified;
}

Выполняя поиск сайтов для звонков isInlineSpecified(), мы находим кодогенератор, в котором мы конвертируем дерево синтаксического анализа C ++ в промежуточное представление LLVM:

if (!CGM.getCodeGenOpts().NoInline) {
  for (auto RI : FD->redecls())
    if (RI->isInlineSpecified()) {
      Fn->addFnAttr(llvm::Attribute::InlineHint);
      break;
    }
} else if (!FD->hasAttr<AlwaysInlineAttr>())
  Fn->addFnAttr(llvm::Attribute::NoInline);

Clang к LLVM

Мы закончили этап синтаксического анализа C ++. Теперь наш спецификатор inline преобразован в атрибут не зависящего от языка объекта LLVM Function. Мы переключаемся с Clang на репозиторий LLVM.

Поиск llvm::Attribute::InlineHint / a> Inliner::getInlineThreshold(CallSite CS) (с устрашающим if блоком без скобок):

// Listen to the inlinehint attribute when it would increase the threshold
// and the caller does not need to minimize its size.
Function *Callee = CS.getCalledFunction();
bool InlineHint = Callee && !Callee->isDeclaration() &&
  Callee->getAttributes().hasAttribute(AttributeSet::FunctionIndex,
                                       Attribute::InlineHint);
if (InlineHint && HintThreshold > thres
    && !Caller->getAttributes().hasAttribute(AttributeSet::FunctionIndex,
                                             Attribute::MinSize))
  thres = HintThreshold;

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

getInlineThreshold(), похоже, имеет только , участник SimpleInliner:

InlineCost getInlineCost(CallSite CS) override {
  return ICA->getInlineCost(CS, getInlineThreshold(CS));
}

Он вызывает виртуальный метод, также называемый getInlineCost, для его указателя-члена на экземпляр InlineCostAnalysis.

Выполняя поиск ::getInlineCost(), чтобы найти версии, являющиеся членами класса, мы находим одну, которая является членом AlwaysInline, что является нестандартной, но широко поддерживаемой функцией компилятора, а другую - членом InlineCostAnalysis. Он использует свой параметр Threshold / а>:

CallAnalyzer CA(Callee->getDataLayout(), *TTI, AT, *Callee, Threshold);
bool ShouldInline = CA.analyzeCall(CS);

CallAnalyzer::analyzeCall() содержит более 200 строк, а

InlineCallIfPossible() выполняет встраивание на основе решения shouldInline().

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

Следовательно, ваше Восприятие B частично неверно, потому что по крайней мере один крупный компилятор изменяет свое поведение оптимизации на основе ключевого слова inline.

Однако мы также можем видеть, что inline - это только намек, и другие факторы могут перевесить его.

person japreiss    schedule 20.11.2014
comment
Комментарий Бьярна Страуструпа: На протяжении десятилетий люди обещали, что компилятор / оптимизатор будет или скоро станет лучше людей для встраивания. Теоретически это может быть правдой, но для хороших программистов это все еще не на практике, особенно в среде, где оптимизация всей программы невозможна. Разумное использование явного встраивания принесет большие выгоды. - person iammilind; 24.11.2014
comment
Спасибо за награду! Было интересно узнать больше о внутреннем устройстве LLVM. - person japreiss; 30.11.2014
comment
@iammilind: Я не уверен, как статический компилятор мог ожидать лучше, чем люди, знать, как оптимизировать вещи, если код не аннотирован, чтобы указать, как часто будут происходить различные вещи. Если изменение сделает программу на 10% быстрее при обработке одних файлов и на 50% медленнее при обработке других, вряд ли можно ожидать, что компилятор узнает, будет ли это изменение хорошим или плохим, не зная, на какие файлы программа потратит больше. время хрустит. Динамический компилятор (JIT) мог бы использовать шаблоны выполнения для выполнения таких определений на лету, но ... - person supercat; 28.05.2015
comment
... статический компилятор не имел бы такой возможности. Статические компиляторы часто могут превзойти динамические в тех случаях, когда их можно направить на оптимизацию для соответствующих случаев, но этот процесс обычно требует знания будущих компиляторов, которые невозможно предсказать. - person supercat; 28.05.2015
comment
Стоит прочитать это. Исследование того, могут ли компиляторы изменить решение о встраивании функции или нет, в зависимости от того, пишете ли вы inline в объявлении, blog.tartanllama.xyz/inline-hints. Рассмотрены GCC и Clang. - person JohnKoch; 12.11.2019

Оба верны.

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

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

person Mike Seymour    schedule 20.11.2014
comment
Конечно, есть оптимизация LTO / всей программы, которая не зависит от определения в том же TU, что и использование. - person Deduplicator; 20.11.2014
comment
@Deduplicator: Действительно, именно поэтому я квалифицировал его для многих компиляторов. Я думал добавить краткое описание других схем, но это выходит за рамки простого вопроса. - person Mike Seymour; 20.11.2014