- Die neueste Version Sep 0.10.0 erreicht auf einem AMD 9950X eine beeindruckende CSV-Parsing-Geschwindigkeit von 21 GB/s
- Durch AVX-512-Unterstützung und das Überwinden von Problemen mit Maskenregistern wurde die Leistung deutlich verbessert
- Ein neuer AVX-512-to-256-Parser übertrifft AVX2 und bestehende AVX-512-Parser
- In einer Multithread-Umgebung werden 1 Million Zeilen in 72 ms verarbeitet, bei einer Bandbreite von 8 GB/s
- Durch kontinuierliche Software- und Hardware-Optimierung wurde in 2 Jahren eine Leistungssteigerung um etwa das Dreifache erreicht
Überblick über das Release Sep 0.10.0 und die Leistungssteigerungen
- Sep hat im jüngsten Release 0.10.0 Optimierungen für CPUs mit AVX-512-Unterstützung (z. B. AMD 9950X, Zen 5) vorgenommen, und die Benchmark-Ergebnisse wurden aktualisiert
- In der neuesten Version wurde beim Low-Level-CSV-Parsing die herausragende Marke von 21 GB/s erreicht
- Gegenüber der vorherigen Version ist das eine deutliche Steigerung von 18 GB/s
- Details zu den Verbesserungen sind in den GitHub-Releases und im README von Sep zu finden
- Im Artikel wird erläutert, wie SIMD-basierten C#-Code und x64-SIMD-Assembler die Ineffizienz des AVX-512-Maschinencodes von .NET 9.0 überwinden und so die Leistung steigern
Die Entwicklung der Leistung von Sep
- Die Entwicklung von Sep wird visuell dargestellt: von den frühen Versionen 0.1.0 bis 0.10.0, von .NET 7.0 bis 9.0 und von AMD 5950X (Zen 3) zu 9950X (Zen 5)
- Die Benchmarks basieren auf Single-Thread-Messungen; je nach Release kann es leichte Schwankungen geben
- Die Kernaussage ist, dass sowohl große Refactorings (Neuschreibung der internen Struktur in 0.2.0) als auch die aufsummierte Wirkung kleiner Optimierungen die Leistung kontinuierlich verbessert haben
- Durch das Zusammenspiel von Hardware- und Software-Fortschritt wurde innerhalb von rund 2 Jahren eine etwa dreifache Verbesserung auf 21 GB/s Parsing-Leistung erreicht
- Allein der Generationswechsel der Hardware (5950X→9950X, 4,9→5,7 GHz) erklärt bereits einen Zuwachs von mehr als dem 1,2-Fachen
AVX-512-Codegenerierung und das Problem mit Maskenregistern
- Sep unterstützt AVX-512 seit Version 0.2.3, hatte aber Einschränkungen bei der Nutzung der Maskenregister (k1-k8) von AVX-512
- In .NET 8 gab es keine direkte Unterstützung für Maskenregister, sodass wiederholtes Kopieren und Umwandeln zwischen normalen Registern zu Leistungsverlusten führte
- Da anfangs keine eigene CPU mit AVX-512-Unterstützung verfügbar war, wurde nur eingeschränkt auf einem Xeon Silver 4316 getestet, wobei sich AVX-512 als am schnellsten erwies
Upgrade auf den 9950X und Vergleich AVX-512 vs. AVX2
- Nach dem jüngsten CPU-Upgrade von Zen 3 (5950X) auf Zen 5 (9950X) ergab ein erneuter Sep-Benchmark 18 GB/s
- Ein direkter Vergleich von AVX-512- und AVX2-Parsern zeigte überraschenderweise, dass AVX2 mit etwa 20 GB/s rund 10 % schneller war als AVX-512
- Das deutet darauf hin, dass die ineffiziente Verarbeitung von Maskenregistern im .NET-JIT weiterhin ein Problem ist
Parser-Code, Assembleranalyse und neuer AVX-512-to-256-Parser
- Alle Parser von Sep verarbeiten
char span-Blöcke von 16K und nutzen Vergleichsoperationen auf Basis von SIMD-Registern (Vector256 usw.)
- Mit SIMD werden Sonderzeichen (Zeilenumbrüche, Anführungszeichen, Trennzeichen usw.) schnell erkannt und in Bitmasken umgewandelt, wodurch Mengenoperationen effizient optimiert werden
- Der AVX-512-basierte Parser hatte viele überflüssige Operationen, weil Maskenregister (
k1 usw.) wiederholt zwischen allgemeinen Registern (zmm usw.) hin- und herbewegt wurden
- In 0.10.0 wurde der
MoveMask-Aufruf vorgezogen, wodurch unnötige Maskenumwandlungen minimiert und die Zahl der Assemblerbefehle reduziert wurde
- Der AVX2-Parser hat keine Maskenregister, ist strukturell deutlich einfacher und war daher in der Praxis schneller als AVX-512
- Der neue AVX-512-to-256-Parser liest Daten mit AVX-512 ein und umgeht das Problem der Maskenverarbeitung durch 256-Bit-Konvertierungsbefehle vollständig; dadurch wurde die Implementierung einfacher und eine Leistung von über 21 GB/s erreicht
Zusammenfassung verschiedener Parser-Benchmarks
- Ein Benchmark-Vergleich aller Parser-Typen per Umgebungsvariable zeigt, dass der AVX-512-to-256-Parser mit 21,5 GB/s am schnellsten ist
- Auch AVX2-basierte und
Vector256-basierte Parser liegen mit weniger als 5 % Abstand sehr nah dran
Vector128- und Vector512-basierte Parser sind 5–10 % langsamer als AVX2, insbesondere ist der Vector512-Parser sogar langsamer als Vector128
- Der
IndexOfAny-Parser ist deutlich langsamer als die anderen SIMD-Parser. Vector64 wird auf dem 9950X nicht beschleunigt und liefert deshalb sehr schwache Leistung
- SIMD-Parser auf Basis von AVX-512 und AVX2 belegen eine deutlich überlegene Leistung gegenüber vergleichbaren CSV-Parsern
Höhere Benchmark-Ebene: Vergleich 5950X und 9950X
- Bei 1 Million Paket-Asset-Zeilen erreichte Sep_MT auf dem 9950X 72 ms (8 GB/s) und auf dem 5950X 119 ms (4,9 GB/s)
- Auch bei realen Nutzdaten (z. B.
float) wurde auf dem 9950X im Multithread-Betrieb eine Bandbreite von ~8 GB/s erzielt
- Durch den Generationswechsel (5950X→9950X) ergibt sich bei realem Anwendungs-Parsing ein Verbesserungsfaktor von etwa 1,5 bis 1,6
- Gegenüber konkurrierenden CSV-Bibliotheken (Sylvan, ReadLine, CsvHelper usw.) wurden deutlich höherer Durchsatz und minimale Speicherallokationen nachgewiesen
Fazit und Zusammenfassung
- Sep 0.10.0 überwindet durch die Kombination aus Software-Optimierung und modernen Hardware-Funktionen (AVX-512, hohe Taktfrequenz) die Grenzen der CSV-Parsing-Leistung
- Entscheidende Treiber der Innovation sind das Design moderner SIMD-Algorithmen sowie Verbesserungen an .NET-JIT-Code und Assemblerstruktur
- Der Effekt aus kumulierter Leistungsoptimierung und Architektur-Generationswechsel innerhalb kurzer Zeit ist beeindruckend
- Sep zeigt im Bereich CSV-Parsing praktisch branchenführende Hochleistung, Multiplattform-Fähigkeit und Skalierbarkeit
1 Kommentare
Hacker-News-Kommentare
llama.cppund Inferenz über Vulkan funktioniert es gut, und ich wünschte, andere Software würde so etwas ebenfalls unterstützen.'\n','\r',';','“') und danach drei OR-Operationen zu machen, kann man mit einem verbreiteten Trick mit genau einem Shuffle, einem Vergleich und null OR-Operationen auskommen. Ich habe diesen Trick in einem Blogpost beschrieben. Wobei auch dieser Artikel die OR-Operationen bereits mitvpternlogdundvporreduziert hat.protobufist wirklich kein besonders schwieriges Format, und trotzdem wird es nur selten eingeführt.