news 2026/9/7 3:04:15

llama.cpp ET 后端深度指南:在开源多核 RISC-V 加速器上构建、运行与调优 LLM 推理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
llama.cpp ET 后端深度指南:在开源多核 RISC-V 加速器上构建、运行与调优 LLM 推理

llama.cpp ET 后端深度指南:在开源多核 RISC-V 加速器上构建、运行与调优 LLM 推理

【免费下载链接】llama.cppLLM inference in C/C++项目地址: https://gitcode.com/GitHub_Trending/ll/llama.cpp

本文基于仓库中的 docs/backend/ET.md 及其引用的 算子支持报告、ggml-et 后端实现 与 内核源码,完整覆盖 ET 后端的背景定位、功能限制与受支持模型清单、从工具链到 CMake 的构建流程、容器化运行方式,并深入到内核开发约束、ET_PERF性能日志格式、运行时 Profiling 与 Uberkernel 内核融合机制,帮助读者在 ET-SOC 平台上真正把 llama.cpp 跑起来并具备二次开发能力。

一、ET 后端的背景与定位

ET是 llama.cpp 针对 ET-SOC 中可以找到对应的两个构建开关:

option(GGML_ET "ggml: use ET backend" OFF) option(GGML_ET_SYSEMU "ggml: use ET backend via sysemu" OFF)

GGML_ET默认关闭,需要显式开启;GGML_ET_SYSEMU则用于在没有实体硬件时通过系统级模拟器(sysemu)运行后端,这对 CI 验证和早期开发非常有用。后端的主实现位于 ggml-et.cpp,算子分发在 ggml-et-ops.cpp,设备端裸机内核则位于 et-kernels/src 目录。

二、当前限制与受支持模型

2.1 功能限制

原文档明确列出了 ET 后端的四项限制,使用与选型时必须心中有数:

限制项说明
算子覆盖仅支持有限的算子集合,具体以 docs/ops.md 与 docs/ops/ET.csv 为准
量化格式仅支持q8_0q4_0fp16q4_K为部分支持)
并发同一时间只允许一个llama.cpp 实例使用该设备(当前固件限制)
MoEMoE 模型支持有限(但可用)

受上述限制影响,只有部分模型能够完全跑在 ET-SOC 上。需要说明的是:任何 llama.cpp 支持的模型都可以加载运行,只是部分甚至大部分算子会回落到 CPU 后端,性能会显著下降。

2.2 完全受支持的模型清单

原文档列出的可全量跑在 ET-SOC 上的模型包括:

  • Qwen3 系列(非 MoE),例如ggml-org/Qwen3-0.6B-GGUF:q8_0ggml-org/Qwen3-14B-GGUF:q8_0
  • Llama 3.2(1B/3B),例如lmstudio-community/Llama-3.2-1B-Instruct-GGUF:q8_0
  • SmolLM2,例如unsloth/SmolLM2-135M-Instruct-GGUF:q8_0
  • Llama 3.1 系列
  • RWKV v7 系列
  • TinyLLaMA

从源码结构看,这一清单与设备端内核的覆盖面是吻合的:et-kernels/src 下已有mul_mat_Q4_0.cmul_mat_Q8_0.crope_f32.cflash_attn_ext_f32.crms_norm_f32.c等面向稠密 Transformer 的核心内核,以及rwkv_wkv6_f32.crwkv_wkv7_f32.c等专门服务 RWKV v6/v7 的非注意力内核,还有mul_mat_id_Q4_0.cmul_mat_id_Q8_0.c用于 MoE 的路由矩阵乘。

2.3 算子支持报告

docs/ops/ET.csv 是机器可读的算子支持矩阵,全文超过一万六千行,逐行记录每个算子在不同参数组合下的支持状态,表头为:

"backend_name","op_name","op_params","test_mode","supported","error_message","backend_reg_name"

例如其中一条记录为:

"ET","ABS","type=f16,ne_a=[128,2,2,2],v=0","support","0","no","ET"

supported字段为0表示该算子在对应参数形态下未在 ET 后端实现(运行时将回落到 CPU 或其他后端)。评估一个新模型是否适合 ET 平台时,对照此文件检查其计算图涉及的算子是最可靠的方法。

三、环境准备与构建

3.1 前置条件

构建 ET 后端需要两套外部组件,两者默认都应安装到/opt/et(也可以分别通过环境变量指定):

  1. 自定义 RISC-V 工具链:按aifoundry-org/riscv-gnu-toolchain仓库et/aifoundry分支的说明安装;
  2. ET 平台组件:按aifoundry-org/et-platform仓库的说明安装。

安装后设置路径:

# 设置工具链与 ET 平台路径(/opt/et 为默认值) export ET_TOOLCHAIN=/opt/et export ET_PLATFORM=/opt/et

这个路径解析逻辑在后端源码中同样体现:ggml-et.cpp 中的ggml_et_get_default_et_path()ET_TOOLCHAINTOOLCHAIN_ROOT的顺序查找环境变量,都未设置时回落到默认的/opt/et。启用 sysemu 模式时,后端会基于该路径定位sys_emu可执行文件以及BootromTrampolineToBL2.elfServiceProcessorBL2_fast-boot.elfMachineMinion.elfMasterMinion.elfWorkerMinion.elf等固件文件(见 ggml-et.cpp 的ggml_et_get_default_sysemu_options()),并在启动前校验这些文件存在且非空——若文件缺失,模拟器会静默挂起。

3.2 构建 llama.cpp

先获取带 ET 后端的 llama.cpp 源码(按原文档说明应检出et分支):

git clone https://github.com/aifoundry-org/llama.cpp cd llama.cpp

然后执行构建:

cmake -B build -DGGML_ET=ON cmake --build build --config Release # 可选: # cmake --install build

若希望在系统模拟器(sysemu)上构建而非面向实体硬件:

cmake -B build -DGGML_ET=ON -DGGML_ET_SYSEMU=ON cmake --build build --config Release

3.3 运行

运行方式与常规 llama.cpp 二进制完全一致(前提是 ET-SOC 设备已安装、内核驱动已加载):

llama-cli -m mymodel.gguf # 或 llama-server -hf ggml-org/Qwen3-8B-GGUF:q8_0

在 Docker 容器内运行时,需要把设备节点透传进去。ET 设备使用两个字符设备:/dev/et0_mgmt(管理通道)与/dev/et0_ops(算子/数据通道):

docker run \ --device=/dev/et0_mgmt:/dev/et0_mgmt \ --device=/dev/et0_ops:/dev/et0_ops \ ...

四、内核开发:裸机 RISC-V 内核的编写约束

4.1 开发位置与构建方式

计算内核开发位于ggml/src/ggml-et/et-kernels目录。内核使用自定义 RISC-V GNU 工具链编译、由 CMake 管理,当前以裸机 ELF 文件形式产出,不依赖标准库或任何其他运行时。核心逻辑大量使用内联汇编直接操作 ET 的向量/SIMD 指令。典型的内核文件包括:

  • 矩阵乘:mul_mat_Q4_0.c、mul_mat_Q8_0.c、mul_mat_f32.c,以及带 matrix engine 后缀的硬件矩阵引擎变体(如mul_mat_f16_matrix_engine.cmul_mat_Q4_0_matrix_engine.c);
  • 注意力:flash_attn_ext_f32.c、flash_attn_ext_f16_me.c
  • 归一化/逐元素:rms_norm_f32.crms_norm_mul_f32.c(融合 RMS_NORM+MUL)、softmax_f32.cglu_f32.cel_map_f32.c等。

原文档特别提示:多数内核目前实现得非常朴素,仍有大量可优化的空间(low hanging fruits),对贡献者是很好的切入点。

4.2 硬件未实现指令的编译期检查

这是 ET 内核开发中最容易踩的坑:编译器可能发出硬件尚未实现、固件软件模拟也尚未就绪的指令。在固件未来能够在异常处理中透明地捕获并模拟这些指令之前,内核构建流程包含一个检查步骤:若编译产物中发现了未实现指令,构建直接失败。问题指令清单如下:

  • 浮点除法/取余:FDIV.PIFDIVU.PIFREMU.PIFREM.PIFDIV.SFDIV.PS
  • 平方根/倒数平方根:FSQRT.SFSQRT.PSFRSQ.PS
  • 三角函数:FSIN.PS
  • 长整型与浮点互转(long cast):FCVT.S.LFCVT.S.LUFCVT.L.SFCVT.LU.S

由此得出的实践约束是:目前应避免任何涉及浮点数的除法、三角函数,以及 long 与 float 之间的直接转换

仓库中已提供的官方绕路方案在 math_fp.h:

  • et_fdiv(a, b):利用 ET 硬件的FRCP.PS(倒数)指令实现a / b = a × (1/b),源码见 math_fp.h;
  • et_powf(base, exp):基于FLOG.PS/FEXP.PS实现pow(base, exp) = exp(exp × ln(base)),并手工处理了 0、负底数(NaN)、exp=0/1等边界情况,见 math_fp.h;
  • long 转 float:假定 long 值足够小(能塞进 32 位),可经int中转,即a = (float)(int)(b)

此外,et_platform 的et-common-libs/include/etsoc/isa/目录中有一批更高层的助手函数(封装了张量扩展指令、同步原语等复杂指令)。它们最初是为固件需求开发的,不参与计算内核的构建流程,但内核开发者完全可以参考或直接尝试链接使用。

4.3 提交前必做事项

修改任何算子和/或内核后,必须按 docs/ops.md 中的说明更新受支持算子报告(即docs/ops/ET.csv一类的文件),否则支持矩阵会与实际能力脱节。

五、性能观测:ET_PERF 日志与运行时 Profiling

5.1 ET_PERF 内核级性能日志

启用日志(例如通过--log-fileCLI 参数)后,每一次计算内核执行都会输出一个以ET_PERF开头、以管道符分隔键值对的性能行。原文档给出的示例:

ET_PERF|op=MUL_MAT|kernel=mul_mat_f32_Q8_0xf32|duration_us=3112|tensor=Qcur-0|shape=[4096,2,1,1]|start_us=48437862009|end_us=48437865121|flops=67100672 ET_PERF|op=ROPE|kernel=rope_f32|duration_us=9266|tensor=Qcur-0|shape=[128,32,2,1]|start_us=48437865128|end_us=48437874394|mode=0x0|n_dims=128|freq_base=500000.00|freq_scale=1.00

各字段含义:

含义
opggml 算子名(如MUL_MATROPE
kernel实际执行的内核名,含类型变体(如mul_mat_f32_Q8_0xf32表示 Q8_0 权重 × f32 激活)
duration_us该次内核执行耗时(微秒)
tensor/shape目标张量名称与四维形状[ne0,ne1,ne2,ne3]
start_us/end_us起止时间戳(微秒)
flops有效浮点运算数(注意:不是每秒浮点运算数 FLOPS)
其余键随算子而异,如 ROPE 的moden_dimsfreq_basefreq_scale,MUL_MAT_ID 的n_expert/n_expert_used

从源码看,这些日志由 ggml-et-ops.h 中的ET_PERF_START()/ET_PERF_END()/ET_PERF_END_EXT()宏生成:宏在ET_PERF_RECORD编译定义下展开为基于ggml_time_us()计时并通过GGML_LOG_DEBUG输出的代码,未定义时退化为空操作。这解释了文档中“启用日志时才输出”的行为——ET_PERF行走的是 DEBUG 日志通道,因此需要--log-file等参数打开 DEBUG 级日志才能看到。每个算子函数(如ggml_et_op_mul_matggml_et_op_ropeggml_et_op_flash_attn_ext)在 ggml-et-ops.cpp 中都遵循“计时起点 → 启动内核 → 计时终点并附算子专属字段”的同一模式。

5.2 GGML_ET_PROFILE:运行时级 Profiling

设置环境变量GGML_ET_PROFILE为某个路径,即可开启 ET-SOC 运行时级别的 profiling。退出时 profiling/tracing 结果会写入该路径下。文档描述为et_runtime_trace.jsonkernel_map;从 ggml-et.cpp 的源码看,实际打开的第二个文件为kernel_id.json(内核名到运行时 KernelId 的映射),配合 trace 文件使用,可用于把 trace 中的内核 ID 还原为可读内核名。

export GGML_ET_PROFILE=/path/to/profile_dir llama-cli -m mymodel.gguf # 退出后在 profile_dir 中查看 trace 结果

六、Uberkernel:内核融合与设备侧分发

ET 后端实现了一个名为Uberkernel的机制(名字源自 Esperanto AI 的编译器),用于缓解 ET SDK 中相当可观的算子间间隙(op-to-op gap):它把多个已有的内核实现在设备侧用同步原语串联调度,一次启动、连续执行,减少算子切换开销。

开发 Uberkernel 难度远高于普通内核:由于处理器设计的原因,子内核调用之间不存在天然的内存可见性边界(no natural memory visibility horizon),缓存一致性必须显式处理。uberkernel.c 中的调度器按kernel_id分发到各内核入口,代码中可以看到大量针对激活张量的 L2 缓存逐出(evict_region_past_l2)调用,以及注释掉的“weights 为只读、永不陈旧”说明——这正是文档所称“开发与调试困难”的直接体现。

关键事实(以原文档与源码为准):

  • Uberkernel 默认关闭,由环境变量GGML_ET_UBERKERNEL控制;源码中(ggml-et.cpp)对非空且非0的值判为启用,即export GGML_ET_UBERKERNEL=1开启;
  • 启用后可带来显著性能提升,但目前仅在 LLaMA 3.2 系列与 Qwen 3.5 上验证过,其他模型不建议开启;
  • 从 uberkernel.c 的分发switch可见其当前覆盖的内核族:逐元素映射(el_map)、GLUROPERMS_NORM/RMS_NORM_MULSOFTMAXSET_ROWS/GET_ROWSSUM_ROWSCUMSUMNORM/L2_NORM/GROUP_NORMREPEATDIAGTRI/SOLVE_TRIPADCONTFILLSETCONCATIM2COL,三类源的MUL_MAT_IDFLASH_ATTN_EXT(f32 与 f16 矩阵引擎变体)、GATED_DELTA_NETSSM_SCAN、RWKV 的WKV6/WKV7,以及多种MUL_MAT(f16/f32/Q8_0/Q4_0,含 matrix engine 变体)。

七、路线图

原文档在 Roadmap 一节给出的后续规划包括:

  1. 所有模型启用 Uberkernel(当前仅限 LLaMA 3.2 与 Qwen 3.5 验证);
  2. 支持更多算子(扩大 docs/ops/ET.csv 中 supported 的覆盖面);
  3. 改进TTS 模型支持;
  4. 支持更多量化格式(当前以q8_0/q4_0为主,fp16q4_K仅部分支持)。

小结

ET 后端让 llama.cpp 跑上了完全开源的 RISC-V 多核加速器:构建只需两个 CMake 开关(-DGGML_ET=ON,模拟器再加-DGGML_ET_SYSEMU=ON),运行与常规二进制无异(容器场景挂载/dev/et0_mgmt/dev/et0_ops);选型时以q8_0/q4_0量化与 受支持模型清单 为准;内核开发需牢记裸机 ELF 形态与未实现指令清单,用math_fp.h中的et_fdiv/et_powf替代除法与幂运算;调优则依赖ET_PERF日志、GGML_ET_PROFILE运行时 trace,以及(在验证过的模型上)GGML_ET_UBERKERNEL融合调度。相关入口文件:后端实现 ggml-et.cpp、算子层 ggml-et-ops.cpp、内核目录 et-kernels/src、算子支持矩阵 docs/ops/ET.csv、算子文档说明 docs/ops.md。

【免费下载链接】llama.cppLLM inference in C/C++项目地址: https://gitcode.com/GitHub_Trending/ll/llama.cpp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 3:02:30

软考高级系统架构设计师备考:精讲真题模拟笔记闭环复习法

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 3:00:49

PyTorch图像分类实战:从零搭建CNN模型与训练调参指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 3:00:24

Music nano轻量音乐模型实战:低配机器从环境配置到批量任务全流程

如果要在低配机器上跑一个轻量音乐生成或音频处理模型,很多人会先看效果演示,再看模型参数量。但真正开始动手时,最先卡住你的往往不是音乐质量,而是环境、路径、输入格式、资源占用这些基础环节。Music nano 这类带 nano 后缀的轻…

作者头像 李华
网站建设 2026/9/7 3:00:12

视频编码评测必备:UVG 4K高帧率原始数据集全解析

简介:一份论文级PDF资料,面向视频编码研究者与多媒体工程开发人员,系统介绍芬兰坦佩雷大学Ultra Video Group发布的UVG开放数据集。该数据集收录16段38402160分辨率的4K原始YUV序列,以50/120fps高帧率采集,支持8-bit与…

作者头像 李华
网站建设 2026/9/7 2:58:47

轻量开源版IDEA社区版实测:配置技巧与日常开发能力全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 2:58:04

周志华《机器学习》代码练习实战:从环境搭建到算法调参

简介:周志华《机器学习》代码练习包,集中整理了书中典型算法的Python实现,面向正在学习该书或希望强化机器学习基础的读者。内容覆盖线性模型、逻辑回归、LDA、决策树、集成学习、SVM等常用方法,每个练习以独立脚本呈现&#xff0…

作者头像 李华