
Proxmox ARM64 iba a 6 GB/s en el ASUS GX10: el cuello de botella estaba a la vista
En un ASUS GX10 con Proxmox VE 9.2 ARM64, el rendimiento de lectura de NVMe/TCP a nivel de kernel aumentó de 6.3 GB/s a 28.1 GB/s tras optimizar la IOMMU, PCIe, la tarjeta de red y las colas. Se trata de pruebas comparativas de lectura en el host en un entorno de pruebas específico, no de una afirmación sobre la velocidad general de las máquinas virtuales o la inferencia de IA.
La prueba publicada por Blockbridge explica la progresión: iommu.strict=0 llevó el iniciador del kernel a 24.4 GB/s, pci=pcie_bus_perf lo elevó a 26.5 GB/s y ajustes de red adicionales permitieron alcanzar el resultado de 28.1 GB/s. El equipo también registró 28.9 GB/s utilizando SPDK.
¿Por qué NVMe/TCP en el ASUS GX10 estaba estancado cerca de 6.3 GB/s?
El resultado publicado en el ASUS GX10 atribuye la baja tasa de lectura inicial principalmente a la invalidación estricta de la IOMMU en la configuración evaluada del host.
El GX10 presenta una ficha técnica que hace que un equipo tan compacto parezca inverosímil: veinte núcleos de CPU Arm, 128 GB de memoria, una GPU y un adaptador de red NVIDIA ConnectX-7 de dos puertos a 200 Gb/s. NVMe over TCP permite acceder a almacenamiento NVMe remoto a través de redes Ethernet estándar, lo que convierte la prueba en una evaluación útil de toda la ruta de recepción en lugar de una simple demostración de la cifra teórica de un chip. Los bytes deben llegar a la tarjeta de red, transferirse por PCIe, superar la gestión de la IOMMU y alcanzar la memoria del host. Cualquier cuello de botella a lo largo de ese recorrido se refleja en el rendimiento final.
Para comprender en detalle cómo interactúan KVM y los dispositivos virtuales, consulta la explicación sobre QEMU frente a KVM.
El ingeniero reveló que trabajaba en Blockbridge, la empresa proveedora del destino NVMe/TCP utilizado en las pruebas. El artículo técnico detalla la configuración completa y los comandos ejecutados. Los investigadores modificaron los ajustes del host por etapas, compararon el iniciador del kernel con SPDK y contrastaron sus resultados frente a la capacidad teórica de los enlaces PCIe.
En el punto de partida, el contraste era rotundo. El iniciador del kernel se quedaba en 6.3 GB/s, mientras que SPDK alcanzaba ya 21.3 GB/s sobre la misma configuración predeterminada del host. El análisis de rendimiento aportó la clave: según el informe, arm_smmu_cmdq_issue_cmdlist consumía el 57% de los ciclos en los núcleos de CPU más grandes. La máquina dedicaba una cantidad desproporcionada de recursos al procesamiento de comandos de la IOMMU. El cuello de botella radicaba en la sobrecarga de la IOMMU.
¿Qué parámetros del kernel mejoraron el rendimiento del GX10?
iommu.strict=0 y pci=pcie_bus_perf solucionaron distintos cuellos de botella; el registro de 28.1 GB/s requirió además ajustes posteriores en la tarjeta de red.
El primer cambio fue iommu.strict=0. En el kernel ARM64 analizado, la invalidación estricta de la IOMMU obligaba a que cada desasignación (unmap) de DMA esperase a la correspondiente invalidación de la caché de traducción del hardware. En un sistema cuya SMMU dispone de una única cola de comandos, esas esperas resultaban excesivamente costosas. Cambiar a invalidación diferida (lazy) permitió procesar operaciones por lotes, llevando el iniciador del kernel de 6.3 a 24.4 GB/s. SPDK pasó de 21.3 a 24.7 GB/s. Supone una diferencia llamativa en cómo reaccionaron ambas vías.
A continuación se aplicó pci=pcie_bus_perf. El firmware del GX10 había inicializado los puertos raíz PCIe de la tarjeta de red con un tamaño máximo de carga útil de 128-byte (128 bytes), a pesar de que el hardware admitía 512 bytes. Linux mantenía el valor más bajo, desaprovechando una capacidad de enlace valiosa. El parámetro de PCIe orientado a rendimiento permitió el tamaño de carga superior, llevando el iniciador del kernel a 26.5 GB/s y SPDK a 28.8 GB/s. Posteriormente, el equipo activó la descarga de recepción grande (LRO), redujo el número de lecturas pendientes y fijó las interrupciones de la NIC a núcleos específicos. Esos cambios adicionales situaron las medias en 28.1 y 28.9 GB/s, respectivamente. Dicho de otro modo: la historia de los dos parámetros es cierta al describir el punto de inflexión, pero la cifra final del kernel también refleja la optimización posterior. Atribuir cada mejora a dos líneas mágicas ocultaría lo que la prueba descubrió en realidad.
El límite físico declarado explica por qué los últimos gigabytes resultaron difíciles de conseguir. Detrás de la ConnectX-7 se encuentran dos enlaces PCIe Gen5 x4 independientes, no una única conexión x16. El equipo calculó un límite de carga útil combinada de NVMe de aproximadamente 29.9 GB/s. Alcanzar 28.9 GB/s resulta por tanto notable para esta topología concreta, pero no demuestra un ancho de banda ilimitado ni un récord absoluto de rendimiento en ARM64. Una ejecución especialmente favorable con SPDK alcanzó cerca de 29.3 GB/s de carga útil, y el ingeniero advirtió de que esas ejecuciones no eran totalmente repetibles porque las conexiones TCP podían repartirse de forma desigual entre las colas de recepción de la tarjeta de red. Incluso rozando el límite físico del hardware, la distribución y programación de los paquetes siguen siendo determinantes.
¿Cómo limitó la topología de la ConnectX-7 al GX10?
La ConnectX-7 expone rutas a través de dos enlaces PCIe Gen5 x4, por lo que el tráfico debe emplear las funciones físicas adecuadas para aprovechar ambos.
Una tarjeta de red de dos puertos suele sugerir dos vías evidentes para enviar tráfico. En este GX10, esa suposición inducía a error. Cada puerto físico de red exponía una función física (PF) en cada una de las dos mitades PCIe, dejando cuatro PF visibles en Linux. Una PF determinada solo podía utilizar su propio enlace x4; dos PF asignadas a la misma mitad seguían compitiendo por el mismo techo de aproximadamente 14.8 GB/s. Al mismo tiempo, el tráfico limitado a un único puerto físico de 200 Gb/s se topaba con el límite aproximado de 24.7 GB/s de ese puerto. Para alcanzar los 28.9 GB/s registrados, el experimento requirió las cuatro PF para que ambos puertos físicos y ambas mitades PCIe transportasen tráfico. El límite relevante venía determinado por la ruta real que seguía cada paquete, no por el llamativo ancho de banda impreso en la ficha técnica del adaptador.
La optimización del almacenamiento solo es útil cuando el diseño se ajusta a la carga de trabajo, una distinción detallada en la comparativa de almacenamiento en Proxmox.
Surgió además un problema de red sorprendentemente inadvertido. Dos PF que compartían un puerto físico podían responder cada una a peticiones ARP dirigidas a la dirección de la otra bajo el comportamiento predeterminado de Linux. La respuesta que llegase primero al destino remoto determinaba por dónde fluía el tráfico. Si ganaba la PF equivocada, una de las rutas quedaba inactiva. No aparecía ningún mensaje de error evidente y la velocidad del enlace parecía correcta. El único indicio fiable era el contador rx_bytes de cada PF. El uso de arp_ignore=1 y arp_announce=2 corrigió ese comportamiento en el entorno de pruebas.
Una pregunta sobre la interfaz de monitoreo de la captura de pantalla desveló otro detalle práctico. El panel visual era un pequeño programa en Python desarrollado con Rich que extraía datos de /proc, /sys y ethtool. Mostraba conjuntamente el rendimiento de red, PCIe y NVMe, junto con la actividad por núcleo, facilitando la identificación de cuellos de botella. El autor aclaró que estaba adaptado a la peculiar disposición de esta máquina y probablemente no fuera reutilizable de forma general. Desarrollar herramientas a medida puede ser justo lo que exige una topología de hardware poco habitual.
¿Deberías usar invalidación diferida de IOMMU en Proxmox?
iommu.strict=0 puede mejorar el rendimiento, pero el kernel de Linux documenta expresamente un menor aislamiento entre dispositivos como contrapartida.
La objeción más clara se centró en iommu.strict=0. Un participante interpretó el resultado como el conocido dilema de sacrificar seguridad para ganar velocidad. El ingeniero no restó importancia a esa preocupación. Por el contrario, explicó que la invalidación diferida no desactiva la traducción de la IOMMU. Las asignaciones siguen utilizándose; el cambio reside en el momento en que el sistema limpia las entradas de traducción obsoletas tras una desasignación de DMA. El procesamiento por lotes mejora el rendimiento, pero puede dejar brevemente a un dispositivo en disposición de acceder a memoria que el kernel ya ha liberado. La documentación de parámetros del kernel de Linux describe esto explícitamente como un compromiso entre rendimiento y aislamiento de dispositivos. Para máquinas con dispositivos no confiables o hardware PCIe asignado mediante passthrough a invitados no confiables, el autor original recomendó mantener el modo estricto y asumir la penalización de rendimiento.
Otro participante preguntó si los sistemas Intel y AMD podrían beneficiarse de los mismos parámetros. La respuesta aportó prudencia frente al entusiasmo: el autor señaló que x86 habitualmente ya utiliza invalidación diferida de IOMMU, por lo que añadir iommu.strict=0 a menudo no cambiaría nada. De igual forma, el inusual tamaño de carga útil de 128 bytes en PCIe obedecía al comportamiento del firmware del GB10 observado aquí; otros sistemas pueden inicializar sus enlaces de otra manera. El equipo también probó el passthrough directo de la IOMMU y no obtuvo mejoras respecto al modo diferido en el GX10, haciendo difícil justificar una reducción más agresiva del aislamiento para este resultado. La configuración más rápida en un benchmark no constituye automáticamente la mejor política de seguridad para un despliegue.
Eso deja una conclusión muy realista. Los investigadores partieron de fallos de arranque, reinicios inexplicables, una limitación de consumo eléctrico y una cifra de red que hacía parecer mediocre a un hardware de alto nivel. Terminaron con un host Proxmox ARM64 operando cerca del límite teórico de lectura de la topología analizada, empleando un kernel corriente y controladores estándar. El resultado presenta a la virtualización ARM64 como una opción prometedora en el GX10, pero no demuestra que esté lista para producción con cualquier máquina virtual, carga de trabajo con GPU o modelo de seguridad. La lección resulta más provechosa que eso: cuando una infraestructura potente decepciona, la vía más rápida para encontrar una respuesta consiste en dejar de mirar el ancho de banda publicitado y rastrear el trayecto real de los bytes.
El salto de 6.3 a 28.1 GB/s no fue un misterio resuelto añadiendo más hardware a la máquina. Se consiguió identificando dónde esperaba el host, dónde se había limitado PCIe sin necesidad y por dónde los paquetes tomaban la ruta equivocada. Quien quiera replicarlo debería empezar con el mismo rigor: medir primero, cambiar un solo parámetro a la vez y entender qué se cede con cada ajuste de rendimiento.
Yo reproduciría el resultado únicamente en un host de pruebas controlado y midiendo cada parámetro de forma individual. En un servidor multiinquilino o sensible al aislamiento, mantendría la invalidación estricta a menos que una evaluación de riesgos documentada justifique la ganancia de rendimiento.
Preguntas frecuentes
¿Qué velocidad alcanzó NVMe/TCP en el ASUS GX10 bajo Proxmox ARM64?
Blockbridge registró 6.3 GB/s con el iniciador NVMe/TCP por defecto del kernel y 28.1 GB/s tras la optimización. La medición se realizó en el host, no desde el interior de una máquina virtual.
¿Qué hace iommu.strict=0?
Habilita la invalidación diferida de la IOMMU donde sea compatible, reduciendo la sobrecarga de desasignación (unmap) de DMA. Linux documenta un mayor rendimiento a cambio de un menor aislamiento de los dispositivos.
¿Consiguen 28.1 GB/s solo dos parámetros del kernel en el GX10?
No. La prueba publicada alcanzó 26.5 GB/s tras los dos cambios de parámetros y requirió ajustes adicionales en la tarjeta de red y las colas para lograr el resultado final de 28.1 GB/s.