Considérez la fonction suivante:
void func(bool& flag)
{
if(!flag) flag=true;
}
Il me semble que si l'indicateur a une valeur booléenne valide, cela équivaudrait à le définir 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 les éléments suivants au -O3
niveau de l'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: est-ce simplement que le code est trop spécial pour être optimisé, ou y a-t-il de bonnes raisons pour lesquelles une telle optimisation serait indésirable, étant donné qu'il flag
ne s'agit pas d'une référence volatile
? Il semble que la seule raison qui pourrait être est que cela flag
pourrait d'une manière ou d'une autre avoir une valeur non- true
ou- false
sans comportement indéfini au moment de la lecture, mais je ne suis pas sûr que cela soit possible.
la source
1
. godbolt.org/g/swe0tcRéponses:
Cela peut avoir un impact négatif sur les performances du programme en raison de considérations de cohérence du cache . Ecrire à
flag
chaque foisfunc()
est appelé salirait la ligne de cache contenant. Cela se produira indépendamment du fait que la valeur en cours d'écriture correspond exactement aux bits trouvés à l'adresse de destination avant l'écriture.ÉDITER
hvd a fourni une autre bonne raison qui empêche une telle optimisation. C'est un argument plus convaincant contre l'optimisation proposée, car elle peut entraîner 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 pour lequel les compilateurs devraient être fortement interdits - à moins qu'ils ne puissent prouver que la transformation est sûre pour un contexte particulier - d'introduire l'écriture inconditionnelle. Considérez ce code:
Avec une écriture inconditionnelle,
func()
cela déclenche définitivement un comportement indéfini (l'écriture dans la mémoire morte mettra fin au programme, même si l'effet de l'écriture serait autrement un no-op).la source
const
pour passer dans une fonction qui peut modifier les données qui sont la source du comportement indéfini, pas l'écriture inconditionnelle. Docteur, ça fait mal quand je fais ça ....Outre la réponse de Léon sur les performances:
Supposons que
flag
c'esttrue
. Supposons que deux threads appellent constammentfunc(flag)
. La fonction telle qu'elle est écrite, dans ce cas, ne stocke rien dansflag
, donc cela doit être thread-safe. Deux threads accèdent à la même mémoire, mais uniquement pour la lire. Définir inconditionnellementflag
surtrue
signifie que deux threads différents écriront 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 aux données déjà présentes.la source
[intro.races]/21
.0x01
sur un octet qui est déjà0x01
provoque un comportement "dangereux". Sur un système avec accès à la mémoire word ou dword, il le ferait; mais l'optimiseur doit en être conscient. Sur un système d'exploitation de PC ou de téléphone moderne, aucun problème ne se produit. Ce n'est donc pas une raison valable.flag
trouve dans une page de copie sur écriture. Maintenant, au niveau du processeur, le comportement peut être défini (défaut de page, laissez le système d'exploitation le gérer), mais au niveau du système d'exploitation, il peut encore être indéfini, non?Je ne suis pas sûr du comportement de C ++ ici, mais en C, la mémoire peut changer car si la mémoire contient une valeur non nulle autre que 1, elle resterait inchangée avec la vérification, mais changée à 1 avec la vérification.
Mais comme je ne maîtrise pas très bien le C ++, je ne sais pas si cette situation est même possible.
la source
_Bool
?bool
/_Bool
et signifietrue
, alors dans cette ABI particulière, vous avez probablement raison._Bool
et C ++bool
sont soit du même type, soit des types compatibles qui suivent les mêmes règles. Avec MSVC, ils ont la même taille et le même alignement, mais il n'y a pas de déclaration officielle indiquant s'ils utilisent les mêmes règles.<stdbool.h>
comprend untypedef _Bool bool;
Et oui, sur x86 (au moins dans l'ABI System V),bool
/_Bool
doivent être 0 ou 1, avec les bits supérieurs de l'octet effacés. Je ne pense pas que cette explication soit plausible.func
été passé dans RDI, alors que Windows utiliserait RDX).