3 Punkte von GN⁺ 3 시간 전 | 1 Kommentare | Auf WhatsApp teilen
  • Unterstützt verschiedene CPUs und gängige Tokenizer und ist als Alternative zu Tiktoken und HuggingFace Tokenizers gedacht, die Text mit GB/s verarbeiten kann
  • Optimiert die sonst von der Regex-Engine übernommene Vor-Tokenisierung mit SIMD, reduziert Branches, Thread-Kommunikation und Python-Interaktionen und cached Token-Zuordnungen für bereits gesehene Wörter effizient
  • Im 11,9-GB-OpenWebText-Benchmark erreichte der GPT-2-Durchsatz auf einem AMD EPYC 9565 24,53GB/s, auf einem Apple M4 Max 8,79GB/s und auf einem Ryzen 7 9800X3D 6,27GB/s
  • Die HuggingFace- und Tiktoken-kompatiblen Modi lassen bestehenden Code fast unverändert, verlieren aber durch die Kosten exakter Ausgabeübereinstimmung an Performance; die Gigatoken API, bei der Rust Dateien direkt liest, bietet die höchste Parallelität und Geschwindigkeit
  • WordPiece und Dateiausgabe werden noch nicht unterstützt, außerdem fehlen Optimierungen für SentencePiece und ausreichende Windows-Validierung; aktuell eignet es sich daher besser für BPE-Tokenizer und Umgebungen mit Linux, macOS oder WSL

Unterstützungsumfang und Verwendung

  • Gigatoken ist ein Hochgeschwindigkeits-Tokenizer für Sprachmodelle und zielt auf moderne x86- und ARM-CPUs sowie nahezu alle gängigen Tokenizer
  • Die Installation erfolgt mit pip install gigatoken; bereitgestellt werden eine eigene API sowie Kompatibilitätsmodi für HuggingFace Tokenizers und Tiktoken
  • Die Kompatibilitätsmodi kapseln bestehende Tokenizer und konvertieren sie jeweils mit .as_hf() oder .as_tiktoken()
    • Dafür wird erheblicher Aufwand betrieben, damit die Ausgabe exakt mit HuggingFace Tokenizers übereinstimmt
    • Diese Kompatibilität verursacht spürbare Performance-Kosten, erreicht also nicht die etwa 1.000-fache Beschleunigung der eigenen API, ist insgesamt aber dennoch schneller als bestehende Implementierungen
  • Die eigene API nimmt HuggingFace-Modellnamen wie "Qwen/Qwen3-8B" und TextFileSource entgegen und kodiert Dateien direkt
    • Die Rust-Implementierung liest Daten direkt ein, überspringt unnötigen Overhead und maximiert die Parallelität
    • Werden Python-Datenstrukturen übergeben, bleibt der Aufwand für das Lesen der Daten in Python bestehen

Implementierung für mehr Geschwindigkeit

  • Die größte Verbesserung kommt meist daher, dass die Vor-Tokenisierung, die sonst von einer Regex-Engine übernommen wird, SIMD-basiert direkt optimiert wurde
  • Branches werden minimiert, und der Cache für Vor-Token-Zuordnungen, der Kodierungstoken bereits gesehener Wörter nachschlägt, wurde gezielt optimiert
    • Der Cache wächst schnell und die Verteilung der Vor-Token hat einen Long-Tail, was die Optimierung erschwert
  • Zusätzliche Performance wird durch weniger Interaktion mit Python und weniger Kommunikation zwischen Threads gewonnen
  • Die Implementierung ist nicht auf eine einzelne CPU oder einen einzelnen Tokenizer zugeschnitten; stattdessen wurden Kombinationen aus modernen x86-/ARM-CPUs und mehreren Tokenizern jeweils optimiert, mit konsistenten Ergebnissen über CPUs und Tokenizer hinweg

11,9-GB-OpenWebText-Benchmark

  • In einer Umgebung mit AMD EPYC 9565 und 144 Kernen erreicht GPT-2 einen Durchsatz von 24,53GB/s, also 989-mal schneller als die 24,8MB/s von HuggingFace Tokenizers und 681-mal schneller als die 36,0MB/s von Tiktoken
    • Wichtige BPE-Familien liegen meist bei etwa 15,49 bis 24,00GB/s
    • Auf SentencePiece basierende Einträge sind mit etwa 2,51 bis 4,82GB/s relativ langsamer
  • Auf einem Apple M4 Max mit 16 Kernen erreicht GPT-2 8,79GB/s, also 1.268-mal so viel wie HuggingFace und 140-mal so viel wie Tiktoken
    • OLMo 2/3 erreicht 1.299-mal, Qwen 2/2.5 1.105-mal die HuggingFace-Leistung
  • Auf einem AMD Ryzen 7 9800X3D mit 16 Kernen erreicht GPT-2 6,27GB/s, also 106-mal so viel wie HuggingFace und 68-mal so viel wie Tiktoken
    • Wichtige BPE-Familien liegen bei etwa 4,21 bis 6,09GB/s, relativ weniger optimierte Familien bei etwa 1,12 bis 2,84GB/s

Messbedingungen und Einordnung

  • OWT (OpenWebText) wurde als Benchmark-Datensatz gewählt, weil es den Text grob repräsentiert, der nach dem Extrahieren von Common-Crawl-Dokumenten entsteht
  • Gigatoken verarbeitet ganze Dateien ohne vorherige Aufteilung und übernimmt daher auch selbst das Finden von Split-Grenzen und die automatische Parallelisierung
  • Die Vergleichssysteme verarbeiten Daten, die vorab anhand von <|endoftext|> aufgeteilt wurden
    • HuggingFace encode_batch_fast verwendet die ersten 100MB
    • Tiktoken encode_ordinary_batch verwendet das erste 1GB
    • Da beide Implementierungen kein Caching nutzen und die Verarbeitungsgeschwindigkeit deshalb weitgehend konstant bleibt, wurde diese Vergleichsbedingung gewählt
  • Tiktoken-Ergebnisse sind nur für offiziell unterstützte Tokenizer enthalten
  • Jede Zeile repräsentiert einen eindeutigen Tokenizer mit identischem Vokabular, identischen Merges und identischem Vor-Tokenizer
    • Mehrere Versionen und abgeleitete Modelle aus den Familien Llama, Qwen, DeepSeek, GLM, Nemotron, Kimi, Phi und Gemma sind derselben Tokenizer-Zeile zugeordnet
  • Die langsamsten Einträge sind SentencePiece-basierte Tokenizer, die in Gigatoken noch nicht ausreichend optimiert sind

Verifizierung der Unterstützung und Verarbeitung großer Datenmengen

  • Mit dem Befehl uvx --with tokenizers gigatoken bench kann die Tokenisierung eines HuggingFace-Modell-Repositories auch ohne Installation verifiziert und zeitlich gemessen werden
  • Im GPT-2-Verifizierungsbeispiel stimmte die Ausgabe für 20.401 Dokumente überein
    • Auf einem Apple M4 Max wurden 11.920,51MB in 1,432 Sekunden mit 8.327,05MB/s verarbeitet, also 1.353,13-mal schneller als HuggingFace
    • Auf einem AMD EPYC 9565 wurden dieselben Daten in 0,486 Sekunden mit 24.532,45MB/s verarbeitet, also 989,21-mal schneller
  • Mit dem EPYC-Durchsatz ließe sich der gesamte Common Crawl im Umfang von 13 Billionen Tokens in weniger als 6,5 Stunden tokenisieren
  • Im Beispiel wird das Stanford CS336 OWT Sample verwendet; die CLI nutzt standardmäßig die ersten 100MB der Datei für Verifikation und HuggingFace-Vergleich
  • Beim ersten Start unter macOS kann Rust-Code wegen Sicherheitsprüfungen langsamer laufen; für genaue Messungen sollte der Befehl daher eventuell zweimal ausgeführt werden
  • Bei Ausgabedifferenzen oder langsamen Fällen wird darum gebeten, diese als GitHub Issue zu melden

Bekannte Einschränkungen

  • Iterative Python-Verarbeitung läuft zwar in Rust, verwendet aber ABI3, das langsamer ist als die internen CPython-APIs je Python-Version
    • Versionsspezifische Optimierungen für Python sind geplant; in ersten Experimenten verdoppelte sich die Geschwindigkeit in Fällen, in denen der Overhead dominierte
  • In der Gigatoken API ist noch kein Dateiausgabe-Sink implementiert
  • WordPiece wird nicht unterstützt
  • Auf SentencePiece basierende Tokenisierung ist weniger stark optimiert als typisches BPE
    • Da vor allem Google-Modelle und die BERT-Familie dies verwenden, hat das aktuell auch eine geringere Priorität
  • Windows wurde noch nicht ausreichend getestet; derzeit wird daher die Nutzung von WSL empfohlen

Umfang des AI-Einsatzes

  • Der Großteil der Codebasis wurde ohne AI direkt geschrieben; das lässt sich im Git-Verlauf des Projekts nachvollziehen
  • In der Schlussphase des Projekts wurde AI für folgende Aufgaben eingesetzt
    • Implementierung der API für Nutzer
    • Generalisierung und Portierung des Vor-Tokenizers für mehr Tokenizer sowie Ausbau der Kompatibilität
    • Unterstützung für Padding, Trunkierung und Unicode-Normalisierung
    • Portierung von SIMD-Strategien zwischen AVX512, AVX2 und NEON
    • Die letzten etwa 4x Leistungssteigerung durch Entfernen von Branches und Verbesserungen an den Vor-Token-Cache-Ebenen
    • Refactoring und Verbesserung der Code-Wiederverwendung

1 Kommentare

 
GN⁺ 3 시간 전
Hacker-News-Kommentare
  • Die Aussage „Der Großteil des Codes wurde direkt ohne KI geschrieben, und das lässt sich auch in der Git-Historie überprüfen“ lässt die Verkündung menschliches Programmieren ist vorbei ziemlich verblassen

  • Es wurde nicht nur ein bestimmter CPU- und Tokenizer-Fall optimiert, sondern die gesamte Kombination aus aktuellem x86·ARM und mehreren Tokenizern übermäßig stark optimiert, um konsistente Leistung zu erzielen
    Die sonst meist einer Regex-Engine überlassene Pre-Tokenisierung wurde per SIMD direkt optimiert und Verzweigungen wurden minimiert; außerdem wurde der Cache für Pre-Token-Mappings verbessert, damit sich die Encoding-Ergebnisse bereits gesehener Wörter schnell nachschlagen lassen. Caches in diesem Bereich wachsen schnell und haben eine Long-Tail-Verteilung, was sie schwer handhabbar macht
    Auch die Interaktion mit Python und die Kommunikation zwischen Threads wurden minimiert

  • Das erinnert an simdjson, das mit kreativem Programmieren kaum glaubhafte Geschwindigkeit erreicht. Wenn es breit eingesetzt würde, könnte das Strom, Kosten und CO2-Ausstoß deutlich senken; ein Rust-Crate wäre daher sehr willkommen, und falls nötig würde ich gern selbst mithelfen

    • Tokenisierung war fast nie ein nennenswerter Bottleneck, und bei JSON-Serialisierung ist es meist ähnlich. Für I/O und Speicher wird weit mehr Energie verbraucht als für Serialisierung und Tokenisierung
      Wenn man Wirtschaftlichkeit und Umwelt betrachtet, bringt Batching von Requests deutlich mehr. Das teuerste Problem ist eine schlechte GPU-Auslastung, und wenn man Arbeit an Batch-Formate anpasst, lassen sich selbst bei OAI derzeit 50 % einsparen. Wenn nicht jede Antwort sofort gebraucht wird, können manche auch ein paar Tage warten, Tool-Calls laufen nicht in Timeouts, und für das LLM selbst existiert keine Wall-Clock-Zeit
  • Ich habe das Repository geklont und angeschaut; der Ersatz der Pre-Tokenisierungs-Regex und die Cache-Optimierung sind allgemein nützliche Ansätze. Das ist so gute Arbeit, dass die gesamte Tokenisierungs-Community wissen wollen dürfte, wie diese Geschwindigkeitssteigerung erreicht wurde

    • Demnächst sollen eine technische Erläuterung und ein Paper zum Projekt sowie ein Präsentationsvideo erstellt und auch auf Discord geteilt werden
    • Das ist nicht nur für Inferenz, sondern auch für Training mit proprietären Datensätzen sehr wertvoll, und beeindruckend ist auch, dass all das von einer einzigen Person umgesetzt wurde
  • Großartige Leistung, aber Tokenisierung macht normalerweise weniger als 0,1 % der gesamten Inferenzzeit aus. Für Anwendungen, die Tokenisierung selbst benötigen, dürfte es aber sehr nützlich sein

    • Je nach Inferenzmethode kann der Anteil der Tokenisierung durchaus erheblich sein. In ersten Messungen mit 8B Qwen3 auf einer einzelnen B200 sank mit gigatoken die Time to First Token (TTFT) im Mittel um 5,5 % bei Eingabelänge 2.048, um 8,4 % bei 8.192 und um 7,8 % bei 32.768
      Je kleiner das Modell oder je schneller die GPU, desto größer der Effekt; vor einer Aufnahme ins README ist aber noch zusätzliche Validierung nötig. Die Benchmarks stammen von fastokens
    • Auf AI-Plattformen muss früh im Request schnell tokenisiert werden, um anschließend Routing·Rate Limiting usw. zu bestimmen. Auch wenn der Anteil an der gesamten Request-Zeit klein ist, ist Effizienz wichtig
    • Tokenisierung läuft meist seriell, daher kann sie bei großen Initial-Prompts einen großen Anteil an der Eingabeverarbeitungszeit haben. Nach der Übergabe an die Modellinferenz können schließlich alle Tokens parallel verarbeitet werden
    • Selbst 1/1.000 der Inferenzrechenlast lässt sich bei großem Maßstab nicht ignorieren. Gartner schätzt die Inferenzausgaben für 2026 auf rund 28 Milliarden US-Dollar; mit der obigen Annahme entspricht das 28 Millionen US-Dollar pro Jahr
      Quelle: https://www.gartner.com/en/newsroom/press-releases/2026-07-2...
    • Gerade bei kleinen Modellen lässt sich die Latenz bis zum ersten Token stark senken. Für Inferenzanbieter wie Groq oder Cerebras ist nicht nur der Gesamtdurchsatz wichtig, sondern auch die Latenz
  • Es wirkt nützlicher für die Offline-Vorbereitung von Pretraining-Daten als für den Inferenzzeitpunkt. Wenn man mehrere Terabyte Text für einen Trainingskorpus tokenisiert, spart das Zeit und Kosten und verkürzt auch den Iterationszyklus beim Abstimmen des Datensatzes

  • Ingenieurskunst darauf zu verwenden, einen Teil, der 0,1 % der Gesamtlaufzeit ausmacht, 1.000-mal schneller zu machen, ist wohl die softwareentwicklertypischste Handlung überhaupt

    • Ich habe in Rust eine hochauflösende Word-Cloud in etwa 100 ms erzeugt und sie mit weiterer Optimierung auf etwa 16 ms gedrückt. Die Welt braucht keinen so schnellen Word-Cloud-Generator, aber wenn man ihn baut, dann sollte er so schnell wie möglich sein
    • Das Streben nach Exzellenz braucht keine Rechtfertigung“
      https://x.com/mitchellh/status/2074225453217505494
    • Das hängt vom Workflow ab. Es gibt auch Anwendungsfälle, in denen Text nicht direkt ins Modell geht und nur tokenisiert wird
    • Selbst wenn es nur eine Unterkomponente ist, ermöglicht eine 1.000-fache Verbesserung qualitativ neue Funktionen. Dass dieser Teil nur 0,1 % des Ganzen ausmacht, ist oft auch das Ergebnis der im Projekt immer wiederholten Haltung: „Warum ordentlich bauen, wenn es die Gesamtleistung nicht beeinflusst?“
      LLMs liegen den Grenzen einer 1.000-fachen Verbesserung viel näher, aber selbst grundlegende PyTorch-Operationen sind oft doppelt so langsam wie eine simple Neuschreibung, und bessere Scheduling-Algorithmen bringen teils 5- bis 10-fache Verbesserungen. Schnelle Tokenisierung könnte andere Funktionen ermöglichen, die bisher ignoriert wurden, weil sie nicht realisierbar war
    • Wenn zur Steuerung ein extrem kleines Sprachmodell (SLM) ausgeführt werden soll und dafür tokenisiert wird, kann der Anteil weit über 0,1 % liegen. Das ist dieselbe Denkweise wie: „PCs stehen die meiste Zeit nur auf dem Desktop herum, also sind Optimierungen des GPU-Treibers nicht wichtig“
  • Die Leistung ist so unglaubwürdig hoch, dass man lange auf die Zahlen im Diagramm schauen muss, um sie zu begreifen

  • Genau so etwas wird auch in ClickHouse gebraucht; wir wollen es unter https://github.com/ClickHouse/ClickHouse/issues/108247 testen
    Das README könnte die Leistung pro Kern stärker betonen, und ich frage mich, ob vollständiges Hash-Table-Matching dem eigentlichen Algorithmus helfen würde

  • Dann fragt man sich, wie viele 1.000-fache Optimierungschancen in anderen Teilen der Inferenz-Pipeline noch übrig sind

    • Anders als bei der Tokenisierung ist bei anderen Änderungen an der Inferenz nicht einfach zu entscheiden, ob sie korrekt sind
    • Davon gibt es viele, und fast jede Komponente hat ein eigenes Team und entsprechende Forschung. Es ist gut möglich, dass noch viele große Durchbrüche kommen
    • In Bereiche, die einen größeren Anteil an der Inferenzzeit ausmachen, ist vermutlich bereits deutlich mehr Optimierungsaufwand geflossen