Skip to content

PocketLLM 针对老硬件的功能规划

目标硬件: 2080Ti (SM75, 22GB, PCIe), Ascend 910A (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_steprun_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,910A 建议 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) - 910A 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/completionslogprobs 计数);流式请求拒绝,投机解码与 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 910A 有 32GB 显存: 可以跑 Qwen2-VL-7B 的 vision encoder

实现优先级: P2 (Phase 5-6)

设计要点: - 先支持 Qwen2-VL-7B(较小,老硬件能跑) - Vision encoder 可以在 CPU 或单独一张 GPU 上跑(与 LLM 分离) - 图像 token 作为额外的 prompt 序列输入 - 老硬件特化: - 2080Ti: 图像压缩到 256 tokens 以内(减少 prefill 开销) - 910A: 可以更激进,512-1024 tokens

收益预估: - 打开本地多模态应用场景(截图问答/文档理解)


二、老硬件针对性优化(特色功能)

2.1 CPU Offloading with Prefetch Pipeline

动机: - 2080Ti 只有 22GB: 跑 27B FP8 勉强够,70B 完全装不下 - Ascend 910A 32GB: 跑 70B FP4 也装不下 - PCIe/HBM 带宽: 2080Ti PCIe 3.0 x16 ~16 GB/s,910A 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_streamh2d_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) - 910A 跑 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 910A 没有 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 已内置

设计方案: - 在 cpp_engine 中增加 FA1 kernel 调用(通过 cuDNN 或 PyTorch C++ API) - 优先级: 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 910A 专项优化

现状: - ✅ 多后端架构已就绪 - ⚠️ 但 Ascend 后端尚未实现(只有 CUDA 后端)

为什么重要: - 国产化需求: 很多场景必须用国产芯片 - 910A 是老硬件: 与 2080Ti 同时代(2019),面临类似问题 - PocketLLM 的差异化: vLLM/SGLang 都不支持 Ascend

Ascend 910A 特点: - 32 AI Core (vs 910B 的 24) - 32MB L2 (vs 910B 的 192MB) ← 这是最大区别 - Cube freq 1000 MHz (vs 910B 的 1850 MHz) - No cube_vector_combine=split (1st gen 限制)

设计要点: - L2 cache blocking: - 910A 只有 32MB L2,权重/KV 分块必须更细 - MoE expert 需要按 8 个一组 stage(vs 910B 的 24 个) - Cube + Vector 串行流水: - 1st gen 的 Cube 和 Vector 不能并行,必须手动流水 - CANN 9.0 的 GQA kernel: - 参考 fastllm 的 Ascend GQA 实现 - 或者等 CANN 9.x 官方 FlashAttention

实现优先级: P2 (Phase 5-6,取决于硬件可用性)

Ascend 910A 收益: - 填补 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 910A Backend P2 ⭐⭐⭐⭐ 国产化

四、Phase 4-6 Roadmap 建议

Phase 4: 基础能力补齐(3-4 个月)

  1. Continuous Batching: 实现动态调度器,默认 max_num_seqs=4
  2. Prefix Caching: Block hash + 全局共享
  3. KV Quantization + Paged: 修复冲突,INT8 KV 默认开启
  4. OpenAI API: logprobs / n>1 / stop 序列(tools 仍缺失,见 1.3)
  5. 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 个月)

  1. Qwen-VL: 支持 Qwen2-VL-7B
  2. Tiny Draft Model: 通用 draft-verify 框架
  3. FlashAttention-1: Batch prefill 用 FA1
  4. Hybrid Quantization: Per-layer dtype 配置

验收标准: - Qwen-VL 图像问答可用 - 27B + 1.5B draft,decode 加速 ≥1.8× - Batch_size=4 的 prefill 激活显存 <2GB/rank

Phase 6: 多后端与高级功能(3-4 个月)

  1. Ascend 910A Backend: CANN 后端完整实现
  2. 结构化输出: JSON mode + FSM filtering
  3. LoRA Adapter: 动态加载和切换
  4. INT4 Tensor Core: SM75 MMQ kernel 移植

验收标准: - Ascend 910A 性能达到 vLLM CUDA 的 60-80% - JSON mode 可用,与 SGLang 对比 - LoRA 切换延迟 <100ms


五、与 vLLM/SGLang 的差异化定位

PocketLLM 的核心竞争力(针对老硬件)

  1. 极致量化: FP4/Q2 让 2080Ti 跑 70B
  2. CPU Offloading: vLLM/SGLang 都不支持——但 2026-09-15 的实测(见 2.1)表明, 在 2080Ti 上让 70B 装得下的是量化而不是逐层 offload:TP2/TP4 零 offload 即可, 而 TP1 的实测上限是 0.83 tok/s
  3. 多后端: Ascend 910A 是独家优势
  4. Qwen 推测解码: MTP/DSpark/DFlash2 比通用 draft model 更高效
  5. 单请求延迟优化: vLLM 为吞吐优化,PocketLLM 为个人/边缘场景优化

不追求的方向(让给 vLLM/SGLang):

  1. 企业级部署: Ray serving/KServe/multi-tenancy → vLLM 更成熟
  2. 广泛模型支持: Llama/Mistral/GPT-J → vLLM 覆盖更全
  3. 结构化输出性能极致: → SGLang 是专家
  4. 最新研究落地: → SGLang 更激进

目标用户

  • 个人开发者(2080Ti/3090/4090 在家跑大模型)
  • 边缘设备(Jetson/嵌入式 GPU)
  • 国产化场景(Ascend 910A)
  • 研究者(需要深度定制 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 910A(取决于硬件可用性和市场需求)

长期: - 与 vLLM/SGLang 错位竞争,聚焦老硬件和国产化 - 保持单请求延迟优势 - 打造 Qwen 系列的最佳推理引擎