Les performances d'accès mémoire non uniforme (NUMA) sont une situation MESI

Accès mémoire non uniforme NUMA

"Nu mă, nu mă iei"
– Dan Mihai Balan

Comme le sait peut-être quiconque a déjà administré un système de fichiers Linux, la mise à niveau vers une nouvelle version du noyau Linux n'est généralement pas trop difficile, mais elle peut parfois avoir des impacts surprenants sur les performances. C'est l'histoire d'une de ces époques.

Qumulo logiciel de système de fichiers est livré avec une distribution Ubuntu assez standard. Nous mettons périodiquement à jour la distribution et le noyau Linux sous-jacent pour rester sur les versions prises en charge à long terme, afin que nous puissions continuer à nous tenir au courant des dernières mises à jour de sécurité et corrections de bogues, ainsi que de prendre en charge les nouveaux périphériques de stockage.

Nous avons récemment mis à jour toutes nos plates-formes pour utiliser Linux 5.4 - auparavant, certaines étaient sur 4.4 et d'autres sur 4.15. Pour la plupart, tout s'est bien passé. Mais lors des tests de performances avec le nouveau noyau, nous avons remarqué quelque chose d'étrange : le débit de nos systèmes Qumulo 4U Dual Intel CPU (connus de nos clients sous le nom de systèmes Qumulo QC104, QC208, QC260 et QC360) avait beaucoup baissé. Dans un test avec un grand nombre de flux d'écriture, le débit est passé d'environ 4.5 Go/s à environ 3.2 Go/s, soit une baisse de près de 30 % !

Ces systèmes utilisent un Haswell à double processeur. Beaucoup de nos clients ont des déploiements importants et actifs dont ils dépendent pour gérer leurs données - et nous travaillons constamment pour rendre nos plates-formes plus rapides au fil du temps, pas plus lentes !

Il était donc temps de creuser, de déterminer ce qui avait rendu notre logiciel plus lent et de le réparer.

Surveillance, dépannage et diagnostic des problèmes de performances Linux

Lors du diagnostic de tout type de problème de performances, nous commençons généralement par examiner les compteurs de performances, les mesures de latence et d'autres métriques générées par l'instrumentation au sein de notre système de fichiers. Les outils de surveillance des performances Linux tels que ceux-ci nous permettent de déterminer facilement où le système passe son temps et de diagnostiquer plus précisément la source du problème. Dans ce cas, les métriques racontaient une histoire claire : les E/S disque étaient normales, l'utilisation du processeur était normale, mais il y avait beaucoup plus de temps consacré à la mise en réseau.

Cela nous a incités à rechercher de plus près tout élément lié au réseau qui aurait pu changer de manière significative dans le cadre de la mise à niveau. Heureusement, nous avons été épargnés par la corvée de fouiller dans le code du noyau, car nous avons tout de suite trouvé un suspect principal : en plus de la mise à niveau vers Linux 5.4, nous avions changé les pilotes Ethernet. Auparavant, nous utilisions OFED pour les cartes réseau Mellanox, mais maintenant nous utilisions la version incluse avec le noyau.

Les détails du code du pilote se sont avérés sans importance non plus, car la véritable cause de la dégradation des performances était un petit changement de configuration : OFED inclut un script qui affine automatiquement les interruptions de réseau avec le processeur le plus proche, contrairement au pilote intégré. Réintroduire le script, ou simplement définir les affinités manuellement, a immédiatement ramené tout le débit.

Nous avons donc eu notre réponse, et avec un petit ajustement, nous avons pu envoyer en toute confiance la nouvelle distribution avec le noyau 5.4 à nos clients.

Dépannage d'un goulot d'étranglement des performances lié à NUMA

Nous ne nous contentons pas de simplement pouvoir résoudre les problèmes. Nous voulons comprendre leurs causes sous-jacentes. Et dans ce cas, quelque chose semblait étrange. Dans un système NUMA (système d'accès à la mémoire non uniforme), il est généralement préférable d'avoir des interruptions localement affinitées, mais aux niveaux de débit considérés (seulement quelques Go/s sur un nœud donné), cela n'avait pas de sens que la communication entre les processeurs pourraient être le goulot d'étranglement.

Le schéma ci-dessous montre une image simplifiée de l'architecture. Il dispose de deux processeurs Xeon E5-2620 v3, de 128 Go de RAM et de nombreux disques :

Diagramme d'architecture du système NUMA

Notez les liens entre les deux CPU. Ce sont des canaux QuickPath Interconnect (QPI), qui sont utilisés chaque fois qu'un CPU a besoin de données qui ne sont disponibles que pour l'autre CPU - par exemple, si le CPU 1 doit traiter des données reçues du réseau, les données devront traverser QPI .

Qu'est-ce que l'interconnexion QuickPath ?

QuickPath Interconnect est une connexion de données entre un processeur et d'autres ressources de la carte mère (telles qu'un concentrateur d'E/S ou d'autres processeurs) dans certaines microarchitectures Intel, introduite pour la première fois en 2008. Son objectif est de fournir une bande passante extrêmement élevée pour permettre une évolutivité élevée à bord - après tout, il ne sert à rien de mettre plus de processeurs sur une carte mère s'ils ne peuvent pas utiliser pleinement les ressources système. (Il a été remplacé en 2017 par une nouvelle version, appelée UltraPath Interconnect, avec la sortie de la microarchitecture Skylake.)

Le E5-2620 v3 dispose de deux canaux d'interconnexion QuickPath 16 bits cadencé à 4 GHz. Chaque canal transfère des données sur les fronts d'horloge montant et descendant, ce qui entraîne 8 gigatransferts (GT) par seconde, ou 16 Go/s de bande passante dans les deux sens. Ainsi, avec deux d'entre eux, nous devrions approcher les 32 Go/s avant que ce lien ne devienne un goulot d'étranglement – ​​plus que suffisant pour gérer les exigences relativement modestes de la carte réseau et des périphériques de stockage !

Cependant, il est clair que nous étions confrontés à un goulot d'étranglement, et il a disparu lorsque nous avons pris des mesures pour éviter la communication entre processeurs. Alors que se passait-il ?

Jetons un coup d'œil à ce qui doit se passer lorsqu'un nœud Qumulo traite une demande de lecture de données, par exemple en utilisant le protocole NFS. Le diagramme ci-dessous montre une version simplifiée du flux de données :

comment les données circulent à travers le nœud Qumulo

  1. Certaines données devront être récupérées à partir d'autres nœuds (la flèche bleue). Ces données arrivent sous la forme d'une série de segments TCP sur la carte réseau, qui sont ensuite déchargés via DMA vers des tampons en anneau dans le noyau, et de là vers le tampon de page interne du système de fichiers Qumulo.
  2. Certaines données seront récupérées à partir de ce nœud (flèche violette). Ceci est lu à partir du disque et copié dans le tampon de page.
  3. Une fois que les données locales et les données distantes ont été collectées, un agent de protocole les assemble en une réponse, qui est ensuite placée dans des tampons de transmission, à partir desquels elle sera envoyée par DMA à la carte réseau et envoyée via le réseau au client demandeur. .

Arriver au centre de l'oignon

Une idée clé ici est que chacune des flèches de l'organigramme ci-dessus (à l'exception de celles qui sortent de la carte réseau) représente un point où il serait possible pour les données de traverser le lien QPI, parfois plus d'une fois.

Par exemple, rappelez-vous du schéma d'architecture qu'il y a des périphériques de stockage connectés aux deux CPU. Si le processeur 0 lit 1 Go de données à partir d'un disque connecté au processeur 1, puis le copie dans une région de tampon de page mappée sur la mémoire connectée au processeur 1, ces données traverseront le lien deux fois. L'agent de protocole qui traite les données peut s'exécuter sur le processeur 0, nécessitant que les mêmes données croisent à nouveau le lien, et ainsi de suite.

Il y a donc un "effet d'amplification" en jeu - même si le nœud peut servir des données à seulement 2 Go/s, il pourrait y avoir plusieurs fois plus de trafic atteignant l'interconnexion QuickPath, en raison des mêmes données qui rebondissent, comme un jeu de tennis de données :

Nœud NUMA

Identifier le véritable coupable du goulot d'étranglement des performances lié à NUMA

Mais attendez, je vous entends dire! Même dans les scénarios les plus pessimaux, cette amplification ne pourrait pas transformer 2 Go/s en 32 Go/s, il n'y a tout simplement pas assez de bords traversant la limite NUMA dans ce graphique !

C'est vrai – nous semblions faire face à un goulot d'étranglement bien en deçà de la vitesse nominale de la liaison. Heureusement, l'Intel Vérificateur de latence de la mémoire (également connu sous le nom d'Intel MLC) peut mesurer directement les performances réelles du système, nous l'avons donc exécuté et cela a confirmé nos soupçons :

Measuring Memory Bandwidths between nodes within system
Bandwidths are in MB/sec (1 MB/sec = 1,000,000 Bytes/sec)
Using all the threads from each core if Hyper-threading is enabled
Using Read-only traffic type
            Numa node
Numa node        0         1
       0    46242.9          6291.7
       1     6276.3         46268.6

CPU 0 could access its directly connected RAM at ~46 GB/s, and the same for CPU 1 - but the moment either of them wanted to access memory connected to the other CPU, a measly 6 GB/s was the best they could do.

At this point, if you're very familiar with Intel's Haswell architecture, you might already know what's going on. We weren't especially, so we resorted to Googling the symptoms, and that's what led us to the correct answer, in an Intel community thread. Simply go into the BIOS, change the "snoop mode" from "early snoop" to "home snoop," and the bottleneck vanishes!

BIOS screenshot of memory RAS and performance configuration

It was really this simple.

So, what the heck is an early snoop? Unfortunately, early snoop has nothing to do with either a cartoon beagle or a certain American rapper getting his morning cup of coffee. Instead, we'll need to talk about one of the two hardest problems in computer science: cache coherence. (The other two are naming things, and off-by-one errors.) Things are about to get MESI. Specifically, snooping is part of the MESI protocol, and by extension the MESIF variant used by Intel processors.

The MESI Cache Coherence Protocol

MESI is a common protocol for enforcing cache coherence, i.e., that all the different caches in the system have a consistent view of the contents of memory. MESI works by assigning one of four states to every line in every cache, which determines how that cache line can be used:

  • Modified: the line has been changed, and no longer matches what is in main memory.
  • Exclusive: the line matches main memory, and is only present in this cache.
  • Shared: the line is unmodified, but may be present in other caches.
  • Invalid: the line is unused or has been invalidated (e.g., by another cache's copy becoming modified).

MESI vs. MESIF

MESIF is an extension of MESI that was developed by Intel. It adds a fifth state, F for "forward." Forward is similar to Shared, but only one cache in the system may have a line in the Forward state. This is mostly an optimization to avoid excess replies when a cache line is requested from the system. Instead of every cache that holds a copy of the line responding, only the cache that holds the line in the F state will respond.

In both MESI and MESIF, the various caches are kept coherent by notifications across the bus when important changes happen - for example, if a line is written in one cache, any other cache with a copy needs to have that copy invalidated.

Early Snoop vs. Home Snoop

The reason this consideration is critical for performance has to do with the layout of caches in Intel's Haswell architecture. The shared, last-level cache (LLC) on each package is divided into a number of slices, one per core, connected to a very high-bandwidth on-die ring. Each cache slice has its own cache "agent." There is also a "home agent" for each memory controller:

diagram how early snoop version messages propagate between CPUs

In "early snoop" mode (shown above, with two CPUs), when a cache miss or cache coherency event occurs, the initiating cache agent will broadcast a message to all other cache agents in the system. This is intended to reduce access latency by reducing time required to settle the state of a cache line, but with all the cache agents on the remote CPU replying across QuickPath Interconnect, the coherency chatter can significantly reduce available cross-node memory bandwidth. Apparently, with the Haswell-EP E5-2620 v3, it's enough to lose 75% of your bandwidth.

By contrast, in "home snoop" mode, messages are handled first by the home agents on each memory controller, and then delegated to LLC agents as needed. The extra hop adds a small amount of latency, but with the benefit of greatly increased throughput. Note how there are far fewer messages being sent across QuickPath Interconnect:

diagram how Home Snoop version messages propagate between CPUs

See this post for a deeper explanation of NUMA cache coherency.

So, how much better is home snoop?

With the snoop mode changed on all the machines in our test cluster, Memory Latency Checker showed dramatically improved throughput between the CPUs:

Measuring Memory Bandwidths between nodes within system
Bandwidths are in MB/sec (1 MB/sec = 1,000,000 Bytes/sec)
Using all the threads from each core if Hyper-threading is enabled
Using Read-only traffic type
            Numa node
Numa node         0          1
       0     45139.0    25323.8
       1     25336.2    45021.7

But better still, alleviating this bottleneck also significantly improved the performance of these systems - not only did it eliminate the 30% regression observed when the interrupt affinity was lost, it added another 30% of extra throughput on top:

Test (4 nodes, QC208) Baseline (early snoop) Home snoop Change
Write throughput 4400 MB/ 6000 MB/ +36%
Read throughput 7650 MB/s 9550 MB/s/s +29%

I remember first troubleshooting performance issues on this platform five or six years ago, very early in my career at Qumulo - and I have a dim recollection that we experimented with the snoop mode way back then. At the time, it didn't make much difference. But over the years, as we have continued to make performance improvements by removing software bottlenecks, the performance of the underlying hardware platform became the limiting factor, so cranking up the QuickPath Interconnect throughput limit became a huge win.

So, in the very next release of Qumulo Core, we added code to flip this setting in the BIOS for all affected models, so that all our customers with existing deployments would benefit from greater throughput capacity.

There's a lot more great work being done at Qumulo to improve our filesystem's performance. Most of it is much harder (and even more interesting) than finding a hidden "go fast" switch, so keep watching this space!

Interested in learning more about engineering at Qumulo? See more posts written by Qumulo engineers, here. Alternatively, have a look at the Qumulo Data Platform - Software Architecture Overview (PDF).


FURTHER READING

Share this post