surchargée - Pourquoi les compilateurs C++ n'optimisent-ils pas cette affectation booléenne conditionnelle en tant qu'affectation inconditionnelle?




surcharge opérateur affectation c++ (2)

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 à lui attribuer la valeur 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 niveau d'optimisation -O3 :

_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 simplement un cas particulier à l'optimisation, ou existe-t-il de bonnes raisons pour qu'une telle optimisation ne soit pas souhaitée, dans la mesure où ce flag n'est pas une référence à volatile ? Il semble que la seule raison possible serait que ce flag pourrait avoir une valeur non true ou false sans comportement indéfini au moment de le lire, mais je ne suis pas sûr que cela soit possible.


Cela peut avoir un impact négatif sur les performances du programme en raison de considérations relatives à la cohérence du cache . Écrire pour flag chaque fois que func() est appelé aurait pour effet de salir la ligne de cache. 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.

MODIFIER

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 il peut en résulter un comportement indéfini, alors que ma réponse (originale) ne traitait que des aspects liés aux performances.

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 sûre 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 le flag est true . Supposons que deux threads appellent constamment func(flag) . La fonction telle qu'écrite, dans ce cas, ne stocke rien à flag , elle doit donc être thread-safe. Deux threads accèdent à la même mémoire, mais uniquement pour la lire. Si vous attribuez la valeur true à flag signifie que deux threads différents vont écrire 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.





optimization