【技术问题】如何使用NPU加速推理(教程、实例、可行性)

陳樂696 2026-01-20 13:46:03

 

  • 主机操作系统 (Host OS): Windows 11或Ubuntu 22.04
  • 目标设备及操作系统 (Target Device & OS): Redmi K90 Pro Max (搭载第五代骁龙8至尊版),16GB运行内存
  • 模型框架与来源 (Model Framework): Qualcomm AI Hub

我们的demo app里的模型是tflite格式。在gradle里写明模型路径进行最基本调用,没有写任何关于硬件加速的代码。现在主要的问题是速度非常慢。我们现在已经能够把模型用qnn-onnx-converter把模型编译成bin和dlc文件。我们想了解这些dlc和bin格式的模型文件如何正确的在安卓app里使用,使用用途包含如何调用模型,如何调用npu,如何硬件加速,如何并行计算。
- 调用模型: 目前app是kotlin和java写的。不知道是否得使用c++去调用bin和dlc。如果是的话,有没有详细的教程可以参考。
-  调用npu:tflite有调用npu的方法吗?有的话有详细的代码教程或者文档吗。dlc和bin文件的npu调用文档和教材在哪里可以找到。有例子最好。
- 调用硬件:除了调用npu,有其他有益于模型速度的硬件加速可以在Snapdragon 8 Elite Gen 5上实现吗, 如果有请问教程和文档/例子在哪里可以找到。
- 并行计算: 我们准备并行跑4个模型。这四个模型分别是QAI-Hub上的easy_ocr, yolov11 detection, yolov11 segmentation, depth_anything_v2. 我们想了解这样强度的并行计算对手机硬件是否现实,然后如何自查。比如说让所有模型并行调用NPU是否现实。还有没有额外我们要做并行计算需要特别考虑的方面,影响我们软件实现或者模型选择。最后如果能有模型并行inference的例子就最好了。

...全文
67 1 打赏 收藏 转发到动态 举报
写回复
用AI写文章
1 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
  1. 调用模型:Kotlin/Java 还是 C++?
    结论:必须使用 C++ (JNI) 来调用 .bin (Context Binary) 或 .dlc。
    QNN SDK 原生仅提供 C API。Qualcomm 官方并没有为 Native QNN (即直接加载 DLC/BIN) 提供直接的 Java/Kotlin 接口封装。
    实现路径:
    Android 层 : 处理相机流、UI、逻辑控制。
    JNI 层 (C/C++): 编写一个 NativeLib.cpp,封装 QNN 的初始化、推理、释放逻辑。
    QNN 库: 链接 libQnnHtp.so 等动态库。
    参考教程位置:
    路径: ${QNN_SDK_ROOT}/examples/QNN/SampleApp
    学习重点: 阅读 SampleApp.cpp 中的 main 函数流程(Initialize -> CreateContext -> ComposeGraphs -> Finalize -> Execute)。您需要把这些流程拆解放入 JNI 函数中。
    JNI 架构伪代码示例:
    code C++

// native-lib.cpp
#include <jni.h>
#include "QnnInterface.h"

// 全局变量保存句柄
Qnn_ContextHandle_t g_context = nullptr;
Qnn_GraphHandle_t g_graph = nullptr;

extern "C" JNIEXPORT jboolean JNICALL
Java_com_example_myapp_AIModel_init(JNIEnv* env, jobject, jstring modelPath) {
// 1. 加载后端 (libQnnHtp.so)
// 2. 创建 Device
// 3. QnnContext_createFromBinary(...) 加载 .bin 文件
// 4. QnnGraph_retrieve(...) 获取图句柄
return true;
}

extern "C" JNIEXPORT jfloatArray JNICALL
Java_com_example_myapp_AIModel_inference(JNIEnv* env, jobject, jbyteArray imageData) {
// 1. 获取输入 Tensor 指针
// 2. 将 imageData 拷贝或零拷贝(RPCMem) 到输入 Tensor
// 3. QnnGraph_execute(g_graph, ...)
// 4. 读取输出 Tensor 并返回
}

  1. 调用 NPU:TFLite 方式 vs QNN Native 方式
    路线 A:TFLite Delegate (最快落地,性能中等)
    如果不想重写大量 C++ 代码,可以使用 QNN TFLite Delegate。这允许您继续使用标准的 TFLite Java API,只需添加一行代码启用 NPU 加速。
    原理:TFLite 解释器加载 .tflite 模型,通过 Delegate 将支持的算子下发给 QNN HTP 后端。
    文档位置:docs/QNN/general/tflite_delegate.html
    代码示例 (Java):
    code Java

Interpreter.Options options = new Interpreter.Options();
// 添加 QNN Delegate
Delegate qnnDelegate = new QnnDelegate(context, QnnDelegate.Options());
options.addDelegate(qnnDelegate);
Interpreter interpreter = new Interpreter(modelFile, options);

注意:您需要将 SDK 中的 libQnnTfliteDelegate.so 放入工程。
路线 B:QNN Native (性能极致,您的目标)
您已经生成了 .bin (Context Binary),这是性能最强、初始化最快的方式,特别是针对 SM8850 的 V81 架构。
文档位置:
核心流程:docs/QNN/general/api_overview.html (查看 Cache-based execution 章节)
HTP 特定:docs/QNN/general/htp/htp_backend.html

  1. 调用硬件:除了 NPU 还有什么?
    在 SM8850 (8 Elite) 上,除了 HTP NPU,您还可以利用:
    GPU (Adreno):
    适用场景:FP16/FP32 浮点模型,或者 NPU 被占满时分流。Depth Anything V2 如果对精度要求极高且难以量化,可以考虑放 GPU。
    调用方法:将 QNN 后端库从 libQnnHtp.so 换成 libQnnGpu.so。接口代码完全不用变!这就是 QNN 统一 API 的优势。

HTP 的 FP16 能力 (V81 特性):
SM8850 的 HTP 原生支持 FP16。如果 YOLOv11 量化掉点,可以在转换时使用 --float_fallback 或指定 FP16 精度,NPU 依然能跑,且比 CPU 快得多。
建议策略:
主力:全部 4 个模型优先尝试 HTP (NPU) INT8 量化。
备选:如果 Depth Anything V2 精度不够,改用 HTP FP16 或 GPU FP16。

  1. 并行计算:4模型并发可行性与实现
    可行性分析:
    SM8850 的 NPU 算力(~100 TOPS 级别)极其强悍。
    EasyOCR (极小) + YOLOv11n/s (小) + YOLOv11 Seg (小/中) + Depth Anything V2 (中/大)。
    结论:硬件完全吃得消。但这取决于内存带宽和VTCM 竞争。如果 4 个模型同时推理,可能会导致 VTCM (8MB) 不够分,从而发生 Spill-fill(数据换出到内存),导致发热和掉帧。
    如何自查 (Benchmark):
    使用 SDK 自带的工具在手机上模拟并发,不要等写完 App 再测。
    将 4 个模型的 .bin 文件推送到手机。
    创建一个 JSON 配置文件,描述 4 个线程并发运行。
    使用 qnn-throughput-net-run 工具。
    文档位置:docs/QNN/general/tools.html -> qnn-throughput-net-run
    命令示例:
    code Bash

    在 PC 端配置 config.json,定义 4 个模型,每个模型分配一个 backend 实例

    adb shell ./qnn-throughput-net-run --config config.json
    如果工具报告 FPS 达标且无报错,则硬件可行。
    软件实现策略 (App 架构):
    单 Context vs 多 Context:
    推荐:为每个模型创建一个独立的 QnnContext 和 QnnGraph。
    虽然 HTP 支持单 Context 多 Graph,但多 Context 更容易在应用层进行线程管理。
    多线程推理 (Multi-threading):
    在 C++ (JNI) 层开启 4 个线程。
    每个线程持有一个模型的 QnnGraphHandle_t。
    关键点:QnnGraph_execute 是阻塞调用。4 个线程同时调用时,高通驱动层(QNN Driver)会自动调度。
    优化:SM8850 支持 异步执行 (QnnGraph_executeAsync)。如果在 C++ 层使用异步队列,可以进一步榨干 NPU 性能。
    资源争抢解决 (VTCM Sharing):
    如果发现并发掉帧严重,可以在生成 .bin 时限制每个模型使用的 VTCM 大小。
    参考文档:docs/QNN/general/htp/htp_vtcm_sharing.html。
    策略:给 Depth Anything (大模型) 分配 4MB,其他小模型各分 1MB,或者让它们分时复用。
    QNN Graph Priority (优先级):
    如果 YOLO 检测(实时性要求高)被 OCR(实时性要求低)卡住了。
    在创建 Graph 时,通过 QNN_GRAPH_PROPERTY_PRIORITY 将 YOLO 设为 QNN_PRIORITY_HIGH,OCR 设为 QNN_PRIORITY_LOW。
    总结行动计划
    验证性能 (PC -> Phone):
    使用 qnn-context-binary-generator 为 SM8850 生成 4 个模型的 .bin 文件。
    使用 qnn-throughput-net-run 在手机上测试 4 模型并发,观察 Latency 和系统负载。
    搭建 JNI 框架:
    参考 SampleApp 编写 C++ Wrapper 类,实现 init(path) 和 execute(input, output)。
    使用 dlopen 加载 libQnnHtp.so。
    集成到 Android:
    开启 4 个 Coroutine 或 Thread。
    每个线程初始化一个 C++ 模型实例,循环调用 execute。
    这套方案能充分利用 SM8850 的性能,远超 TFLite 标准调用的速度。

6,677

社区成员

发帖
与我相关
我的任务
社区描述
本论坛以AI、WoS 、XR、IoT、Auto、生成式AI等核心板块组成,为开发者提供便捷及高效的学习和交流平台。 高通开发者专区主页:https://qualcomm.csdn.net/
人工智能物联网机器学习 技术论坛(原bbs) 北京·东城区
社区管理员
  • csdnsqst0050
  • chipseeker
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧