@ivanalog_com: 调试了一会儿,现在在Google A100上运行Qwen Flash的速度(3000+预填充,90+ tps)已经更快了…
摘要
一个名为collabosm的GitHub仓库提供了一个优化设置,用于在Google Colab A100上运行Qwen3.8-Flash-Next模型,实现了比商业API更快的推理速度,并附有详细的性能指标和说明。
查看缓存全文
缓存时间: 2026/09/26 06:50
调试了一段时间后,现在在Google A100上运行Qwen Flash的速度(预填充3000+ tokens/s,解码90+ tokens/s)已经超过了一般的商用API。对于偶尔的短期高并发使用,可以将其部署在Colab上。为了节省大家的时间,我已经将这个Qwen Flash在Colab上重建的方案上传到了GitHub。有需要的人可以自行获取。
https://github.com/architectds/collabosm
architectds/collabosm
来源: https://github.com/architectds/collabosm
collabosm
在 一台Colab A100-80GB 高内存实例 上运行 Qwen3.8-Flash-Next(125B-A6B MoE,混合门控DeltaNet + 全注意力机制,262144原生上下文),并提供一个OpenAI兼容的端点供客户端连接。
以下是在构建此仓库的实例上测量的性能数据:
| 指标 | 数值 |
|---|---|
| 预填充(30K提示) | 2,806 t/s |
预填充 -gcs 4096(30K提示) | 3,360 t/s |
预填充 -gcs 8192(30K提示) | 3,882 t/s |
解码,MTP ndt=4,30K上下文 | 97.4 t/s |
解码,MTP ndt=4,114K上下文 | 90.1 t/s |
| 解码,无MTP | 56 t/s |
| KV缓存 | 10,752 B/token(262K时2.63 GiB,500K时5.01 GiB) |
| 恢复一个完全被逐出的32K对话 | 0.58 秒,而非9.72秒的重新预填充(16.8倍提升) |
| 成本 | 7.52 CU/小时(A100 高内存)≈ $0.75/小时,≈26.6小时/200 CU |
ExLlamaV3是本仓库中唯一的引擎。详细测量来源和注意事项请参阅 docs/MEASURED.md,关于显卡实际能承载的并发会话数请参阅 docs/CONCURRENCY.md。
要求
- 能够分配 A100 的Colab套餐(本套件在拥有约200 CU/月的Pro套餐上开发)。
- 必须是高内存(HIGH_RAM)。标准的40GB A100 完全无法加载此模型——约63.6 GiB的权重必须常驻显存。
scripts/restore.py会明确请求此规格,并拒绝40GB的实例。 uv tool install google-colab-cli(如果需要fork/push,还需安装gh)。- 约110 GiB的Colab磁盘空间用于存放权重。
快速开始
bash scripts/up.sh # 恢复/创建实例,安装运行时,下载权重,启动服务
bash scripts/down.sh # 停止虚拟机。在按量计费套餐中,这是最重要的命令。
up.sh 在端点响应 /health 时会打印隧道URL。将你的客户端指向 /v1,并使用它打印的API密钥(密钥也位于虚拟机上的 /content/api-key.txt)。
有用的配置项,均通过环境变量传递:
SESSION=mybox CACHE_SIZE=524288 CPU_CACHE_GB=8 bash scripts/up.sh
| 变量 | 默认值 | 含义 |
|---|---|---|
SESSION | collabosm | 本地会话名称 |
CACHE_SIZE | 500224 | 所有任务的KV总令牌数(必须是256的倍数) |
CACHE_QUANT | 4 | KV位数(4 = Q4,允许 2-8) |
CPU_CACHE_GB | 32 | 固定内存二级KV页缓存(0 = 关闭)。按92K令牌/GB估算大小;不要将其视为免费内存——它会作为固定内存被完全分配(36.4 GiB n-gram表旁约32 + 24 = ~56 GB) |
RECURRENT_CACHE_GB | 24 | 用于Gated-DeltaNet检查点的主机RAM存储(约2048令牌间隔,每个116 MB) |
GCS | 8192 | 生成器块大小——这是我们发现的最大的预填充优化杠杆 |
NDT | 4 | MTP草稿深度 |
RUNTIME | wheel | wheel(预编译,无需编译)或 source |
TUNNEL_TOKEN | 未设置 | 命名隧道令牌:提供稳定主机名而非快速隧道。配合 PUBLIC_URL 用于状态中显示的URL |
这些默认值尚未一起加载过:docs/MEASURED.md 中经过验证的负载运行参数是 -gcs 4096 -ccs 16 -rcs 16。GET /v1/status 会报告运行中的服务器实际获得的参数。up.sh 在就绪时退出码为 0——表示API健康且发布了隧道URL——否则退出码为 1(上传/引导失败)、2-6(来自restore.py)、7(超时)、8(serve.sh失败)或 9(虚拟机上健康,但无隧道URL)。
此仓库包含什么
| 路径 | 角色 |
|—|—|—|
| scripts/restore.py | 会话恢复脚本。 从服务器真实状态重新连接孤立的虚拟机,或创建一个并实际请求高内存规格。在消耗任何资源前就拒绝/停止40GB实例。 |
| scripts/probe_gpu.py | 在虚拟机上运行;以单行JSON报告显存/RAM/计算能力/磁盘信息 |
| scripts/up.sh | 恢复 → 上传 → 引导 → 服务 → 等待健康 |
| scripts/down.sh | 停止虚拟机并报告仍在计费的部分 |
| scripts/bootstrap.sh | 运行时(固定版本wheel)+ 权重(来自HF的固定修订版),幂等 |
| scripts/serve.sh | 启动API + cloudflared隧道(在虚拟机上运行) |
| scripts/api_server.py | 极简OpenAI兼容服务器;一个长生命周期的生成器 |
| scripts/status.py | 一目了然的阶段/健康报告 |
| scripts/dev_stub.py | 使用存根引擎在回环地址上运行真正的Handler:在没有GPU和权重的情况下测试线协议格式 |
| scripts/check_surface.py | 通过HTTP驱动运行中的服务器并判断其表面特性(增量、终止事件、ID、序列号) |
| manifest.json | 所有固定修订版、大小和测量数量 |
| docs/MEASURED.md | 测量数据,附来源和注意事项 |
| docs/RUNBOOK.md | 常见陷阱、操作流程、成本防护措施 |
为什么运行时和模型来自不同地方
运行时是来自ExLlamaV3 GitHub发布版的固定预编译wheel——无需编译步骤,约22秒,绑定特定的 (python, torch, cuda) 组合,因此 bootstrap.sh 会探测镜像并选择匹配的资源。
权重来自Hugging Face的固定修订版(约100 GiB,匿名测量传输速度约380 MB/s,耗时4分40秒;当Xet路径停滞时,HF_HUB_DISABLE_XET=1 是文档记载的备选方案)。这里不需要Google Drive、Colab密钥或任何个人路径——仓库无法托管你的Drive,任何依赖Drive的东西都不可分享。
本仓库旨在帮你避开的四个陷阱
colab new --gpu A100是一场赌博。 它从不发送shape参数,因此你得到的是80GB高内存或40GB标准版。连续十一次未打补丁的尝试都得到了40GB版本。restore.py修改了URL构建器以发送shape=hm(google-colab-cli#47:枚举值存在且machineShape可被解析,但从未被发送)。- 你的本地会话记录是可丢弃的。 当运行时代理令牌过期时,CLI会报告“未找到活动会话”并删除其记录——而此时虚拟机仍在运行并仍在计费。我们一天内遇到过两次,其中一次在运行中途,
/content完全完好。restore.py通过list_assignments()重新连接,而非创建第二个虚拟机。 - 绝不要通过查找“80”来识别80GB显卡。 A100报告计算能力
sm_80,因此40GB实例在任何地方都会打印“80”。请比较vram_GiB。 - 新的生成器意味着冷缓存。
PageTable和CPU页缓存在Generator.__init__内部构建。每个请求使用新的生成器会静默禁用提示缓存和固定内存KV层——故障不可见,你只会悄悄地重新预填充所有内容。api_server.py持有一个生成器实例。
端到端验证
2026年9月25日,在一个账户上,除了本仓库中的脚本外无任何手动步骤:
scripts/restore.py重新连接了一个孤立的分配(在此之前Colab CLI当天第三次丢弃了本地记录,虚拟机存活且仍在计费),并确认了实例:79.3 GiB显存,167.1 GiB内存,计算能力8.0,Python 3.13.15。scripts/serve.sh在cache_size 500224(每流500K) 下加载了4.05 bpw打包模型,耗时259.5秒,并在显存使用 76,437 / 81,920 MiB 时达到/health200状态。- 通过公共隧道:
/v1/models在无密钥时返回 401,有密钥时返回200,一次真实的聊天补全在 3.1秒 内返回。 - 当天晚些时候,使用真实客户端重新验证了整个链路:Codex CLI 0.144.6,
wire_api = "responses",通过快速隧道连接到运行中的4.05 bpw打包模型——Reply with exactly: pong返回了pong,退出码0,8,536个提示令牌。
已知的明确缺口:
- 流式传输现在是增量式的。
collect()驱动Generator.enqueue()/iterate()并转发每个解码步骤,因此/v1/chat/completions和/v1/responses都在模型工作时发出文本。通过真实的Codex CLI客户端在本地验证:首个增量在0.01秒到达,共11个增量,存在终止response.completed事件,sequence_number单调递增。旧的阻塞路径作为_generate_blocking()保留,仅在构建版本的Job API不同时使用。 - 请求由锁序列化——一个生成器,一个缓存。“多流”今天意味着排队,而非并行。
max_batch_size > 1未经测试。 所有测量均运行num_slots = 1。- 会话分配是脚本化的;它尚未成为一个可配置的托管层。
无GPU测试
容易出错的是协议,而证明协议变更过去需要一次会话恢复加上四分钟的权重加载。两个脚本消除了这个成本:
python scripts/dev_stub.py --port 8099 --chunk 24 --delay 0.01 # 真实Handler,伪引擎
python scripts/check_surface.py --base http://127.0.0.1:8099/v1 # 13项检查,失败时退出码1
dev_stub.py 导入了正式的 Handler 并仅替换 _engine_fragments(),因此被测试的代码就是部署的代码。check_surface.py 断言了客户端实际依赖的内容:增量在结束前到达,发送终止事件,每个增量携带 item_id / output_index / content_index,sequence_number 单调递增,聊天和Responses视图对同一补全的结果逐字一致。
也可以指向真实客户端——这是了解流格式错误的最快方式:
# CODEX_HOME/config.toml
model = "qwen3.8-flash-next-exl3"
model_provider = "collabosm"
preferred_auth_method = "apikey"
[model_providers.collabosm]
name = "collabosm"
base_url = "http://127.0.0.1:8099/v1"
wire_api = "responses"
env_key = "COLLABOSM_TEST_KEY"
向 dev_stub.py 添加 --think 以测试推理项的生命周期。Codex CLI 0.144.6 接受了两种格式(codex exec --skip-git-repo-check 'Reply with exactly: pong')。
能承载多少会话
在此显卡上测量的承载能力(详情和假设见 docs/CONCURRENCY.md):
| 每流上下文 | 最大并发活动会话数 | 限制因素 |
|---|---|---|
| 500K | 1 | KV缓存 |
| 262K | 2 | KV缓存 |
| 131K | 5 | 两者共同限制 |
| 32K | 11 | 循环状态 |
| 16K | 14 | 循环状态 |
每个活动槽位在无任何KV缓存前就消耗约 546 MiB 的Gated-DeltaNet状态,因此当上下文变短时,并发度在约20个槽位达到饱和(16K:14槽位,8K:17槽位),8-14是实际可用范围。固定内存层不会提高这些数字——逐出仅影响未引用的页面,并且它不是活动流的交换设备——但它能让约147万令牌的空闲对话在不到一秒内恢复,而非重新预填充。docs/CONCURRENCY.md 解释了为什么内存层无法扩展活动容量。
许可证
我们的脚本:MIT(见 LICENSE)。
权重未包含在此仓库中,遵循其自身许可证——见 turboderp/Qwen3.8-Flash-Next-exl3 和 NOTICE。
ExLlamaV3 是 MIT 许可。
API接口
服务器是我们自己的(scripts/api_server.py)——ExLlamaV3不提供HTTP服务器。它支持两种OpenAI方言,且所有错误路径都返回JSON(HTML错误页面是bug,不是客户端问题):
| 端点 | 说明 |
|---|---|
POST /v1/chat/completions | choices[].message.content,finish_reason = stop/length,usage.prompt_tokens_details.cached_tokens;stream: true 返回SSE块 + [DONE](使用 stream_options.include_usage 时包含usage) |
GET /v1/status | 只读合约:启动参数、缓存大小、正常运行时间、视觉能力和图像输入策略 |
POST /v1/responses | Responses接口:status,output[].content[].text,output_text,usage.{input,output,total}_tokens;stream: true 发送以下完整生命周期 |
GET /v1/models | 唯一的模型ID,包含 created |
GET/health | 简单返回 ok,无需认证 |
| 其他任何 | JSON 404/405(从不返回HTML) |
接受的参数:max_tokens 和 max_completion_tokens,temperature,top_p,stop(字符串或列表),stream,stream_options.include_usage。
流式 /v1/responses 在每个事件上发送 sequence_number,顺序如下:
response.created -> response.in_progress -> (如果思考开启) output_item.added(reasoning) -> reasoning_text.delta -> reasoning_text.done -> output_item.done -> output_item.added(message) -> content_part.added -> output_text.delta xN -> output_text.done -> content_part.done -> output_item.done -> response.completed。
增量始终携带 item_id、output_index 和 content_index,终止事件中的ID就是流中宣布的ID——Codex会将增量绑定到已宣布的项,否则会拒绝该流。
文本在引擎解码同时被转发:服务器将一个 Job 入队并排空 Generator.iterate(),而非对完成的补全进行分块。如果某个构建版本的增量路径不产生任何内容,服务器会回退到阻塞生成器并记录 incremental path produced no text (new=... eos=...)——它从不返回空响应。
思考默认关闭,因为这是普通聊天客户端的预期。可通过 enable_thinking: true 或 reasoning_effort: xhigh|medium|low 开启(打包模型自己的模板接受两者);追踪信息在 message.reasoning_content 中返回,且永不泄露到 content 中。
客户端及使用真实WebUI
collabosm.py 是本地客户端。仅使用标准库,无需安装步骤:
python collabosm.py config --init --endpoint https://.trycloudflare.com/v1 --api-key <your-key>
python collabosm.py ui # http://127.0.0.1:8790 (页面 + 代理)
python collabosm.py chat "hello" # 同一路径,在终端
python collabosm.py update-ui # 从本仓库拉取 ui/ 并缓存
它只实现了两种方言,即Codex使用的两种:/v1/responses 和 /v1/chat/completions(加上 /v1/models,所有OpenAI兼容客户端都会首先探测它)。其他任何请求都返回JSON 404。
为什么使用本地代理而非直接将客户端指向端点
- 承载令牌留在此进程中,永不交给浏览器。
- 页面与代理同源,因此完全不存在CORS问题。
- UI可以独立于虚拟机进行更新。
这里故意没有 Access-Control-Allow-Origin: *:在注入承载令牌的代理上使用通配符,会让你访问的任何网页都能读取你GPU所说的内容。单凭这一点并不能阻止页面发送 text/plain 或表单POST——这些不需要预检请求——因此每个POST也必须是 application/json,来自此源(或非浏览器),并在 Host 中命名此代理,这也排除了DNS重绑定攻击。frontend/server.py 对 /control/* 应用相同规则,其中POST可能开始计费。服务器端UI不受影响(它们从自己的后端调用上游);如果浏览器托管的UI需要接入,请添加明确的源白名单,而非通配符。
两个命名窗口
客户端提供三个页面,启动器会打开两个重要的页面,以便一眼就能看出哪个是哪个:
python collabosm.py start # 如需要则启动客户端,然后打开两个窗口
python collabosm.py startup # 在每次登录时执行相同操作(启动文件夹快捷方式)
python collabosm.py startup --remove
| 页面 | 标题 | 描述 |
|---|---|---|
/ | collabosm | 带有两个入口的着陆页,适用于手动打开 |
/chat | collabosm — chat | 对话,流式,思考过程折叠 |
/status | collabosm — status | 端点状态、实时生成以及测量的预填充/解码/首token延迟 |
start 刻意容忍未配置或不可达的端点:客户端无论如何都会启动,以便状态页可以说明缺少什么。启动或停止A100仍使用 scripts/up.sh / scripts/down.sh;此客户端本身不消耗任何费用。
如果你更喜欢在Open WebUI中聊天,可以将聊天窗口指向别处并保留此仪表板:
python collabosm.py config --chat-url http://127.0.0.1:8080
指标合约
OpenAI协议没有用于预
相似文章
在16GB显存的RTX 4080上运行Qwen3.8-27B的提速指南
本指南说明如何配置llama.cpp的DFlash2推测解码,以在配备16GB显存的RTX 4080 GPU上实现Qwen3.8-27B模型最高1.72倍的推理速度提升。
@ngxson: Qwen3.6-27B 在 WebGPU 上 100% 运行。速度不是最快,但仍然不错
一位开发者演示在浏览器中完全通过 WebGPU 运行 Qwen3.6-27B AI 模型,尽管速度并非最优。
在4080与64GB DDR5上运行Qwen Flash Q4_K_M,约8tk/s,98304上下文。
作者演示了如何在4080 GPU和64GB DDR5上运行182B的Qwen模型,通过将ngrams转移到SSD,实现了比27B模型更快、更智能的性能,使大型模型推理在消费级硬件上变得可行。
Qwen3.8-Flash-Next 针对 Mac 优化版
本文详细介绍了在 Mac M1 Max 硬件上运行 Qwen3.8-Flash-Next AI 模型的自定义优化,包括 SSD 流、自定义量化和稀疏注意力机制,以提升性能。
Qwen3.8-Flash 在 RTX3090 + 64GB RAM 上运行(但你只需 12GB VRAM)
本文提供了在 RTX 3090 和 64GB RAM 上运行 Qwen3.8-Flash AI 模型的详细指南,讨论了性能指标、量化设置以及使用 llamacpp 等工具的部署步骤。