Considérons la fonction suivante:
void func(bool& flag)
{
if(!flag) flag=true;
}
Il me semble que si flag a une valeur booléenne valide, cela équivaudrait à le placer inconditionnellement à true
, comme ceci:
void func(bool& flag)
{
flag=true;
}
Pourtant, ni gcc ni clang ne l'optimisent de cette façon - les deux génèrent ce qui suit à -O3
niveau d'optimisation:
_Z4funcRb:
.LFB0:
.cfi_startproc
cmp BYTE PTR [rdi], 0
jne .L1
mov BYTE PTR [rdi], 1
.L1:
rep ret
Ma question est la suivante: le code est-il trop particulier pour être optimisé, ou existe-t-il de bonnes raisons pour lesquelles une telle optimisation serait indésirable, étant donné que flag
n'est pas une référence à volatile
? Il semble que la seule raison possible serait que flag
pourrait en quelque sorte avoir une valeur non -true
- ou -false
sans comportement indéfini au moment de le lire, mais je 'ne suis pas sûr si cela est possible.
Cela peut avoir un impact négatif sur les performances du programme pour des raisons liées à cohérence du cache . Écrire à flag
à chaque fois que func()
est appelé - altérerait la ligne de cache contenant. Cela se produira même si la valeur en cours d'écriture correspond exactement aux bits trouvés à l'adresse de destination avant l'écriture.
[~ # ~] éditer [~ # ~]
hvd a fourni un autre bonne raison qui empêche une telle optimisation. C'est un argument plus convaincant contre l'optimisation proposée, car il peut en résulter un comportement indéfini, alors que ma réponse (originale) ne traitait que des aspects de performance.
Après un peu plus de réflexion, je peux proposer un autre exemple de la raison pour laquelle les compilateurs devraient être fortement interdits - à moins qu’ils puissent prouver que la transformation est sans danger pour un contexte particulier - en introduisant l’écriture inconditionnelle. Considérons ce code:
const bool foo = true;
int main()
{
func(const_cast<bool&>(foo));
}
Avec une écriture inconditionnelle dans func()
, cela déclenche définitivement un comportement indéfini (l'écriture dans une mémoire en lecture seule mettra fin au programme, même si l'effet de l'écriture serait sinon nul).
En plus de la réponse de Leon sur les performances:
Supposons que flag
soit true
. Supposons que deux threads appellent constamment func(flag)
. La fonction telle qu'écrite, dans ce cas, ne stocke rien dans flag
, elle devrait donc être thread-safe. Deux threads accèdent à la même mémoire, mais uniquement pour la lire. Définir inconditionnellement flag
sur true
signifie que deux threads différents écriraient dans la même mémoire. Ce n'est pas sûr, c'est dangereux même si les données en cours d'écriture sont identiques à celles déjà présentes.
Je ne suis pas sûr du comportement de C++ ici, mais en C, la mémoire pourrait changer car si la mémoire contenait une valeur autre que zéro, elle resterait inchangée avec le contrôle, mais passerait à 1 avec le contrôle.
Mais comme je ne parle pas très bien le C++, je ne sais pas si cette situation est possible.