Lors de la mise en œuvre de structures de données sans verrouillage et de code de synchronisation, il est souvent nécessaire de supprimer les optimisations du compilateur. Normalement, les gens le font en utilisant asm volatile
avec memory
dans la liste de clobber, mais vous voyez parfois juste asm volatile
ou simplement une mémoire asm
clobbering simple.
Quel impact ces différentes déclarations ont-elles sur la génération de code (en particulier dans GCC, car il est peu probable qu'il soit portable)?
Juste pour référence, ce sont les variations intéressantes:
asm (""); // presumably this has no effect on code generation
asm volatile ("");
asm ("" ::: "memory");
asm volatile ("" ::: "memory");
Voir la page "Extended Asm" dans la documentation GCC .
Vous pouvez empêcher la suppression d'une instruction
asm
en écrivant le mot clévolatile
après leasm
. [...] Le mot clévolatile
indique que l'instruction a des effets secondaires importants. GCC ne supprimera pas un asmvolatile
s'il est accessible.
et
Une instruction
asm
sans aucun opérande de sortie sera traitée de manière identique à une instructionasm
volatile.
Aucun de vos exemples n'a de opérande de sortie spécifié, donc les asm
et asm volatile
les formulaires se comportent de manière identique: ils créent un point dans le code qui ne peut pas être supprimé (sauf s'il est prouvé qu'il est inaccessible).
Ce n'est pas tout à fait la même chose que de ne rien faire. Voir cette question pour un exemple de mannequin asm
qui change la génération de code - dans cet exemple, le code qui fait le tour d'une boucle 1000 fois est vectorisé en code qui calcule 16 itérations de la boucle immediatement; mais la présence d'un asm
à l'intérieur de la boucle inhibe l'optimisation (le asm
doit être atteint 1000 fois).
Le "memory"
clobber fait supposer à GCC que toute mémoire peut être lue ou écrite arbitrairement par le bloc asm
, donc empêchera le compilateur de réorganiser les charges ou les magasins à travers lui:
Cela empêchera GCC de conserver les valeurs de la mémoire en cache dans les registres de l'instruction d'assembleur et n'optimisera pas les stockages ou les charges dans cette mémoire.
(Cela n'empêche pas un processeur de réorganiser les charges et les magasins par rapport à un autre processeur, cependant, vous avez besoin d'instructions de barrière de mémoire réelles pour cela.)
asm ("")
ne fait rien (ou du moins, il n'est pas censé faire quoi que ce soit.
asm volatile ("")
ne fait rien non plus.
asm ("" ::: "memory")
est une simple barrière de compilation.
asm volatile ("" ::: "memory")
AFAIK est le même que le précédent. Le mot clé volatile
indique au compilateur qu'il n'est pas autorisé à déplacer ce bloc Assembly. Par exemple, il peut être hissé hors d'une boucle si le compilateur décide que les valeurs d'entrée sont les mêmes à chaque appel. Je ne sais pas vraiment dans quelles conditions le compilateur décidera qu'il comprend suffisamment l'assembly pour essayer d'optimiser son placement, mais le mot clé volatile
le supprime complètement. Cela dit, je serais très surpris si le compilateur tentait de déplacer une instruction asm
qui n'avait aucune entrée ou sortie déclarée.
Par ailleurs, volatile
empêche également le compilateur de supprimer l'expression s'il décide que les valeurs de sortie ne sont pas utilisées. Cela ne peut se produire que s'il existe des valeurs de sortie, donc cela ne s'applique pas à asm ("" ::: "memory")
.
Juste pour être complet sur réponse de Lily Ballard , Visual Studio 2010 propose _ReadBarrier()
, _WriteBarrier()
et _ReadWriteBarrier()
pour faire de même (VS2010 ne fait pas ' t autoriser l'assemblage en ligne pour les applications 64 bits).
Celles-ci ne génèrent aucune instruction mais affectent le comportement du compilateur. Un bel exemple est ici .
MemoryBarrier()
génère lock or DWORD PTR [rsp], 0