Вы не включили определение, только документацию. Я думаю, что вы просите помочь понять, почему оно вообще существует, а не определение.
Он останавливает компиляторы от CSEing и поднимает работу из циклов повторения, поэтому вы можете повторять ту же самую работу достаточно раз, чтобы ее можно было измерить. например поместите что-то короткое в цикл, который выполняется 1 миллиард раз, и тогда вы можете легко измерить время для всего цикла (секунда или около того). См. Может ли MOV x86 действительно быть "свободным"? Почему я не могу воспроизвести это вообще? за пример сделать это вручную в asm. Если вам нужен такой код, сгенерированный компилятором, вам нужна функция / макрос типа DoNotOptimizeAway
.
Компиляция всей программы с отключенной оптимизацией была бы бесполезна: хранение / перезагрузка всего между операторами C ++ дает очень разные узкие места (обычно задержка пересылки хранилища). См. Добавление избыточного назначения ускоряет код при компиляции без оптимизации
Возможно, поможет и фактическое определение.
В этом разделе «Вопросы и ответы» ( Барьер оптимизации для микробенчмарков в MSVC: скажите оптимизатору, что вы блокируете память? ) описывается, как работает одна реализация макроса DoNotOptimize
(и спрашивает, как перенести его из GNU C ++ в MSVC ).
Макрос escape
взят из выступления Чандлера Каррута на CppCon2015, «Настройка C ++: тесты, процессоры и компиляторы! О, Боже!» . В этом выступлении также подробно рассказывается о том, зачем это нужно при написании микробенчмарков: чтобы не допустить оптимизации целых циклов при компиляции с включенной оптимизацией.
(Наличие компилятора, поднимающего вещи из циклов вместо их многократного вычисления, труднее понять, если это проблема. Создание функции __attribute__((noinline))
может помочь, если она достаточно велика, чтобы не требовалась к действию. Проверьте вывод asm компилятора, чтобы увидеть, сколько настроек он поднял.)
И кстати, хорошее определение для GNU C / C ++ обычно имеет нулевую дополнительную стоимость:
asm volatile("" :: "r"(my_var));
компилирует в ноль инструкции asm, но требует, чтобы компилятор имел значение my_var
в регистре по своему выбору. (И из-за asm volatile
приходится «многократно запускать» это на абстрактной машине C ++).
Это повлияет на оптимизацию, только если компилятор мог преобразовать вычисление, частью которого он был, во что-то еще. (например, использование этого на счетчике цикла остановит компилятор от использования только приращений указателя и сравнит его с указателем конца, чтобы сделать правильное число итераций for(i=0;i<n;i++) sum+=a[i];
Использование операнда «чтение-изменение-запись», такого как asm volatile("" :"+r"(my_var));
, заставит компилятор забыть всю информацию об ограничении диапазона или распространении константы, которую он знает о значении, и обработать его как входящую функцию arg. например что это 42
, или что это не отрицательно. Это может повлиять на оптимизацию больше.
Когда они говорят, что «издержки исключаются в сравнениях», мы надеемся, что не говорим о явном вычитании чего-либо из одного результата синхронизации и не говорим о собственном бенчмаркинге DoNotOptimizeAway
.
Это не сработает. Анализ производительности для современных процессоров не работает, складывая стоимость каждой инструкции. Конвейерное выполнение не по порядку означает, что дополнительная инструкция asm может легко иметь нулевую дополнительную стоимость, если внешний интерфейс (общая пропускная способность команды) не был узким местом, и если требуемая единица выполнения не была либо.
Если их переносимое определение похоже на volatile T sink = input;
, дополнительное хранилище asm будет стоить только в том случае, если ваш код ограничен в пропускной способности хранилища для кэширования.
Так что заявление об отмене звучит немного оптимистично. Как я объяснил выше, плюс вышеупомянутые факторы, зависящие от контекста / оптимизации. Возможно, что DoNotOptimizeAway
)