Qwen3-Embedding上TPU:16K长上下文怎么稳住

Embedding 服务最容易被低估的性能问题,不是单条文本有多快。

而是:

输入突然从1K tokens
变成15K tokens以后,
系统还能不能稳定批处理?

Google 8月26日公开了 vLLM 在 Cloud TPU 上服务 Qwen3 Embedding 系列的一组工程实现。目标场景已经不是短文本向量化,而是:

文本:4K+ tokens
多模态:15K+ tokens

公开的 Qwen3-Embedding-8B 配置支持 max_model_len=16384;在 Ironwood TPU 上,Google 报告的一个 16K+ 序列、BF16、TP=4 配置达到 83,996 total token/s、5.13 req/s,同时使用跨硬件向量余弦相似度校验精度,文本阈值设为 ≥0.999,多模态阈值为 ≥0.995

真正值得拆的是它为长上下文做的三件事:

Tensor Alignment
Compilation Pre-warming
Chunked Prefill + StepPool

为什么Embedding也会把HBM打爆

很多人对长上下文的第一反应是:

LLM KV Cache

但 Embedding Pooling 同样需要保存长序列中间状态、Pooling State 和 Batch Metadata。

如果 15K tokens 一次性 Prefill:

请求多
+
序列长
+
Batch大

HBM 压力会迅速上升。

所以需要:

Chunked Prefill

把一次长输入拆成多个 Step。

Chunked Prefill最危险的不是慢,而是状态丢失

假设 16K tokens 分成:

4K
4K
4K
4K

每一段执行完都要把 Pooling 所需状态保留下来。

如果请求被 Preempt、Reschedule 或 Batch 重排以后中间状态没有恢复,最终向量可能看起来“有结果”,但数学上已经偏了。

这比直接报错更危险。

所以长Embedding一定要做Golden Parity

不要只测:

API返回200

同一条输入分别跑:

Reference Backend
Candidate Backend

然后比较向量。

import numpy as np

def cosine(a, b):
    a = np.asarray(a)
    b = np.asarray(b)

    return float(
        np.dot(a, b) /
        (
            np.linalg.norm(a)
            * np.linalg.norm(b)
        )
    )

Gate:

assert cosine(
    reference,
    candidate
) >= 0.999

文本模型可以先用 0.999 作为高精度目标。

多模态可按业务设不同阈值。

为什么不能只看TopK一致率

两个 Embedding:

cosine = 0.995

在普通搜索里可能 TopK 还差不多。

但当你的向量库有几千万条、而且很多候选距离很近时,小偏差也可能改变排名。

所以我会同时测:

Vector Parity
TopK Overlap
NDCG
Business Recall

一套完整精度回归

embedding_gate:

  vector_cosine:
    p50: 0.9999
    p01: 0.9990

  top10_overlap:
    min: 0.98

  retrieval_recall:
    regression_max: 0.005

不要因为吞吐翻倍,就接受检索质量悄悄下降。

Google公开配置里有几个值得照抄的测试维度

示意配置:

from vllm import LLM

llm = LLM(
    model="Qwen/Qwen3-Embedding-8B",
    runner="pooling",
    tensor_parallel_size=2,
    max_model_len=16384,
    max_num_batched_tokens=512,
    dtype="bfloat16",
    trust_remote_code=True,
)

真正需要调的是:

tensor_parallel_size
max_model_len
max_num_batched_tokens

它们共同决定:

吞吐
内存
调度
长序列稳定性

max_num_batched_tokens不是越大越好

Batch Token 上限大,吞吐可能提高,但也可能出现:

HBM上涨
尾延迟上涨
长请求挤压短请求

所以压测至少分:

512
1024
2048
4096

看:

Tokens/s
Req/s
P95
HBM
OOM

我会按长度建4个Bucket

0—1K
1K—4K
4K—8K
8K—16K

每个 Bucket 独立看:

P50
P95
Throughput
Queue Time

不要把短文本和长文档平均在一起。

否则“平均延迟80ms”可能掩盖:

15K输入P95 = 4秒

生产路由可以按长度分池

例如:

Short Pool:
 4K

Kubernetes:

embeddingPools:

  short:
    max_tokens: 4096
    replicas: 8

  long:
    max_tokens: 16384
    replicas: 4

流量由 Gateway 按 Token Count 路由。

这样长请求不会把短查询堵住。

Tensor Parallel为什么会遇到Alignment

TPU 的矩阵执行单元对切分维度有对齐要求。

当词表矩阵做 Tensor Parallel Sharding 时,如果维度不能整除拓扑,就可能出现 All-Gather 对齐问题。

Google 的做法是统一做硬件安全的 Vocabulary Padding。

这类优化提醒我们:

跨硬件迁移
不是换一个device参数

尤其是高维 Pooling Model。

Compilation Pre-warming解决的是发布抖动

JAX/XLA 类后端常见:

第一次请求触发编译

线上就会出现:

新Pod Ready
↓
第一批请求进入
↓
P95突然几十倍

更稳的是:

启动
→加载权重
→预热常见Shape
→编译Cache稳定
→Ready

Kubernetes Readiness 不应该只测:

HTTP 200

而要等:

Model Loaded
+
Compile Warmed
+
Memory Stable

才放量。

长Context还要测Preemption

故意把队列打满:

长请求执行一半
↓
被调度器抢占
↓
恢复

最后向量必须和无抢占 Reference 一致。

这是测试 StepPool / Cached State 最关键的一类 Case。

一个Preemption Test

Input:
15,500 tokens

Baseline:
单请求运行

Stress:
并发64
触发Preemption

Check:
cosine >= 0.999

如果只做性能压测,不做向量一致性,这类 Bug 很难发现。

混合硬件弹性也要测结果一致

Google 的架构方向允许 GKE 在 TPU Capacity 不足时回退到其他 GPU Spot / On-demand Pool。

这意味着同一个服务可能出现:

请求A → TPU
请求B → GPU

所以硬件间 Parity 必须是发布门禁。

否则用户可能发现:

同样文本
上午一个向量
下午另一个向量

进而造成索引和查询 Embedding 不一致。

最危险的是Index和Query跑不同后端

比如:

离线索引:
GPU

在线Query:
TPU

如果两边向量偏差明显,Recall 会直接下降。

所以必须记录:

embedding_model_version
backend_profile
precision

Vector Metadata建议增加

{
  "model": "qwen3-embedding-8b",
  "model_version": "v1",
  "dtype": "bf16",
  "backend_profile": "tpu-ironwood-tp4",
  "embedding_schema": "emb-v7"
}

后续迁移可以知道哪些向量需要重建。

一个真正实用的压测矩阵

Length:
1K / 4K / 8K / 16K

Concurrency:
1 / 8 / 32 / 64

Batch Tokens:
512 / 1024 / 2048

Backend:
TPU / GPU

共:
4 × 4 × 3 × 2
=
96组

每组输出:

Req/s
Token/s
P95
HBM
OOM
Cosine
Recall

这样才知道最优点在哪。


长上下文 Embedding 服务真正难的地方,是:

性能问题和精度问题会绑在一起。

Chunked Prefill、TP Sharding、JIT Compilation、Preemption 都可能提升系统效率,也可能制造很隐蔽的向量偏差。

所以发布标准可以压成两句话:

先证明向量没变,
再证明系统变快。

以及:

短请求和长请求分开看,
不要被平均值骗了。

做到这两点,16K Embedding 才算从“能跑”进入“能稳定服务”。


更多企业级 AI 应用、Agent、RAG 与模型工程化内容,我会继续整理在 智元界

https://www.zyentor.com/