- La versión más reciente, Sep 0.10.0, logra una impresionante velocidad de parsing de CSV de 21 GB/s en AMD 9950X
- El rendimiento mejoró notablemente gracias al soporte de AVX-512 y a la superación de problemas con registros de máscara
- El nuevo parser AVX-512-to-256 supera a AVX2 y al parser AVX-512 anterior
- En un entorno multihilo, procesa 1 millón de filas en 72 ms y registra un ancho de banda de 8 GB/s
- Con optimización continua de software y hardware, se logró una mejora de rendimiento de casi 3x en 2 años
Lanzamiento de Sep 0.10.0 y panorama de mejora de rendimiento
- En su lanzamiento reciente 0.10.0, Sep realizó optimizaciones para CPUs con soporte de AVX-512 (por ejemplo, AMD 9950X, Zen 5) y actualizó los resultados de benchmark
- En la versión más reciente registró un resultado sobresaliente de 21 GB/s en el parsing de CSV de bajo nivel
- Es una mejora importante frente a los 18 GB/s de la versión anterior
- Los detalles de las mejoras pueden verse en las releases de GitHub y el README de Sep
- El texto explica el proceso de mejora de rendimiento mediante código C# basado en SIMD y ensamblador SIMD x64 para superar la ineficiencia del código máquina AVX-512 en .NET 9.0
Evolución del rendimiento de Sep
- Se muestra visualmente la evolución de Sep desde la versión inicial 0.1.0 hasta la 0.10.0, de .NET 7.0 a 9.0, y de AMD 5950X (Zen 3) a 9950X (Zen 5)
- Los benchmarks están basados en un solo hilo y puede haber ligeras variaciones entre releases
- El mensaje clave es que tanto un gran refactor (reescritura de la estructura interna en 0.2.0) como pequeñas optimizaciones acumuladas impulsaron mejoras constantes de rendimiento
- Con el avance conjunto de hardware y software, se alcanzó una velocidad de parsing de 21 GB/s, alrededor de 3 veces superior en promedio en 2 años
- Solo el cambio generacional de hardware (5950X→9950X, 4.9→5.7GHz) ya explica una mejora de más de 1.2x
Generación de código AVX-512 y problema de los registros de máscara
- Sep ha soportado AVX-512 desde la versión 0.2.3, pero existían limitaciones en el uso de los registros de máscara (k1-k8) de AVX-512
- En .NET 8 no había soporte directo para registros de máscara, así que las copias y conversiones repetidas entre registros normales provocaban pérdida de rendimiento
- En la etapa inicial, al no contar con una CPU aparte con soporte AVX-512, se probó de forma limitada en un Xeon Silver 4316 y se confirmó que era lo más rápido
Upgrade a 9950X y comparación entre AVX-512 y AVX2
- Tras actualizar recientemente la CPU de Zen 3 (5950X) a Zen 5 (9950X), al ejecutar los benchmarks de Sep se alcanzaron 18 GB/s
- En una comparación directa entre los parsers AVX-512 y AVX2, de forma algo inesperada AVX2 mostró alrededor de 20 GB/s, cerca de un 10% más rápido que AVX-512
- Esto sugiere que la ineficiencia del manejo de registros de máscara en el JIT de .NET seguía siendo un problema
Código del parser, análisis de ensamblador y nuevo parser AVX-512-to-256
- Todos los parsers de Sep procesan bloques de char span de 16K y usan operaciones de comparación basadas en registros SIMD (como Vector256)
- Con SIMD identifican rápidamente caracteres especiales (saltos de línea, comillas, separadores, etc.) y los convierten en bitmasks para optimizar operaciones de conjunto
- El parser basado en AVX-512 tenía muchas operaciones redundantes por los movimientos repetidos entre registros de máscara (k1, etc.) y registros generales (zmm, etc.)
- En la versión 0.10.0 se adelantó la llamada a MoveMask para minimizar conversiones de máscara innecesarias, reduciendo así la cantidad de instrucciones en ensamblador
- El parser AVX2, al no tener registros de máscara, tenía una estructura mucho más simple y en la práctica era más rápido que AVX-512
- El nuevo parser AVX-512-to-256 lee datos con AVX-512 y evita de raíz el problema del manejo de máscaras usando instrucciones de conversión a 256 bits; la implementación se simplificó y logró un rendimiento de más de 21 GB/s
Resumen de benchmarks de varios parsers
- Al comparar mediante variable de entorno los benchmarks de todos los tipos de parser, el parser AVX-512-to-256 fue el más rápido con 21.5 GB/s
- Los parsers basados en AVX2 y Vector256 también mostraron un rendimiento cercano, con una diferencia de apenas 5%
- Los parsers basados en Vector128 y Vector512 fueron entre 5% y 10% más lentos que AVX2; en particular, el parser Vector512 fue incluso más lento que Vector128
- El parser IndexOfAny fue marcadamente más lento que los otros parsers SIMD. Vector64 no está acelerado en 9950X, por lo que mostró un rendimiento muy pobre
- Los parsers SIMD basados en AVX-512 y AVX2 demostraron un rendimiento abrumador frente a otros parsers CSV del mismo tipo
Benchmarks de nivel superior: comparación entre 5950X y 9950X
- Tomando como referencia 1 millón de filas de activos de paquetes, Sep_MT registró 72 ms (8GB/s) en 9950X y 119 ms (4.9GB/s) en 5950X
- Incluso con datos de carga real (como
float), en 9950X se logró un ancho de banda de ~8GB/s en multihilo
- El cambio generacional (5950X→9950X) produjo una mejora de aproximadamente 1.5 a 1.6 veces en parsing de aplicaciones reales
- También se demostró un throughput muy superior y una asignación mínima de recursos frente a bibliotecas CSV competidoras (Sylvan, ReadLine, CsvHelper, etc.)
Conclusión y resumen
- Sep 0.10.0 supera los límites del rendimiento de parsing de CSV combinando optimización de software y funciones de hardware más recientes (AVX-512, alta frecuencia)
- El núcleo de la innovación está en el diseño de algoritmos SIMD modernos y en la mejora de la estructura del código JIT de .NET y del ensamblador
- Es notable el efecto de las mejoras de rendimiento acumuladas y del cambio generacional de arquitectura en un periodo corto
- Sep presenta en el área de parsing de CSV un nivel de alto rendimiento, multiplataforma y escalabilidad que en la práctica está entre los mejores de la industria
1 comentarios
Comentarios de Hacker News
llama.cppy la inferencia por Vulkan funciona bien, así que ojalá otro software también diera ese tipo de soporte'\n','\r',';','“') y luego tres operaciones or, se puede aplicar un truco común para hacer 1 shuffle, 1 comparación y 0 operaciones or; publiqué ese truco en un blog, por si les sirve; dicho eso, en este artículo también redujeron operaciones or convpternlogdyvpor