Considere a seguinte função:
void func(bool& flag)
{
if(!flag) flag=true;
}
Parece-me que se o sinalizador tiver um valor booleano válido, isso seria equivalente a defini-lo true
como incondicional , assim:
void func(bool& flag)
{
flag=true;
}
No entanto, nem o gcc nem o clang o otimizam dessa maneira - ambos geram o seguinte no -O3
nível de otimização:
_Z4funcRb:
.LFB0:
.cfi_startproc
cmp BYTE PTR [rdi], 0
jne .L1
mov BYTE PTR [rdi], 1
.L1:
rep ret
Minha pergunta é: é apenas que o código é muito especial para cuidar de otimizar, ou há algum bom motivo para tal otimização ser indesejada, visto que flag
não é uma referência volatile
? Parece que a única razão que pode ser é que flag
poderia, de alguma forma, ter um não- true
ou- false
valor sem comportamento indefinido no momento da leitura, mas não tenho certeza se isso é possível.
c++
optimization
Ruslan
fonte
fonte
1
sendo usada. godbolt.org/g/swe0tcRespostas:
Isso pode impactar negativamente o desempenho do programa devido a considerações de coerência do cache . Escrever para
flag
cada vez quefunc()
for chamado sujaria a linha de cache que o contém. Isso acontecerá independentemente do fato de que o valor que está sendo escrito corresponde exatamente aos bits encontrados no endereço de destino antes da escrita.EDITAR
hvd forneceu outra boa razão que impede essa otimização. É um argumento mais convincente contra a otimização proposta, uma vez que pode resultar em um comportamento indefinido, enquanto minha resposta (original) abordava apenas aspectos de desempenho.
Após um pouco mais de reflexão, posso propor mais um exemplo de por que os compiladores devem ser fortemente proibidos - a menos que eles possam provar que a transformação é segura para um contexto particular - de introduzir a gravação incondicional. Considere este código:
Com uma gravação incondicional,
func()
isso definitivamente aciona um comportamento indefinido (gravar na memória somente leitura encerrará o programa, mesmo se o efeito da gravação for de outra forma um no-op).fonte
const
passar para uma função que pode modificar os dados que são a origem do comportamento indefinido, não da gravação incondicional. Doutor, dói quando eu faço isso ....Além da resposta de Leon sobre o desempenho:
Suponha que
flag
étrue
. Suponha que dois threads estejam constantemente chamandofunc(flag)
. A função conforme escrita, nesse caso, não armazena nada emflag
, portanto, deve ser thread-safe. Dois threads acessam a mesma memória, mas apenas para lê-la. Definido incondicionalmenteflag
comotrue
significa que dois threads diferentes estariam gravando na mesma memória. Isso não é seguro, isso não é seguro, mesmo se os dados que estão sendo gravados forem idênticos aos dados que já estão lá.fonte
[intro.races]/21
.0x01
de um byte que já está0x01
causando um comportamento "inseguro". Em um sistema com acesso à memória word ou dword, isso aconteceria; mas o otimizador deve estar ciente disso. Em um PC moderno ou sistema operacional de telefone, nenhum problema ocorre. Portanto, este não é um motivo válido.flag
esteja em uma página de cópia na gravação. Agora, no nível da CPU, o comportamento pode ser definido (falha de página, deixe o sistema operacional lidar com isso), mas no nível do sistema operacional, ele ainda pode estar indefinido, certo?Não tenho certeza sobre o comportamento de C ++ aqui, mas em C a memória pode mudar porque se a memória contiver um valor diferente de zero diferente de 1, ela permaneceria inalterada com a verificação, mas mudaria para 1 com a verificação.
Mas como não sou muito fluente em C ++, não sei se essa situação é possível.
fonte
_Bool
?bool
/_Bool
e significatrue
, então nesse ABI em particular, você provavelmente está certo._Bool
e C ++bool
são do mesmo tipo ou tipos compatíveis que seguem as mesmas regras. Com o MSVC, eles têm o mesmo tamanho e alinhamento, mas não há uma declaração oficial sobre se eles usam as mesmas regras.<stdbool.h>
inclui umtypedef _Bool bool;
E sim, em x86 (pelo menos no System V ABI),bool
/_Bool
devem ser 0 ou 1, com os bits superiores do byte apagados. Não acho que essa explicação seja plausível.func
foi passado em RDI, enquanto o Windows usaria RDX).