Страницы

Поиск по вопросам

Показаны сообщения с ярлыком inline. Показать все сообщения
Показаны сообщения с ярлыком inline. Показать все сообщения

среда, 26 февраля 2020 г.

как определить inline функцию?

#cpp #функции #inline


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

#include 

void func () {
    std::cout << "func";
}

inline void func2() { func(); }


int main()
{
    std::cout << uint64_t(&func) << " || " << uint64_t(&func2) << std::endl;
    // Пример вывода: 4199872 || 4204384
    return 0;
}

    


Ответы

Ответ 1



Вот так и определять. С помощью inline, понимая, что компилятор может ее встроить, может не встроить, а может встроить и без всякого inline... В некоторых компиляторах есть расширения, которые заставляют компилятор прибегнуть ко встраиванию (например, __forceinline в Visual C++), но это уже нестандарт...

воскресенье, 9 февраля 2020 г.

Почему не рекомендуют ставить ключевое слово inline в реализации метода?

#cpp #inline


Мой коллега увидел, что я ставлю inline методам get и set и в объявлении метода и
в реализации и сказал, что так делать не стоит, но аргументировать это не смог. Я тоже
когда-то про такое слышал, но информации найти не смог. Я знаю, что компиляторы сейчас
забивают даже на форсинлайны и обычный inline им вообще не указ. Но всё-таки, правда
ли что inline ставить лучше только в объявлении метода и какие у этого есть объективные
причины?     
    


Ответы

Ответ 1



Основное назначение слова inline: дать программисту инструмент определять функции так, чтобы у компилятора была возможность встроить их на этапе компиляции и не вызвать в дальнейшем ошибку компоновки. Вторичное назначение: сообщение другим людям работающим над данным кодом, что автор надеется, что компилятор встроит эту функцию. Кроме того это действительно является подсказкой компилятору встроить функцию (я сам сомневался в этом), которая на самом деле влияет на эвристики компилятора, например, увеличивая пороговые значения размера функции выше которых компилятор откажется её встраивать (смотри ссылку на статью в ответе @ixSci) Пусть есть класс Foo с переменной a, к которой нужно обеспечить доступ; есть несколько вариантов, что можно сделать с getA: Определить прямо в классе. class Foo { int a{0}; public: int Foo::getA () { return a; } inline подразумевается, программист сразу понимает что к чему, компоновщик не жалуется о повторном определении. ИМХО — это предпочтительный вариант покуда реализация не занимает больше двух-трёх (пяти?) строк. Определить в хедере вне класса. class Foo { int a{0}; public: int getA (); } int Foo::getA () { return a;} Это тот случай, когда использование слова inline обязательно и для чего оно и было введено, но где именно оно будет компилятору всё равно, вопрос исключительно стилистический. Распишу плюсы и минусы на мой взгляд (все они крайне эфемерны): 2.1. inline только в объявлении — назовём это «основным вариантом». 2.2. inline только в определении — относительно плохой вариант: - При просмотре объявления программист будет рассчитывать, что компилятор не сможет (и не должен) встроить функцию, что для геттера вызывает ряд вопрсов и потенциально ложных предположений, например то что этот геттер не тривиальный и требует каких-то вычислений. 2.3. inline и в объявлении и в определении + При прочтении и объявления, и определения класса сразу видно, что функция встраиваемая. - Чревато скатыванием к предыдущему варианту, если кто-то удалит слово inline и не удосужится проверить. Определение в отдельном *.cpp. // foo.h class Foo { int a{0}; public: int getA (); } // foo.cpp #include "foo.h" int Foo::getA () { return a;} // main.cpp #include "foo.h" int main(void) { Foo foo; return foo.getA(); } Если inline будет присутствовать в объявлении или определении, то это нарушение стандарта, который требует, чтобы определение функции объявленной как inline было доступно в каждой единице трансляции, где она вызывается и оно было в точности одинаковое.

Ответ 2



У Вас в вопросе не ясно, где у Вас расположена реализация методов get/set. Если она расположена в том же файле, что и декларация (предположим, что это заголовок), то если Вы пометили декларацию функции как inline, то реализацию можно помечать, а можно и не помечать. inline у реализации в этом случае будет избыточен. Но тут возникает вопрос, если всё находится в одном файле, то зачем Вам вообще декларация, может сделать только одно определение и этого будет достаточно? Дело вкуса, конечно. Если же реализация у Вас находится в отдельном файле (скажем, cpp), а заголовок включается не только в него, а ещё в другие файлы, то при использовании функции get/set в двух разных объектах трансляции (translation unit), Ваша программа становится «сломанной» (ill-formed). Т.е. так делать попросту нельзя, смотрите [dcl.inline]p6: An inline function or variable shall be defined in every translation unit in which it is odr-used and shall have exactly the same definition in every case Я знаю, что компиляторы сейчас забивают даже на форсинлайны и обычный inline им вообще не указ. Не стоит верить всему, что находите в сети, слишком много домыслов и непонятных утверждений можно найти. Посмотрите эту статью: «Do compilers take inline as a hint?», там хотя бы конкретные вещи приведены.

четверг, 9 января 2020 г.

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

#cpp #inline


Перевод вопроса: https://stackoverflow.com/q/1759300/5697743.

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

После просмотра различных объяснений возникли вопросы:


Когда мне не следует использовать ключевое слово inline для функции или метода в C++?
Когда комилятор не знает, что нужно сделать функцию 'встроенной'?
Отразится ли как-либо использование многопоточности в приложении на работе inline?

    


Ответы

Ответ 1



inline ближе к static или extern, чем к указанию компилятору встроить ваши функции. extern, static и inline используются в основном линковщиком, а не компилятором. Иногда пишут, что inline указывает компилятору, что по-вашему функция должна быть встроенной. Это было правдой и имело смысл в 1998 году, но спустя двадцать лет компилятору больше не нужны такие подсказки. Не стоит и говорить, что люди часто ошибаются, когда дело доходит до оптимизации кода, так что большинство компиляторов прямо игнорируют такие "подсказки". static - имя функции/переменной не может быть использовано в других единицах трансляции. Линковщик должен убедиться, что это случайно не сделано. extern - использовать это имя в данной единице трансляции, но не жаловаться, если оно не определено. Линковщик разберётся и убедится, что у каждого символа есть адрес. inline - эта функция будет определена в разных единицах трансляции, игнорировать это. Линковщик должен убедится, что все единицы трансляции используют один и тот же экземпляр. Примечание: В целом, определение шаблонов с inline бессмысленно, потому что они уже используют семантику линковки, аналогичную предоставляемой inline. Однако, для явной (explicit) специализации и инициализации шаблонов требуется использовать inline. Ответы на ваши вопросы: Когда мне следует использовать ключевое слово inline для функции или метода в C++? Только когда вы хотите определить функцию в заголовке. Точнее, когда более одного определения функции встречается в различных единицах трансляции. Будет хорошей идеей поместить маленькие (однострочные) функции в заголовок, так как это даст больше информации для оптимизации компилятору. Однако, это увеличит время сборки. Когда мне не следует использовать ключевое слово inline для функции или метода в C++? Не используйте inline только из-за уверенности, что ваш код заработает быстрее. Когда компилятор не знает, что нужно сделать функцию 'встроенной'? В целом, компилятор умеет выполнять такую оптимизацию лучше вас. Однако, он не сможет это сделать, если нет определения функции (в данной единице трансляции). Как правило, в максимально оптимизированном коде все private методы встраиваются, просили вы того, или нет. Для предотвращения встраивания в GCC используйте __attribute__(( noinline )), а в Visual Studio - __declspec(noinline). Отразится ли как-либо использование многопоточности в приложении на работе inline? Многопоточность не влияет как на работу inline, так и на встраивание функций.

Ответ 2



Внутри определения класса все методы с кодом будут inline и определить плохо это или хорошо будет сложно, так как компилятор верит только вам, и предупреждений не будет. class A{ void method(){ очень большой } }; Если код метода выложить вне определения класса с директивой inline в header-файле, то есть шанс, что компилятор сделает предупреждение, что код будет больше. class A{ void method(); }; inline void A::method(){...} Всё-бы хорошо, но компиляторы имеют право на всё, и угадать, как они реализуют сложно. Останется только ваше желание видеть предупреждения или нет.

суббота, 14 декабря 2019 г.

inline термин в контексте C# / JIT компилятора

#c_sharp #методы #компилятор #inline #jit


Добрый день.
Столкнулся с таким вопросом,а именно хочу четко понять определение термина,такого
как inline метод, соответственно в контексте C#(чтобы вопросы такого рода как "заинлайнить
метод" отпали).  

И вторая часть вопроса,почему JIT компилятору предпочтительнее inline методы?
    


Ответы

Ответ 1



В контексте C#, насколько я понимаю, inline-подстановка означает оптимизацию при компиляции, при которой тело вызываемого метода встраивается в вызывающую функцию вместо вызова. На текущий момент такими оптимизациями занимается только JIT-компилятор. В C# вы не можете заставить компилятор заинлайнить функцию, но вы можете попросить его об этом, используя атрибут [MethodImpl(MethodImplOptions.AggressiveInlining)]. Также вы можете попросить компилятор не встраивать функцию, указав атрибут [MethodImpl(MethodImplOptions.NoInlining)]. В отличие от этого в C++ ключевое слово inline означает, что сборщик должен игнорировать факт множественного определения функции в различных модулях компиляции (ослабляя тем самым ODR). По поводу второй части вопроса: инлайнингом в C# занимается JIT-компилятор потому, что он знает точно целевую платформу. Точные критерии решения JIT-компилятора насчёт того, инлайнить ли данную функцию, меняются от версии к версии. Согласно этому сообщению, инлайнингу не подвержены методы, которые: Маркированы атрибутом MethodImplOptions.NoInlining Размер IL-кода которых больше 32 байт (при отсутствии атрибута MethodImplOptions.AggressiveInlining) Виртуальные методы Метод, принимающие тип-значение большого размера как параметр Методы в классах, производных от MarshalByRefObject Методы со сложным потоком управления например, рекурсивные методы и методы с обработкой исключений Методы с экзотическими инструкциями, проверками безопасности и т. п. Вот такой «список предпочтений» JIT-компилятора насчёт инлайн-методов. Этот список, разумеется, не финальный, и будет пересматриваться (в сторону ослабления) в последующих версиях.

понедельник, 2 декабря 2019 г.

Почему inline-функции, определённые в заголовочных файлах не дублируются при линковке?

#cpp #функции #inline #линковка


Я прочёл такой факт насчёт "обычных" и inline- функций:


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


Почему нельзя определять функции в header'е, понятно: произойдёт "multiple definition"
при линковке нескольких модулей, включающих этот хэдер. Но из текста совсем не понятно,
почему это правило не работает для inline-функций. Я понял это так: "inline-функции
не дублируются, потому что они не дублируются". Бред.

Я, конечно, могу принять во внимание этот факт как должное, и всё, но всё таки хочется
понять, почему так происходит, а исчерпывающего объяснения этот текст не даёт.
    


Ответы

Ответ 1



Если inline-функция реально была заинлайнена, то её как бы и нету, поэтому она не может дублироваться в принципе. Если же она не была заинлайнена после оптимизаций, то она получает уникальное имя в каждой единице трансляции, в итоге мы имеем много функций, которые делают одно и то же, но имеют разные имена. Так как имена в итоге разные, multiple definition не происходит. Подробнее читать здесь: Стандарт C++11, 3.2.5 (One definition rule), 7.1.2 (Function specifiers). Где взять стандарт C++?

Ответ 2



Если спецификация языка говорит, что ошибки быть не должно, значит ошибки быть не должно. А дальше уже начинаются детали реализации. Почему вы решили, что они не дублируются? В классической реализации инлайновые функции с внешним связыванием для которых компилятор при компиляции нескольких единиц трансляции решил сгенерировать тела, разумеется, дублируются. В процессе компиляции каждая единица трансляции получает свою копию такой функции в своем объектном файле с одним и тем же именем. Однако такие функции в объектном файле помечены особым образом - так, чтобы при обнаружении множества копий одного и тот же внешнего символа при линковке линкер не выдавал ошибки, а наоборот молча удалял все копии, оставляя только одну. То есть компилятор C++ генерирует "свалку" одинаково именованных функций, раскиданных по разным объектным файлам, а линкер потом собирает всё вместе и занимается чисткой этой "свалки". В компиляторах семейства *nix эта пометка - это обозначение экспортируемого символа, как т.наз. "слабого" (weak) символа. В компиляторе MSVC++ существует аналогичная пометка selectany. Линкеры выдают ошибку множественного определения только если встретят два или более одинаковых "сильных" символа в процессе линковки. Если же "сильный" символ только один (а остальные "слабые"), то побеждает "сильный" символ, а "слабые" символы отбрасываются. Если "сильного" символа нет вообще, а есть только "слабые", то побеждает один (какой-то) из "слабых". Никакой ошибки при этом не рапортуется. Когда компилятор решает сгенерировать тело для inline-функции с внешним связыванием, он просто помечает соответствующий символ для линкера как "слабый" - и все. (На этом же механизме построена трансляция шаблонных функций, которые, как известно, тоже определяются в заголовочных файлах и тоже порождают свои копии во всех объектных файлах, которые потребовали их инстанцирования.) Например, скомпилировав вот такой простой исходник в объектный файл inline void bar() {} void (*foo())() { return bar; } и просмотрев содержимое этого объектного файла при помощи nm мы увидим 0000000000000000 W _Z3barv 0000000000000000 T _Z3foov Буковка W помечает "слабый" символ, а буковка T - "сильный" символ. Во всех объектных файлах, в которых сгенерировалось тело для такой inline функции, она будет фигурировать под одним и тем же именем _Z3barv с пометкой W. Обратите внимание, что ни о каком решении этой проблемы через генерацию множества функций с разными именами не может быть и речи: в всех остальных отношениях инлайновая функция с внешним связыванием должна вести себя так же как и любая другая функция с внешним связыванием, т.е., например, она обязана иметь один и тот же адрес во всех единицах трансляции. Побочным эффектом такого подхода является то, что "классический" подход к формированию объектного файла, в котором у функции нет начала и конца, а есть только точка входа, становится неприемлем. Для того, чтобы иметь возможность исключать функции из объектного файла, С++ компиляторы вынуждены формировать тела функций в объектном файле в компактном виде. Существуют исторические примеры альтернативных реализаций, которые пытались действовать по-другому. "Многопроходные" реализации вообще не порождали тел для инлайновых и шаблонных функций на первом проходе компиляции. Они выполняли предварительную линковку, на которой собирали информацию о том, каким функциям действительно нужны тела, затем снова вызывали компилятор и компилировали уникальные тела для таких функций, и затем уже выполняли финальную линковку. Но среди популярных компиляторов (GCC/Clang/MSVC) такой подход не прижился.

суббота, 30 ноября 2019 г.

Почему функции которые содержат циклы, рекурсию или вызываются по указателю, плохо подвержены встраиванию (inline)

#cpp #функции #циклы #рекурсия #inline


Почему функции которые содержат циклы, рекурсию или вызываются по указателю, плохо
подвержены встраиванию (inline)
    


Ответы

Ответ 1



Вопрос построен на терминологической путанице. Встраивание вызовов функции - это не свойство самой функции, а всегда именно свойство индивидуального вызова функции. Свойства самой функции, разумеется, играют роль в принятии решения о том, будет ли конкретный вызов встроен, но все равно в общем случае это решение принимается для каждого отдельного вызова индивидуально. Одни вызовы функции могут оказаться встроены, в то время как другие вызовы той же функции могут оказаться не встроены. Даже аргументы функции, указанные в конкретном вызове, могут влиять на решение о том, встраивать ли этот конкретный вызов. Утверждение о том, что вызовы функций с циклами не встраиваются, относится к категории "это было давно и неправда". Каждый компилятор реализует какой-то набор эвристических критериев, который дает ему возможность принимать общие решения о том, заслуживают ли вызовы данной функции встраивания. В каком-то из старинных компиляторов этот эвристический критерий также включал проверку тела функции на наличие циклов (Borland или что-то в этом роде). На самом деле циклы не представляют из себя никаких преград для встраивания. Мне не известны никакие современные реализации, которые бы отказывались встраивать вызовы функций с циклами внутри. Утверждение о том, что функции, которые вызываются через указатель, не встраиваются - это как раз яркий пример того терминологической путаницы, о которой я писал выше. Это вызовы, выполненные через указатель, в общем случае не встраиваются. В то же время вызовы той же самой функции, выполненные напрямую, могут прекрасно встраиваться. Почему в общем случае невозможно встроить вызов, сделанный через указатель, очевидно: потому что на стадии компиляции не ясно, какая функция будет вызываться. Однако если компилятор в состоянии сообразить на стадии компиляции, какая функция будет вызвана через указатель, он без проблем встроит и такой вызов. Классический часто задаваемый вопрос, основанный на данной терминологической путанице - вопрос о встраивании вызовов виртуальных методов классов. Они ведь вызываются "через указатель" и значит не могут быть встроены, правда? Не правда. Во-первых, сам пользователь может выполнить вызов виртуального метода напрямую, без использования виртуального механизма. Во-вторых, в огромном количестве контекстов сам компилятор в состоянии сообразить, какой конкретный метод вызывается в данном вызове и, соответственно, встроить этот вызов. Вызовы рекурсивных функций тоже могут встраиваться. Если компилятор на стадии компиляции в состоянии оценить максимальную глубину рекурсии, то он может встроить эти вызовы ("развернуть рекурсию") на всю глубину. Если компилятор не в состоянии выполнить такой оценки, то он может встроить рекурсивные вызовы до определенной фиксированной глубины, после чего выполнить обычный рекурсивный вызов. Встраивание вызовов рекурсивных функции в этом отношении во многом аналогично развертке циклов. Одной из простейших и наиболее очевидных ситуаций целесообразности встраивания вызова функции является, например, ситуация, когда заведомо известно, что некоторая функция вызывается в программе ровно один раз (в пространственном смысле, т.е. исходный код программы содержит ровно одно место с ее вызовом). Такой функции нет никакого смысла существовать в виде отдельной функции, независимо от того, как "тяжела" эта функция. (С этим идет ряд побочных оговорок, но к данной теме они не относятся.) Например, если функция имеет внутреннее связывание и вызывается в своей единице трансляции ровно один раз, то современные компиляторы встроят этот вызов независимо от каких-либо иных критериев. В компиляторе GCC за это отвечает опция -finline-functions-called-once, которая включается уже в режиме -O1. В качестве другого примера, в компиляторе MSVC++ есть #pragma-параметры inline_recursion и inline_depth, которые управляют встраиванием рекурсивных функций и глубиной развертки рекурсии.

Ответ 2



Ну, с рекурсией понятно - если она не преобразована компилятором в цикл, то что получится? встраиваем код, на месте ее вызова опять встраиваем код, в котором на месте вызова... Сколько раз код встраивать? :) С вызовом по указателю - опять же: если встроена, то функция не имеет четкого пролога-эпилога, и не может быть вызвана по адресу - так что при вызове через указатель должен иметься не встроенный экземпляр функции как минимум - чтобы было, что вызывать. В самом месте вызова через указатель встроить ничего нельзя - так как встраивает компилятор, а во время компиляции значение указателя неизвестно. А про циклы - мне кажется, тут вы что-то напутали. Функция с циклом внутри вполне встраиваема - если, конечно, это имеет смысл :) Если вы имеете в виду, что сам цикл раскручивается - то опять же, это делается, но со своими ограничениями - например, компилятор может не знать, сколько реально итераций будет сделано - так как ему цикл разворачивать?..

вторник, 16 апреля 2019 г.

Почему не рекомендуют ставить ключевое слово inline в реализации метода?

Мой коллега увидел, что я ставлю inline методам get и set и в объявлении метода и в реализации и сказал, что так делать не стоит, но аргументировать это не смог. Я тоже когда-то про такое слышал, но информации найти не смог. Я знаю, что компиляторы сейчас забивают даже на форсинлайны и обычный inline им вообще не указ. Но всё-таки, правда ли что inline ставить лучше только в объявлении метода и какие у этого есть объективные причины?


Ответ

Основное назначение слова inline: дать программисту инструмент определять функции так, чтобы у компилятора была возможность встроить их на этапе компиляции и не вызвать в дальнейшем ошибку компоновки.
Вторичное назначение: сообщение другим людям работающим над данным кодом, что автор надеется, что компилятор встроит эту функцию.
Кроме того это действительно является подсказкой компилятору встроить функцию (я сам сомневался в этом), которая на самом деле влияет на эвристики компилятора, например, увеличивая пороговые значения размера функции выше которых компилятор откажется её встраивать (смотри ссылку на статью в ответе @ixSci)
Пусть есть класс Foo с переменной a, к которой нужно обеспечить доступ; есть несколько вариантов, что можно сделать с getA
Определить прямо в классе.
class Foo { int a{0}; public: int Foo::getA () { return a; }
inline подразумевается, программист сразу понимает что к чему, компоновщик не жалуется о повторном определении. ИМХО — это предпочтительный вариант покуда реализация не занимает больше двух-трёх (пяти?) строк. Определить в хедере вне класса.
class Foo { int a{0}; public: int getA (); }
int Foo::getA () { return a;}
Это тот случай, когда использование слова inline обязательно и для чего оно и было введено, но где именно оно будет компилятору всё равно, вопрос исключительно стилистический. Распишу плюсы и минусы на мой взгляд (все они крайне эфемерны):
2.1. inline только в объявлении — назовём это «основным вариантом».
2.2. inline только в определении — относительно плохой вариант:
- При просмотре объявления программист будет рассчитывать, что компилятор не сможет (и не должен) встроить функцию, что для геттера вызывает ряд вопрсов и потенциально ложных предположений, например то что этот геттер не тривиальный и требует каких-то вычислений.
2.3. inline и в объявлении и в определении
+ При прочтении и объявления, и определения класса сразу видно, что функция встраиваемая. - Чревато скатыванием к предыдущему варианту, если кто-то удалит слово inline и не удосужится проверить. Определение в отдельном *.cpp
// foo.h class Foo { int a{0}; public: int getA (); }
// foo.cpp #include "foo.h" int Foo::getA () { return a;}
// main.cpp #include "foo.h" int main(void) { Foo foo; return foo.getA(); }
Если inline будет присутствовать в объявлении или определении, то это нарушение стандарта, который требует, чтобы определение функции объявленной как inline было доступно в каждой единице трансляции, где она вызывается и оно было в точности одинаковое.

пятница, 15 февраля 2019 г.

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

Перевод вопроса: https://stackoverflow.com/q/1759300/5697743
Когда мне следует использовать ключевое слово inline для функции или метода в C++?
После просмотра различных объяснений возникли вопросы:
Когда мне не следует использовать ключевое слово inline для функции или метода в C++? Когда комилятор не знает, что нужно сделать функцию 'встроенной'? Отразится ли как-либо использование многопоточности в приложении на работе inline?


Ответ

inline ближе к static или extern, чем к указанию компилятору встроить ваши функции. extern, static и inline используются в основном линковщиком, а не компилятором.
Иногда пишут, что inline указывает компилятору, что по-вашему функция должна быть встроенной. Это было правдой и имело смысл в 1998 году, но спустя двадцать лет компилятору больше не нужны такие подсказки. Не стоит и говорить, что люди часто ошибаются, когда дело доходит до оптимизации кода, так что большинство компиляторов прямо игнорируют такие "подсказки".
static - имя функции/переменной не может быть использовано в других единицах трансляции. Линковщик должен убедиться, что это случайно не сделано. extern - использовать это имя в данной единице трансляции, но не жаловаться, если оно не определено. Линковщик разберётся и убедится, что у каждого символа есть адрес. inline - эта функция будет определена в разных единицах трансляции, игнорировать это. Линковщик должен убедится, что все единицы трансляции используют один и тот же экземпляр.
Примечание: В целом, определение шаблонов с inline бессмысленно, потому что они уже используют семантику линковки, аналогичную предоставляемой inline. Однако, для явной (explicit) специализации и инициализации шаблонов требуется использовать inline

Ответы на ваши вопросы:
Когда мне следует использовать ключевое слово inline для функции или метода в C++?
Только когда вы хотите определить функцию в заголовке. Точнее, когда более одного определения функции встречается в различных единицах трансляции. Будет хорошей идеей поместить маленькие (однострочные) функции в заголовок, так как это даст больше информации для оптимизации компилятору. Однако, это увеличит время сборки. Когда мне не следует использовать ключевое слово inline для функции или метода в C++?
Не используйте inline только из-за уверенности, что ваш код заработает быстрее. Когда компилятор не знает, что нужно сделать функцию 'встроенной'?
В целом, компилятор умеет выполнять такую оптимизацию лучше вас. Однако, он не сможет это сделать, если нет определения функции (в данной единице трансляции). Как правило, в максимально оптимизированном коде все private методы встраиваются, просили вы того, или нет. Для предотвращения встраивания в GCC используйте __attribute__(( noinline )), а в Visual Studio - __declspec(noinline) Отразится ли как-либо использование многопоточности в приложении на работе inline?
Многопоточность не влияет как на работу inline, так и на встраивание функций.

вторник, 23 октября 2018 г.

inline термин в контексте C# / JIT компилятора

Добрый день. Столкнулся с таким вопросом,а именно хочу четко понять определение термина,такого как inline метод, соответственно в контексте C#(чтобы вопросы такого рода как "заинлайнить метод" отпали).
И вторая часть вопроса,почему JIT компилятору предпочтительнее inline методы?


Ответ

В контексте C#, насколько я понимаю, inline-подстановка означает оптимизацию при компиляции, при которой тело вызываемого метода встраивается в вызывающую функцию вместо вызова. На текущий момент такими оптимизациями занимается только JIT-компилятор.
В C# вы не можете заставить компилятор заинлайнить функцию, но вы можете попросить его об этом, используя атрибут [MethodImpl(MethodImplOptions.AggressiveInlining)]. Также вы можете попросить компилятор не встраивать функцию, указав атрибут [MethodImpl(MethodImplOptions.NoInlining)]

В отличие от этого в C++ ключевое слово inline означает, что сборщик должен игнорировать факт множественного определения функции в различных модулях компиляции (ослабляя тем самым ODR).

По поводу второй части вопроса: инлайнингом в C# занимается JIT-компилятор потому, что он знает точно целевую платформу. Точные критерии решения JIT-компилятора насчёт того, инлайнить ли данную функцию, меняются от версии к версии.
Согласно этому сообщению, инлайнингу не подвержены методы, которые:
Маркированы атрибутом MethodImplOptions.NoInlining Размер IL-кода которых больше 32 байт (при отсутствии атрибута MethodImplOptions.AggressiveInlining) Виртуальные методы Метод, принимающие тип-значение большого размера как параметр Методы в классах, производных от MarshalByRefObject Методы со сложным потоком управления
например, рекурсивные методы и методы с обработкой исключений Методы с экзотическими инструкциями, проверками безопасности и т. п.
Вот такой «список предпочтений» JIT-компилятора насчёт инлайн-методов.
Этот список, разумеется, не финальный, и будет пересматриваться (в сторону ослабления) в последующих версиях.