PocketLLM 针对老硬件的功能规划¶
Repository scope note. This is a historical planning document, written when PocketLLM carried the whole multi-GPU stack. The repository has since been cut to single-card, edge-and-mobile scope: the C++ engine it plans against is archived in relic-engine, the multi-card runtimes and their scheduler moved to RelicLLM, and the native kernels to relic-core. Entries below that name the C++ engine, the Ascend backend, the
BatchScheduler, CPU offload or the Qwen model family therefore describe work that is no longer in this tree. It is kept as the planning record, not as a description of the current checkout.
目标硬件: 2080Ti (SM75, 22GB, PCIe), Ascend 910B (1st gen, 32GB HBM)
分析时间: 2026-09-11
当前状态: Phase 3 (paged KV 已默认开启)
一、基础能力补齐(必需项)¶
1.1 动态调度器与真正的 Continuous Batching¶
现状(2026-09-12 复核源码后更正):
- ✅ Slot 化基础设施已就绪(KV cache/position/prefix cache/snapshots 已 per-slot)
- ✅ Batching kernel 已实现
- ✅ 真批处理 forward 已就位:batch_decode_step → run_batched_decode
一次 forward 覆盖整批(qwen_engine.cpp:3829,3790),不是逐请求循环
- ✅ waiting 队列与准入控制已存在:BatchScheduler::admit_requests() +
基于 worst_case_blocks 的 block 预算(batch_scheduler.hpp:174,214,217)
- ✅ per-request 采样参数已按行生效;openai_server.hpp 默认 max_batch_size=8
- ⚠️ 并发吞吐从未实测——本节验收标准(4 并发 ≥ 3×)是否已达成属未知
- ❌ 与投机解码(MTP/DSpark/DFlash2)及量化 KV 互斥,两者都被显式拒绝
- ❌ 没有抢占与优先级(FCFS,block 分配后不回收)
因此本项的首要任务是实测而非实现:先跑 scripts/run_qwen_concurrent_tp4.py
拿到 serial/2/4/8 的数字,再决定剩余工作量。
为什么重要(老硬件视角): - 2080Ti PCIe 带宽受限: 22GB 显存装不下大 batch 的激活,但可以装 4-8 个请求的 KV cache - 提高 GPU 利用率: 单请求 decode 只有 ~45 tok/s,GPU 利用率已 96%,但 prefill 有大量空档 - 多租户场景: 个人部署的 2080Ti 往往服务多个本地客户端(IDE/浏览器插件/命令行)
实现优先级: P0 (Phase 4 核心任务)
设计要点:
- 借鉴 vLLM 的三队列架构(waiting/running/finished)
- 调度约束基于 KV cache 容量 + token 预算,不是固定 batch size
- Prefill 优先(短请求快速响应),decode 填充空闲 slot
- 初期不做 preemption(简化实现),队列满时直接拒绝新请求
- 老硬件特化: 动态调整 max_num_running_reqs(2080Ti 建议 4-8,910B 建议 8-16)
收益预估: - 吞吐量 2-4× (多请求并发) - 平均延迟下降(短请求不再排队等长请求)
1.2 Prefix Caching(跨请求 KV 共享)¶
现状: - ✅ Paged KV 已默认开启 - ✅ Block 池化已实现 - ❌ 没有 block hash 和跨请求共享机制 - ❌ Prefix cache 只在单请求内复用(session 内)
为什么重要(老硬件视角): - 节省 prefill 时间: 2080Ti prefill 1500-1800 tok/s,长 system prompt (2K tokens) 需要 ~1.1s - 多轮对话场景: 个人助手每轮都重复发整个历史,没有 prefix cache 每轮都重算 - Few-shot prompting: 相同 demonstrations 在多个请求间共享,只计算一次
实现优先级: P0 (Phase 4)
设计要点:
- 为每个 KV block 计算 hash(prompt tokens 的内容哈希)
- 在调度器中维护全局 hash -> block_id 映射
- 新请求 prefill 时先查表,命中的 block 直接复用
- LRU 淘汰策略(KV cache 满时优先淘汰未命中的 block)
- 老硬件特化:
- 2080Ti 22GB 显存紧张,prefix cache 占比不宜超过 30%(~6.6GB)
- 910B 32GB 可以更激进,50% 给 prefix cache(~16GB)
收益预估: - 多轮对话:首轮后的 prefill 时间降至 ~0(只计算新增部分) - 共享 system prompt 的批量推理:prefill 加速 2-10×(取决于前缀长度)
1.3 OpenAI API 语义完整性¶
现状:
- ✅ /v1/chat/completions 基础功能可用
- ✅ Streaming (SSE) 已支持
- ❌ 缺失 tools/function calling
- ✅ logprobs 已实现(2026-09-15):sampler 在 decode 时对整份词表做 log-softmax,逐位置返回
采样 token 自身的 logprob 与 top-N 候选(chat 用 top_logprobs,/v1/completions 用
logprobs 计数);流式请求拒绝,投机解码与 Ascend 后端声明为不支持
- ✅ multiple choices (n>1) 已实现(2026-09-15):native C++ server 为每个 choice 提交一个独立的
scheduler 请求,逐 choice 的 seed / grammar / KV,流式响应交错输出;上限 128
- ❌ 缺失 JSON mode / structured output
为什么重要(老硬件视角): - 生态兼容性: 个人部署的 2080Ti 往往对接现有工具链(LangChain/Cursor/Continue) - 无需改客户端代码: 直接替换 OpenAI endpoint,零成本切换
实现优先级: P1 (Phase 4-5)
设计要点:
- Tools/function calling: 在 Python 控制平面实现(调度器触发函数调用,返回 tool_calls)
- Logprobs: 在 C++ decode 时记录 top-k logits 已实现:不记录 top-k,而是对整份词表算
log-softmax(vocab_logsumexp_rows + 逐行采样 kernel),因此概率是真实归一化概率;代价是
每个 decode step 多一次全词表归约,只在请求要求时开启
- Multiple choices (n>1): 在调度器中为同一 prompt 创建 n 个独立 slot,并行采样
- JSON mode: 简化版可以用 post-filter(生成后校验),完整版需要 FSM token filtering
收益预估: - 生态兼容性大幅提升 - 可以对接 Cursor/Continue/Open WebUI 等主流客户端
1.4 多模态支持(Qwen-VL)¶
现状: - ❌ 当前完全不支持 - ✅ Qwen-VL 模型架构与 Qwen 文本模型类似(主要是多了 vision encoder)
为什么重要(老硬件视角): - 2080Ti 边缘场景: 本地 OCR/图像理解,不依赖云服务 - Ascend 910B 有 32GB 显存: 可以跑 Qwen2-VL-7B 的 vision encoder
实现优先级: P2 (Phase 5-6)
设计要点: - 先支持 Qwen2-VL-7B(较小,老硬件能跑) - Vision encoder 可以在 CPU 或单独一张 GPU 上跑(与 LLM 分离) - 图像 token 作为额外的 prompt 序列输入 - 老硬件特化: - 2080Ti: 图像压缩到 256 tokens 以内(减少 prefill 开销) - 910B: 可以更激进,512-1024 tokens
收益预估: - 打开本地多模态应用场景(截图问答/文档理解)
二、老硬件针对性优化(特色功能)¶
2.1 CPU Offloading with Prefetch Pipeline¶
动机: - 2080Ti 只有 22GB: 跑 27B FP8 勉强够,70B 完全装不下 - Ascend 910B 32GB: 跑 70B FP4 也装不下 - PCIe/HBM 带宽: 2080Ti PCIe 3.0 x16 ~16 GB/s,910B HBM2 ~1.2 TB/s(片上)
现有方案的问题: - vLLM/SGLang 都假设权重常驻 GPU,不支持 CPU offloading - llama.cpp 有 CPU offloading 但没有 prefetch,每层都同步等待
PocketLLM 优势: - ✅ 已有 expert CPU-GPU 混合路径(GLM/Qwen4-Exp) - ✅ H2D 拷贝已优化(pinned staging)
设计方案:
- 权重分层:
- 前 N 层常驻 GPU(prefill/decode 都快)
- 后 M 层放 CPU,按需 H2D
- Prefetch 流水线:
- 第 i 层计算时,异步启动第 i+1 层权重 H2D
- 用两个 CUDA stream:compute_stream 和 h2d_stream
- 计算和拷贝并行,hide H2D latency
- 自适应边界:
- 根据显存占用动态调整 N(多少层常驻 GPU)
- Prefill 时可以多放几层(激活小),decode 时少放(batch 大)
实现优先级: P1 (Phase 4-5)
老硬件收益: - 2080Ti 跑 70B FP4: 前 16 层 GPU(~10GB)+ 后 64 层 CPU offload - Prefill: ~200 tok/s(受 H2D 带宽限制) - Decode: ~5 tok/s(每层 ~200ms,其中 H2D ~100ms + 计算 ~100ms) - 910B 跑 70B Q2: 前 24 层 GPU(~15GB)+ 后 56 层 CPU - HBM2 带宽更高,decode ~8-10 tok/s
现状(2026-09-15 实测,PR #253):
上面两段收益预估是规划值,没有一条在本机量过。完整测量与复现命令见 CPU offload 与 prefetch 的实测天花板, 逐条对照如下:
- ✅ prefill 侧的算术成立:TP1 下 512 行 chunk 搬 12.28 GiB,完美流水时 439 tok/s, 与上面「~200 tok/s(受 H2D 带宽限制)」同一量级。
- ❌ decode ≥ 5 tok/s 被带宽而不是实现质量否决:5 tok/s 意味着每 token 200 ms, 也就是实测 10.53 GiB/s 下 2.11 GiB 的可搬运量。一个 70B FP4 层是 0.42 GiB, 所以最多只放得下 4 层;上面「前 16 层 GPU + 后 64 层 CPU」每 token 要搬 27.1 GiB, 实测 0.39 tok/s,差 13×。本机 checkpoint 上实测的 offload 上限是 0.025–0.29 tok/s (Flash-Next TP4)与 0.83 tok/s(70B FP4 TP1)。
- ❌ prefetch 隐藏 > 80% 在原理上不可达:可隐藏比例的上限就是
compute / copy。 GLM-5.2 MoE block decode 实测 1.18 ms 计算对 540 ms 拷贝(0.09%),70B dense 层是 3.58 ms 对 39.8 ms(6.67%);全部测量里最好的一档是 512 行 prefill 的 28.04%, 而那正是本节没有针对性优化的阶段。要隐藏 80% 的传输,需要compute >= 0.8 × copy。 - ⚠️ 前提只部分成立:70B FP4 在 TP2(16.93 GiB/rank)和 TP4(8.47 GiB/rank) 下零 offload 即可放下,所以只有 TP1 真的需要本方案。本机没有 70B FP4 checkpoint, 这一条是公开几何数据而不是实测。
- ⚠️ 权重必须常驻主机内存:本机所有 checkpoint 挂载点都是机械盘
(冷读 0.096 / 0.194 / 0.209 GiB/s,对 pinned H2D 实测平台 10.53 GiB/s),
不能依赖 mmap 缺页换入——
/mnt/data2在规划时按 SSD 对待,实际不是。 - ❌ 本方案尚未实现:全仓库 grep
gpu_layers|gpu-layers|n_gpu_layers只匹配到上面那个测量页和它的脚本,两者都只是在说明这个 flag 没有实现。
结论:按各 checkpoint 的真实几何与实测带宽算出的 offload ceiling 是 0.025–0.29 tok/s
(跨 checkpoint),低于仓库已经交付的 expert 粒度路径在本机的实测值——GLM-5.2 走
expert staging 的 decode 是 0.54–0.66 tok/s(GLM-5.2 模型页)。
而唯一真正受益的 70B FP4 TP1 配置在本机没有 checkpoint,无法端到端验证。
是否仍然实现 --gpu-layers,由这条测量结论决定(见 #156)。
2.2 Aggressive Quantization(FP4/Q2/Q3)¶
现状: - ✅ PocketLLM 已支持 FP4/NVFP4/GGUF Q2/Q3 - ✅ Token parity 已验证 - ⚠️ 但在 SM75 上 FP4 慢于 FP8(0.45-0.65×)
为什么仍然重要(老硬件视角): - 显存容量 > 速度: 22GB 的 2080Ti 跑 70B 模型,只能靠 FP4/Q2 - Ascend 910B 没有 FP8: CANN 原生不支持 FP8,FP4 是最接近的选项
设计改进: - Hybrid quantization: - Prefill 瓶颈层用 FP8/INT8(速度优先) - Decode 瓶颈层用 FP4/Q2(显存优先) - 动态切换(根据 batch size) - Per-layer dtype 配置: - Attention 用 FP8(compute-bound) - MLP 用 FP4(memory-bound) - INT4 Tensor Core on SM75: - 研究 llama.cpp 的 MMQ kernel(已证明在 SM75 上比 DP4A 快) - 移植到 PocketLLM Q2/Q4 路径
实现优先级: P1 (Phase 4-5)
老硬件收益: - 2080Ti 跑 70B FP4: 显存占用 ~18GB(vs FP8 的 36GB 装不下) - 如果 INT4 Tensor Core 实现成功,FP4/Q2 速度可能接近 FP8 的 0.8-1.0×
2.3 FlashAttention Fallback for SM75¶
现状: - ✅ PocketLLM 已有 exact GQA kernel(用 m16n8k8 tensor core) - ✅ 32K prefill 已破 1K tok/s - ⚠️ 但没有 memory-efficient attention(O(N) 显存)
为什么重要(老硬件视角): - 2080Ti 22GB 显存紧: 长上下文 prefill 的激活占用巨大 - 65K context, TP4: 激活 ~2GB/rank(batch_size=1 已经这样) - 如果 batch_size=4,激活 ~8GB/rank,挤压 KV cache 空间 - SM75 不支持 FlashAttention-2: FA2 需要 SM80+ - 但可以用 FlashAttention-1: FA1 支持 SM75,PyTorch 2.x 已内置
设计方案: - 在 C++ engine 中增加 FA1 kernel 调用(通过 cuDNN 或 PyTorch C++ API)——该 engine 现已归档到 relic-engine - 优先级: 1. Prefill batch_size >= 2 时强制用 FA1(节省激活显存) 2. 单请求长上下文(>32K)时可选 FA1(减少 HBM 读写) - Decode 仍用现有 split kernel(FA1 对 decode 无优势)
实现优先级: P2 (Phase 5)
老硬件收益: - Prefill 激活显存降至 O(sqrt(N))(FA1 的复杂度) - 2080Ti batch_size=4 时,65K prefill 激活从 ~8GB 降至 ~1.5GB - 为 continuous batching 腾出显存空间
2.4 Speculative Decoding with Tiny Draft Models¶
现状: - ✅ PocketLLM 已有 Qwen MTP/DSpark/DFlash2 - ❌ 但这些都是 Qwen 原生方法,不支持通用 draft model
为什么重要(老硬件视角): - 2080Ti 的典型用例: 跑一个 27B target + 一个 1.5B draft - 27B FP8 ~14GB,1.5B FP16 ~3GB,总共 ~17GB < 22GB - Decode 加速: draft model 可以在剩余显存/CPU 上跑,target 只验证
设计方案: - Draft model 放 CPU: - 1.5B 模型 decode ~20 tok/s(CPU) - 生成 k=4 个 draft 需要 ~200ms - Target model verify 4 个 token 只需 ~100ms(比单独 decode 4 次快 4×) - Hybrid placement: - 2080Ti 剩余显存 >5GB: draft 放 GPU - 显存紧张: draft 放 CPU,target 独占 GPU - Tree attention for verify: - Verify 阶段用 tree attention 并行验证多个 draft - 借鉴 vLLM 的 batched verify 实现
实现优先级: P2 (Phase 5-6)
老硬件收益: - 2080Ti 27B decode: 45 tok/s → ~80-100 tok/s(1.8-2.2×) - CPU draft 的延迟可以被 target verify 隐藏
2.5 KV Cache Quantization 默认开启¶
现状: - ✅ FP8 KV / TurboQuant K8V4 / INT8 per-token-head 都已实现 - ✅ Token parity 已验证 - ⚠️ 但 paged KV 只支持 FP16(这是个矛盾)
为什么重要(老硬件视角): - KV cache 是长上下文的显存瓶颈: - 65K context, FP16 KV: ~2GB/rank (TP4) - 65K context, INT8 KV: ~1GB/rank(省 50%) - 65K context, K8V4: ~0.76GB/rank(省 62%) - 2080Ti 跑 4 个并发请求: - FP16 KV: 4 × 2GB = 8GB(太大) - INT8 KV: 4 × 1GB = 4GB(可接受)
设计改进: - 修复 paged KV + quantized KV 的冲突: - 当前 paged KV 假设每个 block 是连续的 FP16 数组 - 量化 KV 需要 scale/zero-point,不能简单分块 - 方案 1: Per-block quantization - 每个 block(16 tokens)有独立的 scale/zp - Block metadata 增大,但仍可接受 - 方案 2: 放弃 paged,用 contiguous + quantized - 回到 contiguous KV arena,但默认量化 - 对老硬件来说,省显存 > paged 的灵活性
实现优先级: P1 (Phase 4-5)
老硬件收益: - 2080Ti continuous batching (4 并发): KV 显存从 8GB 降至 4GB - 可以支持更长的 context 或更多并发
2.6 Ascend 910B 专项优化¶
现状: - ✅ 多后端架构已就绪 - ⚠️ 但 Ascend 后端尚未实现(只有 CUDA 后端)
为什么重要: - 国产化需求: 很多场景必须用国产芯片 - 910B 是老硬件: 与 2080Ti 同时代(2019),面临类似问题 - PocketLLM 的差异化: vLLM/SGLang 都不支持 Ascend
Ascend 910B 特点(第一代 Short_SoC_version=Ascend910;本机是 Ascend910B.ini profile,
30 AI Core / 900 MHz;同一代的 Ascend910A.ini 是 32 Core / 1000 MHz):
- 30-32 AI Core(第二代 Ascend910B1-B4 为 24-20)
- 32MB L2(第二代 192MB/96MB)← 这是最大区别
- Cube freq 900-1100 MHz(第二代 1500-1850 MHz)
- No cube_vector_combine=split (1st gen 限制)
设计要点:
- L2 cache blocking:
- 910B 只有 32MB L2,权重/KV 分块必须更细
- MoE expert 需要按 8 个一组 stage(第二代 192MB L2 下为 24 个)
- Cube + Vector 串行流水:
- 1st gen 的 Cube 和 Vector 不能并行,必须手动流水
- CANN 9.0 的 GQA kernel:
- 参考 fastllm 的 Ascend GQA 实现
- 或者等 CANN 9.x 官方 FlashAttention
实现优先级: P2 (Phase 5-6,取决于硬件可用性)
Ascend 910B 收益: - 填补 vLLM/SGLang 的空白 - 国产化场景的唯一高性能选择
三、功能优先级矩阵¶
| 功能 | 优先级 | 实现难度 | 老硬件收益 | 备注 |
|---|---|---|---|---|
| Continuous Batching | P0 | 中 | ⭐⭐⭐⭐⭐ | 吞吐量 2-4×,必需 |
| Prefix Caching | P0 | 中 | ⭐⭐⭐⭐⭐ | 多轮对话必需 |
| KV Quant + Paged | P1 | 高 | ⭐⭐⭐⭐⭐ | 显存省 50-62% |
| CPU Offload + Prefetch | P1 | 高 | ⭐⭐ | 70B 可跑——但 TP2/TP4 零 offload 即可,TP1 上限实测 0.83 tok/s(见 2.1) |
| OpenAI API 完整 | P1 | 低-中 | ⭐⭐⭐ | 生态兼容 |
| Hybrid Quantization | P1 | 中 | ⭐⭐⭐⭐ | 速度 vs 显存平衡 |
| FlashAttention-1 (SM75) | P2 | 中 | ⭐⭐⭐ | Batch prefill |
| Tiny Draft Model | P2 | 中-高 | ⭐⭐⭐ | Decode 1.8-2.2× |
| Qwen-VL | P2 | 高 | ⭐⭐⭐ | 多模态场景 |
| Ascend 910B Backend | P2 | 高 | ⭐⭐⭐⭐ | 国产化 |
四、Phase 4-6 Roadmap 建议¶
Phase 4: 基础能力补齐(3-4 个月)¶
- ✅ Continuous Batching: 实现动态调度器,默认
max_num_seqs=4 - ✅ Prefix Caching: Block hash + 全局共享
- ✅ KV Quantization + Paged: 修复冲突,INT8 KV 默认开启
- ✅ OpenAI API: logprobs / n>1 / stop 序列(tools 仍缺失,见 1.3)
- ❌ CPU Offload: 前 N 层 GPU + 后 M 层 CPU + prefetch 流水线——尚未实现。 这一行原先标 ✅ 属于规划标记;2026-09-15 的实测否证了它的 decode 与 prefetch 目标(见 2.1),是否实现待定
验收标准: - 4 并发请求下,吞吐量 ≥ 单请求的 3× - 多轮对话第 2 轮起,prefill 时间 <100ms(前缀全命中) - 70B FP4 在 2080Ti 上可跑,decode ≥5 tok/s——前半句成立但不需要本方案(TP2/TP4 零 offload 即可放下),后半句被实测否证(TP1 上限 0.83 tok/s,见 2.1)
Phase 5: 生态与性能(2-3 个月)¶
- ✅ Qwen-VL: 支持 Qwen2-VL-7B
- ✅ Tiny Draft Model: 通用 draft-verify 框架
- ✅ FlashAttention-1: Batch prefill 用 FA1
- ✅ Hybrid Quantization: Per-layer dtype 配置
验收标准: - Qwen-VL 图像问答可用 - 27B + 1.5B draft,decode 加速 ≥1.8× - Batch_size=4 的 prefill 激活显存 <2GB/rank
Phase 6: 多后端与高级功能(3-4 个月)¶
- ✅ Ascend 910B Backend: CANN 后端完整实现
- ✅ 结构化输出: JSON mode + FSM filtering
- ✅ LoRA Adapter: 动态加载和切换
- ✅ INT4 Tensor Core: SM75 MMQ kernel 移植
验收标准: - Ascend 910B 性能达到 vLLM CUDA 的 60-80% - JSON mode 可用,与 SGLang 对比 - LoRA 切换延迟 <100ms
五、与 vLLM/SGLang 的差异化定位¶
PocketLLM 的核心竞争力(针对老硬件):
- 极致量化: FP4/Q2 让 2080Ti 跑 70B
- 2026-09-25 的 Ternary-Bonsai-2-27B 是这条更强的例子:1.75 bit 的 GGUF
PTQ1_0, 单卡 2080 Ti 上整个 27B 走 OpenAI 接口,权重 5.53 GiB,prefill 636 tok/s 与 上游 llama.cpp 的 642.5 tok/s 持平。见 served runtime - CPU Offloading: vLLM/SGLang 都不支持——但 2026-09-15 的实测(见 2.1)表明, 在 2080Ti 上让 70B 装得下的是量化而不是逐层 offload:TP2/TP4 零 offload 即可, 而 TP1 的实测上限是 0.83 tok/s
- 多后端: Ascend 910B 是独家优势
- Qwen 推测解码: MTP/DSpark/DFlash2 比通用 draft model 更高效
- 单请求延迟优化: vLLM 为吞吐优化,PocketLLM 为个人/边缘场景优化
不追求的方向(让给 vLLM/SGLang):
- 企业级部署: Ray serving/KServe/multi-tenancy → vLLM 更成熟
- 广泛模型支持: Llama/Mistral/GPT-J → vLLM 覆盖更全
- 结构化输出性能极致: → SGLang 是专家
- 最新研究落地: → SGLang 更激进
目标用户:
- 个人开发者(2080Ti/3090/4090 在家跑大模型)
- 边缘设备(Jetson/嵌入式 GPU)
- 国产化场景(Ascend 910B)
- 研究者(需要深度定制 kernel)
六、总结¶
必做(P0-P1): - Continuous Batching + Prefix Caching(吞吐量和多轮对话) - KV Quantization + Paged(显存优化) - CPU Offloading(让 70B 在 TP1 可跑;TP2/TP4 不需要,见 2.1) - OpenAI API 完整(生态兼容)
选做(P2): - Qwen-VL / Tiny Draft / FlashAttention-1(锦上添花) - Ascend 910B(取决于硬件可用性和市场需求)
长期: - 与 vLLM/SGLang 错位竞争,聚焦老硬件和国产化 - 保持单请求延迟优势 - 打造 Qwen 系列的最佳推理引擎