3 points par GN⁺ 3 시간 전 | 1 commentaires | Partager sur WhatsApp
  • Alternative à Tiktoken et à HuggingFace Tokenizers, compatible avec divers CPU et les tokenizers les plus courants, capable de traiter du texte à des débits de l’ordre du Go/s
  • Optimise en SIMD le pré-tokenizing habituellement confié au moteur d’expressions régulières, réduit les branches, la communication entre threads et les interactions avec Python, et met efficacement en cache les correspondances de tokens pour les mots déjà vus
  • Sur le benchmark OpenWebText de 11,9 Go, le débit GPT-2 atteint 24,53 Go/s sur AMD EPYC 9565, 8,79 Go/s sur Apple M4 Max et 6,27 Go/s sur Ryzen 7 9800X3D
  • Les modes compatibles HuggingFace et Tiktoken permettent de conserver presque tel quel le code existant, mais les performances baissent à cause du coût de la fidélité des sorties ; l’API Gigatoken, où Rust lit directement les fichiers, offre le parallélisme maximal et la meilleure vitesse
  • WordPiece et la sortie vers fichier ne sont pas encore pris en charge, et l’optimisation de SentencePiece ainsi que la validation sous Windows restent limitées ; l’outil est donc pour l’instant mieux adapté aux tokenizers BPE et aux environnements Linux, macOS ou WSL

Périmètre de prise en charge et utilisation

  • Gigatoken est un tokenizer haute performance pour modèles de langage, conçu pour les CPU x86 et ARM modernes ainsi que pour presque tous les tokenizers courants
  • Installation via pip install gigatoken, avec sa propre API ainsi que des modes compatibles HuggingFace Tokenizers et Tiktoken
  • Les modes compatibles encapsulent les tokenizers existants et les convertissent respectivement via .as_hf() ou .as_tiktoken()
    • Un travail important est appliqué pour faire correspondre exactement les sorties de HuggingFace Tokenizers
    • Ce traitement de compatibilité a un coût de performance non négligeable : il n’atteint donc pas l’accélération d’environ 1 000× de l’API native, mais reste globalement plus rapide que les implémentations existantes
  • L’API native prend un nom de modèle HuggingFace comme "Qwen/Qwen3-8B" et un TextFileSource, puis encode directement les fichiers
    • L’implémentation Rust lit directement les données, évite les surcoûts inutiles et maximise le parallélisme
    • Si l’on passe des structures de données Python, le coût de lecture des données par Python demeure

Implémentation axée sur la vitesse

  • Le gain principal vient d’une optimisation poussée en SIMD du pré-tokenizing, habituellement laissé au moteur d’expressions régulières
  • L’implémentation minimise les branches et optimise fortement le cache de correspondance des pré-tokens, qui retrouve les tokens encodés pour les mots déjà rencontrés
    • Ce cache grossit rapidement et la distribution des pré-tokens a une longue traîne, ce qui le rend difficile à gérer
  • Des gains supplémentaires proviennent de la réduction des interactions avec Python et de la communication entre threads
  • Il ne s’agit pas d’une implémentation optimisée pour un seul CPU ou tokenizer : plusieurs combinaisons de CPU x86/ARM modernes et de tokenizers ont été optimisées, avec des résultats cohérents sur l’ensemble des CPU et tokenizers

Benchmark OpenWebText de 11,9 Go

  • Sur un AMD EPYC 9565 à 144 cœurs, le débit GPT-2 atteint 24,53 Go/s, soit 989× plus rapide que les 24,8 Mo/s de HuggingFace Tokenizers et 681× plus rapide que les 36,0 Mo/s de Tiktoken
    • Les principales variantes BPE enregistrent en général environ 15,49 à 24,00 Go/s
    • Les variantes basées sur SentencePiece sont relativement plus lentes, autour de 2,51 à 4,82 Go/s
  • Sur Apple M4 Max à 16 cœurs, GPT-2 atteint 8,79 Go/s, soit 1 268× HuggingFace et 140× Tiktoken
    • OLMo 2/3 atteint 1 299× HuggingFace, et Qwen 2/2.5 1 105×
  • Sur AMD Ryzen 7 9800X3D à 16 cœurs, GPT-2 atteint 6,27 Go/s, soit 106× HuggingFace et 68× Tiktoken
    • Les principales variantes BPE se situent autour de 4,21 à 6,09 Go/s, et les familles relativement moins optimisées autour de 1,12 à 2,84 Go/s

Conditions de mesure et interprétation

  • OWT (OpenWebText) a été retenu comme jeu de benchmark car il représente approximativement le texte obtenu après extraction de documents Common Crawl
  • Gigatoken traite les fichiers sans les découper à l’avance, et gère donc lui-même la recherche des frontières de découpage et le parallélisme automatique
  • Les outils comparés traitent, eux, des données déjà découpées à l’avance sur la base de <|endoftext|>
    • HuggingFace encode_batch_fast utilise les 100 premiers Mo
    • Tiktoken encode_ordinary_batch utilise le premier Go
    • Aucune des deux implémentations n’utilise de cache, donc leur vitesse de traitement reste globalement stable, d’où ce protocole de comparaison
  • Les résultats Tiktoken ne sont inclus que pour les tokenizers officiellement pris en charge
  • Chaque ligne représente un tokenizer unique partageant le même vocabulaire, les mêmes fusions et le même pré-tokenizer
    • Plusieurs versions et modèles dérivés des familles Llama, Qwen, DeepSeek, GLM, Nemotron, Kimi, Phi et Gemma sont regroupés sur la même ligne de tokenizer
  • L’élément le plus lent est le tokenizer basé sur SentencePiece, encore insuffisamment optimisé dans Gigatoken

Vérification de la prise en charge et traitement à grande échelle

  • Sans installation, la commande uvx --with tokenizers gigatoken bench permet de valider la tokenisation d’un dépôt de modèles HuggingFace et d’en mesurer le temps
  • Dans l’exemple de validation GPT-2, les sorties de 20 401 documents correspondent exactement
    • Sur Apple M4 Max, 11 920,51 Mo sont traités en 1,432 seconde, soit 8 327,05 Mo/s, ce qui est 1 353,13× plus rapide que HuggingFace
    • Sur AMD EPYC 9565, les mêmes données sont traitées en 0,486 seconde, soit 24 532,45 Mo/s, ce qui est 989,21× plus rapide
  • À ce débit sur EPYC, l’intégralité de Common Crawl, soit 130 billions de tokens, pourrait être tokenisée en moins de 6,5 heures
  • L’exemple utilise le Stanford CS336 OWT sample, et le CLI emploie par défaut les 100 premiers Mo du fichier pour la validation et la comparaison avec HuggingFace
  • Au premier lancement sur macOS, le code Rust peut être ralenti par les contrôles de sécurité ; il peut donc être nécessaire d’exécuter la commande deux fois pour une mesure précise
  • Les écarts de sortie ou les cas lents sont à signaler via les GitHub Issues

Limitations connues

  • Le traitement itératif Python est exécuté en Rust, mais utilise ABI3, plus lent que les API internes CPython propres à chaque version
    • Une spécialisation par version de Python est prévue, et les premiers essais ont doublé les performances dans les cas où ce surcoût domine
  • L’API Gigatoken ne dispose pas encore d’un sink de sortie fichier
  • WordPiece n’est pas pris en charge
  • La tokenisation basée sur SentencePiece est moins optimisée que le BPE classique
    • Comme elle est surtout utilisée par les modèles Google et la famille BERT, sa priorité reste pour l’instant plus faible
  • Les tests sous Windows étant insuffisants, l’usage de WSL est actuellement recommandé

Périmètre d’utilisation de l’IA

  • L’essentiel du code a été écrit directement sans IA, ce qui peut être vérifié dans l’historique Git du projet
  • Dans la phase finale du projet, l’IA a été utilisée pour les tâches suivantes
    • implémentation de l’API utilisateur
    • généralisation et portage du pré-tokenizer pour davantage de tokenizers, avec extension de la compatibilité
    • prise en charge du padding, du trimming et de la normalisation Unicode
    • portage des stratégies SIMD entre AVX512, AVX2 et NEON
    • dernier gain d’environ grâce à la suppression de branches et à l’amélioration de la hiérarchie du cache de pré-tokens
    • refactoring et amélioration de la réutilisation du code

1 commentaires

 
GN⁺ 3 시간 전
Avis de Hacker News
  • Le fait que « la majeure partie du code a été écrite directement sans IA, comme le confirme aussi l’historique Git » rend un peu creuse la proclamation selon laquelle la programmation humaine est finie.

  • Il ne s’agit pas d’un seul CPU ni d’un seul tokenizer, mais d’une optimisation poussée de l’ensemble des combinaisons de x86 et ARM récents avec plusieurs tokenizers, afin d’obtenir des performances cohérentes.
    La pré-tokenisation, habituellement confiée à un moteur d’expressions régulières, a été optimisée directement avec SIMD en minimisant les branchements, et le cache de correspondance des tokens du vocabulaire a aussi été amélioré pour retrouver rapidement le résultat d’encodage des mots déjà vus. Dans ce domaine, les caches grossissent vite et ont une longue traîne de distribution, ce qui les rend délicats à gérer.
    Les interactions avec Python et la communication entre threads ont également été réduites au minimum.

  • Cela rappelle simdjson, qui atteint des vitesses difficiles à croire grâce à une programmation créative. S’il est largement adopté, il pourrait réduire fortement la consommation d’énergie, les coûts et les émissions carbone ; ce serait donc bien de publier aussi un crate Rust, et je serais prêt à aider si besoin.

    • La tokenisation n’a presque jamais été un goulot d’étranglement significatif, et c’est globalement pareil pour la sérialisation JSON. On dépense bien plus d’énergie dans les entrées-sorties et le stockage que dans la sérialisation et la tokenisation.
      Si l’on pense économie et environnement, le traitement des requêtes par lots a un effet plus important. Le problème le plus coûteux est la baisse d’utilisation des GPU ; en adaptant les tâches au traitement par lots, on peut déjà économiser 50 % chez OAI aujourd’hui. Si toutes les réponses ne sont pas nécessaires immédiatement, certaines peuvent attendre quelques jours, les appels d’outils n’expirent pas, et le LLM lui-même n’a pas de notion de temps réel.
  • J’ai cloné le dépôt pour y jeter un œil : le remplacement des expressions régulières de pré-tokenisation et l’optimisation du cache sont des approches utiles de manière générale. C’est un excellent travail, au point que toute la communauté de la tokenisation voudra comprendre les secrets de ces gains de vitesse.

    • Une explication technique du projet et un article, ainsi qu’une vidéo de présentation, seront bientôt préparés et partagés aussi sur Discord.
    • C’est très précieux non seulement pour l’inférence, mais aussi pour l’entraînement à partir de jeux de données propriétaires, et le fait qu’une seule personne ait réalisé tout cela est également impressionnant.
  • C’est une belle réussite, mais la tokenisation représente généralement moins de 0,1 % du temps total d’inférence. Cela dit, ce sera très utile pour les applications qui ont besoin de tokenisation en tant que telle.

    • Selon la méthode d’inférence, la part de la tokenisation peut aussi devenir importante. Dans des mesures initiales avec Qwen3 8B sur un seul B200, le passage à gigatoken a réduit le temps de génération du premier token (TTFT) en moyenne de 5,5 % avec une entrée de 2 048 tokens, de 8,4 % avec 8 192, et de 7,8 % avec 32 768.
      L’effet augmente avec des modèles plus petits ou des GPU plus rapides, et des validations supplémentaires sont nécessaires avant de l’ajouter au README. La source du benchmark est fastokens.
    • Sur les plateformes d’IA, il faut tokeniser rapidement au début d’une requête afin de décider ensuite du routage, de la limitation de débit, etc. Même si cela ne représente qu’une faible part du temps total de la requête, l’efficacité reste importante.
    • La tokenisation est le plus souvent sérielle ; si le prompt initial est volumineux, elle peut donc représenter une grande part du temps de traitement de l’entrée. Une fois passée à l’inférence du modèle, tous les tokens peuvent en effet être traités en parallèle.
    • Même 1/1 000 des calculs d’inférence devient difficile à ignorer à grande échelle. Gartner estime les dépenses d’inférence en 2026 à environ 28 milliards de dollars ; avec l’hypothèse précédente, cela représente 28 millions de dollars par an.
      Source : https://www.gartner.com/en/newsroom/press-releases/2026-07-2...
    • Cela peut notamment réduire fortement la latence de génération du premier token pour les petits modèles. Pour des fournisseurs d’inférence comme Groq ou Cerebras, la latence compte autant que le débit total.
  • Cela semble plus utile pour la préparation hors ligne des données de pré-entraînement qu’au moment de l’inférence. Lorsqu’on tokenise plusieurs téraoctets de texte pour un corpus d’entraînement, cela permet d’économiser du temps et de l’argent, et de raccourcir les cycles d’itération lors de l’ajustement des jeux de données.

  • Consacrer des efforts d’ingénierie à rendre 1 000 fois plus rapide une partie qui représente 0,1 % du temps total d’exécution, c’est précisément le comportement le plus typique d’un développeur logiciel.

    • J’ai créé en Rust un nuage de mots haute résolution en environ 100 ms, puis d’autres optimisations l’ont ramené à environ 16 ms. Le monde n’a pas besoin d’un générateur de nuages de mots aussi rapide, mais si on en construit un, autant qu’il soit aussi rapide que possible.
    • « La quête de l’excellence n’a pas besoin de justification. »
      https://x.com/mitchellh/status/2074225453217505494
    • Cela dépend du flux de travail. Il existe aussi des usages où l’on fait uniquement de la tokenisation, sans envoyer ensuite le texte directement dans un modèle.
    • Même pour un sous-composant, une amélioration d’un facteur 1 000 peut rendre possibles des fonctionnalités qualitativement nouvelles. Si cette partie ne représente que 0,1 % du total, c’est souvent aussi le résultat d’une attitude répétée dans tout le projet : « pourquoi le faire correctement si cela n’a pas d’impact sur les performances globales ? »
      Les LLM sont beaucoup plus proches des limites d’une amélioration d’un facteur 1 000, mais les opérations PyTorch de base sont souvent deux fois plus lentes qu’une simple réécriture, et de meilleurs algorithmes d’ordonnancement peuvent parfois apporter des gains de 5 à 10 fois. Une tokenisation rapide pourrait aussi ouvrir d’autres fonctionnalités jusqu’ici ignorées parce qu’elles semblaient impraticables.
    • Si l’on tokenise pour exécuter un très petit modèle de langage (SLM) destiné au routage, la part peut être bien supérieure à 0,1 %. C’est le même raisonnement que de dire que « comme le PC passe la plupart de son temps sur le bureau, l’optimisation des pilotes GPU n’est pas importante ».
  • Les performances sont si difficiles à croire qu’il faut fixer le graphique un bon moment pour comprendre les chiffres.

  • C’est exactement le type de fonctionnalité dont ClickHouse a besoin aussi, donc je vais l’essayer dans https://github.com/ClickHouse/ClickHouse/issues/108247.
    Ce serait bien que le README mette davantage l’accent sur les performances par cœur, et je me demande si, dans l’algorithme réel, une correspondance par table de hachage parfaite serait utile.

  • On se demande alors combien d’autres occasions d’optimisation par un facteur 1 000 restent encore dans les autres parties du pipeline d’inférence.

    • Contrairement à la couche de tokenisation, les autres changements dans l’inférence rendent plus difficile de juger simplement de leur exactitude.
    • Il y en a beaucoup, et presque chaque composant dispose d’équipes dédiées et de travaux de recherche. Il est très probable que de nombreuses percées importantes arrivent encore.
    • Les parties qui représentent une plus grande part du temps d’inférence ont sans doute déjà reçu beaucoup plus d’efforts d’optimisation.