Когда я должен использовать forceinline вместо inline?

Visual Studio включает поддержку _ _ forceinline. В документации Microsoft Visual Studio 2005 говорится:

ключевое слово __forceinline переопределяет анализ затрат и выгод по решению программиста вместо.

Это поднимает вопрос: когда анализ затрат/выгод компилятора неправильный? И откуда мне знать, что это неправильно?

в каком сценарии предполагается, что я знаю лучше, чем мой компилятор по этому вопросу?

10 ответов


компилятор принимает свои решения на основе статического анализа кода, в то время как если вы профилируете, как говорит Дон, вы проводите динамический анализ, который может быть гораздо более широким. Количество вызовов определенного фрагмента кода часто в значительной степени определяется контекстом, в котором он используется, например данными. Профилирование типичного набора вариантов использования сделает это. Лично я собираю эту информацию, включив профилирование в моих автоматических регрессионных тестах. В дополнение к форсированию линий, I развернули петли и провели другие ручные оптимизации на основе таких данных, что дало хороший эффект. Также необходимо снова профилировать после этого, так как иногда ваши лучшие усилия могут привести к снижению производительности. Опять же, автоматизация делает это намного менее болезненным.

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


вы знаете лучше, чем компилятор, только когда ваши данные профилирования говорят вам об этом.


единственное место, где я его использую, - это проверка лицензии.

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


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


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


единственный способ быть уверенным, чтобы измерить производительность С и без. Если вы не пишете высокопроизводительный критический код, это обычно не требуется.


встроенная директива будет полностью бесполезна при использовании для функций, которые:

рекурсивный, длинный, состоит из петель,

Если вы хотите заставить это решение использовать _ _ forceinline


на самом деле, даже с ключевое слово __forceinline. Visual C++ иногда выбирает не вставлять код. (Источник: результирующий исходный код сборки.)

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

иногда использование #define вместо inline сделает трюк. (конечно, вы теряете много проверки, используя #define, поэтому используйте его только тогда, когда и где это действительно важно).


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

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


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


на самом деле boost загружен с ним.

 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(); }