Могу ли я выборочно (принудительно) встроить функцию?

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

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

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

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

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


person Samaursa    schedule 18.08.2011    source источник
comment
Если вы не используете препроцессор, что совсем не одно и то же, все, что вы делаете, чтобы указать, что функция должна быть встроена, может быть проигнорировано компилятором.   -  person IronMensan    schedule 18.08.2011
comment
gcc предлагает опцию -Winline, чтобы предупредить вас о функциях, которые были помечены inline, но не были отмечены. Это начало, но не решение вашей проблемы.   -  person pmr    schedule 18.08.2011
comment
Поскольку все функции находятся в правильном исходном файле и больше нигде не используются, я бы посоветовал просто пометить их static inline и позволить компилятору продолжить работу. Если он думает, что создаст лучший код, не встраивая их, кто знает, может быть, он прав. Если бы вы указали компилятор, люди могли бы предложить любые варианты, которые он должен принудительно встроить, но поскольку вам, похоже, нужно переносимое решение, он позволяет компилятору оптимизировать, он знает о платформе больше, чем вы, поскольку вы точно ничего не знаете о платформе.   -  person Steve Jessop    schedule 18.08.2011
comment
Эта тема старая, но я только что наткнулся на нее. Я нахожусь в ситуации, когда у меня ужасный код, который я разбиваю, чтобы понять классы и более мелкие функции. Хотя это создает накладные расходы, гораздо более вероятно, что я смогу оптимизировать хорошо написанный код, если есть проблема, чем исправить быстрый код.   -  person Samuel    schedule 18.03.2014
comment
Я думаю, что должен быть способ сделать это. Я создаю свою версию функции одного алгоритма STL и компилятора GCC, и clang, не встраивайте функцию, и это занимает в 10 раз больше времени, чем STL, а когда я вставляю функцию внутрь, это занимает всего 0,9X. Так что да! иногда необходимо принудительно встроить.   -  person Moises Rojo    schedule 09.05.2018


Ответы (7)


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

В противном случае вы можете... попробовать... использовать макрос.

person Puppy    schedule 18.08.2011
comment
Я изменил свой стиль, чтобы соответствовать предложениям авторов различных книг (которые я уверен, знаю лучше, чем я), написав функции так, чтобы они делали то, что предполагает их название, и ничего более, и сохраняли небольшую длину функции по вертикали. Это привело к множеству небольших функций, которые повлияли на производительность моего последнего кода, чувствительного к производительности. После прочтения книги Агнера Фога я начал думать о другом аспекте и задаюсь вопросом, должен ли я найти баланс между двумя противоположными советами (селективное встраивание было бы идеальным решением - и макрос может быть им) - person Samaursa; 19.08.2011
comment
Теперь вы можете использовать __forceinline (msdn.microsoft.com/en-us/bw1hbe6y) - person gj13; 10.11.2016
comment
_forceinline указывает компилятору сделать все возможное для встраивания функции без проведения анализа затрат и выгод. docs.microsoft.com/ en-us/cpp/cpp/ - person Sachin Joseph; 06.12.2018

Ничто не мешает вам поместить inline в статическую функцию в файле .cpp.

Некоторые компиляторы имеют возможность принудительно использовать встроенную функцию, см., например. атрибут((always_inline)) GCC и множество опций для точной настройки оптимизации встраивания (см. параметры -minline-*).

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

person rodrigo    schedule 18.08.2011
comment
Это хорошее предложение. Это не работает в моей конкретной ситуации, но определенно следует иметь в виду (+1). - person Samaursa; 19.08.2011
comment
В некоторых, в основном встраиваемых, проектах часто возникает необходимость логического разделения, но физической непрерывности кода. Рассмотрим, например, подпрограммы обработки прерываний, где ряд обязательных операций должен быть выполнен как в начале, так и в конце. Эти операции могут быть идеально реализованы как статические встроенные функции, если гарантируется, что подпрограмма прерывания не будет загрязнена дополнительными командами по стеку и вызовам (т.е. функция действительно встроена). - person Géza Török; 08.02.2017

Нет, inline является рекомендацией компилятору ; это не заставляет его делать что-либо. Кроме того, если вы работаете с MSVC++, обратите внимание, что __forceinline также является неправильным; это просто более сильная рекомендация, чем inline.

person Jacob    schedule 18.08.2011
comment
Это я понимаю. Компилятор не может встроить некоторые функции, несмотря ни на что, но он не будет проводить собственный анализ, когда используется __forceinline, и функция будет встроена, если это возможно. - person Samaursa; 19.08.2011
comment
@Samaursa: Да, но все дело в том, что вы никогда не можете предполагать, что функция будет встроена с любым вариантом ключевого слова inline (согласно вашему вопросу). В лучшем случае вы можете увеличить вероятность того, что он будет встроен. - person Jacob; 19.08.2011
comment
@Jacob: Вы не можете этого предположить, но вы можете проверить это, включив все предупреждения с помощью /Wall и отметив, какие функции не были встроены. - person user541686; 15.08.2012
comment
@Jacob Действительно, я сомневался в этом, но вы правы, Вы не можете заставить компилятор встроить конкретную функцию, даже с ключевым словом __forceinline. - person bobobobo; 11.09.2012
comment
Тонна информации здесь: docs.microsoft.com/en-us/cpp/cpp/ - person Sachin Joseph; 06.12.2018

Это как о старом добром прямом C, так и о C++. Я размышлял об этом на днях, потому что во встраиваемом мире, где и скорость, и пространство должны тщательно контролироваться, это действительно может иметь значение (в отличие от слишком частого «не беспокойтесь об этом, ваш компилятор умен и память дешевая, распространенная при разработке настольных/серверных систем).

Возможное решение, которое мне еще предстоит проверить, состоит в том, чтобы в основном использовать два имени для разных вариантов, что-то вроде

inline int _max(int a, int b) {
    return a > b ? a : b;
}

а потом

int max(int a, int b) {
    return _max(a, b);
}

Это дало бы возможность выборочно вызывать _max() или max() и при этом иметь алгоритм, определенный один раз и только один раз.

person Travis Griggs    schedule 28.11.2012

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

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

person Mark B    schedule 18.08.2011
comment
Неприменимо в некоторых случаях: иногда inline используется как альтернатива макросам, так как они поддаются отладке, более читабельны и могут быть помещены в пространство имен. Иногда выделение кадра в стеке может привести к задержке в драгоценные микросекунды, и компилятор может не знать частоту выполнения без контекста (что, если вы встраиваете функцию sin(), а компилятор по какой-то причине не встраивает ее?). - person Kotauskas; 30.03.2019

Встраивание. Например, если существует функция A, которая часто вызывает функцию B, а функция B относительно мала, то оптимизация на основе профиля будет встраивать функцию B в функцию A.

Оптимизация VS на основе профиля

Вы можете использовать автоматизированный подключаемый модуль Profile Guided Optimization for Visual C++ в Центре производительности и диагностики, чтобы упростить и оптимизировать процесс оптимизации в Visual Studio, или выполнить шаги оптимизации вручную в Visual Studio или в командной строке. Мы рекомендуем подключаемый модуль, поскольку он проще в использовании. Информацию о том, как получить подключаемый модуль и использовать его для оптимизации приложения, см. на странице Подключаемый модуль оптимизации на основе профилей.

person deeznutts    schedule 28.02.2017

Если у вас есть известная горячая функция, и вы хотите, чтобы компилятор встраивался более агрессивно, чем обычно, атрибут flatten, предлагаемый gcc/clang, может быть чем-то, на что стоит обратить внимание. В отличие от встроенного ключевого слова и атрибутов, оно применяется к решениям встраивания относительно функций, вызываемых в помеченной функции.

__attribute__((flatten)) void hot_code() {
    // functions called here will be inlined if possible
}

См. https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html и https://clang.llvm.org/docs/AttributeReference.html#flatten для получения официальной документации.

person textshell    schedule 30.05.2021