比华为官方 torch_npu 更快:我用 C++/Ascend 写了一个 7B 推理引擎
项目地址:
https://github.com/luogantt/LLM-inference-engine
最近我在做一个实验性项目:LLM-inference-engine。
它不是基于 PyTorch,也不是基于 transformers 推理栈,而是直接用 C++ 写推理核心,并针对 Ascend NPU 做了一条 direct.so推理路径。
这次在Ascend-super分支上,我把 DeepSeek-R1-Distill-Qwen-7B 的单 batch decode 跑到了一个很有意思的结果:
同一个 prompt,同样生成 128 tokens,我的 direct.so稳态速度已经超过华为官方 torch_npu baseline。
当前公开 tag:
ascend-super-36tok这意味着:在这个单 batch、低延迟 decode 算例里,一条手写 C++ / AscendCL / ACLNN direct runtime 路径,已经跑过了官方通用 PyTorch NPU 路径。下一步就是继续对标并挑战 vLLM-Ascend。
下载项目指定 tag
直接下载这次性能记录对应的 tag:
gitclone--branchascend-super-36tok--depth1https://github.com/luogantt/LLM-inference-engine.gitcdLLM-inference-engine如果你已经 clone 过项目:
cd~/LLM-inference-enginegitfetch origin--tagsgitcheckout ascend-super-36tok下载模型
模型使用:
deepseek-ai/DeepSeek-R1-Distill-Qwen-7B中国大陆网络建议使用 ModelScope:
pipinstall-Umodelscope python download_model.py\--sourcemodelscope\--modeldeepseek-ai/DeepSeek-R1-Distill-Qwen-7B\--local-dir ./deepseek-r1-7b如果你的 ModelScope 下载目录缺少 HuggingFace 风格的校验文件,可以跳过下载后的文件名检查:
python download_model.py\--sourcemodelscope\--modeldeepseek-ai/DeepSeek-R1-Distill-Qwen-7B\--local-dir ./deepseek-r1-7b\--skip-check也可以使用 HuggingFace Hub:
pipinstall-Uhuggingface_hub python download_model.py\--sourcehuggingface\--modeldeepseek-ai/DeepSeek-R1-Distill-Qwen-7B\--local-dir ./deepseek-r1-7b编译 Ascend direct .so
cd~/LLM-inference-enginemake-fMakefile.cuda_lib clean-libmake-fMakefile.cuda_lib lib-ascendASCEND_HOME=/usr/local/Ascend/cann-8.5.1测试模型和任务
模型:
DeepSeek-R1-Distill-Qwen-7Bprompt:
黑格尔的哲学思想可以概括为生成长度:
max_new_tokens = 128 max_seq = 8001. torch_npu baseline
先跑华为官方 torch_npu 路径:
cd~/LLM-inference-engineexportASCEND_VISIBLE_DEVICES=4python python_infer_ascend.py\--model./deepseek-r1-7b\--prompt"黑格尔的哲学思想可以概括为"\--max-new-tokens128\--max-seq800\--devicenpu:0\--dtypefloat16\2>&1|teetorch_npu_128.log实测结果:
generated_tokens=128 elapsed_s=3.696 tokens_per_s=34.627也就是:
torch_npu = 34.627 tok/s2. Ascend-super direct .so
然后跑我自己的 direct.so推理路径:
cd~/LLM-inference-engineexportASCEND_VISIBLE_DEVICES=4exportASCEND_DEVICE_ID=0exportASCEND_LOAD_WEIGHTS=allexportASCEND_WEIGHT_LOAD_LOG=0exportASCEND_HOST_RAW_CACHE=0exportASCEND_RUN_EMBED=1exportASCEND_DIRECT_DECODE=all_layers_refexportASCEND_REF_CACHE_WEIGHTS=1exportASCEND_REF_CACHE_LOG=0exportASCEND_REF_KV_CACHE=1exportASCEND_REF_U16_WEIGHTS=1exportASCEND_REF_FAST_DOT=1exportASCEND_REF_DOT4=0exportASCEND_REF_NEON_DOT=0exportASCEND_ATTN_BACKEND=cpuexportASCEND_QKV_BACKEND=aclnnexportASCEND_QKV_FALLBACK=0exportASCEND_QKV_LOG=0exportASCEND_ATTN_PROJ_BACKEND=aclnnexportASCEND_ATTN_PROJ_FALLBACK=0exportASCEND_ATTN_PROJ_LOG=0exportASCEND_MLP_BACKEND=aclnnexportASCEND_MLP_FALLBACK=0exportASCEND_MLP_LOG=0exportASCEND_LM_HEAD_BACKEND=aclnnexportASCEND_LM_HEAD_FALLBACK=0exportASCEND_LM_HEAD_LOG=0exportASCEND_ACLNN_CUBE_MATH_TYPE=0exportASCEND_REF_LINEAR_THREADS=16exportASCEND_REF_ATTN_LINEAR_THREADS=16exportASCEND_REF_ATTN_THREADS=16exportASCEND_REF_ATTN_THREAD_MIN_SEQ=32exportASCEND_REF_MLP_THREADS=24exportASCEND_REF_DOWN_THREADS=24exportASCEND_LM_HEAD_THREADS=16exportASCEND_REF_PROFILE_LAYERS=0exportASCEND_REF_PROFILE_TOKEN_LIMIT=0python python_infer.py\--model./deepseek-r1-7b\--lib./build/libllm_ascend.so\--prompt"黑格尔的哲学思想可以概括为"\--max-new-tokens128\--max-seq800\--tokenizer-backend tokenizers\--no-chat-template\2>&1|teeascend_super_128.log查看尾部 decode 速度:
grep"decode all_layers_ref finished"ascend_super_128.log|tail-20查看最后一个 token:
grep"decode all_layers_ref finished"ascend_super_128.log|tail-1其中一个稳定尾部结果是:
elapsed_ms=27.985649换算:
1000 / 27.985649 = 35.73 tok/s也就是:
Ascend-super direct .so ≈ 35.7 tok/s结果对比
torch_npu baseline : 34.627 tok/s Ascend-super direct .so : 35.73 tok/s提升比例:
(35.73 / 34.627 - 1) * 100% ≈ 3.2%这不是一个靠 batch 堆出来的吞吐数字,而是单 batch decode 的低延迟结果。
这也不是一个黑盒框架调参结果,而是把推理链路拆开之后,一步一步把 QKV、MLP、attention projection、lm_head 等路径迁到 AscendCL / ACLNN 后得到的结果。
为什么这个结果有意义
很多人做 Ascend 推理,第一反应是直接上 torch_npu,或者用 vLLM-Ascend 这样的上层推理框架。
但这个算例说明了一件事:
在单 batch decode 场景下,手写 C++ direct runtime 路径仍然有非常明确的优化空间,甚至可以跑过官方 torch_npu baseline。
当前Ascend-super路径里已经做了这些优化:
QKV: ACLNN BF16 O projection: ACLNN BF16 MLP: ACLNN BF16 LM Head: ACLNN BF16 KV Cache: enabled weight cache: enabled CPU attention: optimized fallback RoPE cache: enabled当前尾部瓶颈大概是:
all_layers ≈ 25.5 ms lm_head ≈ 2.2 ms decode ≈ 27.9 ms后面还有继续优化的空间,尤其是:
1. 把 lm_head argmax/top1 继续搬到 device 上 2. 写真正融合的 Ascend attention kernel 3. 减少同步点和 D2H/H2D 数据搬运 4. 进一步融合 decode 路径项目定位
这个项目不是为了包装一个黑盒框架,而是为了把大模型推理路径拆开:
embedding RMSNorm QKV RoPE attention MLP KV cache lm_head decode loop每一层都尽量透明,每一个耗时都能看到。
它适合:
1. 学习 LLM 推理底层实现 2. 研究 Ascend NPU 上的 decode 优化 3. 对比 torch_npu / vLLM-Ascend / 自定义 runtime 的性能边界 4. 做单 batch、低延迟推理实验一句话总结
在ascend-super-36tok这个 tag 上,LLM-inference-engine 已经用 direct.so路径,在 DeepSeek-R1-Distill-Qwen-7B 的 128-token decode 算例中,跑出了约35.7 tok/s的稳态速度,超过了同机 torch_npu baseline 的34.6 tok/s。
项目地址:
https://github.com/luogantt/LLM-inference-engine
欢迎 star、fork、复现 benchmark,也欢迎拿 vLLM-Ascend 的同 prompt、同 128 tokens、同单 batch 配置来正面对比。