📌 本文重點
- NInfer + A3B 讓 35B 長上下文在單卡可行
- C++/CUDA 專門優化長序列 decode 提升 tok/s
- 適合單卡、長上下文、低併發高價值請求場景
在做長上下文推理服務時,最大的痛點通常是:單卡吞吐上不去、65K token 類型的長序列一跑就卡死整張 GPU。NInfer + Qwen3.6-35B-A3B 這套組合的價值,就在於:在單張 RTX 5090 上,65,536 token decode 仍能穩定 542 tok/s,讓「高吞吐長上下文 API」在單卡環境變成可行選項,而不是只能堆多卡集群。
💡 關鍵: 在單張 RTX 5090 上達到約 542 tok/s 的 65K 長序列 decode,意味著 35B 長上下文模型不再一定需要多卡集群即可實用化
重點說明:NInfer 能快的關鍵
1. A3B 量化與權重重排:把算力真正用在 GEMM 上
Qwen3.6-35B-A3B 使用的是針對 NInfer 特化的 A3B 量化格式:
- 3-bit activation + 8-bit/混合權重布局(實作細節在 NInfer repo),搭配自訂權重重排
- 權重在轉換階段就依據 GPU memory coalescing 規則重新排列,確保 warp 讀取連續、避免 strided access
- 實際效果:在 35B 這種級別模型上,VRAM 需求從完整 FP16 大幅下降到單卡可容納,同時減少 global memory 帶寬壓力
對你的專案意味著:
- 如果你卡在「35B 單卡裝不下或 decode 隨著 context 變長越來越慢」,A3B + 重排可以把 VRAM 壓到可部署範圍,同時保持高吞吐
- 相比一般 INT4/FP8 方案,因為權重 layout 為 NInfer kernel 量身打造,訪存效率更可預期,不用自己調一堆 tile 參數
💡 關鍵: A3B 量化加上專門的權重重排,核心價值是在不犧牲太多精度下,把 35B 模型壓進單卡 VRAM,並穩定撐起長上下文推理
2. C++/CUDA 層的 kernel fusion 與 KV cache 策略
NInfer 沒有走通用框架路線,而是針對 Qwen3.6-35B decode path 完全手工展開與 fusion:
- Kernel fusion:將 RMSNorm、linear、bias、activation 等步驟在 CUDA kernel 層面合併,減少 kernel launch overhead 與中間結果回寫 global memory
- 分段 KV cache(segmented KV):KV cache 依 context 長度切段管理,而不是單一巨大 buffer
- 長序列時只把當前段載入 SM,減少無效訪存
- 用 ring buffer / sliding window 思路,在 65K token 上仍維持穩定延遲
- 長序列 decode 優化:
- decode kernel直接針對「單 request、長序列」做 tuning,而非多併發
blockDim.x/gridDim.x固定在實測最優配置,犧牲部分通用性換極限吞吐
對開發者的直接好處:
- 如果你的場景是 少量高價值請求 + 極長上下文(RAG, 多輪會話, code review),NInfer 可以在單卡提供接近「專用推理盒」的效能
- 不用自己啃 CUDA kernel,只要用它給好的 Qwen3.6-35B-A3B 權重與啟動方式,就能拿到接近 Reddit 文中 542 tok/s 的表現(硬體足夠時)
3. 與 vLLM / TensorRT-LLM 的差異與適用場景
vLLM / TensorRT-LLM:
- 優點:多模型、多硬體、多併發排程,KV cache 管理通用、與 Python 生態整合好
- 缺點:單模型、單卡、長序列極致 tuning 受限於通用抽象(scheduler、通用 kernel)
NInfer:
- 優點:
- 針對 Qwen3.6-27B/35B 深度優化,長序列 decode 有明顯優勢
- C++ 單 binary,方便嵌入 C++ backend 或自訂 server
- 缺點:
- 模型種類有限,目前專注 Qwen3.6 特定 checkpoint
- 生態與工具較少,要自己接 gRPC/HTTP
什麼時候真的值得換堆疊?
- 你的主服務 pattern 是:單卡 + 長上下文 + 單/低併發 + 想把 tok/s 壓到極限
- 願意為一個主模型專門維護一套 C++ 推理 binary,而不是通用 LLM 平台
如果你是多模型、多 tenant API、需要動態換模型,那就維持 vLLM/TensorRT-LLM;如果只有一個主力 35B 長上下文模型要跑滿一張高階 GPU,NInfer 值得考慮。
實作範例:單卡部署 NInfer + Qwen3.6-35B-A3B
1. 環境準備與模型下載
硬體參考:
- RTX 5090:目標是 Reddit 實測 542 tok/s 等級
- 4090 / A100 也可,但 tok/s 與可用 context 會較低,細節後面說
CUDA / driver:
- 建議使用 CUDA 12.x 並搭配對應的最新 NVIDIA driver
- 確保
nvcc --version與nvidia-smi顯示版本相容
# 取得 NInfer 原始碼
git clone https://github.com/Neroued/ninfer.git
cd ninfer
# 建置(示例,實際依 repo 說明調整)
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)
# 模型權重(在 Hugging Face 上)
# 例如:Qwen3.6-35B-A3B 已轉好的 ninfer artifact
huggingface-cli download Neroued/qwen3_6_35b_a3b --local-dir ./models/qwen35_a3b
如果只拿到原始 Qwen3.6-35B 權重,通常要執行 repo 提供的 權重轉換工具(例如 convert_qwen36_to_a3b 類型的 binary),把 FP16/FP32 權重轉成 A3B 格式並依 NInfer 的 layout 重排:
./tools/convert_qwen36_to_a3b \
--src /path/to/qwen3.6-35b-fp16 \
--dst ./models/qwen35_a3b \
--quant a3b \
--reorder-layout
關鍵是 --quant a3b 與 --reorder-layout 這類選項,一定要開啟才能達到官方的吞吐水準。
💡 關鍵: 權重轉換時開啟正確的量化與重排選項,是從原始 Qwen 權重複現 NInfer 官方性能的必要步驟
2. 最小推理程式碼(C++)
以下是簡化版推理程式碼示例,展示如何啟動 A3B 量化模型並執行單次長序列 decode:
#include "ninfer/ninfer.h" // 假設 NInfer 封裝 header
int main() {
// 1. 初始化引擎與裝置設定
ninfer::EngineConfig cfg;
cfg.model_path = "./models/qwen35_a3b";
cfg.device_id = 0;
cfg.enable_a3b = true; // **啟用 A3B 量化**
cfg.max_context_len = 65536; // **長上下文設定**
auto engine = ninfer::Engine::Create(cfg);
// 2. 準備輸入(這裡假設已有 tokenizer)
std::string prompt = "You are a helpful assistant...";
std::vector<int32_t> input_ids = tokenize(prompt); // 自行實作
ninfer::DecodeParams params;
params.max_new_tokens = 65536; // decode 長度
params.temperature = 0.7f;
params.top_p = 0.9f;
// 3. 單 request 推理
auto result = engine->Decode(input_ids, params);
std::cout << detokenize(result.output_ids) << std::endl;
std::cerr << "tok/s: " << result.tokens_per_sec << std::endl; // **實測吞吐**
return 0;
}
實務上你會:
- 用自己的 tokenizer / pre-processor 替換示例中的
tokenize/detokenize - 把
Engine包成 service 層,避免每次 request 重新載入權重
3. 接上 gRPC / HTTP 與監控
NInfer 本身是 C++ library,你可以:
- 用 cpp-httplib / Crow / Pistache 做 HTTP server
- 用 gRPC C++ 做 RPC 層,暴露
/Generate之類的 API
簡化 HTTP 伺服器示例:
#include "httplib.h"
#include "ninfer/ninfer.h"
int main() {
ninfer::EngineConfig cfg = ...; // 同上
auto engine = ninfer::Engine::Create(cfg);
httplib::Server svr;
svr.Post("/generate", [&](const httplib::Request &req, httplib::Response &res) {
auto json = nlohmann::json::parse(req.body);
std::string prompt = json["prompt"];
int max_tokens = json.value("max_tokens", 2048);
std::vector<int32_t> input_ids = tokenize(prompt);
ninfer::DecodeParams params;
params.max_new_tokens = max_tokens;
auto result = engine->Decode(input_ids, params);
nlohmann::json out;
out["text"] = detokenize(result.output_ids);
out["tok_s"] = result.tokens_per_sec;
out["latency_ms"] = result.latency_ms;
res.set_content(out.dump(), "application/json");
});
svr.listen("0.0.0.0", 8080);
}
監控方面:
- 在 wrapper 層計算 qps、平均/95th latency、tokens/sec,用 Prometheus client C++ export
- 定期拉
nvidia-smi --query-gpu=utilization.gpu,memory.used或使用 NVML API,收集 GPU utilization / memory 指標
建議與注意事項:踩坑指北
1. VRAM 規劃:65K token 與 batch size 的取捨
- Qwen3.6-35B-A3B 在 5090 上跑 單 request 65K token 是合理目標,但同時放大 batch size 就會立刻撞 VRAM
- 建議配置:
- 以 長上下文優先時,batch size 控制在 1–2,避免 KV cache 乘上 batch 維度
- 想提高併發:將
max_context_len降到 16K–32K,或改用 Qwen3.6-27B 變種
實務做法:在測試環境跑一個 VRAM profiling:
# 監控長序列 decode 中的 VRAM
watch -n 1 "nvidia-smi --query-gpu=memory.used --format=csv,noheader"
觀察在不同 max_new_tokens / batch 組合下的峰值 VRAM,用來決定 service 預設值。
2. GPU 驅動 / CUDA 版本相容性
- 使用 過舊的 driver 或 CUDA 會直接造成 kernel 無法載入、或效能大幅下降(無法使用新指令集)
- 建議:跟隨 NInfer repo 中的 tested CUDA version,不要自行回退到 11.x
- 若要在 A100 上跑,確認你有對應的 data center driver;consumer driver 在 data center 卡上常有意料外問題
3. 不同 GPU 的性能預期
大致可以這樣預估(以長序列單 request 為主):
- RTX 5090:
- 目標:~500 tok/s @ 65K decode(與 Reddit 實測同量級)
- 瓶頸:主要是 memory 帶寋與 SM 排程,已由 NInfer kernel 盡量打滿
- RTX 4090:
- 預估:250–350 tok/s @ 65K decode,視具體 OC/散熱而定
- 瓶頸:VRAM 較小,可能需要調低 context 或關閉部分優化
- A100 80G:
- 預估:350–450 tok/s,但優化方向不同(更多重視多併發 vs 單 request)
- 若你已在 TensorRT-LLM 上有穩定 pipeline,要評估是否真的需要切到 NInfer 的專用堆疊
4. 何時不要用 NInfer
- 需要支援多種模型(Llama, Mistral, 自訓模型)且頻繁切換:用 vLLM / TGI / TensorRT-LLM 更實際
- 主要場景是 高併發短上下文聊天 API:通用框架的排程優化會比 NInfer 的單 request 長序列優化更有價值
5. 維運架構建議:高吞吐長上下文推理 API
一個可維運的典型架構可以是:
- Layer 1:API Gateway
- 負責認證、流量控制、路徑切分(例如
/chatvs/long-context) - Layer 2:NInfer Service(C++ binary)
- 每張 GPU 一個 service process,固定跑 Qwen3.6-35B-A3B
- 使用 gRPC / HTTP 暴露
GenerateLongContextAPI,限制最大 context / tokens - Layer 3:監控與自動化
- Prometheus 收集:
qps,latency_ms,tokens_per_sec,gpu_util,gpu_mem_used - Alert 針對:latency 機率分佈偏移、tokens/sec 突然下降(可能是 driver/CUDA 問題)
對團隊來說:
- 把 NInfer 看成一個專用長上下文推理引擎,只做一件事(跑滿那張 GPU)
- 其他模型與場景繼續走既有的 Python/vLLM/TensorRT-LLM 堆疊,減少大規模遷移風險
總結來說,Qwen3.6-35B-A3B + NInfer 提供了一條實務友善的路:在單卡上達成 35B 級別模型的長上下文高吞吐推理,而不必一開始就堆多卡集群。只要你能接受專用 C++ binary、模型種類受限,這套方案可以非常直接地改善「長上下文慢到不可用」的痛點。
🚀 你現在可以做的事
- 到 GitHub 搜尋並
git cloneNeroued/ninfer,依說明完成編譯與基本測試- 到 Hugging Face 搜尋
Qwen3.6-35B-A3B或相關 artifact,實際下載並執行權重轉換- 在你的現有服務中加入一個長上下文專用路由,接上 NInfer C++ service 做 tok/s 與延遲對比測試

