【ggml系列】【第五篇】ggml_compute_forward_mul_mat 矩阵乘法算子源码与性能优化深度解析

【ggml系列】【第五篇】ggml_compute_forward_mul_mat 矩阵乘法算子源码与性能优化深度解析

上一篇:【ggml系列】【第四篇】ggml_graph_compute 多线程计算引擎与算子分发源码深度解析 剖析了线程池对整体计算图的调度控制。本篇我们将聚焦 LLM 推理框架中计算量最大、优化最极致的核心算子------矩阵乘法(Matrix Multiplication)


  • 核心源码文件src/ggml-cpu/ggml-cpu.c
  • 核心函数声明
c 复制代码
void ggml_compute_forward_mul_mat(const struct ggml_compute_params * params, struct ggml_tensor * dst);

该函数展现了现代高性能计算在 CPU 端极致工程优化的典范设计:快速分支路由、在线动态量化/转换、无锁动态抢占调度(Atomic Chunk Stealing)、NUMA 感知划分 以及缓存行友好(Cache-Friendly)的分块计算


整体架构与执行流程

在深入代码细节前,我们先通过全局流程图把握 ggml_compute_forward_mul_mat 的整体执行逻辑:
#mermaid-svg-y329bBHUTR5tKGCH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-y329bBHUTR5tKGCH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-y329bBHUTR5tKGCH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-y329bBHUTR5tKGCH .error-icon{fill:#552222;}#mermaid-svg-y329bBHUTR5tKGCH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-y329bBHUTR5tKGCH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-y329bBHUTR5tKGCH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-y329bBHUTR5tKGCH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-y329bBHUTR5tKGCH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-y329bBHUTR5tKGCH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-y329bBHUTR5tKGCH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-y329bBHUTR5tKGCH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-y329bBHUTR5tKGCH .marker.cross{stroke:#333333;}#mermaid-svg-y329bBHUTR5tKGCH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-y329bBHUTR5tKGCH p{margin:0;}#mermaid-svg-y329bBHUTR5tKGCH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-y329bBHUTR5tKGCH .cluster-label text{fill:#333;}#mermaid-svg-y329bBHUTR5tKGCH .cluster-label span{color:#333;}#mermaid-svg-y329bBHUTR5tKGCH .cluster-label span p{background-color:transparent;}#mermaid-svg-y329bBHUTR5tKGCH .label text,#mermaid-svg-y329bBHUTR5tKGCH span{fill:#333;color:#333;}#mermaid-svg-y329bBHUTR5tKGCH .node rect,#mermaid-svg-y329bBHUTR5tKGCH .node circle,#mermaid-svg-y329bBHUTR5tKGCH .node ellipse,#mermaid-svg-y329bBHUTR5tKGCH .node polygon,#mermaid-svg-y329bBHUTR5tKGCH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-y329bBHUTR5tKGCH .rough-node .label text,#mermaid-svg-y329bBHUTR5tKGCH .node .label text,#mermaid-svg-y329bBHUTR5tKGCH .image-shape .label,#mermaid-svg-y329bBHUTR5tKGCH .icon-shape .label{text-anchor:middle;}#mermaid-svg-y329bBHUTR5tKGCH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-y329bBHUTR5tKGCH .rough-node .label,#mermaid-svg-y329bBHUTR5tKGCH .node .label,#mermaid-svg-y329bBHUTR5tKGCH .image-shape .label,#mermaid-svg-y329bBHUTR5tKGCH .icon-shape .label{text-align:center;}#mermaid-svg-y329bBHUTR5tKGCH .node.clickable{cursor:pointer;}#mermaid-svg-y329bBHUTR5tKGCH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-y329bBHUTR5tKGCH .arrowheadPath{fill:#333333;}#mermaid-svg-y329bBHUTR5tKGCH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-y329bBHUTR5tKGCH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-y329bBHUTR5tKGCH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-y329bBHUTR5tKGCH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-y329bBHUTR5tKGCH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-y329bBHUTR5tKGCH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-y329bBHUTR5tKGCH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-y329bBHUTR5tKGCH .cluster text{fill:#333;}#mermaid-svg-y329bBHUTR5tKGCH .cluster span{color:#333;}#mermaid-svg-y329bBHUTR5tKGCH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-y329bBHUTR5tKGCH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-y329bBHUTR5tKGCH rect.text{fill:none;stroke-width:0;}#mermaid-svg-y329bBHUTR5tKGCH .icon-shape,#mermaid-svg-y329bBHUTR5tKGCH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-y329bBHUTR5tKGCH .icon-shape p,#mermaid-svg-y329bBHUTR5tKGCH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-y329bBHUTR5tKGCH .icon-shape .label rect,#mermaid-svg-y329bBHUTR5tKGCH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-y329bBHUTR5tKGCH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-y329bBHUTR5tKGCH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-y329bBHUTR5tKGCH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是













ggml_compute_forward_mul_mat(params, dst)

  1. 获取 src0 / src1 张量
    是否为 Hadamard 变换分支?
    ggml_compute_forward_fwht() 快速返回
    结束
  2. 提取维度 (ne) 与跨步 (nb)
  3. 查询 CPU Type Traits

(确定 vec_dot_type / from_float / nrows)
4. 内存连续性与布局合法性校验 (ASSERT)
llamafile GEMM 可用

且 src1 连续?
llamafile_sgemm() 硬件加速
执行成功?
回退至 GGML 原生 GEMM 路径
src1->type != vec_dot_type?

(需要在线量化/转换)
计算 wdata 缓冲区布局
所有 Worker 线程并行执行 from_float()
将 src1 (FP32) 量化写入 wdata
无需转换 (直接复用原数据)
主线程初始化 atomic current_chunk
【核心屏障】ggml_barrier() 全局同步
进入第二阶段:矩阵分块与计算
压缩高维空间:nr0 = ne0, nr1 = ne1×ne2×ne3
nr0 == 1 或 nr1 == 1?

(GEMV 向量场景)
设置 chunk_size = 64
设置 chunk_size = 16 (GEMM 场景)
计算 2D 切块数 (nchunk0, nchunk1)
Chunk 数量过少 或 处于 NUMA 环境?
按线程数重划(主维度一维切分)
保持二维网格切块划分
计算单块步长 dr0 / dr1
Worker 初始化: current_chunk = ith
current_chunk < 总 Chunk 数?
Worker 线程计算完成
一维 Chunk ID 映射为二维坐标 (ith0, ith1)
计算当前块物理边界 [ir0_start, ir0_end) × [ir1_start, ir1_end)
调用 ggml_compute_forward_mul_mat_one_chunk()
原子抢占下一个任务:

atomic_fetch_add(current_chunk, 1)


阶段详解

阶段 1:特殊分支快速路由与元数据准备

算子入口首先拦截特定算法加速分支(例如 Fast Walsh-Hadamard Transform),避免不必要的矩阵乘开销:

c 复制代码
const int32_t hint = ggml_get_op_params_i32(dst, 1);

// 如果被标记为 Hadamard 变换提示且未使用 reference 模式,直接走专用核函数
if (hint == GGML_HINT_SRC0_IS_HADAMARD && !params->use_ref) {
    ggml_compute_forward_fwht(params, dst);
    return;
}

随后提取各张量的核心几何信息:

  • ne[](Number of Elements):记录各维度的元素个数(0~3 维)。
  • nb[](Number of Bytes / Strides):记录各维度步进 1 个元素所需的内存字节跨步。
c 复制代码
// 权重矩阵 / 左矩阵
src0: ne00, ne01, ne02, ne03 | nb00, nb01, nb02, nb03
// 激活矩阵 / 右矩阵
src1: ne10, ne11, ne12, ne13 | nb10, nb11, nb12, nb13
// 目标矩阵
dst:  ne0,  ne1,  ne2,  ne3  | nb0,  nb1,  nb2,  nb3

阶段 2:确定 Dot Kernel 与类型特征系统

GGML 的类型系统(Type Traits)解耦了存储类型计算类型

c 复制代码
const enum ggml_type vec_dot_type = type_traits_cpu[src0->type].vec_dot_type;
const ggml_from_float_t from_float = type_traits_cpu[vec_dot_type].from_float;
const int64_t vec_dot_num_rows     = type_traits_cpu[src0->type].nrows;
复制代码
src0->type (如 Q4_K / Q8_0)
      │
      ▼
vec_dot_type (如 Q8_K / Q8_0)
      ├── 1. 决定 src1 输入需要在线量化为何种格式
      ├── 2. 绑定对应的底层硬件 SIMD vec_dot 内核
      └── 3. 确定单次 SIMD 指令联合处理的行数(vec_dot_num_rows)

阶段 3:严格的内存布局与连续性断言

在进入计算前,通过一系列断言验证张量形状与内存排布是否满足当前 CPU Kernel 的硬性假设:

c 复制代码
// 1. 验证矩阵乘法输出形状契合性
GGML_ASSERT(ne0 == ne01);
GGML_ASSERT(ne1 == ne11);
GGML_ASSERT(ne2 == ne12);
GGML_ASSERT(ne3 == ne13);

// 2. 核心假设:第 0 维(即内积发生的主维度)必须在物理内存中完全连续
// 连续内存是发挥 SIMD 向量加载性能与 L1 Cache 预取(Prefetch)的关键前提
GGML_ASSERT(nb00 == ggml_type_size(src0->type));
GGML_ASSERT(nb10 == ggml_type_size(src1->type));

// 3. 输出矩阵单调连续性断言
GGML_ASSERT(nb0 == sizeof(float)); // 输出固定为 FP32
GGML_ASSERT(nb0 <= nb1);
GGML_ASSERT(nb1 <= nb2);
GGML_ASSERT(nb2 <= nb3);

阶段 4:llamafile 极速汇编路径(可选加速)

GGML 集成了 Mozilla llamafile(由 Justine Tunney 开发)高度优化的汇编 GEMM 内核。若环境与布局支持,优先调用该加速路径以压榨 AVX-512 / AVX2 / ARM Neon 等指令集极限;若失败或未启用,则平滑回退至 GGML 原生实现。


阶段 5:src1 在线动态量化 / 类型转换

src1->type != vec_dot_type(例如模型权重为 Q4_K,而激活值为 FP32,计算需要 Q8_K)时,多线程协作将 src1 在线量化并写入临时的 params->wdata 工作缓冲区。
#mermaid-svg-Sts9Ba4SeZEMRfFC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Sts9Ba4SeZEMRfFC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Sts9Ba4SeZEMRfFC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Sts9Ba4SeZEMRfFC .error-icon{fill:#552222;}#mermaid-svg-Sts9Ba4SeZEMRfFC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Sts9Ba4SeZEMRfFC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Sts9Ba4SeZEMRfFC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Sts9Ba4SeZEMRfFC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Sts9Ba4SeZEMRfFC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Sts9Ba4SeZEMRfFC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Sts9Ba4SeZEMRfFC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Sts9Ba4SeZEMRfFC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Sts9Ba4SeZEMRfFC .marker.cross{stroke:#333333;}#mermaid-svg-Sts9Ba4SeZEMRfFC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Sts9Ba4SeZEMRfFC p{margin:0;}#mermaid-svg-Sts9Ba4SeZEMRfFC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Sts9Ba4SeZEMRfFC .cluster-label text{fill:#333;}#mermaid-svg-Sts9Ba4SeZEMRfFC .cluster-label span{color:#333;}#mermaid-svg-Sts9Ba4SeZEMRfFC .cluster-label span p{background-color:transparent;}#mermaid-svg-Sts9Ba4SeZEMRfFC .label text,#mermaid-svg-Sts9Ba4SeZEMRfFC span{fill:#333;color:#333;}#mermaid-svg-Sts9Ba4SeZEMRfFC .node rect,#mermaid-svg-Sts9Ba4SeZEMRfFC .node circle,#mermaid-svg-Sts9Ba4SeZEMRfFC .node ellipse,#mermaid-svg-Sts9Ba4SeZEMRfFC .node polygon,#mermaid-svg-Sts9Ba4SeZEMRfFC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Sts9Ba4SeZEMRfFC .rough-node .label text,#mermaid-svg-Sts9Ba4SeZEMRfFC .node .label text,#mermaid-svg-Sts9Ba4SeZEMRfFC .image-shape .label,#mermaid-svg-Sts9Ba4SeZEMRfFC .icon-shape .label{text-anchor:middle;}#mermaid-svg-Sts9Ba4SeZEMRfFC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Sts9Ba4SeZEMRfFC .rough-node .label,#mermaid-svg-Sts9Ba4SeZEMRfFC .node .label,#mermaid-svg-Sts9Ba4SeZEMRfFC .image-shape .label,#mermaid-svg-Sts9Ba4SeZEMRfFC .icon-shape .label{text-align:center;}#mermaid-svg-Sts9Ba4SeZEMRfFC .node.clickable{cursor:pointer;}#mermaid-svg-Sts9Ba4SeZEMRfFC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Sts9Ba4SeZEMRfFC .arrowheadPath{fill:#333333;}#mermaid-svg-Sts9Ba4SeZEMRfFC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Sts9Ba4SeZEMRfFC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Sts9Ba4SeZEMRfFC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Sts9Ba4SeZEMRfFC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Sts9Ba4SeZEMRfFC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Sts9Ba4SeZEMRfFC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Sts9Ba4SeZEMRfFC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Sts9Ba4SeZEMRfFC .cluster text{fill:#333;}#mermaid-svg-Sts9Ba4SeZEMRfFC .cluster span{color:#333;}#mermaid-svg-Sts9Ba4SeZEMRfFC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Sts9Ba4SeZEMRfFC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Sts9Ba4SeZEMRfFC rect.text{fill:none;stroke-width:0;}#mermaid-svg-Sts9Ba4SeZEMRfFC .icon-shape,#mermaid-svg-Sts9Ba4SeZEMRfFC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Sts9Ba4SeZEMRfFC .icon-shape p,#mermaid-svg-Sts9Ba4SeZEMRfFC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Sts9Ba4SeZEMRfFC .icon-shape .label rect,#mermaid-svg-Sts9Ba4SeZEMRfFC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Sts9Ba4SeZEMRfFC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Sts9Ba4SeZEMRfFC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Sts9Ba4SeZEMRfFC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 单行内多线程并行分工 (当前线程 ith / 总线程数 nth)
三层循环控制 (i13, i12, i11)
否 (类型已匹配)
是 (需要转换)


开始判断条件
src1->type != vec_dot_type ?
跳过转换,直接进入计算
初始化工作缓冲区 wdata 指针

计算转换后的步长 (nbw0, nbw1, nbw2, nbw3)
断言检查

  1. 缓冲区大小 params->wsize 是否足够

  2. src1->type 是否为 GGML_TYPE_F32
    4D 维度嵌套循环遍历
    Dim3 循环: i13 (0 ~ ne13-1)
    Dim2 循环: i12 (0 ~ ne12-1)
    Dim1 循环: i11 (0 ~ ne11-1)
    多线程切片计算 (Dim0 / 最内层列维度)
    计算当前线程处理的 Block 起始索引:

ne10_block_start = (ith * ne10 / bs) / nth
计算当前线程处理的 Block 结束索引:

ne10_block_end = ((ith + 1) * ne10 / bs) / nth
计算内存偏移量:

• 输入 F32 偏移: ... + ne10_block_start * bs * nb10

• 输出 wdata 偏移: ... + ne10_block_start * nbw0
调用 from_float 函数:

将 (ne10_block_end - ne10_block_start) * bs 个

FP32 浮点数打包转换/量化为 vec_dot_type 格式
所有 Dimension 遍历完成?
等待进入下一阶段: ggml_barrier 同步


阶段 6:关键屏障同步(Memory Barrier)的必要性

在转换完成后,代码调用了全局屏障:

c 复制代码
ggml_barrier(params->threadpool);

为什么此处必须 Barrier?

本质是跨线程的生产者-消费者依赖

  • 阶段 1(生产者) :所有线程按块并行对 src1 实施量化并写入 wdata
  • 阶段 2(消费者) :在后续矩阵乘法中,任意线程要消费的 wdata 区域,极大概率是由其他线程量化生成的

若缺少该屏障,快线程提前进入矩阵乘计算时将读取到未完成量化的脏内存,造成致命的计算错误。


阶段 7 & 8:任务空间扁平化与分块(Chunking)尺寸确定

为了统一高维张量(如 Multi-Head Attention 的 Batch/Head 维度)的调度逻辑,GGML 将 4D 计算任务压平成 2D 任务网格:

n r 0 = n e 0 nr_0 = ne_0 nr0=ne0

n r 1 = n e 1 × n e 2 × n e 3 nr_1 = ne_1 \times ne_2 \times ne_3 nr1=ne1×ne2×ne3

c 复制代码
const int64_t nr0 = ne0;
const int64_t nr1 = ne1 * ne2 * ne3;

// 1. 默认设置 Chunk 大小为 16(兼顾调度均衡性与 L1/L2 缓存驻留)
int chunk_size = 16;

// 2. 针对 GEMV(单 Token 生成阶段,nr0=1 或 nr1=1),扩大 chunk_size 至 64 以减少调度开销
if (nr0 == 1 || nr1 == 1) {
    chunk_size = 64;
}

// 3. 向上取整计算 2D 切块数量
int64_t nchunk0 = (nr0 + chunk_size - 1) / chunk_size;
int64_t nchunk1 = (nr1 + chunk_size - 1) / chunk_size;

阶段 9:NUMA 架构与分块粒度自适应调整

c 复制代码
// 若总 Chunk 数不足以喂饱所有线程(< nth * 4)或者处于 NUMA 架构下:
if (nchunk0 * nchunk1 < nth * 4 || ggml_is_numa()) {
    // 放弃细粒度 2D 网格,直接沿数据量最大的主维度按线程数一维切分
    nchunk0 = nr0 > nr1 ? nth : 1;
    nchunk1 = nr0 > nr1 ? 1 : nth;
}

NUMA 优化考量:在多路 CPU(NUMA)架构下,跨 Node 内存访问与缓存一致性同步开销极大。粗粒度的一维切分能够显著降低跨 Socket 的 Cache Line 争用与内存颠簸。


阶段 10 & 11:动态工作窃取(Work Stealing)与坐标反解

为解决由于各核性能波动(如大小核架构、降频)引起的负载不均衡问题,GGML 采用了轻量级的原子无锁任务抢占机制

c 复制代码
int current_chunk = ith; // 每个 Worker 首先认领属于自身线程 ID 的初始分块

while (current_chunk < nchunk0 * nchunk1) {
    // 1. 将 1D 线性 Chunk ID 反解为 2D 空间网格坐标
    const int64_t ith0 = current_chunk % nchunk0;
    const int64_t ith1 = current_chunk / nchunk0;

    // 2. 映射当前块对应的输出张量物理行列边界 [start, end)
    const int64_t ir0_start = dr0 * ith0;
    const int64_t ir0_end   = MIN(ir0_start + dr0, nr0);

    const int64_t ir1_start = dr1 * ith1;
    const int64_t ir1_end   = MIN(ir1_start + dr1, nr1);

    // 3. 执行单块物理核计算
    ggml_compute_forward_mul_mat_one_chunk(params, dst, src0->type, 
                                          num_rows_per_vec_dot, 
                                          ir0_start, ir0_end, 
                                          ir1_start, ir1_end);

    // 4. 原子递增抢占下一个未分配的 Chunk
    current_chunk = atomic_fetch_add_explicit(&params->threadpool->current_chunk, 
                                              1, 
                                              memory_order_relaxed);
}

阶段 12:底层单分块核心计算 mul_mat_one_chunk

ggml_compute_forward_mul_mat_one_chunk 是真正完成向量乘加运算的微内核入口。它通过两级 Tiling(分块)策略、广播适配以及避免伪共享(False Sharing)设计,确保内积计算跑满硬件带宽:
#mermaid-svg-N9qfsXI4Ok2zjcP2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-N9qfsXI4Ok2zjcP2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .error-icon{fill:#552222;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .marker.cross{stroke:#333333;}#mermaid-svg-N9qfsXI4Ok2zjcP2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-N9qfsXI4Ok2zjcP2 p{margin:0;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .cluster-label text{fill:#333;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .cluster-label span{color:#333;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .cluster-label span p{background-color:transparent;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .label text,#mermaid-svg-N9qfsXI4Ok2zjcP2 span{fill:#333;color:#333;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .node rect,#mermaid-svg-N9qfsXI4Ok2zjcP2 .node circle,#mermaid-svg-N9qfsXI4Ok2zjcP2 .node ellipse,#mermaid-svg-N9qfsXI4Ok2zjcP2 .node polygon,#mermaid-svg-N9qfsXI4Ok2zjcP2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .rough-node .label text,#mermaid-svg-N9qfsXI4Ok2zjcP2 .node .label text,#mermaid-svg-N9qfsXI4Ok2zjcP2 .image-shape .label,#mermaid-svg-N9qfsXI4Ok2zjcP2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .rough-node .label,#mermaid-svg-N9qfsXI4Ok2zjcP2 .node .label,#mermaid-svg-N9qfsXI4Ok2zjcP2 .image-shape .label,#mermaid-svg-N9qfsXI4Ok2zjcP2 .icon-shape .label{text-align:center;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .node.clickable{cursor:pointer;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .arrowheadPath{fill:#333333;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-N9qfsXI4Ok2zjcP2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-N9qfsXI4Ok2zjcP2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-N9qfsXI4Ok2zjcP2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .cluster text{fill:#333;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .cluster span{color:#333;}#mermaid-svg-N9qfsXI4Ok2zjcP2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-N9qfsXI4Ok2zjcP2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .icon-shape,#mermaid-svg-N9qfsXI4Ok2zjcP2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .icon-shape p,#mermaid-svg-N9qfsXI4Ok2zjcP2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .icon-shape .label rect,#mermaid-svg-N9qfsXI4Ok2zjcP2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-N9qfsXI4Ok2zjcP2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-N9qfsXI4Ok2zjcP2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-N9qfsXI4Ok2zjcP2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 7. 计算与写回
6. 维度解码与内存定位
是 (无任务)
否 (有任务)








函数入口: ggml_compute_forward_mul_mat_one_chunk

  1. 初始化与展开局部变量
  • GGML_TENSOR_BINARY_OP_LOCALS

  • 获取 vec_dot 函数指针与类型

  • 计算广播因子 r2, r3
    区间有效性检查

ir0_start >= ir0_end 或

ir1_start >= ir1_end ?
直接返回 return
2. 确定数据指针与跨步

  • wdata 寻址与 row_size 计算

  • src1_col_stride 步长计算

  1. 外层 Tile 循环: iir1

步长: blck_1 = 16
4. 内层 Tile 循环: iir0

步长: blck_0 = 16
5. 展平列/批次循环: ir1

ir1 从 iir1 到 iir1+16

步长: num_rows_per_vec_dot
解码 ir1 为三维坐标

  • i13 = ir1 / ne12*ne1

  • i12 = ir1 余数 / ne1

  • i11 = 最终列偏移
    计算 src0 广播坐标

  • i03 = i13 / r3

  • i02 = i12 / r2
    计算张量物理内存首地址

  • src0_row: 行基址

  • src1_col: 跨步/连续列地址

  • dst_col: 目标内存地址
    内层行点积循环: ir0

ir0 从 iir0 到 iir0+16

步长: num_rows_per_vec_dot
调用 SIMD/MMLA 优化点积

vec_dot 写入本地 tmp 栈数组

避免多线程 False Sharing
ir0 循环结束?
批量写回目标张量

memcpy tmp 结果至 dst_col
ir1 循环结束?
iir0 循环结束?
iir1 循环结束?
函数执行完毕


核心设计亮点总结

优化维度 工程设计手段 性能增益 / 解决痛点
内存与量化 在线 JIT 量化(from_float)+ 共享 wdata 避免显存暴涨,使权重与激活值均能以定点/量化格式进入 SIMD 点积
并发同步 显式 ggml_barrier 分阶段同步 解除「量化生产者」与「点积消费者」之间跨线程的数据竞态
负载均衡 2D 任务分块 + atomic_fetch_add 动态抢占 消除多核性能抖动与木桶效应,充分压榨 CPU 全核吞吐
硬件适配 NUMA 自适应重切分 + SIMD Cache Tiling 削减多路跨 Socket 内存访问延迟,规避 L1 Cache 颠簸与伪共享

下一篇将介绍整个后端设计

相关推荐
ai产品老杨1 小时前
边云协同视频分析性能优化指南:多站点部署下的参数配置与排查清单(边缘推理+云端管理)
性能优化·音视频
TechEdu2026062 小时前
[通信与计算]线性代数:概念及其在通信中的应用
线性代数·机器学习·通信系统·信息通信
苏打水com2 小时前
《llama.cpp性能优化实战:从显存、KV Cache到Token/s》
性能优化·llama
该用户可能存在4 小时前
【Win11优化】StartAllBack实战:解决任务栏合并、右键折叠等痛点
windows·性能优化·win11·windows 11·startallback·任务栏·开始菜单
灯澜忆梦14 小时前
【MySQL12】进阶篇 | SQL优化
数据库·sql·mysql·性能优化
想吃火锅100518 小时前
【leetcode】54. 螺旋矩阵
算法·leetcode·矩阵
瑞码空间21 小时前
线性代数核心概念与实战应用详解
线性代数
知识分享小能手1 天前
线性代数学习教程,从入门到精通,矩阵的初等变换与线性方程组(6)
学习·线性代数·矩阵
爱喝水的鱼丶1 天前
SAP-ABAP:性能优化效果验证与回归测试:优化前后性能对比、业务逻辑兼容性校验方案
性能优化·sap·abap·回归测试·开发交流·交流学习