116 votes

Pourquoi les compilateurs C++ n'optimisent-ils pas cette affectation booléenne conditionnelle comme une affectation inconditionnelle ?

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 donner inconditionnellement la valeur suivante true comme ceci :

void func(bool& flag)
{
    flag=true;
}

Pourtant, ni gcc ni clang ne l'optimisent de cette façon - tous deux génèrent ce qui suit à l'adresse suivante -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 : est-ce que le code est simplement un cas trop spécial pour se soucier de l'optimiser, ou y a-t-il de bonnes raisons pour lesquelles une telle optimisation ne serait pas souhaitée, étant donné que flag n'est pas une référence à volatile ? Il semble que la seule raison qui pourrait l'être soit que flag pourrait en quelque sorte avoir un non true -ou- false sans comportement indéfini au moment de sa lecture, mais je ne suis pas sûr que cela soit possible.

102voto

Leon Points 20011

Cela peut avoir un impact négatif sur les performances du programme en raison de cohérence de la mémoire cache considérations. Écrire à flag à chaque fois func() est appelée salirait la ligne de cache qui la contient. Cela se produira indépendamment du fait que la valeur écrite correspond exactement aux bits trouvés à l'adresse de destination avant l'écriture.


EDIT

hvd a fourni un autre bonne raison qui empêche une telle optimisation. C'est un argument plus convaincant contre l'optimisation proposée, puisqu'elle peut entraîner un comportement non dé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 pour lequel les compilateurs devraient être fortement interdits - à moins qu'ils puissent prouver que la transformation est sûre pour un contexte particulier - d'introduire l'écriture inconditionnelle. Considérons ce code :

const bool foo = true;

int main()
{
    func(const_cast<bool&>(foo));
}

Avec une inscription inconditionnelle func() cela déclenche définitivement un comportement non défini (écrire dans une mémoire en lecture seule mettra fin au programme, même si l'effet de l'écriture serait autrement un no-op).

48voto

Mis à part la réponse de Léon sur les performances :

Supposons que flag est true . Supposons que deux threads appellent constamment func(flag) . La fonction telle qu'elle est écrite, dans ce cas, ne stocke rien dans le fichier flag Il devrait donc être sûr pour les threads. Deux threads peuvent accéder à la même mémoire, mais uniquement pour la lire. En fixant inconditionnellement flag à true signifie que deux threads différents écriraient dans la même mémoire. Ce n'est pas sûr, ce n'est pas sûr même si les données écrites sont identiques à celles qui sont déjà présentes.

13voto

glglgl Points 35668

Je ne suis pas sûr du comportement du C++ ici, mais en C la mémoire pourrait changer car si la mémoire contient une valeur non nulle autre que 1, elle resterait inchangée avec la vérification, mais passerait à 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 possible.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X