[bug] 骁龙8gen4 设备端运行qnn报错
在linux上交叉编译之后,在骁龙8gen4上测试报错
linux上编译
cmake .. -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-28 -DCMAKE_C_FLAGS="-march=armv8.7a" -DCMAKE_CXX_FLAGS="-march=armv8.7a" -DGGML_OPENMP=OFF -DGGML_LLAMAFILE=OFF -DGGML_QNN=ON -DGGML_QNN_DEFAULT_LIB_SEARCH_PATH=/data/local/tmp
我用的qnn_sdk_version=2.31.0.250130。将高通的动态库push到设备端。
在设备端测试
export LD_LIBRARY_PATH=/data/local/tmp/mllm/install-android/lib:/data/local/tmp/mllm/qnn-lib
./llama-cli -m ../../models/Qwen2.5-0.5B-Instruct-F16.gguf
报错:
llama_context: n_ctx_per_seq (4096) < n_ctx_train (32768) -- the full capacity of the model will not be utilized
extend_lib_search_path is nullptr, will use /data/local/tmp as default
failed to load /data/local/tmp/libQnnSystem.so, fallback to libQnnSystem.so
initialize qnn system successfully
failed to load /data/local/tmp/libQnnGpu.so, fallback to libQnnGpu.so
device property is not supported
create QNN device successfully
failed to load /data/local/tmp/libcdsprpc.so, fallback to libcdsprpc.so
qnn device name qnn-gpu
extend_lib_search_path is nullptr, will use /data/local/tmp as default
failed to load /data/local/tmp/libQnnSystem.so, fallback to libQnnSystem.so
initialize qnn system successfully
failed to load /data/local/tmp/libQnnHtp.so, fallback to libQnnHtp.so
device counts 1
deviceID:0, deviceType:0, numCores 1
htp_type:0(ON_CHIP)
soc_model:Snapdragon 8 Elite(SM8750), htp_arch:HTP_V79(79), vtcm_size:8 MB
failed to create QNN device
failed to load /data/local/tmp/libcdsprpc.so, fallback to libcdsprpc.so
why failed to initialize qnn context
failed to init qnn backend qnn-npu
idx 1, name:qnn-gpu
llama_init_from_model: failed to initialize the context: failed to initialize qnn-npu backend
common_init_from_params: failed to create context with model '../../models/Qwen2.5-0.5B-Instruct-F16.gguf'
main: error: unable to load model
今天重新编译,再push上去测试了一下。流程可以走通,只是速度很慢。这里把编译命令贴这里:
mkdir build-android && cd build-android
cmake -H.. -B. \
-DBUILD_SHARED_LIBS=off -DGGML_QNN_ENABLE_CPU_BACKEND=on -DGGML_OPENMP=off \
-DANDROID_ABI="arm64-v8a" \
-DANDROID_PLATFORM=android-31 \
-DANDROID_NDK=$ANDROID_NDK_ROOT \
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_ROOT/build/cmake/android.toolchain.cmake
-DGGML_QNN=on \
-DGGML_QNN_SDK_PATH="$QNN_SDK_ROOT" \
-DCMAKE_BUILD_TYPE=Release
cmake --build . --config Release -- -j$(nproc)
push必须的库到设备端,并到设备端用命令测试:
chmod +x ./llama-cli && ./llama-cli -m ../models/qwen1.5-0.5B_fp32.gguf
速度很慢,还没有cpu快。
但是感觉模型输出有问题。模型通过
python3 convert_hf_to_gguf.py ~/qwen1.5-0.5B/ --outfile qwen1.5-0.5B_fp32.gguf --outtype f32导出的。
设备的打印信息
> hello
你好!有什么可以帮助你的吗?
> what 1+1?
What is 1+1? 1+1 is a math problem where you have to add two numbers together. Here's how you do it:
1. Write down 1+1 in your mind.
2. Write down the number 1+1.
3. Multiply the two numbers together.
4. Add the two numbers together.
5. Write down the result.
For example, if you have 7 plus 5, your result would be 12. That's right! 1+1 is a tricky math problem that requires some practice to solve.
性能问题的话可以关注下这个issue: https://github.com/chraac/llama.cpp/issues/34
今天重新编译,再push上去测试了一下。流程可以走通,只是速度很慢。这里把编译命令贴这里:
mkdir build-android && cd build-android cmake -H.. -B. \ -DBUILD_SHARED_LIBS=off -DGGML_QNN_ENABLE_CPU_BACKEND=on -DGGML_OPENMP=off \ -DANDROID_ABI="arm64-v8a" \ -DANDROID_PLATFORM=android-31 \ -DANDROID_NDK=$ANDROID_NDK_ROOT \ -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_ROOT/build/cmake/android.toolchain.cmake -DGGML_QNN=on \ -DGGML_QNN_SDK_PATH="$QNN_SDK_ROOT" \ -DCMAKE_BUILD_TYPE=Release cmake --build . --config Release -- -j$(nproc)push必须的库到设备端,并到设备端用命令测试:
chmod +x ./llama-cli && ./llama-cli -m ../models/qwen1.5-0.5B_fp32.gguf速度很慢,还没有cpu快。 但是感觉模型输出有问题。模型通过
python3 convert_hf_to_gguf.py ~/qwen1.5-0.5B/ --outfile qwen1.5-0.5B_fp32.gguf --outtype f32导出的。设备的打印信息
> hello 你好!有什么可以帮助你的吗? > what 1+1? What is 1+1? 1+1 is a math problem where you have to add two numbers together. Here's how you do it: 1. Write down 1+1 in your mind. 2. Write down the number 1+1. 3. Multiply the two numbers together. 4. Add the two numbers together. 5. Write down the result. For example, if you have 7 plus 5, your result would be 12. That's right! 1+1 is a tricky math problem that requires some practice to solve.
我按照这个编译命令完成编译并在骁龙8gem3上成功运行,显示有qnn-cpu和qnn-gpu参与,但速度奇慢,decode只有0.02token/s,如下图(这是有qnn-cpu和qnn-gpu参与的意思吗?)
尝试使能GGML_QNN_ENABLE_HEXAGON_BACKEND,提示需要指定HEXAGON_SDK,但我按照https://docs.qualcomm.com/bundle/publicresource/topics/80-77512-1/hexagon-dsp-sdk-getting-started.html?product=1601111740010422 中描述的步骤进行下载,安装了高通软件包下载器qpm3,但始终无法login(我确信账号密码是正确的),导致无法完成hexagon sdk的下载。
请问您有尝试使用NPU吗?有没有遇到过类似的问题呢
我按照这个编译命令完成编译并在骁龙8gem3上成功运行,显示有qnn-cpu和qnn-gpu参与,但速度奇慢,decode只有0.02token/s,如下图(这是有qnn-cpu和qnn-gpu参与的意思吗?)
看截图这里确实用到了npu/gpu/cpu QNN这个backend用到NPU的话确实是比较慢的,现在的QNN框架下,除了自己写个Op加进去可能也没有啥其他好办法优化
看截图这里确实用到了npu/gpu/cpu QNN这个backend用到NPU的话确实是比较慢的,现在的QNN框架下,除了自己写个Op加进去可能也没有啥其他好办法优化
现在这个Log里有显示用到npu吗?qnn的npu/gpu/cpu以及非qnn的CPU调用优先级是什么样的呢,有点困惑。 另外看到您在#34 Issue中提到“添加完全独立于qnn的hexagon-npu,可操作HVX寄存器”,这里的hexagon-npu和qnn-npu有什么区别?是否可以理解为hexagon-npu是底层接口可直接操作寄存器,而qnn-npu是上层抽象,因此前者会更快?
尝试使能GGML_QNN_ENABLE_HEXAGON_BACKEND,提示需要指定HEXAGON_SDK,但我按照https://docs.qualcomm.com/bundle/publicresource/topics/80-77512-1/hexagon-dsp-sdk-getting-started.html?product=1601111740010422 中描述的步骤进行下载,安装了高通软件包下载器qpm3,但始终无法login(我确信账号密码是正确的),导致无法完成hexagon sdk的下载。
- 这里你需要有qualcomm的账号,然后在cn的话还需要处理一些网络连接的问题,才能成功登录
- 成功登录,并下载完成qualcomm的hexagon-sdk后,可以参考这个文件设置环境然后编译:build_in_container.sh
现在这个Log里有显示用到npu吗?qnn的npu/gpu/cpu以及非qnn的CPU调用优先级是什么样的呢,有点困惑。
你的log里面显示初始化了npu/gpu/cpu,然后llama.cpp framework会按照注册顺序去询问backends是否支持op,所以这里framework发现npu支持op,就会使用npu执行
这里的hexagon-npu和qnn-npu有什么区别?是否可以理解为hexagon-npu是底层接口可直接操作寄存器,而qnn-npu是上层抽象,因此前者会更快?
这个是一个好问题,qnn框架有他自己的限制,1.仅支持有限的tensor data type,2.qnn的graph没办法修改也没办法销毁 这两个限制导致如果要在qnn的npu里面跑某些op会需要先convert,而直接用hexagon的intrinsic写的op,灵活性会大很多,可以规避以上两个缺点
你的log里面显示初始化了npu/gpu/cpu,然后llama.cpp framework会按照注册顺序去询问backends是否支持op,所以这里framework发现npu支持op,就会使用npu执行
我在llama.cpp划分子图的位置加打印看了下,确实是您说的按照注册顺序去判断当前后端是否支持op,但疑惑的是,qnn_gpu/qnn_npu/qnn_cpu这三个后端似乎是共用一套qnn接口(包括supports_op这一接口),至少我没看到这三个后端各自的op支持列表;且split出的子图是qnn_gpu / qnn_cpu / cpu这三类,没有qnn_npu.
我的问题是:
- qnn的三个后端之间是怎么区分和管理的?在qnn sdk内部吗?
- 如果我想调整offload优先级,比如 qnn_gpu 和 qnn_npu 同时支持的情况下,优先放到 qnn_npu去运行,应该怎么做呢,直接调整backends注册顺序吗?
qnn的三个后端之间是怎么区分和管理的?在qnn sdk内部吗?
qnn这个backend,我们能做的只是指定初始化的设备,其他tensor和graph的接口,不同设备是一样的,所以代码一样
如果我想调整offload优先级,比如 qnn_gpu 和 qnn_npu 同时支持的情况下,优先放到 qnn_npu去运行,应该怎么做呢,直接调整backends注册顺序吗
这种情况可以直接只注册npu backend,不过因为qnn本身tensor datatype的限制,估计支持的op不多
另外还是建议尝试用hexagon-npu跑,这个虽然编译环境设置麻烦点,但是性能确实好些
另外还是建议尝试用
hexagon-npu跑,这个虽然编译环境设置麻烦点,但是性能确实好些
我在尝试使用hexagon-npu,但仍无法下载该sdk,发现可能是因为我是自己的docker开发环境,缺少systemd,导致qpm安装不完整。权限问题暂时不好解决这个问题。
请问您在How-to-Build中提供的docker build脚本是否能够自动下载相关组件?我尝试了一下,提示无法拉取chraac/llama-cpp-qnn-hexagon-builder,进您docker hub的主页看也确实没有。这个docker是否还能获取到?
我在尝试使用hexagon-npu,但仍无法下载该sdk,发现可能是因为我是自己的docker开发环境,缺少systemd,导致qpm安装不完整。权限问题暂时不好解决这个问题。
请问您在How-to-Build中提供的docker build脚本是否能够自动下载相关组件?我尝试了一下,提示无法拉取chraac/llama-cpp-qnn-hexagon-builder,进您docker hub的主页看也确实没有。这个docker是否还能获取到?
- Qualcomm的license说hexagon-sdk只能每个账号的所有者自己用,所以鉴于法律风险,可能需要自己创建镜像
- 其实也不复杂,可以弄个linux的host,先装完qpm和hexagon-sdk,然后mount到docker里面,问了gpt,这样只要把本地镜像发布出去,是在license的许可范围内的
这种情况可以直接只注册npu backend,不过因为qnn本身tensor datatype的限制,估计支持的op不多
hexagon-sdk我再尝试一下,感谢指路。
想再请教一个QNN的问题把这一块搞懂。只注册qnn_npu后发现所有算子都被offload到了CPU了,qnn_npu上没有一个算子,观察到是因为npu op support在图示位置被返回了false
npu的ctx->supported_types为0,那和tensor->type按位与岂不是必定为0、不支持任何算子?相比较而言qnn_gpu的supported_types为3,还是有一些type返回true的. 这样看来npu是不支持任何算子吗……
这种情况可以直接只注册npu backend,不过因为qnn本身tensor datatype的限制,估计支持的op不多
hexagon-sdk我再尝试一下,感谢指路。
想再请教一个QNN的问题把这一块搞懂。只注册qnn_npu后发现所有算子都被offload到了CPU了,qnn_npu上没有一个算子,观察到是因为npu op support在图示位置被返回了false npu的ctx->supported_types为0,那和tensor->type按位与岂不是必定为0、不支持任何算子?相比较而言qnn_gpu的supported_types为3,还是有一些type返回true的. 这样看来npu是不支持任何算子吗……
不好意思,看了下这里确实有问题,这个应该是之前加config的时候弄坏了,弄了个patch,本地跑了下能正常跑到npu能支持的op,有空的话可以参考下
Subject: [PATCH] qnn fix: update device capabilities for quantized types in
qnn-lib to improve compatibility
---
ggml/src/ggml-qnn/qnn/ggml-qnn.cpp | 3 +++
ggml/src/ggml-qnn/qnn/qnn-lib.cpp | 6 +++---
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/ggml/src/ggml-qnn/qnn/ggml-qnn.cpp b/ggml/src/ggml-qnn/qnn/ggml-qnn.cpp
index e559cfdb2..4a13f3ec0 100644
--- a/ggml/src/ggml-qnn/qnn/ggml-qnn.cpp
+++ b/ggml/src/ggml-qnn/qnn/ggml-qnn.cpp
@@ -283,8 +283,11 @@ ggml_backend_t ggml_backend_qnn_init_with_device_context(ggml_backend_dev_t dev,
qnn::get_backend_desc(dev_ctx->device));
dev_ctx->description = buffer;
}
+
+#ifdef GGML_HEXAGON_ENABLE_QUANTIZED_TENSORS
// TODO: remove npu from here if hardware quantization is supported
dev_ctx->enable_cpu_dequantize = device == QNN_BACKEND_CPU;
+#endif
ggml_backend_t qnn_backend = new ggml_backend{
/* .guid = */ ggml_backend_qnn_guid(),
diff --git a/ggml/src/ggml-qnn/qnn/qnn-lib.cpp b/ggml/src/ggml-qnn/qnn/qnn-lib.cpp
index e32bab5f9..7dbcaf968 100644
--- a/ggml/src/ggml-qnn/qnn/qnn-lib.cpp
+++ b/ggml/src/ggml-qnn/qnn/qnn-lib.cpp
@@ -38,7 +38,7 @@ constexpr const qnn::device_caps kDeviceCaps[] = {
// all quantized types can be offload to CPU, at current implementation, those types will be dequantized into float32 on cpu
0xFFFFFE,
#else
- 0,
+ (1L << GGML_TYPE_F32),
#endif
0, // 0 for no limitation
@@ -50,7 +50,7 @@ constexpr const qnn::device_caps kDeviceCaps[] = {
// all quantized types can be offload to GPU, at current implementation, those types will be dequantized into float32 on cpu
0xFFFFFE,
#else
- 0,
+ (1L << GGML_TYPE_F32) | (1L << GGML_TYPE_F16),
#endif
(128256L * 4096 *
sizeof(float)), // tested on 8 gen 2, failed to allocate tensor with size 128256x4096 and float32
@@ -62,7 +62,7 @@ constexpr const qnn::device_caps kDeviceCaps[] = {
(1L << GGML_TYPE_F32) | (1L << GGML_TYPE_F16) | (1L << GGML_TYPE_I16),
(1L << GGML_TYPE_Q2_K) | (1L << GGML_TYPE_Q3_K) | (1L << GGML_TYPE_Q4_K) | (1L << GGML_TYPE_Q8_K),
#else
- 0,
+ (1L << GGML_TYPE_F32) | (1L << GGML_TYPE_F16),
#endif
(8192L * 2048 + 8192 * 512 + 2048 * 512) * sizeof(float), // TODO: should have a better way to get this value
},
--
2.49.0.windows.1
+#ifdef GGML_HEXAGON_ENABLE_QUANTIZED_TENSORS // TODO: remove npu from here if hardware quantization is supported dev_ctx->enable_cpu_dequantize = device == QNN_BACKEND_CPU; +#endif
测试是可以了的,所有的MUL和ADD算子都被offload到了npu上,感谢。 MUL_MAT无法offload过去是因为我使用的量化模型吗,gpu/npu不支持量化计算?
测试是可以了的,所有的MUL和ADD算子都被offload到了npu上,感谢。 MUL_MAT无法offload过去是因为我使用的量化模型吗,gpu/npu不支持量化计算?
我使用fp16模型进行补充测试,注册gpu时mul_mat成功offload到了gpu上,且运行正确;只注册npu时显示mul_mat offload到了npu上,但推理时会崩溃
MUL_MAT无法offload过去是因为我使用的量化模型吗,gpu/npu不支持量化计算?
QNN的tensor确实现在还没有好的办法去支持ggml的量化,可以看看hexagon-npu这个backend
只注册npu时显示mul_mat offload到了npu上,但推理时会崩溃
可以创建issue,另外附上stacktrace,这样才有更多的信息分析