3.1 LLM
本节介绍如何在 CIX P1 开发平台上运行本地大语言模型,主要包括 llama.cpp 和 MNN 两种推理路径。
本节使用 llama.cpp 和 MNN 社区版本进行部署。开发者可以从上游仓库获取当前最新源码,根据实际需要启用 CPU、KleidiAI、Vulkan 或 OpenCL 等能力。
由于 llama.cpp 和 MNN 均为持续更新的开源项目,不同版本的构建选项、程序名称、命令参数及后端支持可能发生变化。使用社区源码时,应记录实际使用的 Git Commit,并以当前版本的 README、构建说明和程序 –help 输出为准。
两种框架的主要区别如下:
路径 |
模型格式 |
适用场景 |
使用建议 |
|---|---|---|---|
llama.cpp CPU / KleidiAI |
GGUF |
首次跑通和基础文本生成 |
推荐优先验证 |
llama.cpp Vulkan |
GGUF |
Vulkan GPU 后端验证 |
可选,需确认 Vulkan Runtime |
MNN CPU / KleidiAI |
MNN 模型目录 |
MNN 模型加载和文本推理 |
使用经过验证的 MNN 模型 |
MNN OpenCL |
MNN 模型目录 |
OpenCL GPU 后端验证 |
可选,以板端实际结果为准 |
推荐验证顺序如下:
llama.cpp CPU / KleidiAI
→ MNN CPU / KleidiAI
→ llama.cpp Vulkan
→ MNN OpenCL
建议先完成 CPU 路径验证,再验证 Vulkan 和 OpenCL。GPU 路径依赖 CIX GO 图形引擎、GPU 驱动及对应 Runtime,系统能够识别 GPU 并不代表推理框架一定已经成功使用 GPU。
3.1.1 llama.cpp
llama.cpp 是面向本地大语言模型推理的轻量级开源框架,主要用于运行 GGUF 格式模型。
本节介绍:
llama.cpp 社区源码获取和编译;
CPU / KleidiAI 推理;
CPU 线程数和 taskset 绑核;
Vulkan GPU 推理;
CPU 与 Vulkan 性能测试。
KleidiAI 是面向 Arm CPU 的优化微内核库,属于 llama.cpp CPU 后端的性能优化能力,并不是独立的硬件推理后端。
Vulkan 用于将部分或全部模型层卸载至 GPU。
3.1.1.1 准备环境
开始前,请确认开发板已经启动至赛事指定的 Debian 12 系统,并具备网络连接和足够的磁盘空间。
建议预留:
模型存储空间:根据实际 GGUF 模型大小确定;
源码和编译空间:建议至少预留 5 GB;
运行内存:应大于模型文件、KV Cache 和运行时缓冲区所需空间之和。
安装基础依赖:
sudo apt update
sudo apt install -y \
git \
cmake \
build-essential \
wget \
ca-certificates \
util-linux
其中,util-linux 软件包提供后续绑核使用的 taskset 命令。
检查基础工具:
git --version
cmake --version
gcc --version
taskset --version
3.1.1.2 获取 llama.cpp 社区源码
创建工作目录:
mkdir -p ~/local-llm-test
cd ~/local-llm-test
获取社区源码:
git clone https://github.com/ggml-org/llama.cpp.git
进入源码目录:
cd ~/local-llm-test/llama.cpp
记录当前源码 Commit:
git rev-parse HEAD
建议将输出保存到文件:
git rev-parse HEAD \
| tee ~/local-llm-test/llama-cpp-commit.txt
如果源码目录已经存在,可更新至当前社区版本:
cd ~/local-llm-test/llama.cpp
git pull --ff-only
更新完成后再次记录 Commit,并重新执行编译。
3.1.1.3 编译 CPU / KleidiAI 版本
清理旧构建目录:
cd ~/local-llm-test/llama.cpp
rm -rf build-cpu
配置 Release 构建并启用 KleidiAI:
cmake -S . -B build-cpu \
-DCMAKE_BUILD_TYPE=Release \
-DGGML_CPU_KLEIDIAI=ON
检查编译配置:
grep "GGML_CPU_KLEIDIAI" \
build-cpu/CMakeCache.txt
预期输出包含:
GGML_CPU_KLEIDIAI:BOOL=ON
开始编译:
cmake --build build-cpu -j4
-j4 表示同时使用四个编译任务。
如果开发板内存充足,可以适当提高:
cmake --build build-cpu -j8
如果编译过程中出现进程被终止、Killed 或内存不足,应降低并行数:
cmake --build build-cpu -j2
检查主要编译产物:
ls -lh build-cpu/bin/llama-cli
ls -lh build-cpu/bin/llama-bench
查看程序版本和参数:
./build-cpu/bin/llama-cli --version
./build-cpu/bin/llama-cli --help
./build-cpu/bin/llama-bench --help
如果 llama-cli 和 llama-bench 均已生成,说明 CPU 版本编译完成。
3.1.1.4 准备 GGUF 模型
本节以 Qwen2.5-0.5B-Instruct-GGUF 的 Q4_K_M 量化模型作为基础验证模型。
该模型参数规模较小,适合优先验证模型下载、模型加载和文本生成链路。
创建模型目录:
mkdir -p ~/models/qwen2.5-0.5b
cd ~/models/qwen2.5-0.5b
模型页面:
下载 Q4_K_M 模型:
wget -c \
-O qwen2.5-0.5b-instruct-q4_k_m.gguf \
"https://modelscope.cn/models/Qwen/Qwen2.5-0.5B-Instruct-GGUF/resolve/master/qwen2.5-0.5b-instruct-q4_k_m.gguf"
检查模型文件:
ls -lh \
~/models/qwen2.5-0.5b/qwen2.5-0.5b-instruct-q4_k_m.gguf
进一步检查文件大小:
stat -c "%n: %s bytes" \
~/models/qwen2.5-0.5b/qwen2.5-0.5b-instruct-q4_k_m.gguf
如果模型文件只有几 KB,通常表示下载到了错误页面、网络连接中断或模型未完整下载。
删除异常文件后重新下载:
rm -f \
~/models/qwen2.5-0.5b/qwen2.5-0.5b-instruct-q4_k_m.gguf
如果赛事资源包已经提供经过验证的 GGUF 模型,建议优先使用赛事资源包中的模型,并将后续命令中的模型路径替换为实际路径。
设置模型路径变量:
MODEL=~/models/qwen2.5-0.5b/qwen2.5-0.5b-instruct-q4_k_m.gguf
确认变量指向有效文件:
ls -lh "$MODEL"
3.1.1.5 检查 CPU 拓扑并设置绑核范围
CIX P1 包含 8 个 Cortex-A720 核心和 4 个 Cortex-A520 小核。CPU 推理时建议绑定 Cortex-A720 核心,避免推理线程被调度到低性能核心,从而影响性能和测试稳定性。
查看 CPU 编号和最高频率:
lscpu -e=CPU,CORE,ONLINE,MAXMHZ,MINMHZ
在赛事指定的 CIX P1 Debian 12 环境中,CPU 核心对应关系如下:
CPU 编号 |
核心类型 |
最高频率 |
|---|---|---|
0、1 |
Cortex-A720 高性能核心 |
约 2.6 GHz |
10、11 |
Cortex-A720 高性能核心 |
约 2.5 GHz |
6、7 |
Cortex-A720 核心 |
约 2.3 GHz |
8、9 |
Cortex-A720 核心 |
约 2.2 GHz |
2–5 |
Cortex-A520 小核 |
约 1.8 GHz |
推荐使用全部 8 个 Cortex-A720 核心进行 CPU / KleidiAI 推理:
CPU_LIST=0,1,6-11
THREAD_NUM=8
如需测试 4 核配置,绑定当前频率最高的 4 个 Cortex-A720 核心:
CPU_LIST=0,1,10,11
THREAD_NUM=4
检查当前设置:
echo "CPU_LIST=$CPU_LIST"
echo "THREAD_NUM=$THREAD_NUM"
taskset -c "$CPU_LIST" true
CPU_LIST 中的 CPU 数量应与 THREAD_NUM 保持一致。
-t 仅用于设置 llama.cpp 的 CPU 推理线程数,不能代替 taskset 的 CPU 亲和性设置。
3.1.1.6 运行 CPU / KleidiAI 推理
进入 llama.cpp 源码目录:
cd ~/local-llm-test/llama.cpp
推荐使用 taskset 固定 CPU 范围,并使线程数与绑定 CPU 数量保持一致:
taskset -c "$CPU_LIST" \
./build-cpu/bin/llama-cli \
-m "$MODEL" \
-p "请用一句话介绍你自己。" \
-t "$THREAD_NUM" \
-n 128
主要参数说明如下:
参数 |
说明 |
|---|---|
taskset -c |
限制进程只能在指定逻辑 CPU 上运行 |
-m |
GGUF 模型文件路径 |
-p |
输入提示词 |
-t |
CPU 推理线程数 |
-n |
最大生成 Token 数 |
如果模型能够正常加载并输出文本,说明 llama.cpp CPU 推理链路基本可用。
不同 llama.cpp 版本在对话模板、交互模式和单轮运行参数方面可能存在差异。如果程序生成结束后进入交互状态,可输入:
exit
或按:
Ctrl+C
退出程序。
如果当前版本提供单轮运行参数,可通过以下命令查询:
./build-cpu/bin/llama-cli --help \
| grep -Ei "single|conversation|interactive"
不要直接假设不同版本均支持完全相同的单轮参数。
如果输出内容异常、出现大量重复文本或未按照指令回答,应检查:
模型是否完整;
当前版本是否识别模型内置 Chat Template;
是否启用了不合适的交互模式;
模型量化格式是否与当前版本兼容;
当前版本的 llama-cli –help 是否提供对应的对话参数。
3.1.1.7 验证 KleidiAI
保存完整运行日志:
taskset -c "$CPU_LIST" \
./build-cpu/bin/llama-cli \
-m "$MODEL" \
-p "请用一句话介绍你自己。" \
-t "$THREAD_NUM" \
-n 128 \
2>&1 | tee ~/local-llm-test/llama-cpu.log
检查日志中的 KleidiAI 信息:
grep -i "KLEIDIAI" \
~/local-llm-test/llama-cpu.log
启用 KleidiAI 后,日志中可能出现类似内容:
CPU_KLEIDIAI
GGML_CPU_KLEIDIAI=ON 表示编译时已经集成 KleidiAI 支持。
运行日志中出现 CPU_KLEIDIAI 相关信息,可以进一步说明当前运行过程加载了 KleidiAI CPU 后端。
由于社区版本可能调整日志格式,如果未搜索到 KleidiAI 字符串,应同时检查:
grep "GGML_CPU_KLEIDIAI" \
build-cpu/CMakeCache.txt
./build-cpu/bin/llama-cli --version
日志中没有出现 KleidiAI 信息并不一定代表程序运行失败,但不能仅凭模型成功输出判断 KleidiAI 已经实际生效。
正式性能验证时,应结合以下信息综合判断:
llama.cpp Commit;
CMake 配置;
运行日志;
CPU 指令能力;
相同模型和参数下的性能结果。
3.1.1.8 CPU 性能测试
使用 llama-bench 进行基础测试:
cd ~/local-llm-test/llama.cpp
先执行一次预热:
taskset -c "$CPU_LIST" \
./build-cpu/bin/llama-bench \
-m "$MODEL" \
-p 128 \
-n 32 \
-t "$THREAD_NUM" \
-r 1
正式测试:
taskset -c "$CPU_LIST" \
./build-cpu/bin/llama-bench \
-m "$MODEL" \
-p 512 \
-n 128 \
-t "$THREAD_NUM" \
-r 3 \
| tee ~/local-llm-test/llama-cpu-bench.log
主要参数说明:
参数 |
说明 |
|---|---|
-p 512 |
Prompt Processing 测试使用 512 个输入 Token |
-n 128 |
Text Generation 测试生成 128 个 Token |
-t |
CPU 推理线程数 |
-r 3 |
每项重复测试三次 |
重点记录:
pp512;
tg128;
Token/s;
线程数;
CPU affinity;
模型名称及量化方式;
llama.cpp Commit;
系统温度和后台负载。
CPU 绑核通常用于减少操作系统调度带来的波动,提高多次性能测试结果的一致性。
进行正式性能对比时,应固定:
模型文件;
CPU 线程数;
CPU 绑定范围;
Prompt Token 数;
生成 Token 数;
系统电源状态;
散热状态;
后台进程负载。
3.1.1.9 可选:编译 Vulkan 版本
llama.cpp Vulkan 后端依赖 CIX GO 图形引擎提供的 GPU 驱动和 Vulkan Runtime。
执行本节前,应先确认 GO 图形引擎已经正常安装。随后按照本节命令安装
vulkan-tools,并使用 vulkaninfo 检查 Vulkan Runtime 和板载 GPU。
安装 Vulkan 编译和检查工具:
sudo apt update
sudo apt install -y \
vulkan-tools \
libvulkan-dev \
glslc \
spirv-headers
检查 Vulkan Runtime:
vulkaninfo --summary
如果当前版本的 vulkaninfo 不支持 –summary,可以执行:
vulkaninfo | head -n 100
输出中能够识别板载 GPU,且未出现 Vulkan Loader、ICD 或设备初始化错误,说明 Vulkan Runtime 基本可用。
进入源码目录并清理旧构建目录:
cd ~/local-llm-test/llama.cpp
rm -rf build-vulkan
配置 Vulkan 构建,同时保留 KleidiAI CPU 支持:
cmake -S . -B build-vulkan \
-DCMAKE_BUILD_TYPE=Release \
-DGGML_VULKAN=ON \
-DGGML_CPU_KLEIDIAI=ON
检查编译配置:
grep -E "GGML_VULKAN|GGML_CPU_KLEIDIAI" \
build-vulkan/CMakeCache.txt
预期输出包含:
GGML_VULKAN:BOOL=ON
GGML_CPU_KLEIDIAI:BOOL=ON
开始编译:
cmake --build build-vulkan -j4
检查编译产物:
ls -lh build-vulkan/bin/llama-cli
ls -lh build-vulkan/bin/llama-bench
3.1.1.10 运行 Vulkan 推理
Vulkan 推理的主要计算由 GPU 承担,但 CPU 仍负责 Tokenizer、任务调度、内存管理以及部分未卸载至 GPU 的计算。
Vulkan 推理默认将宿主进程绑定至当前最高频的 4 个 Cortex-A720 核心:
GPU_CPU_LIST=0,1,10,11
GPU_THREAD_NUM=4
检查设置:
echo "GPU_CPU_LIST=$GPU_CPU_LIST"
echo "GPU_THREAD_NUM=$GPU_THREAD_NUM"
taskset -c "$GPU_CPU_LIST" true
执行 Vulkan 推理:
cd ~/local-llm-test/llama.cpp
taskset -c "$GPU_CPU_LIST" \
./build-vulkan/bin/llama-cli \
-m "$MODEL" \
-p "请用一句话介绍你自己。" \
-t "$GPU_THREAD_NUM" \
-n 128 \
-ngl 99 \
2>&1 | tee ~/local-llm-test/llama-vulkan.log
其中:
taskset -c “$GPU_CPU_LIST” 将宿主进程绑定至 CPU 0、1、10、11;
-t “$GPU_THREAD_NUM” 将 llama.cpp CPU 线程数设置为 4;
-ngl 99 表示尝试将尽可能多的模型层卸载至 GPU。
实际能够卸载的模型层数取决于:
模型结构和模型大小;
可用内存;
GPU 驱动版本;
Vulkan Runtime;
llama.cpp Commit;
Vulkan 后端算子支持情况。
运行时重点检查:
vulkaninfo 能够识别板载 GPU;
CMakeCache.txt 中显示 GGML_VULKAN=ON;
执行的是 build-vulkan/bin/llama-cli;
日志中出现 Vulkan 设备信息;
日志中显示模型层被卸载至 GPU;
模型能够正常生成文本;
未出现 Vulkan 初始化失败或完全回退至 CPU。
检查关键日志:
grep -Ei \
"vulkan|gpu|offload|layer|fallback|error" \
~/local-llm-test/llama-vulkan.log
满足以下条件,可以认为 Vulkan 推理路径基本跑通:
Vulkan Runtime 能够识别 GPU;
llama.cpp 已启用 Vulkan 后端;
运行日志显示 Vulkan 设备和模型层卸载信息;
模型能够完成文本生成。
如果出现:
warning: no usable GPU found
或者:
--gpu-layers option will be ignored
依次检查:
grep "GGML_VULKAN" \
~/local-llm-test/llama.cpp/build-vulkan/CMakeCache.txt
vulkaninfo --summary
ls -lh \
~/local-llm-test/llama.cpp/build-vulkan/bin/llama-cli
并确认实际执行的是:
./build-vulkan/bin/llama-cli
而不是:
./build-cpu/bin/llama-cli
如果 Vulkan 能够识别 GPU,但模型运行时因内存不足退出,可以降低卸载层数,例如:
taskset -c "$GPU_CPU_LIST" \
./build-vulkan/bin/llama-cli \
-m "$MODEL" \
-p "请用一句话介绍你自己。" \
-t "$GPU_THREAD_NUM" \
-n 128 \
-ngl 10
随后逐步提高 -ngl,观察内存占用、模型层卸载数量和运行结果。
3.1.1.11 CPU 与 Vulkan 性能对比
CPU / KleidiAI 测试使用全部 8 个 Cortex-A720 核心:
CPU_LIST=0,1,6-11
THREAD_NUM=8
执行 CPU 测试:
taskset -c "$CPU_LIST" \
./build-cpu/bin/llama-bench \
-m "$MODEL" \
-p 512 \
-n 128 \
-t "$THREAD_NUM" \
-r 3 \
| tee ~/local-llm-test/llama-cpu-bench.log
Vulkan 测试将宿主进程绑定至当前最高频的 4 个 Cortex-A720 核心:
GPU_CPU_LIST=0,1,10,11
GPU_THREAD_NUM=4
执行 Vulkan 测试:
taskset -c "$GPU_CPU_LIST" \
./build-vulkan/bin/llama-bench \
-m "$MODEL" \
-p 512 \
-n 128 \
-t "$GPU_THREAD_NUM" \
-ngl 99 \
-r 3 \
| tee ~/local-llm-test/llama-vulkan-bench.log
两次测试应使用相同的:
模型文件;
模型量化方式;
Prompt Token 数;
生成 Token 数;
测试重复次数;
系统镜像;
系统电源状态;
散热状态;
后台进程负载。
CPU 和 Vulkan 使用不同的 CPU 绑定配置:
推理路径 |
CPU 绑定范围 |
线程参数 |
|---|---|---|
CPU / KleidiAI |
0,1,6-11 |
-t 8 |
Vulkan |
0,1,10,11 |
-t 4 |
该测试用于比较两条推荐部署路径的实际性能。由于两条路径承担主要计算的硬件不同,CPU 线程数和 CPU 绑定范围不要求完全一致,但必须在测试结果中分别记录。
如需严格控制 CPU 资源进行后端对比,可以额外将两条路径都绑定至相同的 4 个 Cortex-A720 核心:
CPU_LIST=0,1,10,11
THREAD_NUM=4
但正式发布的默认测试配置仍建议使用:
CPU / KleidiAI:8 个 Cortex-A720;
Vulkan:4 个最高频 Cortex-A720。
llama-bench 的结果主要用于比较模型推理后端和参数配置,不等同于完整应用的端到端响应时间。
测试完成后,建议记录:
git rev-parse HEAD
uname -a
lscpu
vulkaninfo --summary
同时记录:
模型名称;
量化类型;
CPU 推理线程数;
CPU 绑定范围;
-ngl 数值;
Prompt Processing 性能;
Text Generation 性能;
系统温度;
后台负载;
异常日志。
3.1.1.12 llama.cpp 常见问题
问题现象 |
可能原因 |
建议处理方式 |
|---|---|---|
找不到 llama-cli |
编译失败或构建路径错误 |
检查编译日志及 build-cpu/bin |
模型文件只有几 KB |
模型未完整下载 |
删除异常文件后重新下载 |
模型加载失败 |
模型路径错误、文件损坏或不是 GGUF |
检查 -m、文件大小和模型格式 |
输出乱码或大量重复 |
模型不完整、Chat Template 不匹配或参数异常 |
检查模型、模板和当前版本帮助 |
日志中没有 CPU_KLEIDIAI |
未启用或日志格式变化 |
检查 CMake 配置、Commit 和完整日志 |
taskset 执行失败 |
CPU 编号不存在或 CPU 不在线 |
使用 lscpu -e 检查 CPU 范围 |
线程数大于绑定核心数 |
配置不一致 |
调整 -t 或扩大 CPU_LIST |
vulkaninfo 找不到 GPU |
GPU 驱动或 Vulkan Runtime 异常 |
检查 GO 图形引擎和系统 Release |
-ngl 被忽略 |
未启用 Vulkan 或执行了错误二进制 |
检查构建参数和执行路径 |
Vulkan 内存不足 |
模型或卸载层数过大 |
使用更小模型或降低 -ngl |
编译进程被终止 |
可用内存不足 |
将编译并行数从 -j4 降至 -j2 |
多次测试波动较大 |
调度、温度或后台负载不同 |
固定绑核、线程数和测试环境 |
3.1.2 MNN
MNN 是面向移动端、嵌入式设备和边缘设备的轻量级推理引擎,可运行经过转换的 MNN 大语言模型,并支持 CPU、OpenCL 等推理后端。
MNN LLM 使用包含模型结构、模型权重、Tokenizer 和运行配置的完整模型目录,不能直接加载 llama.cpp 使用的 GGUF 模型。
常见 MNN LLM 模型目录中可能包含:
config.json
llm_config.json
llm.mnn
llm.mnn.weight
tokenizer.mtok
embeddings_bf16.bin
不同模型和不同 MNN 版本的文件组成可能存在差异,应以模型仓库中的 README 和 config.json 为准。
本节介绍:
MNN 社区源码获取和编译;
MNN 格式模型下载;
CPU / KleidiAI 推理;
taskset 绑核;
OpenCL GPU 推理;
CPU 与 OpenCL 性能测试。
3.1.2.1 准备环境并获取 MNN 源码
安装基础依赖:
sudo apt update
sudo apt install -y \
git \
git-lfs \
cmake \
build-essential \
wget \
ca-certificates \
clinfo \
ocl-icd-opencl-dev \
opencl-headers \
util-linux
初始化 Git LFS:
git lfs install
创建工作目录:
mkdir -p ~/mnn
cd ~/mnn
获取 MNN 社区源码:
git clone https://github.com/alibaba/MNN.git
进入源码目录:
cd ~/mnn/MNN
记录当前 Commit:
git rev-parse HEAD \
| tee ~/mnn/mnn-commit.txt
如果源码目录已经存在,可以更新:
cd ~/mnn/MNN
git pull --ff-only
更新后应重新记录 Commit,并重新编译。
3.1.2.2 编译 MNN CPU / KleidiAI 版本
清理旧构建目录:
cd ~/mnn/MNN
rm -rf build-cpu
配置 CPU、LLM 和 KleidiAI 构建:
cmake -S . -B build-cpu \
-DCMAKE_BUILD_TYPE=Release \
-DMNN_BUILD_SHARED_LIBS=ON \
-DMNN_BUILD_LLM=ON \
-DMNN_LOW_MEMORY=ON \
-DMNN_SUPPORT_TRANSFORMER_FUSE=ON \
-DMNN_KLEIDIAI=ON \
-DMNN_KLEIDIAI_DEFAULT_ON=ON
主要构建选项如下:
构建选项 |
作用 |
|---|---|
MNN_BUILD_SHARED_LIBS=ON |
构建动态库 |
MNN_BUILD_LLM=ON |
构建 MNN LLM 引擎、llm_demo 和相关工具 |
MNN_LOW_MEMORY=ON |
启用低内存相关能力 |
MNN_SUPPORT_TRANSFORMER_FUSE=ON |
启用 Transformer 融合优化 |
MNN_KLEIDIAI=ON |
集成 Arm KleidiAI |
MNN_KLEIDIAI_DEFAULT_ON=ON |
默认优先使用 KleidiAI Kernel |
检查配置:
grep -E \
"^MNN_(BUILD_SHARED_LIBS|BUILD_LLM|LOW_MEMORY|SUPPORT_TRANSFORMER_FUSE|KLEIDIAI|KLEIDIAI_DEFAULT_ON):" \
build-cpu/CMakeCache.txt
开始编译:
cmake --build build-cpu -j4
如果编译时出现内存不足,可以降低并行数:
cmake --build build-cpu -j2
检查编译产物:
find ~/mnn/MNN/build-cpu \
-maxdepth 3 \
-type f \
\( -name "llm_demo" -o -name "llm_bench" \)
设置程序路径:
LLM_DEMO="$(find ~/mnn/MNN/build-cpu \
-type f -name llm_demo \
| head -n 1)"
LLM_BENCH="$(find ~/mnn/MNN/build-cpu \
-type f -name llm_bench \
| head -n 1)"
检查:
echo "$LLM_DEMO"
echo "$LLM_BENCH"
test -x "$LLM_DEMO"
test -x "$LLM_BENCH"
查看帮助:
"$LLM_DEMO" --help || true
"$LLM_BENCH" --help || true
3.1.2.3 检查动态库加载
如果系统中已经安装其他版本的 MNN,源码编译的 llm_demo 可能错误加载系统中的旧版 libMNN.so,从而出现:
undefined symbol;
程序异常退出;
模型加载失败;
OpenCL 后端缺失;
运行结果与构建配置不一致。
检查动态库:
ldd "$LLM_DEMO" \
| grep -E "libMNN|libllm|Express|not found"
MNN 相关动态库应优先指向当前 build-cpu 目录。
如果出现 not found,或者加载了系统中其他版本的 MNN 库,可以在当前终端设置:
export LD_LIBRARY_PATH="$HOME/mnn/MNN/build-cpu:$HOME/mnn/MNN/build-cpu/express:${LD_LIBRARY_PATH:-}"
再次检查:
ldd "$LLM_DEMO" \
| grep -E "libMNN|libllm|Express|not found"
不建议在没有确认影响范围的情况下永久写入 ~/.bashrc,避免影响系统中其他依赖 MNN 的程序。
3.1.2.4 准备 MNN 模型
本节以 Qwen2.5-0.5B-Instruct-MNN 作为基础验证模型。
如果赛事资源包已经提供经过验证的 MNN 模型,建议优先使用赛事模型,并将后续路径替换为实际路径。
创建工作目录并下载模型:
mkdir -p ~/mnn
cd ~/mnn
git clone https://www.modelscope.cn/MNN/Qwen2.5-0.5B-Instruct-MNN.git
进入模型目录并下载完整权重:
cd ~/mnn/Qwen2.5-0.5B-Instruct-MNN
git lfs pull
检查 Git LFS 文件:
git lfs ls-files
查看模型目录:
ls -lh
检查是否仍存在 Git LFS 指针文件:
grep -Il \
"^version https://git-lfs.github.com/spec/v1" \
./* 2>/dev/null
如果该命令输出了模型文件名称,说明实际权重没有完整下载,应重新执行:
git lfs pull
设置模型目录:
MODEL_DIR=~/mnn/Qwen2.5-0.5B-Instruct-MNN
确认主要配置文件:
ls -lh "$MODEL_DIR/config.json"
3.1.2.5 创建 MNN CPU 配置
MNN CPU / KleidiAI 推理默认绑定全部 8 个 Cortex-A720 核心:
CPU_LIST=0,1,6-11
THREAD_NUM=8
其中,实际绑定的逻辑 CPU 为:
0,1,6,7,8,9,10,11
CPU 2、3、4、5 为 Cortex-A520 小核,不纳入默认 CPU / KleidiAI 推理的绑核范围。
如需测试 4 核配置,应绑定当前最高频的 4 个 Cortex-A720 核心:
CPU_LIST=0,1,10,11
THREAD_NUM=4
检查当前设置:
echo "CPU_LIST=$CPU_LIST"
echo "THREAD_NUM=$THREAD_NUM"
taskset -c "$CPU_LIST" true
本节后续默认使用 8 个 Cortex-A720 核心:
CPU_LIST=0,1,6-11
THREAD_NUM=8
为避免直接修改原始配置,复制 CPU 配置:
cd "$MODEL_DIR"
cp config.json config-cpu.json
根据 THREAD_NUM 修改配置:
python3 - "$THREAD_NUM" <<'PY'
import json
import sys
from pathlib import Path
thread_num = int(sys.argv[1])
path = Path("config-cpu.json")
with path.open("r", encoding="utf-8") as file:
config = json.load(file)
config["backend_type"] = "cpu"
config["thread_num"] = thread_num
config.setdefault("precision", "low")
config.setdefault("memory", "low")
with path.open("w", encoding="utf-8") as file:
json.dump(config, file, indent=4, ensure_ascii=False)
print(json.dumps(config, indent=4, ensure_ascii=False))
PY
检查配置:
grep -nE \
'"backend_type"|"thread_num"|"precision"|"memory"' \
"$MODEL_DIR/config-cpu.json"
使用默认 8 核配置时,预期包含:
"backend_type": "cpu",
"thread_num": 8
创建测试 Prompt:
cat > ~/mnn/text_baseline_prompt.txt <<'EOF'
请用一句话介绍你自己。
EOF
进行 MNN CPU / KleidiAI 推理时,应确保以下三项一致:
CPU_LIST 中包含的 CPU 数量;
config-cpu.json 中的 thread_num;
llm_bench 命令中的 -t 参数。
默认配置为:
taskset CPU 数量:8
config-cpu.json thread_num:8
llm_bench -t:8
3.1.2.6 运行 MNN CPU / KleidiAI 推理
设置当前构建目录的动态库路径:
export LD_LIBRARY_PATH="$HOME/mnn/MNN/build-cpu:$HOME/mnn/MNN/build-cpu/express:${LD_LIBRARY_PATH:-}"
执行绑核后的 CPU 推理:
taskset -c "$CPU_LIST" \
"$LLM_DEMO" \
"$MODEL_DIR/config-cpu.json" \
~/mnn/text_baseline_prompt.txt
保存完整日志:
taskset -c "$CPU_LIST" \
"$LLM_DEMO" \
"$MODEL_DIR/config-cpu.json" \
~/mnn/text_baseline_prompt.txt \
2>&1 | tee ~/mnn/mnn-cpu.log
如果模型能够正常加载并输出文本,说明 MNN CPU 推理链路基本可用。
检查 KleidiAI 信息:
grep -i "kleidiai" \
~/mnn/mnn-cpu.log
同时检查构建配置:
grep -E \
"^MNN_KLEIDIAI(:|_DEFAULT_ON:)" \
~/mnn/MNN/build-cpu/CMakeCache.txt
其中:
MNN_KLEIDIAI=ON
表示构建时集成了 KleidiAI;
MNN_KLEIDIAI_DEFAULT_ON=ON
表示运行时默认优先使用 KleidiAI Kernel。
但不能仅凭模型能够输出文本判断 KleidiAI 已经生效。正式测试时,应结合:
MNN Commit;
CMake 配置;
运行日志;
CPU 指令能力;
线程数和绑核范围;
相同模型下的性能数据。
3.1.2.7 MNN CPU 性能测试
先查看当前版本参数:
"$LLM_BENCH" --help
使用相同 CPU 绑定范围执行测试:
taskset -c "$CPU_LIST" \
"$LLM_BENCH" \
-m "$MODEL_DIR/config-cpu.json" \
-a cpu \
-t "$THREAD_NUM" \
-p 32,128 \
-n 32 \
-rep 3 \
-kv true \
-fp ~/mnn/mnn-cpu-bench.md
主要参数说明如下:
参数 |
说明 |
|---|---|
-m |
模型目录中的 config.json 路径 |
-a cpu |
使用 CPU 后端 |
-t |
CPU 推理线程数 |
-p |
Prompt 长度 |
-n |
生成长度 |
-rep |
每项重复测试次数 |
-kv true |
Decode 阶段考虑历史 KV Cache |
-fp |
将结果写入指定文件 |
如果当前社区版本不支持其中某个参数,应删除该参数,并以:
“$LLM_BENCH” –help
的实际输出为准。
正式测试时,config-cpu.json 中的 thread_num、llm_bench -t 和 taskset 绑定的 CPU 数量应保持一致。
3.1.2.8 可选:编译 MNN OpenCL 版本
MNN OpenCL 后端依赖 CIX GO 图形引擎提供的 GPU 驱动和 OpenCL Runtime。
检查 OpenCL Runtime:
clinfo
过滤关键信息:
clinfo | grep -E \
"Platform Name|Platform Version|Device Name|Device Version|Driver Version"
如果能够识别 OpenCL Platform 和板载 GPU Device,且未出现 Loader 或设备初始化错误,说明 OpenCL Runtime 基本可用。
清理构建目录:
cd ~/mnn/MNN
rm -rf build-opencl
配置 OpenCL、LLM 和 KleidiAI 构建:
cmake -S . -B build-opencl \
-DCMAKE_BUILD_TYPE=Release \
-DMNN_BUILD_SHARED_LIBS=ON \
-DMNN_BUILD_LLM=ON \
-DMNN_OPENCL=ON \
-DMNN_USE_SYSTEM_LIB=ON \
-DMNN_SEP_BUILD=OFF \
-DMNN_LOW_MEMORY=ON \
-DMNN_SUPPORT_TRANSFORMER_FUSE=ON \
-DMNN_KLEIDIAI=ON \
-DMNN_KLEIDIAI_DEFAULT_ON=ON
检查配置:
grep -E \
"^MNN_(BUILD_LLM|OPENCL|USE_SYSTEM_LIB|SEP_BUILD|KLEIDIAI|KLEIDIAI_DEFAULT_ON):" \
build-opencl/CMakeCache.txt
预期至少包含:
MNN_BUILD_LLM:BOOL=ON
MNN_OPENCL:BOOL=ON
MNN_USE_SYSTEM_LIB:BOOL=ON
MNN_SEP_BUILD:BOOL=OFF
开始编译:
cmake --build build-opencl -j4
如果内存不足:
cmake --build build-opencl -j2
查找程序:
LLM_DEMO_OPENCL="$(find ~/mnn/MNN/build-opencl \
-type f -name llm_demo \
| head -n 1)"
LLM_BENCH_OPENCL="$(find ~/mnn/MNN/build-opencl \
-type f -name llm_bench \
| head -n 1)"
检查:
echo "$LLM_DEMO_OPENCL"
echo "$LLM_BENCH_OPENCL"
test -x "$LLM_DEMO_OPENCL"
test -x "$LLM_BENCH_OPENCL"
设置动态库路径:
export LD_LIBRARY_PATH="$HOME/mnn/MNN/build-opencl:$HOME/mnn/MNN/build-opencl/express:${LD_LIBRARY_PATH:-}"
检查动态库:
ldd "$LLM_DEMO_OPENCL" \
| grep -E "libMNN|libllm|OpenCL|not found"
3.1.2.9 创建 OpenCL 配置
复制原始模型配置:
cd "$MODEL_DIR"
cp config.json config-opencl.json
修改 OpenCL 配置:
python3 - <<'PY'
import json
from pathlib import Path
path = Path("config-opencl.json")
with path.open("r", encoding="utf-8") as file:
config = json.load(file)
config["backend_type"] = "opencl"
config["thread_num"] = 4
config.setdefault("precision", "low")
config.setdefault("memory", "low")
with path.open("w", encoding="utf-8") as file:
json.dump(config, file, indent=4, ensure_ascii=False)
print(json.dumps(config, indent=4, ensure_ascii=False))
PY
检查配置:
grep -nE \
'"backend_type"|"thread_num"|"precision"|"memory"' \
"$MODEL_DIR/config-opencl.json"
预期包含:
"backend_type": "opencl"
需要注意:
在 MNN OpenCL LLM 测试中,thread_num=4 或 llm_bench -t 4 不应简单理解为使用四个 CPU 线程。对于 OpenCL 后端,该参数与 GPU Mode 有关,当前社区版本通常推荐使用 4,但应以当前版本文档和 llm_bench –help 为准。
3.1.2.10 运行 MNN OpenCL 推理
OpenCL 推理的主要计算由 GPU 承担,但 CPU 仍可能负责:
Tokenizer;
模型调度;
内存管理;
数据搬运;
部分 OpenCL 不支持的算子。
OpenCL 推理默认将宿主进程绑定至当前最高频的 4 个 Cortex-A720 核心:
GPU_CPU_LIST=0,1,10,11
检查绑定范围:
echo "GPU_CPU_LIST=$GPU_CPU_LIST"
taskset -c "$GPU_CPU_LIST" true
执行 OpenCL 推理并保存日志:
taskset -c "$GPU_CPU_LIST" \
"$LLM_DEMO_OPENCL" \
"$MODEL_DIR/config-opencl.json" \
~/mnn/text_baseline_prompt.txt \
2>&1 | tee ~/mnn/mnn-opencl.log
检查日志:
grep -Ei \
"opencl|backend|device|fallback|error" \
~/mnn/mnn-opencl.log
模型能够输出文本,只能说明推理流程能够运行,不能单独证明 OpenCL 已实际参与计算。
满足以下条件,可以认为 MNN OpenCL 推理路径基本跑通:
clinfo 能够识别 OpenCL Platform 和 GPU Device;
构建配置中显示 MNN_OPENCL=ON;
使用的是 build-opencl 中编译的程序;
config-opencl.json 中设置了 backend_type=opencl;
运行日志显示 OpenCL 后端或 GPU Device 已初始化;
未出现 OpenCL 初始化失败或完全回退至 CPU;
模型能够正常生成文本。
OpenCL 推理过程中,日志中出现 CPU 信息不一定表示 OpenCL 失败。重点应确认是否出现 OpenCL 初始化失败,或者整个模型完全回退至 CPU。
需要注意:
"thread_num": 4
以及 llm_bench 命令中的:
-t 4
在当前 MNN OpenCL LLM 路径中与 OpenCL GPU Mode 有关,不能简单理解为使用 4 个 CPU 推理线程。
真正限制宿主进程 CPU 运行范围的是:
taskset -c "$GPU_CPU_LIST"
即:
taskset -c 0,1,10,11
OpenCL 首次运行时可能执行 Kernel Tuning 并生成缓存,因此首次运行结果通常不适合作为正式性能数据。建议先运行一次预热,再执行正式测试。
3.1.2.11 CPU 与 OpenCL 性能对比
MNN CPU / KleidiAI 测试使用全部 8 个 Cortex-A720 核心:
CPU_LIST=0,1,6-11
THREAD_NUM=8
执行 CPU 测试:
taskset -c "$CPU_LIST" \
"$LLM_BENCH" \
-m "$MODEL_DIR/config-cpu.json" \
-a cpu \
-t "$THREAD_NUM" \
-p 32,128 \
-n 32 \
-rep 3 \
-kv true \
-fp ~/mnn/mnn-cpu-bench.md
OpenCL 测试将宿主进程绑定至当前最高频的 4 个 Cortex-A720 核心:
GPU_CPU_LIST=0,1,10,11
OpenCL 预热:
taskset -c "$GPU_CPU_LIST" \
"$LLM_BENCH_OPENCL" \
-m "$MODEL_DIR/config-opencl.json" \
-a opencl \
-t 4 \
-p 32 \
-n 16 \
-rep 1 \
-kv true
OpenCL 正式测试:
taskset -c "$GPU_CPU_LIST" \
"$LLM_BENCH_OPENCL" \
-m "$MODEL_DIR/config-opencl.json" \
-a opencl \
-t 4 \
-p 32,128 \
-n 32 \
-rep 3 \
-kv true \
-fp ~/mnn/mnn-opencl-bench.md
正式对比时,应固定:
模型及量化配置;
Prompt 长度;
生成长度;
测试重复次数;
MNN Commit;
系统镜像;
GPU Runtime;
系统电源状态;
系统温度和散热状态;
后台进程负载。
CPU 和 OpenCL 使用不同的 CPU 绑定配置:
推理路径 |
CPU 绑定范围 |
线程或模式参数 |
|---|---|---|
CPU / KleidiAI |
0,1,6-11 |
thread_num=8、-t 8 |
OpenCL |
0,1,10,11 |
thread_num=4、-t 4 |
CPU 测试中的 -t 表示 CPU 推理线程数,应与以下两项保持一致:
config-cpu.json 中的 thread_num;
taskset 绑定的 CPU 数量。
即默认 CPU 配置为:
CPU_LIST=0,1,6-11
THREAD_NUM=8
config-cpu.json thread_num=8
llm_bench -t 8
OpenCL 测试中的 -t 4 与 OpenCL GPU Mode 有关,不能直接与 CPU 测试中的 4 个或 8 个 CPU 推理线程作等价解释。
OpenCL 宿主进程实际绑定的 CPU 范围由以下命令决定:
taskset -c "$GPU_CPU_LIST"
默认对应:
CPU 0、1、10、11
该测试用于比较两条推荐部署路径的实际性能。由于 CPU 和 OpenCL 路径承担主要计算的硬件不同,CPU 线程数和 CPU 绑定范围不要求完全一致,但必须分别记录。
如需严格控制 CPU 资源进行后端对比,可以额外将 CPU 和 OpenCL 路径都绑定至相同的 4 个 Cortex-A720 核心:
CPU_LIST=0,1,10,11
THREAD_NUM=4
GPU_CPU_LIST=0,1,10,11
此时还需要将 config-cpu.json 中的:
"thread_num": 8
修改为:
"thread_num": 4
并将 CPU Benchmark 命令中的:
-t 8
修改为:
-t 4
正式发布的默认测试配置仍建议使用:
MNN CPU / KleidiAI:全部 8 个 Cortex-A720;
MNN OpenCL:4 个最高频 Cortex-A720。
测试结果用于比较当前设备和当前环境下不同后端的相对性能,不应直接作为不同开发板、不同模型或不同软件版本之间的统一性能基准。
3.1.2.12 MNN 常见问题
问题现象 |
可能原因 |
处理建议 |
|---|---|---|
找不到 llm_demo |
未启用 MNN_BUILD_LLM 或编译失败 |
检查 CMake 配置和编译日志 |
找不到 llm_bench |
当前版本未生成或构建失败 |
使用 find 查找并检查编译日志 |
模型文件只有几百字节 |
下载到 Git LFS 指针 |
在模型目录执行 git lfs pull |
MNN 无法加载 GGUF |
模型格式不兼容 |
使用完整 MNN 模型目录 |
找不到配置或权重 |
模型目录不完整或相对路径失效 |
保持配置文件位于模型原目录 |
动态库显示 not found |
当前构建库不在搜索路径 |
临时设置 LD_LIBRARY_PATH |
出现 undefined symbol |
程序与 libMNN.so 版本不一致 |
使用 ldd 检查实际库路径 |
CPU 可以运行但无法确认 KleidiAI |
仅编译了支持,日志未说明实际使用 |
检查构建配置、日志和性能 |
taskset 失败 |
CPU 编号不存在或 CPU 不在线 |
使用 lscpu -e 检查范围 |
CPU 配置线程数不一致 |
thread_num、-t 和绑核数量不同 |
统一三者配置 |
clinfo 找不到设备 |
OpenCL Runtime、ICD 或驱动异常 |
检查 GO 图形引擎和系统版本 |
OpenCL 初始化失败 |
未构建 OpenCL 或 Runtime 异常 |
检查 MNN_OPENCL、clinfo 和日志 |
OpenCL 回退至 CPU |
后端初始化失败或算子不支持 |
检查完整日志和后端信息 |
OpenCL 首次运行较慢 |
正在执行 Kernel Tuning |
预热一次后重新测试 |
性能波动较大 |
温度、调度、绑核或负载不同 |
固定测试条件并重复测试 |