121 votes

Qu'est-ce qui est le plus efficace, un verrou mutex de base ou un entier atomique ?

Pour quelque chose de simple, comme un compteur, si plusieurs threads augmentent le nombre. J'ai lu que les verrous mutex peuvent diminuer l'efficacité puisque les threads doivent attendre. Donc, pour moi, un compteur atomique serait le plus efficace, mais j'ai lu qu'en interne, c'est fondamentalement un verrou ? Je ne comprends donc pas comment l'un ou l'autre pourrait être plus efficace que l'autre.

153voto

yahe Points 34

Les opérations atomiques tirent parti du support du processeur (instructions de comparaison et d'échange) et n'utilisent pas du tout les verrous, alors que les verrous dépendent davantage du système d'exploitation et fonctionnent différemment sous Win et Linux, par exemple.

Les verrous suspendent en fait l'exécution du fil, libérant les ressources du processeur pour d'autres tâches, mais entraînant une surcharge évidente de changement de contexte lors de l'arrêt et du redémarrage du fil. Au contraire, les threads qui tentent des opérations atomiques n'attendent pas et continuent d'essayer jusqu'à ce qu'ils réussissent (ce qu'on appelle le busy-waiting), donc ils n'encourent pas de surcharge de changement de contexte, mais ne libèrent pas non plus de ressources du processeur.

En résumé, en général, les opérations atomiques sont plus rapides si la contention entre les threads est suffisamment faible. Vous devez absolument faire des tests comparatifs car il n'y a pas d'autre méthode fiable pour savoir quel est le surcoût le plus faible entre le changement de contexte et le busy-waiting.

66voto

Cort Ammon Points 1584

Si vous avez un compteur pour lequel les opérations atomiques sont supportées, il sera plus efficace qu'un mutex.

Techniquement, l'atomique verrouille le bus mémoire sur la plupart des plateformes. Cependant, il y a deux détails qui améliorent la situation :

  • Il est impossible de suspendre un thread pendant le verrouillage du bus mémoire, mais il est possible de suspendre un thread pendant le verrouillage d'un mutex. C'est ce qui permet d'obtenir une garantie sans verrouillage (qui ne dit rien sur l'absence de verrouillage - elle garantit simplement qu'au moins un thread progresse).
  • Les mutex finissent par être implémentés avec des atomiques. Comme il faut au moins une opération atomique pour verrouiller un mutex, et une opération atomique pour le déverrouiller, il faut au moins deux fois plus de temps pour effectuer un verrouillage de mutex, même dans le meilleur des cas.

33voto

LWimsey Points 2916

Une implémentation minimale (conforme aux normes) de mutex nécessite deux ingrédients de base :

  • Un moyen de transmettre atomiquement un changement d'état entre les threads (l'état "verrouillé").
  • des barrières de mémoire pour obliger les opérations de mémoire protégées par le mutex à rester dans la zone protégée.

Il est impossible de faire plus simple que cela en raison de la relation "synchronise avec" exigée par la norme C++.

Une mise en œuvre minimale (correcte) pourrait ressembler à ceci :

class mutex {
    std::atomic<bool> flag{false};

public:
    void lock()
    {
        while (flag.exchange(true, std::memory_order_relaxed));
        std::atomic_thread_fence(std::memory_order_acquire);
    }

    void unlock()
    {
        std::atomic_thread_fence(std::memory_order_release);
        flag.store(false, std::memory_order_relaxed);
    }
};

En raison de sa simplicité (elle ne peut pas suspendre le fil d'exécution), il est probable que, dans le cas d'une faible contention, cette mise en œuvre surpasse une std::mutex . Mais même dans ce cas, il est facile de voir que chaque incrément de nombre entier, protégé par ce mutex, nécessite les opérations suivantes :

  • un atomic stocker pour libérer le mutex
  • un atomic compare-and-swap (lecture-modification-écriture) pour acquérir le mutex (éventuellement plusieurs fois)
  • un incrément entier

Si vous comparez cela avec un standalone std::atomic<int> qui est incrémenté avec une seule lecture-modification-écriture (inconditionnelle) (ex. fetch_add ), il est raisonnable de s'attendre à ce qu'une opération atomique (utilisant le même modèle d'ordonnancement) soit plus performante dans le cas où un mutex est utilisé.

14voto

RonTLV Points 1147

L'entier atomique est un mode utilisateur car c'est beaucoup plus efficace qu'un mutex qui tourne en mode noyau . La portée de l'entier atomique est une seule application alors que la portée du mutex est pour tous les logiciels en cours d'exécution sur la machine.

6voto

Ajay Points 507

Les classes de variables atomiques de Java sont capables de tirer parti des instructions Compare et Swap fournies par le processeur.

Voici une description détaillée des différences : http://www.ibm.com/developerworks/library/j-jtp11234/

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