Рассмотрим следующую функцию:
void func(bool& flag)
{
if(!flag) flag=true;
}
Мне кажется, что если флаг имеет допустимое логическое значение, это будет эквивалентно безусловной установке его на true
, например:
void func(bool& flag)
{
flag=true;
}
Однако ни gcc, ни clang не оптимизируют это таким образом - оба генерируют следующее на -O3
уровне оптимизации:
_Z4funcRb:
.LFB0:
.cfi_startproc
cmp BYTE PTR [rdi], 0
jne .L1
mov BYTE PTR [rdi], 1
.L1:
rep ret
Мой вопрос: просто ли код слишком особенный, чтобы его можно было оптимизировать, или есть какие-то веские причины, по которым такая оптимизация была бы нежелательной, учитывая, что flag
это не ссылка volatile
? Кажется, единственная причина, которая может быть, состоит в том, что flag
может каким-то образом иметь значение, отличное от true
или, false
без неопределенного поведения в момент его чтения, но я не уверен, возможно ли это.
c++
optimization
Руслан
источник
источник
1
. godbolt.org/g/swe0tcОтветы:
Это может отрицательно сказаться на производительности программы из-за соображений согласованности кеша . Запись в
flag
каждыйfunc()
вызов будет загрязнять содержащуюся строку кэша. Это произойдет независимо от того, что записываемое значение точно соответствует битам, найденным в адресе назначения до записи.РЕДАКТИРОВАТЬ
hvd предоставил еще одну вескую причину , препятствующую такой оптимизации. Это более веский аргумент против предложенной оптимизации, поскольку она может привести к неопределенному поведению, тогда как мой (исходный) ответ касался только аспектов производительности.
После небольшого размышления я могу предложить еще один пример, почему компиляторам следует строго запретить - если они не могут доказать, что преобразование безопасно для конкретного контекста - вводить безусловную запись. Рассмотрим этот код:
При безусловной записи
func()
это определенно вызывает неопределенное поведение (запись в постоянную память завершит программу, даже если эффект записи в противном случае был бы безоперационным).источник
const
передачи в функцию, которая может изменять данные , являющиеся источником неопределенного поведения, а не безусловной записи. Доктор, мне больно, когда я делаю это ....Помимо ответа Леона о производительности:
Предположим , что
flag
естьtrue
. Предположим, что два потока постоянно звонятfunc(flag)
. В этом случае функция в том виде, в котором она написана, ничего не сохраняетflag
, поэтому она должна быть потокобезопасной. Два потока обращаются к одной и той же памяти, но только для ее чтения. Безоговорочно установкиflag
сtrue
помощью двух разных потоков будет написание одной и той же памяти. Это небезопасно, это небезопасно, даже если записываемые данные идентичны данным, которые уже есть.источник
[intro.races]/21
.0x01
в уже существующий байт0x01
вызывает "небезопасное" поведение. В системе с доступом к памяти слова или двойного слова это было бы; но оптимизатор должен знать об этом. На современном ПК или ОС телефона проблем не возникает. Так что это не веская причина.flag
находится на странице копирования при записи. Теперь на уровне ЦП поведение может быть определено (ошибка страницы, пусть ОС обрабатывает это), но на уровне ОС оно все еще может быть неопределенным, верно?Я не уверен в поведении C ++ здесь, но в C память может измениться, потому что, если память содержит ненулевое значение, отличное от 1, оно останется неизменным при проверке, но изменится на 1 при проверке.
Но поскольку я не очень хорошо владею C ++, я не знаю, возможна ли такая ситуация.
источник
_Bool
?bool
/ допустимо любое ненулевое значение,_Bool
и означаетtrue
, что в этом конкретном ABI, вы, вероятно, правы._Bool
и C ++bool
либо одного типа, либо совместимые типы, которые подчиняются одним и тем же правилам. В MSVC они имеют одинаковый размер и выравнивание, но нет официального заявления о том, используют ли они одни и те же правила.<stdbool.h>
включает.typedef _Bool bool;
И да, на x86 (по крайней мере, в ABI System V)bool
/_Bool
должны быть либо 0, либо 1, с очищенными старшими битами байта. Я не думаю, что это объяснение правдоподобно.func
был передан в RDI, а Windows будет использовать RDX).