开源Hugging Face Blog·原文 2026年9月21日

Hugging Face发布tokenizers v1:单线程编码比v0.23快 3 到 30 倍

v1保持与v0.23完全相同的token ID、API和词表,重写分词编码路径;在Apple M4 Max单线程下对覆盖的十个模型家族编码提速 3 至 30 倍,八线程扩展性为线性的 76%。

AI解读:Hugging Face放出了tokenizers v1的候选版本,核心卖点很直接:输入文字变成token ID的速度比v0.23快 3 到 30 倍,但输出、API、词表和合并排名都没变。换句话说,升级后模型读到的整数序列和原来一模一样,改的只是算得有多快。

这事影响的是那类已经喂不饱GPU的场景。训练超大数据集、同时服务大量请求、反复处理长输入时,分词会变成瓶颈,让GPU空等CPU。v1把正则切分换成手写的位流操作、给重复词加缓存、让合并循环不再反复分配内存,目标就是让分词别再拖后腿。

对普通读者来说不需要立刻动手:编码行为没变,只是换一个安装版本。真正相关的是大规模训练和推理服务团队,他们可以拿自己的模型和硬件跑一遍基准,看看提速幅度落在 3 倍还是 30 倍——差距取决于所用模型的分词规则是否落在已支持的五种语法里。

限制也要说清:bitcannon带来的大幅提速依赖事先识别分词模式,目前覆盖GPT-2、cl100k、o200k、Tekken和DeepSeek,不属于这些语法的模型仍走原来的正则路径,拿不到这份收益。缓存对重复词多的文本最有效,重复少时反而要为查找付出开销。

官方称v1会保持与v0.23相同的输出、API、词表和合并排名,并在 1.0.0 前把更多模型家族迁到新合并循环,之后再把改进带进transformers等依赖方。这些是项目方的计划,不是已完成的事实。

Hugging Face在其博客中公布了tokenizers v1候选版本(release candidate),称这项重写让编码速度比v0.23快 3 到 30 倍。该项目强调,v1产生与v0.23完全相同的token ID,API、词表和合并排名均保持不变,因此升级只改变安装的构建版本,不改变模型读到的整数序列。

根据博客,在Apple M4 Max单线程条件下,v1覆盖的十个模型家族中,最低提速为t5-base的 3 倍,最高为gpt2的 30 倍;跨八个工作线程的扩展性为线性的 76%。所有测量由tokbench仓库运行,该仓库也提供命令让用户在自己的硬件上复现基准。

v1候选版本已发布在crates.io,可用cargo add tokenizers --pre安装。训练功能位于默认开启的feature后,会引入一个C++依赖;只需要编码的用户可以关闭默认特性:cargo add tokenizers --pre --no-default-features --features http。

博客写道,分词器历史上并非ML工作流的瓶颈,因为相比模型计算,分词很轻。但当模型变快、负载规模扩大,训练超大数据集、服务大量并发请求或反复处理长输入会给分词器足够压力,导致它无法及时向模型供数据。项目称这正是v1重点做性能的原因,并强调GPU不应空等CPU完成分词。

v1保留了哪些行为,删掉了哪些开销

博客称v1的目标是保留输出、API、词表和合并排名,只改进能改进的部分,包括广度:库仍对各类分词器保持通用,而不是专攻BPE,因此v1能加载v0.23能加载的一切。

分词器把文本转换为模型读取的整数列表,tokenizers分四个阶段完成:归一化(如小写或Unicode归一化)、预分词(切成pre-token)、模型阶段(把每个pre-token转成token并映射到词表ID)、后处理(加入模型期望的特殊token)。博客称本文描述的大部分工作发生在模型阶段。文章测量的十个模型家族中,八个使用字节对编码(BPE),另外两个使用WordPiece和Unigram。

BPE从pre-token的字节出发,反复合并排名最高的相邻对,直到没有可合并的排名对。排名在分词器训练时学得并随其分发,因此同一文本总是产生相同ID;合并不会跨越pre-token边界。

  • 博客列出的一系列改动包括:workspace split,把单一crate拆成语义上按需链接的tk-encode、tk-serialize、tk-convert、tk-train;no-alloc model,合并工作集放在调用方提供的scratch缓冲中;bitcannon,用位流上的布尔运算配合SIMD找切分点,替代正则引擎;merge-loop rewrite,被合并的片段构成一个预分配缓冲内的侵入式双向链表;word cache,线程本地地从pre-token字节到最终ID的记忆化;native parallelism,一个共享分词器可被多线程同时编码,每个线程从自己的子池取scratch缓冲和词缓存(#2365)。

bitcannon用位流替掉正则,但只覆盖五种语法

BPE模型用一个正则表达式把输入切成pre-token,这个正则随分词器分发、运行时不变,因此不需要通用正则引擎在每次编码时解释它。博客称可以为给定模型的模式手写一次等价的切分函数,并用现代CPU的SIMD指令一次处理多个字节,适合UTF-8文本。

bitcannon把输入字节视作并行的比特流,边界由整寄存器的布尔运算得出,而不是逐字符扫描,每次寄存器操作处理 64 字节。博客称同样思路驱动了文本处理的Parabix和JSON的simdjson。

这套做法依赖识别出模式。博客称少数几种语法覆盖了大多数字节级BPE模型,模式不在其中的分词器仍走正则路径,拿不到这份提速——这也是各模型收益差异很大的原因。bitcannon替换掉了最初发布的有限状态机,覆盖GPT-2、cl100k、o200k、Tekken和DeepSeek(#2201 #2317)。

词缓存和合并循环:重复多的输入最受益

真实文本包含大量重复词。由于BPE对同一pre-token总是产生相同token ID,v1可以在处理一次后保存结果。一个线程本地缓存把每个pre-token的字节映射到其token ID,后续出现可跳过合并过程。博客称输入越长,唯一词数量的增长可以慢于总词数,重复词占比随之上升,但新词仍会出现,因此缓存偶尔未命中。

缓存最适合输入含重复pre-token的场景;重复少的输入可能为查找付出成本却收获不多。博客给出了复现共享前缀结果的命令:tokbench measure prefix-sharing --engine pipeline --engine hf-tokenizers --compare-to pipeline-no-cache --corpus agentic_swe。

BPE合并循环是另一大成本:对每个pre-token,循环反复找最高优先级的相邻对并合并它。旧实现每次调用都分配新内存,并为每个pre-token建新优先队列。v1复用调用方持有的scratch缓冲,去掉了这些重复分配;把符号存在扁平数组里,用位置链接相邻符号,使合并时的更新更便宜;它还在一次模型调用中处理一批pre-token。每个候选对被打包进一个 64 位值,合并排名放在高位,于是比较两个候选就是比较两个整数,而“此处无合并”是最大可能值,循环无需分支即可找到下一个合并。

基准方法:为了可比性做了什么

博客称基准设计上的微小差异会造成分词器性能的巨大差异,因此设定了一致规则。

  • one timing loop:所有引擎跑同一个循环,没有按引擎区分的快速路径。
  • load excluded:词表加载单独计时,绝不放在encode内。
  • id-hash verified:对输出ID做FNV-1a校验,必须与基线完全一致。
  • common cells only:中位数只取所有引擎都运行并验证过的单元。
  • complete sweep per process:每次重复都在新进程中开始,并保留每个单元。
  • physical-core pinning:工作线程绑定到八个不同的物理核心,绝不用SMT兄弟线程。
  • independent jobs:独立Job用于测量主机间差异。
  • 博客还说明,“热”缓存有两种不同工作负载:反复编码同一文档可能比编码一串不同文档更快,前者衡量整个文档已在缓存中的性能,后者衡量新输入、同时允许已见过的pre-token留在缓存中的性能。文章标题数据使用不同文档,且完整语料太大无法放入缓存。文章称分词器基准应说明用的是哪种工作负载,因为选择可能主导结果。

1.0.0 之前还有哪些未完成项

博客把基准覆盖的工作列为“已完成”,其余部分标为 1.0.0 仍需完成或之后计划探索的内容。候选版本中已实现的还包括:更快的查找与合并结构(FlatCache、MPHF RankStore、增量合并、BucketVocabStore,#2190 #2188)、可复用的模型内存(把临时状态移入scratch缓冲,使tokenization每次调用不再分配,#2175 #2183)、把后处理暴露为STAGE_POST管线阶段(#2182)、批量模型调用(#2304)、更快的解码(直接写入可复用缓冲、避免中间字符串和拷贝、加速token查找、支持缓冲流式、并行解码批次,以及role_to_token支持 #2343),以及Node.js绑定(#2281)。

1.0.0 待办包括:用tk-encode做训练验证,使训练和推理不能产生不同的分词结果;把offsets和masks改成按需计算,让纯token-ID路径不承担这份开销;重做归一化器(bitnorm支持,基于atomnorm #2209);spm预编译;简化Python绑定(减少锁、包装类型和手写分派,同时保留子类化、序列化、自定义解码器、变更行为以及自由线程CPython支持);面向ExecuTorch和llama.cpp的仅推理C和C++绑定,之后可能跟进JVM、Swift和Go绑定。

1.0.0 之后计划探索tok-devices:在设备上做GPU编码和批量解码,同时让文本和token ID留在设备上;解码器会一次性上传词表,并行计算输出位置,并在GPU上收集对应字节。博客称这将是可选组件,面向大批量场景,还需进一步原型验证和测量。

博客称下一个优先级是支持更多模型家族,会在 1.0.0 之前把更多模型迁到新合并循环;候选版本稳定后,下一步是把这些改进带入transformers库以及依赖tokenizers的生态其他部分。文章称本博文由tokbench结果生成,并将随支持范围扩大而更新。

信息来源

Hugging Face Blog原始来源