Les mutexes peuvent être considérablement plus rapides que les canaux dans des cas d'utilisation spécifiques principalement en raison de leur nature légère et de la façon dont elles gèrent les affirmations et la synchronisation. Voici des explications détaillées avec des contextes où les mutex surpassent les canaux:
Différences fondamentales affectant les performances
Les mutexes fournissent une exclusion mutuelle en permettant à un seul thread ou à la goroutine d'accéder à une section critique à la fois. En interne, une opération mutex implique généralement des instructions atomiques et peut impliquer le blocage au niveau du noyau uniquement lorsqu'il y a une affirmation. Pour cette raison, les opérations de verrouillage / déverrouillage de mutex non contractées ne coûtent souvent que quelques nanosecondes.
Les canaux, en revanche, sont des abstractions de niveau supérieur conçues pour la communication entre les goroutines ou les fils. L'envoi ou la réception sur un canal consiste à gérer une file d'attente, à des allocations de mémoire possibles, à planifier des goroutines et à les réveiller en cas de sommeil. Ces frais généraux signifient que des mutations d'état simples gardées par un canal entraînent plus de coûts par rapport à un mutex en raison de la commutation contextuelle et du travail de coordination.
Ces différences fondamentales indiquent déjà pourquoi, pour une protection variable partagée simple ou des sections critiques, les mutex sont généralement plus rapides.
Les cas d'utilisation où les mutex sont plus rapides
Protection d'état partagée simple
Lorsqu'un programme doit protéger les variables partagées telles que les compteurs, les cartes ou les drapeaux simples - les mutex sont beaucoup plus rapides car la section critique implique des frais généraux de synchronisation. Les exemples incluent:
- Compter les demandes dans un serveur Web: Chaque opération d'incrément de demande peut être gardée par un mutex sans nécessiter la surcharge d'envoi de messages via un canal, ce qui ajoute des retards de file d'attente et de planification. Les mutex permettent un accès direct et direct et il a été démontré qu'il améliore le débit par un ordre de grandeur ou plus.
- Accéder aux caches ou cartes partagées: la protection des structures de données avec Mutexes offre des lectures en ligne et des écritures avec un minimum de frais généraux. L'utilisation de canaux comme intermédiaires ici introduit une latence supplémentaire, car chaque accès devient un aller-retour à la réponse de demande.
Les tests de référence montrent que les compteurs basés sur Mutex peuvent être environ 75 fois plus rapides que les compteurs basés sur les canaux en raison de la réduction des frais généraux de synchronisation et d'éviter le coût de la gestion des files d'attente et de la commutation de contexte inhérente aux canaux.
Scénarios à faible affirmation ou non
Dans les environnements à faible affirmation, les opérations de verrouillage et de déverrouillage de mutex ne sont presque que des opérations atomiques sans temps d'attente. Le cas non condamné est l'endroit où les mutex brillent car le verrouillage est léger et ne déclenche généralement pas la planification au niveau du noyau.
Les canaux, cependant, entraînent des coûts même dans une faible affirmation car chaque envoi / réception implique une gestion, une tampon et une synchronisation plus complexes. Ainsi, pour un petit nombre de goroutines ou dans des scénarios avec peu de controverse, les mutex surpassent considérablement les canaux.
Haute concurrence avec un verrouillage simple
Dans les scénarios où de nombreux goroutines effectuent des sections critiques très courtes qui modifient l'état partagé, les mutexes ont toujours tendance à être plus efficaces que les canaux. La principale raison est que le bloc Mutexes rivalise les goroutines dans le noyau, permettant une planification efficace et du réveil exactement une goroutine lors du déverrouillage. Les canaux, en revanche, impliquent la planification active de Goroutine et la gestion des files d'attente qui crée des frais généraux sous charge.
Par exemple, les repères où jusqu'à 10 goroutines sont impliqués montrent que les mutex sont plusieurs fois plus rapides et que les mutex restent compétitifs même avec des centaines de goroutines.
Distribution de travail lorsque les mutations de l'État sont minimes
Pour gérer des tranches ou des listes de tâches, les mutex peuvent être plus rapides lors du verrouillage brièvement pour faire sauter ou pousser les tâches. Bien que les canaux soient très naturels pour la distribution des tâches, si la section critique est courte et que l'état partagé nécessite un verrouillage rapide et un déverrouillage, les mutex évitent les frais généraux supplémentaires de la coordination des canaux et apportent un meilleur débit.
Dans de nombreux systèmes du monde réel tels que les pools de travailleurs ou les files d'attente, les mutex peuvent être plus simples et plus rapides pour la gestion des listes de tâches par rapport aux canaux.
Pourquoi les mutex sont plus rapides dans ces cas
- Atterst-frais plus bas: les mutexes utilisent directement les instructions du processeur atomique pour le verrouillage et le déverrouillage, souvent sans interrupteur de contexte ni décisions de planification coûteuses.
- Blocage du noyau avec file d'attente: Contre que les goroutines dorment sur mutexes efficacement, formant une file d'attente, et le planificateur les réveille en série. Les canaux provoquent des modèles de réveil et de planification plus complexes.
- Aucun message de passage de message: les canaux doivent allouer des tampons ou des entrées de file d'attente et des données de copie / transfert, ce qui est inutile lorsque la propriété et l'exclusivité simples suffisent.
- Accès à la mémoire directe: les mutex permettent un accès direct à la mémoire dans une section critique, tandis que les canaux nécessitent l'envoi de données via un support de communication, ajoutant des couches d'indirection et de latence.
contextes moins adaptés aux canaux
Bien que les canaux fournissent un moyen élégant et sûr de communiquer entre les goroutines et sont très précieux pour le traitement des pipelines et la manipulation des événements, leurs frais généraux les rendent moins adaptés aux mutations de l'état partagé brèves et fréquentes.
Les canaux sont idéaux lors de la synchronisation des calculs complexes impliquant plusieurs goroutines où la sémantique de transmission de messages est naturelle et bénéfique. Mais pour un verrouillage simple, les mutex brillent.
preuve expérimentale et de référence
- Les repères avec les primitives de synchronisation de Go montrent que les compteurs mutex fonctionnent avec des latences dans la gamme nanosecondes tandis que les compteurs de canal sont des ordres de grandeur plus lents (par exemple, 0,8 ns contre 60 ns par opération).
- Les performances ne se renversent qu'à des échelles très élevées (par exemple, des milliers de goroutines) lorsque les canaux peuvent surpasser les mutex dans certains cas, car les canaux évitent les frais généraux de verrouillage et un meilleur modèle de pipelines simultanés.
- Sous les contributions, les mutex surpassent les spinlocks en raison de l'efficacité de planification au niveau du noyau.
- Les mutex évitent les allocations de mémoire et la commutation de contexte présents dans les canaux, ce qui conduit à des gains significatifs de débit et à une utilisation inférieure du processeur pour protéger les variables partagées.
Résumé des recommandations de cas d'utilisation
- Utilisez des mutexes lors de la protection de la mémoire partagée ou de l'état mutable accessible simultanément, surtout si la section critique est courte et implique des opérations simples.
- Utilisez des canaux pour orchestrer les calculs, les pipelines et les architectures motivées par des événements où les messages représentent l'état ou les tâches à traiter de manière asynchrone.
- Pour le code critique de performance impliquant des mutations à l'état direct par plusieurs threads ou goroutines, les mutex fournissent généralement un débit supérieur et une latence inférieure.
- Considérez les canaux lorsque votre logique concurrencée bénéficie de la sémantique de transfert de messages, mais évitez-les pour les besoins de verrouillage simples à haute fréquence.
Dive profonde technique
Les mutexes utilisent généralement des opérations atomiques comme comparer-and-swap (CAS) pour le verrouillage et le déverrouillage dans l'espace utilisateur et entrent uniquement le noyau que lorsque les affirmations se produisent pour bloquer le thread. Cela minimise la commutation de contexte et les frais généraux dans le cas non condamné.
Les canaux implémentent les files d'attente FIFO pour les messages et sont souvent soutenus par des tampons qui peuvent redimensionner. L'envoi sur un canal consiste à vérifier si le récepteur est prêt, à copier des données dans le tampon ou à bloquer, et à planifier le goroutine récepteur. Ces étapes ajoutent des coûts qui se multiplient avec la fréquence de fonctionnement.
Mutexes Sleep Contest Threads et maintenez une file d'attente pour qu'un seul thread se réveille à la fois. Les canaux peuvent réveiller plusieurs goroutines à mesure que les messages deviennent disponibles, conduisant à des coûts de planification et de commutation de contexte plus complexes.
Conclusion
En résumé, les mutex sont nettement plus rapides que les canaux dans des scénarios nécessitant des mutations d'état partagées rapides, simples et fréquentes avec une faible affirmation ou des sections critiques courtes. Leur mise en œuvre de fonctionnement atomique légère, le blocage efficace du noyau et le réveil des threads et l'accès direct à la mémoire offrent des performances supérieures par rapport à la planification, à la copie et à des messages qui passent les canaux aériens. Les canaux sont mieux adaptés à la communication du travail complexe et à la coordination des goroutines, mais paient un coût en vitesse de synchronisation brute.
Cette compréhension est soutenue par plusieurs repères et des exemples pratiques des systèmes de production et des résultats expérimentaux. Ainsi, la décision entre les mutex et les canaux devrait être basée sur les caractéristiques de la charge de travail et les besoins de synchronisation, avec des mutexés préférés pour la vitesse brute pour garder la mémoire partagée et les canaux réservés aux modèles de coordination et de communication de niveau supérieur.