1 Punkte von GN⁺ 2025-05-11 | 1 Kommentare | Auf WhatsApp teilen
  • 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

 
GN⁺ 2025-05-11
Hacker-News-Kommentare
  • Es ist ziemlich absurd, dass Intel jahrelang sogar Chipfläche investiert hat, um AVX-512 in Consumer-Produkten zu unterstützen, und es dann genau zu dem Zeitpunkt aus den Consumer-SKUs entfernt hat, als Bibliotheken allmählich begannen, es tatsächlich zu nutzen. Dabei ist AMDs AVX-512-Unterstützung nicht einmal unbedingt besser; ironischerweise hat AMD AVX-512 in Consumer-CPUs jetzt nur deshalb, weil Intel seine eigene Investition selbst aufgegeben hat.
    • Intel wiederholt ständig dasselbe Muster: erst einen Technologiemarkt aufbauen (Optane) und dann plötzlich aussteigen (Depth Cameras), wodurch Kunden verwirrt zurückbleiben. Es wird massiv auf neue Technik gesetzt, und wenn die Akzeptanz ausbleibt, wird sie sofort fallen gelassen. Optane wurde eingestellt, als die Unterstützung im Linux-Kernel gerade erst reif zu werden begann. Dazu kommen seltsame Sparstrategien. Wenn man in der Geschichte zurückgeht, macht Intel im Grunde schon seit dem Intel iAPX 432 dieselben Fehler.
    • In diesem Artikel wurden auf einer AMD-CPU Geschwindigkeiten von Original: 18GB/s, AVX2: 20GB/s und AVX512: 21GB/s gemessen. AVX-512 bringt gegenüber AVX2 also kaum Vorteile. Intels Consumer-CPUs unterstützen AVX2 auch auf den E-Cores. Der Benchmark ist Single-Threaded. Intel hat AVX-512 vom Chip entfernt, um mehr Kerne unterzubringen, weshalb das Topmodell 24 Kerne hat, während AMD bei 16 liegt. Da der Unterschied zwischen AVX2 und AVX512 gering ist, könnte in Multi-Thread-Benchmarks die CPU mit mehr Kernen gewinnen. Für die meisten realen Workloads bringt eine höhere Kernzahl mehr als AVX-512. Abgesehen von einigen sehr speziellen Aufgaben halte ich Intels Entscheidung daher für nachvollziehbar.
    • AVX-10 soll bald erscheinen und nahezu dieselben Funktionen wie AVX-512 bieten (wobei ich selbst nicht genau weiß, worin der Unterschied besteht).
    • Das Interessanteste an diesem Artikel war für mich, dass der AVX2-Parser auf dem AMD 9950X etwa 10% schneller war als der AVX-512-basierte Parser (20GB/s gegenüber 21GB/s). Das spricht dafür, dass AVX-512 für echte Consumer letztlich keinen großen Nutzen hat. Schon 20GB/s beim CSV-Parsen wären für mich mehr als genug. Eigentlich interessieren sich nur Assembler-Enthusiasten dafür, ob es unterstützt wird.
    • Es ist wirklich lächerlich, wie dumm Intel sich anstellt.
    • Falls es tröstet: Sep verwendet AVX-512, wann immer es möglich ist, auch ohne besondere Konfiguration. Es läuft gut in JIT-Runtimes, sodass man selbst dann nichts verliert, wenn man unbeabsichtigt auf die niedrigste Performance-Basis zielt.
    • Intel ist auch bei der Software-Unterstützung nicht besonders gut. Die iGPU in meinem Laptop ist eigentlich ziemlich ordentlich, wird aber in PyTorch und ähnlichen Projekten nicht richtig unterstützt, was schade ist. Mit llama.cpp und Inferenz über Vulkan funktioniert es gut, und ich wünschte, andere Software würde so etwas ebenfalls unterstützen.
  • Anstatt für jedes Zeichen vier Vergleiche ('\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 mit vpternlogd und vpor reduziert hat.
  • Es ist unerquicklich, dass Intel bei Consumer-CPUs langsame Kerne hineinpacken muss und dann AVX-512 komplett entfernt, ohne auch nur über „Multi-Pumping“ nachzudenken.
    • Der Hauptgrund für diese Entscheidung waren die Probleme mit dem 10nm-Prozess. Die Ausbeute war schlecht und die Kosten viel zu hoch, also hat Intel versucht, möglichst viel vom Die zu beschneiden und mit Atom-artigen Kernen sowie Low-Power-Marketing doch noch Gewinn zu erzielen. Bei den teuren Produkten wurde die Fläche vergrößert und die Marge reduziert, um den Server-/Cloud-Markt zu halten. Am Ende sanken sowohl Profitabilität als auch Marktanteil, aber man hat es zumindest versucht.
  • Die Behauptung, seit dem Release von Sep (Juni 2023) in zwei Jahren ungefähr eine Verdreifachung der Geschwindigkeit erreicht zu haben, ist umstritten, wenn man den Hardware-Sprung mit berücksichtigt.
    • Auf derselben Hardware beträgt die Verbesserung der Software (0.9.0 zu 0.10.0) 17%. Wenn man auf 13088 aus 0.9.0 noch 17% aufschlägt, kommt man auf 15375. Verglichen mit den 7335 aus 0.1.0 entspricht das etwa einer 2,1-fachen Verbesserung.
    • Es wird behauptet, dass sep auf derselben Hardware gegenüber der vorherigen Version um 3GB/s schneller geworden ist, und die tatsächliche Geschwindigkeit sowie die Hardware-Informationen sind transparent angegeben.
    • Heute leben wir nicht mehr in einer Ära wie zu Zeiten von Moores Gesetz, daher ist eine 3x-Steigerung allein durch Hardware kaum zu erwarten. Selbst dieses Ausmaß ist nach heutigen Maßstäben schon beeindruckend genug.
    • Natürlich ist die Aussage einer Verdreifachung diskussionswürdig, wenn der Hardware-Sprung nicht berücksichtigt wird, aber als eine Perspektive auf die reale Software-Performance über die Jahre hinweg ist es trotzdem interessant.
    • Das Benchmark-Diagramm überspringt gleich vier CPU-Generationen und lässt es dadurch plötzlich wie einen „riesigen Performance-Sprung“ aussehen, weshalb ich es nicht besonders glaubwürdig finde.
  • Ich hoffe, dass Arthur Whitney sich von diesem Ergebnis anstacheln lässt und es mit einer einzigen Zeile Code übertrifft, oder den Rekord zusammen mit einem Update der shakti engine mit einer Zeile bricht, oder zumindest ein News-Update dazu kommt. Ich freue mich auf weitere Fortschritte.
  • Schon der Gedanke daran, wer CSVs mit zehn Millionen Zeilen in dieser Geschwindigkeit verarbeiten muss, ist erschreckend.
    • Ich habe so etwas ebenfalls erlebt. Am Anfang wählt man CSV für kleine Datenmengen, weil es auch für Nicht-Entwickler – besonders für Leute, die gut mit Excel umgehen können – leicht lesbar ist, und Logs oder Prozesse lassen sich damit sauber handhaben. Wenn die Datenmenge dann aber auf das 10- oder 100-Fache anwächst, entsteht ganz real der Bedarf, CSV-Dateien mit hunderten Millionen Zeilen zu optimieren und schnell zu ingestieren. Solche Optimierungen verschaffen einem letztlich nur Zeit, um interne Prozesse nach und nach auf geeignetere Formate umzustellen.
    • CSV ist auch intern ein überraschend verbreitetes Format und hat den Vorteil, dass es sich leicht komprimieren lässt (Deflate). Ich habe einmal Code betreut, bei dem CSV Netflow-Daten mit der Geschwindigkeit einer NIC-Karte ausspuckte. Persönlich denke ich, wenn man ohnehin so komplex arbeitet, könnte man auch gleich Protocol Buffers verwenden. protobuf ist wirklich kein besonders schwieriges Format, und trotzdem wird es nur selten eingeführt.
    • Noch beängstigender finde ich den Gedanken, die Ergebnisse einer CSV-Verarbeitung mit 21GB/s irgendwo abspeichern zu müssen. Selbst wenn die Aggregation nützlich ist, muss das bei dieser Geschwindigkeit ja letztlich irgendwo landen, und das wirft Fragen auf.
    • Trotz all seiner Nachteile ist CSV meiner Meinung nach immer noch das am weitesten verbreitete Format für den Datenaustausch.
    • Im Finanzbereich kann jeder mit CSVs umgehen, und weil sie textbasiert sind, kann man sie praktisch überall hineinwerfen und verarbeiten.
    • Ein Beispiel wären die Dateien mit kartesischem Produkt, die die Buchhaltungsabteilung zum Jahresende verschickt.
    • Ich bin tatsächlich in der Lage, mich mit genau dieser Art altertümlicher CSVs herumschlagen zu müssen.
    • In fast allen Fällen ist HDF5 besser als CSV. Dass trotzdem CSV verwendet wird, lässt sich eigentlich nur mit Unwissenheit, Faulheit oder „es funktioniert eben“ erklären.
  • Ich war wegen Assembler-Code hierhergekommen und war überrascht und beeindruckt, dass es C# ist. Tolles Ergebnis.
    • Modernes .NET ist die „High-Level-Sprache“ mit der tiefsten Integration von SIMD und Vektor-Intrinsics. Tanner Gooding von Microsoft hat viele dieser Fortschritte vorangetrieben, und auch seine Blogposts dazu sind großartig.
  • Verwirrend ist, dass im Haupttext nicht klar definiert wird, was der 21GB/s-Code eigentlich genau macht. Zum Beispiel ist unklar, welches Format genau geparst wird (etwa wie CSV-Quoting behandelt wird) und wie die Ergebnisse nach dem Parsen verwendet werden (z. B. ob sie in Datenstrukturen geschrieben werden).
    • Die im Artikel berechneten ns/row liegen bei ungefähr 27ns/row (37.000 Zeilen pro Sekunde), aber bei 21GB/s entspräche das etwa 570KB pro Zeile, was wie ein völlig unplausibler Benchmark wirkt.
  • Meiner Erfahrung nach war es mit benutzerdefiniertem SIMD-Code schwer, gegenüber der Auto-Vektorisierung moderner Compiler große Vorteile herauszuholen, besonders bei ohnehin vektorfreundlichem Code. Bei Sonderfällen wie JSON-Parsing ist das allerdings etwas anders.
  • Ich habe kürzlich mit einem 300GB-CSV-Export gearbeitet, und für Manipulation, Integritätsprüfungen und Ähnliches ging viel zu viel Zeit drauf, sodass ich einen so schnellen CSV-Parser wirklich dringend bräuchte.
    • Ich verstehe nicht, warum man für die Speicherung von Fließkommadaten kein spezialisiertes Format verwendet. HDF5 ist eine deutlich bessere Alternative.