Visual Studio включает поддержку __forceinline. В документации Microsoft Visual Studio 2005 говорится:
The __forceinline keyword overrides the cost/benefit analysis and relies on the judgment of the programmer instead.
Возникает вопрос: в каких случаях анализ затрат / выгод компилятора неверен? И откуда мне знать, что это неправильно?
В каком сценарии предполагается, что я знаю эту проблему лучше, чем мой компилятор?





Единственный способ быть уверенным - это измерить производительность с и без. Если вы не пишете высокопроизводительный критически важный код, в этом обычно нет необходимости.
Вы знаете лучше, чем компилятор, только тогда, когда ваши данные профилирования говорят вам об этом.
Есть несколько ситуаций, когда компилятор не может категорически определить, целесообразно или выгодно встроить функцию. Встраивание может включать в себя компромиссы, которые компилятор не желает делать, но вы это делаете (например, раздувание кода).
В общем, современные компиляторы довольно хорошо принимают это решение.
Компилятор принимает свои решения на основе статического анализа кода, тогда как если вы профилируете, как говорит Дон, вы выполняете динамический анализ, который может быть гораздо более обширным. Количество вызовов определенного фрагмента кода часто во многом определяется контекстом, в котором он используется, например данные. Это сделает профилирование типичного набора сценариев использования. Лично я собираю эту информацию, включив профилирование своих автоматических регрессионных тестов. В дополнение к форсированию встроенных строк, я развернул циклы и провел другие ручные оптимизации на основе таких данных, что дало хороший эффект. Также обязательно после этого снова выполнить профилирование, так как иногда ваши максимальные усилия могут фактически привести к снижению производительности. Опять же, автоматизация делает это намного менее болезненным.
Однако чаще всего, по моему опыту, настройка алгоритмов дает гораздо лучшие результаты, чем прямая оптимизация кода.
Я разрабатывал программное обеспечение для устройств с ограниченными ресурсами в течение 9 лет или около того, и время Только, в течение которого я когда-либо видел необходимость использования __forceinline, было в жестком цикле, когда драйверу камеры требовалось копировать данные пикселей из буфера захвата на экран устройства. . Там мы могли ясно видеть, что стоимость вызова конкретной функции действительно снижает производительность отрисовки оверлея.
Единственное место, где я его использую, - это проверка лицензии.
Одним из важных факторов защиты от простого * взлома является проверка наличия лицензии в нескольких местах, а не только в одном, и вы не хотите, чтобы в этих местах использовался один и тот же вызов функции.
*) Пожалуйста, не превращайте это в обсуждение того, что все можно взломать - я знаю. Кроме того, одно это не очень помогает.
Я планирую использовать __foceinline по той же причине. Можете ли вы быть на 100%, что он будет встроен при использовании __forceinline. Я сомневаюсь, поскольку msdn также говорит: «Вы не можете заставить компилятор встроить определенную функцию даже с ключевым словом __forceinline».
@Koray: вы должны проверить сгенерированный код. По моему опыту, да, ti работает даже для гигантских функций. Одно исключение (IIRC) - это функции с переменными аргументами, и вполне вероятно, что документация ссылается на это.
@mentat: Вы не можете быть уверены на 100%, но можете дать себе знать. Предупреждение C4714 сделает это - это предупреждение уровня отладки 4, поэтому в зависимости от ваших настроек его необходимо явно включить.
@peterchen другая возможность заключается в том, что документы просто учитывают тот факт, что рекурсивные функции (обычно) не могут быть полностью встроены сами по себе
Встроенная директива будет совершенно бесполезна при использовании для функций, которые:
рекурсивный, длинная, состоит из петель,
Если вы хотите заставить это решение использовать __forceinline
Если вы знаете, что функция будет вызываться в одном месте несколько раз для сложных вычислений, тогда рекомендуется использовать __forceinline. Например, умножение матриц для анимации может потребоваться столько раз, что вызовы функции начнут замечать ваш профилировщик. Как говорили другие, компилятор не может знать об этом, особенно в динамической ситуации, когда выполнение кода неизвестно во время компиляции.
Собственно, даже с ключевым словом __forceinline. Visual C++ иногда предпочитает не встраивать код. (Источник: полученный исходный код сборки.)
Всегда смотрите на результирующий ассемблерный код, где важна скорость (например, для каждого кадра необходимо запускать жесткие внутренние циклы).
Иногда использование #define вместо inline может помочь. (конечно, вы теряете много проверок, используя #define, поэтому используйте его только тогда и там, где это действительно важно).
Собственно, ассемблер проверять не надо (не то чтобы сильно больно). VS имеет предупреждение за это - C4714.
Собственно буст этим и загружен.
Например
BOOST_CONTAINER_FORCEINLINE flat_tree& operator=(BOOST_RV_REF(flat_tree) x)
BOOST_NOEXCEPT_IF( (allocator_traits_type::propagate_on_container_move_assignment::value ||
allocator_traits_type::is_always_equal::value) &&
boost::container::container_detail::is_nothrow_move_assignable<Compare>::value)
{ m_data = boost::move(x.m_data); return *this; }
BOOST_CONTAINER_FORCEINLINE const value_compare &priv_value_comp() const
{ return static_cast<const value_compare &>(this->m_data); }
BOOST_CONTAINER_FORCEINLINE value_compare &priv_value_comp()
{ return static_cast<value_compare &>(this->m_data); }
BOOST_CONTAINER_FORCEINLINE const key_compare &priv_key_comp() const
{ return this->priv_value_comp().get_comp(); }
BOOST_CONTAINER_FORCEINLINE key_compare &priv_key_comp()
{ return this->priv_value_comp().get_comp(); }
public:
// accessors:
BOOST_CONTAINER_FORCEINLINE Compare key_comp() const
{ return this->m_data.get_comp(); }
BOOST_CONTAINER_FORCEINLINE value_compare value_comp() const
{ return this->m_data; }
BOOST_CONTAINER_FORCEINLINE allocator_type get_allocator() const
{ return this->m_data.m_vect.get_allocator(); }
BOOST_CONTAINER_FORCEINLINE const stored_allocator_type &get_stored_allocator() const
{ return this->m_data.m_vect.get_stored_allocator(); }
BOOST_CONTAINER_FORCEINLINE stored_allocator_type &get_stored_allocator()
{ return this->m_data.m_vect.get_stored_allocator(); }
BOOST_CONTAINER_FORCEINLINE iterator begin()
{ return this->m_data.m_vect.begin(); }
BOOST_CONTAINER_FORCEINLINE const_iterator begin() const
{ return this->cbegin(); }
BOOST_CONTAINER_FORCEINLINE const_iterator cbegin() const
{ return this->m_data.m_vect.begin(); }
Для кода SIMD.
Код SIMD часто использует константы / магические числа. В обычной функции каждый const __m128 c = _mm_setr_ps(1,2,3,4); становится ссылкой на память.
С __forceinline компилятор может загрузить его один раз и повторно использовать значение, если ваш код не исчерпывает регистры (обычно 16).
Кеши процессора хороши, но регистры все еще быстрее.
P.S. Только на одном __forceinline производительность улучшилась на 12%.
Почему в таких случаях недостаточно inline?
Должен быть главный ответ;)