El rendimiento de acceso a memoria no uniforme (NUMA) es una situación MESI

Acceso a memoria no uniforme NUMA

"Nu mă, nu mă iei"
- Dan Mihai Bălan

Como sabrá cualquiera que haya administrado un sistema de archivos Linux anteriormente, la actualización a una nueva versión del kernel de Linux no suele ser demasiado difícil, pero a veces puede tener un impacto sorprendente en el rendimiento. Esta es una historia de una de esas épocas.

De qumulo software del sistema de archivos se envía sobre una distribución de Ubuntu bastante estándar. Actualizamos periódicamente la distribución y el kernel de Linux subyacente para mantenernos en las versiones compatibles a largo plazo, de modo que podamos seguir estando al tanto de las últimas actualizaciones de seguridad y correcciones de errores, así como para admitir dispositivos de almacenamiento más nuevos.

Recientemente actualizamos todas nuestras plataformas para usar Linux 5.4; anteriormente, algunas estaban en 4.4 y otras en 4.15. En su mayor parte, todo salió bien. Pero en las pruebas de rendimiento con el nuevo kernel, notamos algo extraño: el rendimiento de nuestros sistemas Qumulo con CPU Intel dual 4U (conocidos por nuestros clientes como los sistemas Qumulo QC104, QC208, QC260 y QC360) había disminuido, mucho. En una prueba con una gran cantidad de flujos de escritura, el rendimiento pasó de ~ 4.5 GB / s a ​​aproximadamente 3.2 GB / s, ¡casi una caída del 30%!

Estos sistemas utilizan un Haswell de doble procesador. Muchos de nuestros clientes tienen implementaciones grandes y activas de las que dependen para administrar sus datos, y trabajamos constantemente para que nuestras plataformas sean más rápidas con el tiempo, ¡no más lentas!

Así que era hora de investigar, averiguar qué había hecho que nuestro software se ejecutara más lento y solucionarlo.

Supervisión, resolución de problemas y diagnóstico de problemas de rendimiento de Linux

Al diagnosticar cualquier tipo de problema de rendimiento, por lo general, comenzamos mirando los contadores de rendimiento, las mediciones de latencia y otras métricas generadas por la instrumentación dentro de nuestro sistema de archivos. Las herramientas de supervisión del rendimiento de Linux como estas nos permiten analizar fácilmente dónde pasa el tiempo el sistema y diagnosticar con mayor precisión la fuente del problema. En este caso, las métricas contaron una historia clara: la E / S del disco era normal, el uso de la CPU era normal, pero se dedicaba mucho más tiempo a las redes.

Esto nos llevó a buscar más de cerca cualquier cosa relacionada con la red que pudiera haber cambiado significativamente como parte de la actualización. Afortunadamente, nos ahorramos la tarea de buscar en el código del kernel, ya que encontramos un sospechoso principal de inmediato: además de actualizar a Linux 5.4, habíamos cambiado los controladores de Ethernet. Anteriormente, usábamos OFED para las NIC de Mellanox, pero ahora usábamos la versión incluida con el kernel.

Los detalles del código del controlador tampoco resultaron ser importantes, ya que la causa real de la degradación del rendimiento fue un pequeño cambio de configuración: OFED incluye un script que afinita automáticamente las interrupciones de red con la CPU más cercana, el controlador en caja no. Reintroducir el script, o simplemente establecer las afinidades manualmente, devolvió inmediatamente todo el rendimiento.

Entonces, teníamos nuestra respuesta, y con un pequeño ajuste pudimos enviar con confianza la nueva distribución con kernel 5.4 a nuestros clientes.

Solución de problemas de un cuello de botella de rendimiento relacionado con NUMA

No estamos satisfechos con simplemente poder solucionar problemas. Queremos entender sus causas subyacentes. Y en este caso, algo parecía extraño. En un sistema NUMA (sistema de acceso a memoria no uniforme), generalmente es mejor tener las interrupciones afinizadas localmente, pero en los niveles de rendimiento en consideración (solo un par de GB / s en un nodo determinado) no tenía sentido que la comunicación entre las CPU podrían ser el cuello de botella.

El siguiente diagrama muestra una imagen simplificada de la arquitectura. Tiene dos CPU Xeon E5-2620 v3, 128 GB de RAM y varios discos:

Diagrama de arquitectura del sistema NUMA

Tenga en cuenta los vínculos entre las dos CPU. Estos son canales de interconexión QuickPath (QPI), que se utilizan siempre que una CPU necesita datos que solo están disponibles para la otra CPU; por ejemplo, si la CPU 1 necesita procesar datos que se recibieron de la red, los datos deberán cruzar QPI .

¿Qué es QuickPath Interconnect?

QuickPath Interconnect es una conexión de datos entre una CPU y otros recursos de la placa base (como un concentrador de E / S u otras CPU) en algunas microarquitecturas de Intel, introducidas por primera vez en 2008. Su objetivo es proporcionar un ancho de banda extremadamente alto para permitir una alta escalabilidad integrada, después de Todo, no tiene sentido poner más CPU en una placa base si no pueden hacer un uso completo de los recursos del sistema. (Fue reemplazado en 2017 por una nueva versión, llamada UltraPath Interconnect, con el lanzamiento de la microarquitectura Skylake).

El E5-2620 v3 tiene dos canales de interconexión QuickPath de 16 bits cronometrado a 4GHz. Cada canal transfiere datos tanto en flancos de reloj ascendentes como descendentes, lo que da como resultado 8 gigatransferencias (GT) por segundo, o 16 GB / s de ancho de banda en ambas direcciones. Entonces, con dos de ellos, deberíamos acercarnos a los 32 GB / s antes de que este enlace se convierta en un cuello de botella, ¡más que suficiente para manejar los requisitos relativamente modestos de la NIC y los dispositivos de almacenamiento!

Sin embargo, claramente, estábamos experimentando un cuello de botella y desapareció cuando tomamos medidas para evitar la comunicación entre CPU. Entonces, ¿qué estaba pasando?

Echemos un vistazo a lo que debe suceder cuando un nodo de Qumulo procesa una solicitud para leer datos, digamos usando el protocolo NFS. El siguiente diagrama muestra una versión simplificada del flujo de datos:

cómo fluyen los datos a través del nodo Qumulo

  1. Algunos datos deberán obtenerse de otros nodos (la flecha azul). Estos datos llegan como una serie de segmentos TCP a la NIC, que luego se descargan a través de DMA a los búferes de anillo en el kernel, y desde allí al búfer de página interno del sistema de archivos Qumulo.
  2. Se obtendrán algunos datos de este nodo (flecha violeta). Esto se lee del disco y se copia en el búfer de la página.
  3. Una vez que se han recopilado los datos locales y los datos remotos, un trabajador de protocolo lo ensambla todo en una respuesta, que luego se coloca en búferes de transmisión, desde los cuales se enviará DMA a la NIC y se enviará a través de la red al cliente solicitante. .

Llegar al centro de la cebolla

Una idea clave aquí es que cada una de las flechas en el diagrama de flujo anterior (excepto las que salen de la NIC) representan un punto donde sería posible que los datos crucen el enlace QPI, a veces más de una vez.

Por ejemplo, recuerde del diagrama de arquitectura que hay dispositivos de almacenamiento conectados a ambas CPU. Si la CPU 0 lee 1 GB de datos de un disco conectado a la CPU 1 y luego lo copia en una región del búfer de página asignada a la memoria adjunta a la CPU 1, esos datos cruzarán el enlace dos veces. El trabajador de protocolo que procesa los datos puede ejecutarse en la CPU 0, requiriendo los mismos datos para cruzar el enlace nuevamente, y así sucesivamente.

Por lo tanto, hay un "efecto de amplificación" en juego: aunque el nodo puede estar entregando datos a solo 2 GB / s, podría haber varias veces más tráfico en la interconexión QuickPath, debido a que los mismos datos rebotan hacia adelante y hacia atrás, como un juego de tenis de datos:

Nodo NUMA

Identificar al verdadero culpable del cuello de botella de rendimiento relacionado con NUMA

¡Pero espera, te escucho decir! Incluso en los escenarios más pesimistas, esta amplificación no podría convertir 2 GB / s en 32 GB / s, ¡simplemente no hay suficientes bordes que crucen el límite de NUMA en ese gráfico!

Eso es cierto: parecía que nos estábamos enfrentando a un cuello de botella muy por debajo de la velocidad nominal del enlace. Afortunadamente, Intel Comprobador de latencia de memoria (también conocido como Intel MLC) puede medir el rendimiento real del sistema directamente, así que lo ejecutamos y confirmó nuestras sospechas:

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