banner
Hogar / Noticias / Actualizando NVMe/TCP
Noticias

Actualizando NVMe/TCP

May 22, 2023May 22, 2023

Descarga la presentación:Actualizando NVMe/TCP

Soy Sagi Grimberg. Soy CTO y cofundador de Lightbits Labs, y he sido coautor de la especificación del estándar NVMe sobre TCP.

Entonces, comenzaremos con una breve introducción. ¿Qué es NVMe sobre TCP? NVMe sobre TCP es el enlace de transporte estándar que ejecuta NVMe sobre redes TCP/IP estándar. Sigue la especificación NVMe estándar que define la interfaz de cola y la interfaz de cola múltiple que se ejecuta justo encima de los sockets TCP/IP. Tiene el conjunto de comandos NVMe estándar, pero resulta que está encapsulado en lo que llamamos PDU NVMe/TCP que asignan los flujos de TCP. Entonces, en el diagrama aquí, tenemos básicamente la arquitectura NVMe, las arquitecturas centrales que definen el administrador, las E/S y otros conjuntos de comandos.

01:14 ES: Debajo tenemos NVMe sobre Fabrics que define cápsulas, propiedades y descubrimiento. Y NVMe sobre TCP básicamente define funciones y mensajes adicionales, y también el mapeo de transporte al propio tejido subyacente, que en nuestro caso es TCP/IP.

Entonces, ¿cómo se procesa una cola mediante NVMe sobre TCP o cómo se define en NVMe sobre TCP? Básicamente, cada cola se asigna a una conexión TCP bidireccional y la transferencia de datos ordenada generalmente se procesa mediante un contexto dedicado, ya sea en software o de alguna manera en hardware. Entonces, en el diagrama aquí, tenemos en el lado izquierdo el host que tiene una interfaz de cola para el transporte NVMe en sí, tiene una cola de envío y una cola de finalización. Todos los envíos y finalizaciones se manejan en lo que llamamos un subproceso de E/S NVMe-TCP, o algún contexto de E/S que se activa desde el host que emite E/S o desde la red, generalmente completando E/S o recibiendo datos.

02:24 ES: La misma imagen ocurre en el lado derecho con el controlador, y básicamente, estos son los contextos que se encargan de transferir datos entre el host y el controlador. Entonces, cada una de estas colas en realidad está asignada a CPU dedicadas generalmente, pero no necesariamente, podría ser más, podría ser menos en realidad, pero el punto es que no hay serialización en todo el controlador, por lo que cada cola no depende de un compartido cinta con otras colas, lo que la hace extremadamente paralela. Y el diagrama aquí es un diagrama estándar que se ha mostrado antes sobre el conjunto de colas, también tiene la cola de administración, entre el host y el controlador, y luego un conjunto de colas de E/S, pares de colas que son colas de envío y finalización. . En NVMe/TCP, básicamente cada una de estas colas se asigna a una conexión TCP bidireccional. Entonces, si observamos las contribuciones de latencia, tenemos algunas que podrían aparecer. En primer lugar, en la serialización, pero en NVMe/TCP, es bastante liviano, se realiza por cola, por lo que escala. bastante bien.

Este artículo es parte de

03:43 ES: Cambio de contexto. Entonces, tenemos dos como mínimo aportados por el propio controlador, copia de memoria, generalmente antigüedades, podemos hacer copia cero como controlador a nivel de kernel. En RX, sin embargo, copiamos la memoria, no es un factor importante, pero en cargas muy, muy altas, puede contribuir a una latencia adicional. Las interrupciones (interrupciones de NIC) son definitivamente impactantes, consumen CPU y afectan la escalabilidad de cuánto puede lograr una sola cola. Tenemos LRO y GRO o la moderación de interrupciones adaptativa puede mitigar eso un poco, pero entonces la latencia podría ser menos consistente. Luego tenemos la sobrecarga del socket, existe pero en realidad no es enorme, es bastante rápido dado que los sockets no tienen competencia en una interfaz de múltiples colas, pero en E/S pequeñas, podría tener un impacto. La afinidad entre interrupciones, aplicaciones y subprocesos de E/S definitivamente puede tener un impacto si no se configura correctamente, y tocaremos más sobre eso. Obviamente, tenemos algunas contaminaciones de caché como resultado de la copia de memoria, pero no es algo tan excesivo en los núcleos de CPU modernos que tienen cachés suficientemente grandes.

05:15 ES: Y luego tenemos el bloqueo de cabecera de línea, que podría ser evidente en cargas de trabajo mixtas, y abordaremos cómo abordamos eso. Entonces, tomemos un ejemplo de flujo de datos directo del host. Comienza cuando el usuario básicamente emite un archivo o bloquea E/S a un dispositivo o un descriptor de archivo, y luego ignoramos muchas capas en la pila, pero eventualmente se debe a la devolución de llamada en cola que se encuentra en el propio controlador. Estamos hablando de Linux, por supuesto. Esa devolución de llamada es una solicitud de cola NVMe/TCP y prepara una PDU NVMe/TCP que la coloca en una cola interna para su posterior procesamiento, luego tenemos el trabajo de E/S, ese es el contexto de E/S o el subproceso de E/S. recoge esta E/S y comienza a procesarla, enviándola al controlador. Luego, el controlador lo procesa, completa la E/S y envía datos si se trata de una lectura o finaliza la finalización al host.

06:21 ES: Una vez que el host lo recibe, el primero en verlo es la NIC, genera una interrupción que dice que tiene más datagramas adicionales que deben ser procesados ​​por el host, luego se activa NAPI, que básicamente obtiene todos estos datagramas y los inserta en el Pila TCP/IP y la procesa. Luego tenemos el controlador cuando el consumidor TCP recibe una devolución de llamada de datos listos, que en ese momento activa el contexto para procesar básicamente los datos y completarlos, y de hecho completar la E/S. Y el siguiente es básicamente que el contexto del usuario completa la E/S. Si se trata de una interfaz asíncrona, probablemente atravesará la defensa de la puerta o una señal. Ahora, lo que hay que notar aquí es, en primer lugar, que tenemos un cambio de contexto entre la parte en la que estamos preparando la E/S y la parte que ha programado el trabajo de E/S para recoger la E/S y procesarla. Luego tenemos una cola de E/S de software entre la interrupción y cuando se activa NAPI y luego tenemos un contexto adicional, cambiamos una vez que se llaman los datos listos y el contexto de trabajo de E/S realmente continúa y procesa la E/S. .

07:36 ES: Entonces, estos son los tres puntos del área donde podemos ayudar a eliminar. Ahora, las primeras optimizaciones que se realizaron son para abordar la carga de trabajo mixta, como dijimos, el cronometraje del encabezado de la cola podría ser evidente en el caso de que tengamos una escritura grande que proviene del host y debe enviarse con una cola y luego un host encuentra envía un mensaje grande porque NVMe/TCP define el mensaje en la parte superior del flujo TCP, y luego detrás de él, es una lectura pequeña que no puede avanzar hasta que esta escritura grande, en realidad se completa si está en una sola cola.

08:16 ES: Entonces, este problema es evidente y la mitigación comienza con la separación de diferentes mapas de colas que la capa de bloques de Linux permite hoy en día, que básicamente pueden definir los diferentes tipos de E/S que pueden usar diferentes colas. Entonces tenemos tres mapas de colas diferentes en Linux. Uno de ellos es el predeterminado, básicamente albergará el conjunto predeterminado de colas, generalmente si solo se define el mapa de cola predeterminado y todas las E/S lo usarán en un mapa de cola.

Luego tenemos un mapa de cola dedicado solo para E/S de lectura, de modo que las lecturas tendrán un conjunto dedicado de colas asociadas con eso, y el resto irá al mapa de cola predeterminado, y luego el tercero es el mapa de cola de polos, Por lo general, cuando la aplicación señala a través de un indicador, un indicador de E/S de alta prioridad al kernel que está interesado en un comando de E/S de alta prioridad, será dirigido al mapa de cola de polos, y este mapa de cola de polos puede realmente diseñar el E/S sensibles a la latencia del host.

09:38 ES: Ahora, lo que hicimos con eso es que una vez que hemos mezclado cargas de trabajo, tenemos diferentes lectores y escritores que están todos alojados en la misma aplicación o incluso en diferentes aplicaciones del mismo host, estas lecturas y escrituras ahora se dirigen a través de diferentes colas, por lo que nunca verán un bloqueo de encabezado de línea entre lecturas y escrituras. Entonces, básicamente lo que hicimos fue asignar colas adicionales en NVMe/TCP dependiendo de la solicitud del usuario, y luego ejecutamos una prueba comparativa conectando . . . Lo conectamos a la banda de bloqueo y realizamos una prueba comparativa para ver cuál es la mejora. Entonces, la prueba consistió básicamente en tener 16 lectores, cada uno de ellos realiza lecturas sincrónicas, solo una por una, y en segundo plano teníamos una especie de escritor independiente que emite una gran ráfaga de escrituras de un megabyte. Ahora, esto es ilimitado, lo que significa que este hilo, que emite escrituras de un megabyte en una cola de alta profundidad, rotará entre los diferentes núcleos de CPU y terminará compartiendo colas con el resto de estos 16 lectores.

11:10 ES: Entonces, con la línea de base, vemos la QoS de lectura, los IOPS de lectura están alrededor de 80 000 IOPS con una latencia promedio de 396 microsegundos y la latencia de cola puede llegar hasta 14 milisegundos, lo que obviamente no es muy bueno, pero ahora que separamos la lectura y escribe en diferentes mapas de cola, lo que tenemos ahora es que los IOPS se han más que duplicado con 171K IOPS, la latencia de lectura promedio es menos de la mitad con 181 microsegundos y la latencia de cola de lectura de cuatro nueves ha mejorado en un orden de magnitud. Entonces es importante entender eso.

A continuación, tenemos optimizaciones de afinidad. Ahora que tenemos diferentes mapas de colas, ahora surge la cuestión de cómo se afinizan con los subprocesos de E/S y cómo se afinizan con las aplicaciones. Básicamente, cada cola NVMe/TCP en Linux tiene una CPU de E/S que define dónde se ejecuta el contexto de E/S. Lo que hicimos fue usar una alineación separada para diferentes mapas de cola, cada uno comenzando con cero, por lo que se contabilizan individualmente y no se combinan todos juntos, lo que logra una muy buena alineación entre. . . Eso es lo que desea, entre la aplicación y el subproceso de E/S, independientemente del mapa de cola en el que termine aterrizando la E/S.

12:50 ES: Así que hicimos un micro-benchmark para probar la mejora, y en el benchmark aquí probamos la profundidad de la cola con una latencia canónica de 4K para lectura y luego usamos, nuevamente, una aplicación de un solo subproceso, una carga de trabajo de un solo subproceso que tiene una profundidad de cola de 32, nuevamente, lecturas 4K. Entonces, en la profundidad de cola 1, obtuvimos una mejora del 10%, lo cual es excelente, y luego en la profundidad de cola 32, en realidad logramos más entre 179 000 IOPS y 231. Eso sucede de manera inmediata, así que definitivamente fue un buen resultado. mejora.

Luego, nos centraremos en el cambio de contactos y en lo que se ha hecho para mitigarlo. Entonces, el objetivo en el camino de las tomas es recordar que una vez en las dos diapositivas anteriores mostramos que una vez que la devolución de llamada en cola y la devolución de llamada en cola del controlador, la solicitud de cola NVMe/TCP obtiene una E/S, en realidad se publica en su señal interna y luego activa nuestro contexto cambia al contexto de E/S que posee la cepa TCP. Entonces queremos eliminar eso.

14:10 ES: Básicamente, lo que hacemos aquí es que, básicamente, en la devolución de llamada en sí, en realidad seguimos adelante y enviamos la E/S directamente desde ese contexto, eliminando el contexto del subproceso de E/S en ese cambio de contexto. Obviamente, ahora que tenemos dos contextos que realmente pueden acceder a la conexión TCP a la vez, es necesario serializarlos. Por lo tanto, debemos agregar el contexto de serialización entre ellos, que era una forma de prueba de silencio, y tampoco se garantiza que el envío de red sea atómico, por lo que debemos cambiar el esquema de bloqueo en el corazón y la interfaz de cola en el bloque. capa cambiándola de RC a SRC.

Pero dado que tenemos dos contextos que pueden acceder a la transmisión juntos, es posible que no necesitemos discutirlo. Entonces solo hacemos esta optimización si la cola está vacía, lo que significa que el contexto es realmente innecesario, e incluso si la cola está vacía, lo cual es una indicación de asesoramiento, solo si la CPU del mapa coincide con la CPU en ejecución, lo que significa que si terminamos sobre la . . . Si el contexto de E/S y el contexto en el que se envía se ejecutan en el mismo puerto de CPU, lo que significa que si competimos con alguien, es con nosotros mismos, entonces es probable que podamos agarrar sin que se nos escape el horario para el cerrar con llave.

15:40 ES:Entonces lo hicimos y una segunda optimización es agregar prioridad de software, que básicamente define la cola de métricas con una tecnología ADQ en la que el tráfico de salida se dirige básicamente al conjunto dedicado de ADQ y NCQ.

Ahora, pasando al plano RX, como mencionamos, una vez que tenemos una interrupción en la que no procesamos la E/S directamente desde la interrupción misma, en realidad activamos el trabajo de los contextos de E/S del subproceso de trabajo, para procesar realmente el datos entrantes y finalización. Entonces, si la aplicación está sondeando, mencionamos esa cola de sondeo configurada y mencionamos el indicador de hiperprioridad que es compatible con Linux.

Ahora tenemos aplicaciones que pueden sondear mediante E/S directa o mediante anillo de E/S. Entonces, básicamente agregamos devolución de llamada de sondeo NVMe/TCP y la conectamos a la interfaz de sondeo de bloques de colas múltiples. Ahora que la interfaz de sondeo llama, simplemente llama, aprovecha la funcionalidad BC en la pila de red, y luego, si estamos sondeando y la aplicación está sondeando, entonces podemos identificarlo en los datos y cuando recibimos la devolución de llamada y no No programamos un contexto adicional, no programamos el hilo de trabajo de E/S porque sabemos que vamos a procesar los datos entrantes directamente desde la encuesta.

17:27 ES: Básicamente, con la moderación de interrupciones de las CPU de NIC modernas, funciona relativamente bien para reducir algunas de las interrupciones, pero con ADQ busca dónde las interrupciones se mitigan de manera más agresiva, funciona muy bien. Así lo hicimos. . .

Una vez que agregamos estas dos eliminaciones de los cambios de contexto, nuevamente ejecutamos el mismo punto de referencia. Ahora la línea de base fue con la optimización de E/S aplicada y logramos un 10% adicional de latencia canónica de profundidad de cola 1 para lecturas de 4K, y también logramos, creo, entre un 5 y un 10% de mejora en la profundidad de cola 32 entre 230K IOPS y 247K IOPS desde un solo hilo. Bueno, eso es bueno. En el futuro, por supuesto, la última serie E800, la última NIC de Intel, introdujo algunas mejoras en ADQ.

18:37 ES: Entonces, ¿cuáles son las mejoras de ADQ que están disponibles? En general, en primer lugar, se trata de aislamiento de tráfico: puede asociar un conjunto de colas específico y configurar la NIC, de modo que pueda dirigir el flujo de trabajo de la aplicación específica a un conjunto de colas dedicado que no se comparte con la otra aplicación del host. Entonces, de entrada, básicamente la configuración consiste básicamente en definir la configuración del conjunto de colas, aplicar el filtrado de flores TC y luego agregar una selección de cola a través de RSS o director de flujo. El saliente es establecer la prioridad del software, que mencionamos que activamos en el controlador NVMe/TCP a través del parámetro de modo, y luego también configuramos extensiones para transmitir paquetes, dirigiendo XPS que coincida con la cola simétrica.

19:36 ES: El valor es que no tenemos tráfico ruidoso entre cargas de trabajo vecinas y la oportunidad de personalizar los parámetros de red para un flujo de trabajo específico, y eso es algo que aprovechamos en NVMe/TCP. Otras mejoras, obviamente, son minimizar el cambio de contacto y las interrupciones mediante la aplicación de sondeo. Entonces, una vez que la aplicación es compatible con el sondeo, básicamente ahora tenemos colas dedicadas que están configuradas con ADQ y actúan como nuestras colas de sondeo en NVMe/TCP, ese es el conjunto de colas. Lo que estamos haciendo es agotar la finalización de la red dentro del contexto de la aplicación mientras está sondeando, y procesamos las finalizaciones directamente en el contexto de la aplicación. Manejamos la solicitud cuando enviamos la respuesta, como dije, directamente eliminando el cambio de contexto entre el contexto de E/S y la aplicación.

También tenemos la capacidad de agrupar varias colas en una única cola de hardware de NIC para simplificar básicamente el uso compartido de las colas de hardware de NIC sin incurrir en cambios de contexto innecesarios. Entonces, si recordamos que en el controlador teníamos dos cambios de contexto, uno DTX, PAX, uno RxPax y teníamos una interrupción suave impulsada desde la interrupción NIC, ahora los eliminamos a los tres junto con las mejoras y ADQ juntos. . Por lo tanto, el valor ha reducido la utilización de la CPU, la latencia y la fluctuación de latencia que son bajas en general.

21:25 ES: Estas son algunas de las medidas que logra NVB TCP con ADQ habilitado o deshabilitado y la latencia en QDAP1. . . Eso, por supuesto, olvidé mencionarlo, es la latencia agregada además del propio medio, y la sobrecarga de transporte en este momento, con ADQ es 17.7, podría ser menor dependiendo también de la clase de CPU. Y en QDAP32, obtenemos una mejora masiva de casi el 30%, si no más, entre 245.000 IOPS y 341.000 IOPS en un solo subproceso. Ahora bien, si multiplica este hilo y lo aplica al rendimiento general que puede obtener del host, básicamente puede escalar de manera bastante lineal hasta un punto en el que puede lograr la saturación del host con poco más de un puñado de núcleos, o 10 núcleos.

22:37 ES: Bueno. Ahora daremos como resultado las optimizaciones finales, que son casi independientes de toda la optimización de latencia en las cargas de trabajo de sondeo, y pasaremos a optimizaciones de latencia que están relacionadas con el procesamiento por lotes, que son más para aplicaciones que están menos preocupadas por la latencia canónica y la alta prioridad. /O, pero más sobre tener una carga de trabajo orientada al ancho de banda y aun así lograr una buena latencia en presencia de un gran ancho de banda. Entonces, las optimizaciones están orientadas a lotes. Lo que queremos es aprovechar la información que la capa de bloque puede proporcionar en el controlador sobre la formación de una cola. Entonces, desde la perspectiva del conductor, no se tiene una vista real de toda la cola que se está acumulando en la capa de bloques.

23:35 ES: Entonces, lo primero que queremos hacer es lo primero que esté disponible y la capa de bloque básicamente puede indicarle al controlador si la solicitud que está en cola es en realidad la última o no. Lo que entonces estamos haciendo es modificar la cola y cómo maneja su cola interna. Básicamente lo hacemos más liviano, moviéndolo de una lista protegida por un candado a una lista sin candado. Y luego, con eso, básicamente... . . Entre la interfaz de la cola y entre el contexto de E/S que extrae solicitudes de esa cola, lo convertimos en un push and pull orientado por lotes, para que podamos obtener una mejor utilización o una operación atómica menos frecuente y más procesamiento por lotes.

24:43 ES: A eso, toda esta información, básicamente ahora tenemos una mejor vista de la cola que se está acumulando e hicimos que el procesamiento alrededor de ella sea más eficiente, luego agregamos esta información y accedemos a la pila de red a través de indicadores de mensajes además de nuestras operaciones de envío a que la red actúe de manera más eficiente. Entonces, por ejemplo, si sabemos que vamos a enviar un dato a la red y sabemos que tenemos una cola detrás de nosotros, lo indicaremos con el mensaje más. Entonces, le indicaremos a la pila que necesita esperar por más datos y no necesariamente seguir adelante y enviarlos juntos, tiene la oportunidad de agruparlos. Pero si este es el último dato en la cola que vamos a enviar y tampoco sabemos de nada que se esté acumulando detrás de él, activamos el final del registro del mensaje para indicarle a la pila que Lo que sea que haya acumulado, debería seguir adelante y enviarlo ahora mismo.

25:53 ES: Y el último es un trabajo de un equipo de la Universidad de Cornell que creó un programador de E/S optimizado para este tipo de carga de trabajo de procesamiento por lotes de flujo TCP y optimización del ancho de banda y la latencia. Puede encontrarlo en el documento i10 que está disponible a través de un enlace, o simplemente puede buscar "i10 Cornell".

Este programador de E/S está en proceso y se enviará pronto. Un par de resultados de referencia que el equipo de Cornell ha realizado, podemos verlos aquí en el lado izquierdo, esto es simplemente estándar, o NVMe/TCP ascendente sin el programador de E/S i10 y la optimización. Vemos los IOPS de lectura de 4K que puede lograr, vemos aquí que la latencia es de alrededor de 100 microsegundos, y una vez que llegamos a 145 o poco menos de 150, la latencia comienza a aumentar porque no tenemos más eficiencia y simplemente comienza la latencia. Al acumular, nos topamos con una pared. Y con i10, hay algo de sacrificio, un pequeño sacrificio por la latencia en QDAP bajo, pero en QDAP más altos, podemos ver que podemos superar los 200 000 IOPS o casi 225 000 IOPS hasta que lleguemos a este muro en el que la latencia simplemente comienza a aumentar. Entonces, lo que nos muestra es que, en primer lugar, hay un mayor rendimiento, eso es para un solo subproceso. Mayor rendimiento y mayor IOPS, y además la latencia se mantiene bastante constante.

28:01 ES: En el lado derecho, podemos ver el rendimiento, tenemos 16 núcleos frente al dispositivo RAM. Tenemos 16 núcleos y vemos que, en general, el rendimiento en todos los tamaños de solicitudes hasta que casi satura la NIC se mejora con i10, y eso debería sugerirnos que básicamente el procesamiento por lotes y por lotes es útil específicamente cuando hablamos de una transmisión. aplicación basada en NVMe/TCP, que en realidad es NVMe/TCP. Bueno. Estamos en una grabación, así que no tenemos ninguna pregunta, así que gracias por escuchar.

Descarga la presentación:01:14 ES:02:24 ES:03:43 ES:05:15 ES:06:21 ES:07:36 ES:08:16 ES:09:38 ES:11:10 ES:12:50 ES:14:10 ES:15:40 ES:17:27 ES:18:37 ES:19:36 ES:21:25 ES:22:37 ES:23:35 ES:24:43 ES:25:53 ES:28:01 ES: