Когда мне следует использовать __forceinline вместо встроенного?

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

The __forceinline keyword overrides the cost/benefit analysis and relies on the judgment of the programmer instead.

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

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
36
0
21 202
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 11

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

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

Должен быть главный ответ;)

aaronsnoswell 23.10.2017 09:13

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

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

Ответ принят как подходящий

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

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

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

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

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


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

Я планирую использовать __foceinline по той же причине. Можете ли вы быть на 100%, что он будет встроен при использовании __forceinline. Я сомневаюсь, поскольку msdn также говорит: «Вы не можете заставить компилятор встроить определенную функцию даже с ключевым словом __forceinline».

mentat 01.06.2011 11:04

@Koray: вы должны проверить сгенерированный код. По моему опыту, да, ti работает даже для гигантских функций. Одно исключение (IIRC) - это функции с переменными аргументами, и вполне вероятно, что документация ссылается на это.

peterchen 23.06.2011 18:25

@mentat: Вы не можете быть уверены на 100%, но можете дать себе знать. Предупреждение C4714 сделает это - это предупреждение уровня отладки 4, поэтому в зависимости от ваших настроек его необходимо явно включить.

Cookie 18.09.2017 17:31

@peterchen другая возможность заключается в том, что документы просто учитывают тот факт, что рекурсивные функции (обычно) не могут быть полностью встроены сами по себе

hanshenrik 25.09.2019 21:58

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

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

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

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

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

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

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

Собственно, ассемблер проверять не надо (не то чтобы сильно больно). VS имеет предупреждение за это - C4714.

Martin Ba 12.02.2013 00:37

Собственно буст этим и загружен.

Например

 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?

einpoklum 15.04.2020 11:01

Другие вопросы по теме