前情提要 :在上篇【ggml系列】【第三篇】DAG 计算图构建与内存平铺切分(Bump Allocation)源码解析中,我们剖析了 ggml 如何将计算节点展平至
struct ggml_cgraph的nodes与leafs数组中。本文我们将深入 ggml 最核心的多线程计算引擎,探究其如何以极低的同步开销与零动态内存频繁分配的模式,最高效地执行这些计算节点。

1. ggml_graph_compute_with_ctx:计算执行入口
在 ggml 中,执行计算图最基础的顶层 API 是 ggml_graph_compute_with_ctx。它的设计极其简洁,展示了 ggml "先规划,后执行" 的惰性计算思想:
cpp
enum ggml_status ggml_graph_compute_with_ctx(struct ggml_context * ctx, struct ggml_cgraph * cgraph, int n_threads) {
// 1. 生成计算计划(分析 Work Buffer 需求与任务并行度)
struct ggml_cplan cplan = ggml_graph_plan(cgraph, n_threads, NULL);
// 2. 动态分配临时工作内存(Scratch / Work Buffer)
cplan.work_data = (uint8_t *)ggml_new_buffer(ctx, cplan.work_size);
// 3. 驱动多线程引擎执行计算图
return ggml_graph_compute(cgraph, &cplan);
}
2. ggml_graph_plan:静态计算规划
2.1 设计理念
传统深度学习框架在算子执行过程中往往会频繁申请和释放临时 Memory (例如 GEMM 的 Packing 缓冲区、Softmax 的 Intermediate reduction 空间等),这会导致严重的内存碎片与系统调用开销。
ggml_graph_plan 的作用是一次性遍历整个 cgraph**,精准计算出整个图执行过程中 单个算子所需的峰值临时工作内存(work_size),并确定最优的并行线程数(n_threads)。通过提前在内存池中一次性分配最大的 work_size,后续所有算子在执行时均复用这块内存,实现了零动态内存频繁分配**。
2.2 执行流程图
#mermaid-svg-n3Q5Gx3Gpk49frJZ{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-n3Q5Gx3Gpk49frJZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .error-icon{fill:#552222;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .marker.cross{stroke:#333333;}#mermaid-svg-n3Q5Gx3Gpk49frJZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-n3Q5Gx3Gpk49frJZ p{margin:0;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .cluster-label text{fill:#333;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .cluster-label span{color:#333;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .cluster-label span p{background-color:transparent;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .label text,#mermaid-svg-n3Q5Gx3Gpk49frJZ span{fill:#333;color:#333;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .node rect,#mermaid-svg-n3Q5Gx3Gpk49frJZ .node circle,#mermaid-svg-n3Q5Gx3Gpk49frJZ .node ellipse,#mermaid-svg-n3Q5Gx3Gpk49frJZ .node polygon,#mermaid-svg-n3Q5Gx3Gpk49frJZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .rough-node .label text,#mermaid-svg-n3Q5Gx3Gpk49frJZ .node .label text,#mermaid-svg-n3Q5Gx3Gpk49frJZ .image-shape .label,#mermaid-svg-n3Q5Gx3Gpk49frJZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .rough-node .label,#mermaid-svg-n3Q5Gx3Gpk49frJZ .node .label,#mermaid-svg-n3Q5Gx3Gpk49frJZ .image-shape .label,#mermaid-svg-n3Q5Gx3Gpk49frJZ .icon-shape .label{text-align:center;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .node.clickable{cursor:pointer;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .arrowheadPath{fill:#333333;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-n3Q5Gx3Gpk49frJZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-n3Q5Gx3Gpk49frJZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-n3Q5Gx3Gpk49frJZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .cluster text{fill:#333;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .cluster span{color:#333;}#mermaid-svg-n3Q5Gx3Gpk49frJZ 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-n3Q5Gx3Gpk49frJZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-n3Q5Gx3Gpk49frJZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .icon-shape,#mermaid-svg-n3Q5Gx3Gpk49frJZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .icon-shape p,#mermaid-svg-n3Q5Gx3Gpk49frJZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .icon-shape .label rect,#mermaid-svg-n3Q5Gx3Gpk49frJZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-n3Q5Gx3Gpk49frJZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-n3Q5Gx3Gpk49frJZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-n3Q5Gx3Gpk49frJZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 3. 对齐处理与生成计划
2. 遍历计算图节点,预估峰值内存
- 线程数初始化与平台校准
是
否
是
否
否 (使用内置逻辑)
是 (已计算 cur)
GGML_OP_CPY / DUP / ADD ...
GGML_OP_MUL_MAT / MUL_MAT_ID
GGML_OP_SOFT_MAX / ROPE
GGML_OP_CONV_2D / 3D
GGML_OP_FLASH_ATTN_EXT
其他算子 / default
否
是
是
否
开始: ggml_graph_plan
初始化与参数校验
n_threads <= 0 ?
优先使用 threadpool 线程数
或默认 GGML_DEFAULT_N_THREADS
是否为 Emscripten
且未开启 Pthreads ?
强制 n_threads = 1
初始化变量:
work_size = 0
max_tasks = 1
遍历 cgraph 中的每个节点 node
计算当前节点的并行任务数 n_tasks
更新 max_tasks = MAX
ggml_cpu_extra_work_size
处理成功 ?
true: 专用/扩展 CPU 路径
false: 默认内置逻辑
匹配 node->op 算子类型
更新全局峰值内存:
work_size = MAX work_size, cur
计算解量化/类型转换所需 FP32 缓冲区
计算矩阵乘法 Packing / Tile 缓存
计算 Softmax/位置编码临时缓存
计算 im2col 内存需求
取 Prefill 和 Decode 路径的最大缓存
cur = 0
是否遍历完所有节点 ?
work_size > 0 ?
加上 Cache Line 边界填充
避免多线程伪共享 False Sharing
打包生成 ggml_cplan:
-
n_threads = MIN max_tasks, n_threads
-
work_size = work_size
-
work_data = NULL
返回 cplan
补充:特定硬件/Backend 的 Scratch Buffer 机制
在高性能 CPU 算子(如基于 AVX-512/AMX/SVE 的 GEMM 或 GEMV)中,算子需要额外的内存用于:
- 数据 Packing / Layout 重排 :将非连续或量化格式(Q4_K / Q8_0)的 Tensor 重排为适合 SIMD 向量化并行加载的连续内存(如 SGEMM 要求的 A A A 矩阵与 B B B 矩阵 Packing)。
- Tile Scratch Buffer:在 Intel AMX 或 ARM SME 等 Tile 矩阵硬件扩展中,分配临时的 Tile 寄存器溢出与换页空间。
3. ggml_graph_compute:引擎核心与多线程调度
3.1 时序交互逻辑 (SPMD 数据并行范式)
ggml 的多线程不是 Task Parallelism(任务并行:线程 A 算 Node 1,线程 B 算 Node 2) ,而是 SPMD(Single Program, Multiple Data,数据并行:所有线程共同协作计算 Node 1 的切片,完成后通过 Barrier 同步,再共同计算 Node 2)。
整个计算图的生命周期与 Worker 线程的协同序列如下:
Worker 线程 2 (ith=2) Worker 线程 1 (ith=1) threadpool (n_graph 版本控制) 主线程 (ith=0) Worker 线程 2 (ith=2) Worker 线程 1 (ith=1) threadpool (n_graph 版本控制) 主线程 (ith=0) #mermaid-svg-M6WyQQOD2Gdg8N0g{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-M6WyQQOD2Gdg8N0g .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-M6WyQQOD2Gdg8N0g .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-M6WyQQOD2Gdg8N0g .error-icon{fill:#552222;}#mermaid-svg-M6WyQQOD2Gdg8N0g .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-M6WyQQOD2Gdg8N0g .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-M6WyQQOD2Gdg8N0g .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-M6WyQQOD2Gdg8N0g .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-M6WyQQOD2Gdg8N0g .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-M6WyQQOD2Gdg8N0g .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-M6WyQQOD2Gdg8N0g .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-M6WyQQOD2Gdg8N0g .marker{fill:#333333;stroke:#333333;}#mermaid-svg-M6WyQQOD2Gdg8N0g .marker.cross{stroke:#333333;}#mermaid-svg-M6WyQQOD2Gdg8N0g svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-M6WyQQOD2Gdg8N0g p{margin:0;}#mermaid-svg-M6WyQQOD2Gdg8N0g .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-M6WyQQOD2Gdg8N0g text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-M6WyQQOD2Gdg8N0g .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-M6WyQQOD2Gdg8N0g .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-M6WyQQOD2Gdg8N0g .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-M6WyQQOD2Gdg8N0g .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-M6WyQQOD2Gdg8N0g #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-M6WyQQOD2Gdg8N0g .sequenceNumber{fill:white;}#mermaid-svg-M6WyQQOD2Gdg8N0g #sequencenumber{fill:#333;}#mermaid-svg-M6WyQQOD2Gdg8N0g #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-M6WyQQOD2Gdg8N0g .messageText{fill:#333;stroke:none;}#mermaid-svg-M6WyQQOD2Gdg8N0g .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-M6WyQQOD2Gdg8N0g .labelText,#mermaid-svg-M6WyQQOD2Gdg8N0g .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-M6WyQQOD2Gdg8N0g .loopText,#mermaid-svg-M6WyQQOD2Gdg8N0g .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-M6WyQQOD2Gdg8N0g .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-M6WyQQOD2Gdg8N0g .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-M6WyQQOD2Gdg8N0g .noteText,#mermaid-svg-M6WyQQOD2Gdg8N0g .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-M6WyQQOD2Gdg8N0g .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-M6WyQQOD2Gdg8N0g .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-M6WyQQOD2Gdg8N0g .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-M6WyQQOD2Gdg8N0g .actorPopupMenu{position:absolute;}#mermaid-svg-M6WyQQOD2Gdg8N0g .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-M6WyQQOD2Gdg8N0g .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-M6WyQQOD2Gdg8N0g .actor-man circle,#mermaid-svg-M6WyQQOD2Gdg8N0g line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-M6WyQQOD2Gdg8N0g :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 【阶段一:图级别唤醒 (Graph-level Launch)】 par 线程检测到新图任务 (check_for_work) 【阶段二:进入 ggml_graph_compute_thread 函数】 所有线程各自独立进入节点遍历循环 (node_n 从 0 到 n_nodes - 1) ─── 节点 Node 0 计算 ─── par 按 ith 并行切片计算 ─── 节点间屏障同步 ggml_barrier() ─── ─── 节点 Node 1 计算 ─── par 按 ith 并行切片计算 ─── 节点间屏障同步 ggml_barrier() ─── 循环直到所有节点算完 【阶段三:退出 ggml_graph_compute_thread】 Worker 线程返回外层 while(true) 继续等待下一个 n_graph 1. 提交整张计算图 cgraph,更新 n_graph 版本号 1 2. 读取 n_graph 变化 (pending = true) 2 3. 读取 n_graph 变化 (pending = true) 3 算 Node 0 中属于 ith=0 的切片 4 算 Node 0 中属于 ith=1 的切片 5 算 Node 0 中属于 ith=2 的切片 6 到达 barrier,自旋等待... 7 到达 barrier,自旋等待... 8 到达 barrier,所有人到齐,放行! 9 算 Node 1 中属于 ith=0 的切片 10 算 Node 1 中属于 ith=1 的切片 11 算 Node 1 中属于 ith=2 的切片 12 到达 barrier,自旋等待... 13 到达 barrier,自旋等待... 14 到达 barrier,所有人到齐,放行! 15
3.2 线程池的构建与 CPU 亲和性(绑核)机制
在 ggml_graph_compute 内部,若未启用 OpenMP,ggml 会初始化一套自研的原生轻量级线程池。
#mermaid-svg-gw8Jq59PWkyCXh3r{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-gw8Jq59PWkyCXh3r .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gw8Jq59PWkyCXh3r .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gw8Jq59PWkyCXh3r .error-icon{fill:#552222;}#mermaid-svg-gw8Jq59PWkyCXh3r .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gw8Jq59PWkyCXh3r .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gw8Jq59PWkyCXh3r .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gw8Jq59PWkyCXh3r .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gw8Jq59PWkyCXh3r .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gw8Jq59PWkyCXh3r .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gw8Jq59PWkyCXh3r .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gw8Jq59PWkyCXh3r .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gw8Jq59PWkyCXh3r .marker.cross{stroke:#333333;}#mermaid-svg-gw8Jq59PWkyCXh3r svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gw8Jq59PWkyCXh3r p{margin:0;}#mermaid-svg-gw8Jq59PWkyCXh3r .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-gw8Jq59PWkyCXh3r .cluster-label text{fill:#333;}#mermaid-svg-gw8Jq59PWkyCXh3r .cluster-label span{color:#333;}#mermaid-svg-gw8Jq59PWkyCXh3r .cluster-label span p{background-color:transparent;}#mermaid-svg-gw8Jq59PWkyCXh3r .label text,#mermaid-svg-gw8Jq59PWkyCXh3r span{fill:#333;color:#333;}#mermaid-svg-gw8Jq59PWkyCXh3r .node rect,#mermaid-svg-gw8Jq59PWkyCXh3r .node circle,#mermaid-svg-gw8Jq59PWkyCXh3r .node ellipse,#mermaid-svg-gw8Jq59PWkyCXh3r .node polygon,#mermaid-svg-gw8Jq59PWkyCXh3r .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gw8Jq59PWkyCXh3r .rough-node .label text,#mermaid-svg-gw8Jq59PWkyCXh3r .node .label text,#mermaid-svg-gw8Jq59PWkyCXh3r .image-shape .label,#mermaid-svg-gw8Jq59PWkyCXh3r .icon-shape .label{text-anchor:middle;}#mermaid-svg-gw8Jq59PWkyCXh3r .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-gw8Jq59PWkyCXh3r .rough-node .label,#mermaid-svg-gw8Jq59PWkyCXh3r .node .label,#mermaid-svg-gw8Jq59PWkyCXh3r .image-shape .label,#mermaid-svg-gw8Jq59PWkyCXh3r .icon-shape .label{text-align:center;}#mermaid-svg-gw8Jq59PWkyCXh3r .node.clickable{cursor:pointer;}#mermaid-svg-gw8Jq59PWkyCXh3r .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-gw8Jq59PWkyCXh3r .arrowheadPath{fill:#333333;}#mermaid-svg-gw8Jq59PWkyCXh3r .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gw8Jq59PWkyCXh3r .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gw8Jq59PWkyCXh3r .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gw8Jq59PWkyCXh3r .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gw8Jq59PWkyCXh3r .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gw8Jq59PWkyCXh3r .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-gw8Jq59PWkyCXh3r .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gw8Jq59PWkyCXh3r .cluster text{fill:#333;}#mermaid-svg-gw8Jq59PWkyCXh3r .cluster span{color:#333;}#mermaid-svg-gw8Jq59PWkyCXh3r 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-gw8Jq59PWkyCXh3r .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gw8Jq59PWkyCXh3r rect.text{fill:none;stroke-width:0;}#mermaid-svg-gw8Jq59PWkyCXh3r .icon-shape,#mermaid-svg-gw8Jq59PWkyCXh3r .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gw8Jq59PWkyCXh3r .icon-shape p,#mermaid-svg-gw8Jq59PWkyCXh3r .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-gw8Jq59PWkyCXh3r .icon-shape .label rect,#mermaid-svg-gw8Jq59PWkyCXh3r .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gw8Jq59PWkyCXh3r .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-gw8Jq59PWkyCXh3r .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-gw8Jq59PWkyCXh3r :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
否
是
否
是
是
否
开始: ggml_threadpool_new_impl
分配并对齐线程池内存
ggml_aligned_malloc
初始化结构体控制变量
cgraph, cplan, pause, n_threads等
分配并清零 Worker 状态数组
workers_size = sizeof * n_threads
遍历初始化 Worker
设置 workersj.threadpool 和 workersj.ith
是否启用 OpenMP?
#ifdef GGML_USE_OPENMP
遍历所有 Worker
计算各线程 CPU 掩码
ggml_thread_cpumask_next
返回 threadpool 指针
初始化互斥锁与条件变量
ggml_mutex_init / ggml_cond_init
遍历从属线程 j = 1 到 n_threads-1
计算次级线程 CPU 掩码
创建次级线程
ggml_thread_create -> secondary_thread
线程创建成功?
rc == 0
触发断言 GGML_ASSERT
次级线程是否全部创建完成?
计算主线程 workers0 CPU 掩码
线程池是否未暂停?
!threadpool->pause
应用主线程优先级与 CPU 亲和性
ggml_thread_apply_priority/affinity
关键源码解析:CPU 亲和性绑定
j 从 1 开始迭代,因为主线程自身扮演 ith = 0 的角色,避免了主线程只发号施令而空转等待的浪费:
cpp
int32_t cpumask_iter = 0;
// j 从 1 开始:主线程作为 Worker 0 参与计算
for (int j = 1; j < tpp->n_threads; j++) {
// 挨个计算绑核掩码,确保每个线程固定在一个物理 CPU 核心上
ggml_thread_cpumask_next(tpp->cpumask, workers[j].cpumask, tpp->strict_cpu, &cpumask_iter);
int32_t rc = ggml_thread_create(&workers[j].thrd, NULL, ggml_graph_compute_secondary_thread, &workers[j]);
GGML_ASSERT(rc == 0);
}
// 主线程 (Worker 0) 的绑核设置
ggml_thread_cpumask_next(tpp->cpumask, workers[0].cpumask, tpp->strict_cpu, &cpumask_iter);
为什么 LLM 推理极其依赖 CPU 亲和性绑定(CPU Affinity)?
- 极大幅度提升 L1/L2/L3 Cache 命中率:矩阵乘法(GEMM/GEMV)是严重的 Memory-bound 或 Compute-bound 任务。固定 CPU 核心可确保当前 core 的 L1/L2 Cache 中缓存的权重解量化查找表(LUT)与激活值不会因 OS 调度切换而失效。
- 消除上下文切换(Context Switch):避免 CPU 核心间搬运线程寄存器、栈帧及 TLB 页表的昂贵开销。
3.3 Secondary Thread 轮询与等待机制
Worker 线程的生命周期函数如下:
cpp
static thread_ret_t ggml_graph_compute_secondary_thread(void* data) {
struct ggml_compute_state * state = (struct ggml_compute_state *) data;
struct ggml_threadpool * threadpool = state->threadpool;
// 设置线程优先级与 CPU 绑核
ggml_thread_apply_priority(threadpool->prio);
if (ggml_thread_cpumask_is_valid(state->cpumask)) {
ggml_thread_apply_affinity(state->cpumask);
}
while (true) {
// 1. 休眠等待逻辑:当无计算任务 (threadpool->pause == true) 时,线程挂起
while (threadpool->pause) {
ggml_mutex_lock_shared(&threadpool->mutex);
if (threadpool->pause) {
// 进入条件变量等待,避免盲目 Spin 耗尽 CPU
ggml_cond_wait(&threadpool->cond, &threadpool->mutex);
}
ggml_mutex_unlock_shared(&threadpool->mutex);
}
if (threadpool->stop) break;
// 2. 检查是否有新计算图提交
ggml_graph_compute_check_for_work(state);
if (state->pending) {
state->pending = false;
// 3. 进入真正图遍历计算过程
ggml_graph_compute_thread(state);
}
}
return (thread_ret_t) 0;
}
3.4 ggml_graph_compute_thread 的内部循环
该函数是真正驱动单图计算的执行引擎:
#mermaid-svg-qXC2rkYN87w0fbBn{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-qXC2rkYN87w0fbBn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qXC2rkYN87w0fbBn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qXC2rkYN87w0fbBn .error-icon{fill:#552222;}#mermaid-svg-qXC2rkYN87w0fbBn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qXC2rkYN87w0fbBn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qXC2rkYN87w0fbBn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qXC2rkYN87w0fbBn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qXC2rkYN87w0fbBn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qXC2rkYN87w0fbBn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qXC2rkYN87w0fbBn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qXC2rkYN87w0fbBn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qXC2rkYN87w0fbBn .marker.cross{stroke:#333333;}#mermaid-svg-qXC2rkYN87w0fbBn svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qXC2rkYN87w0fbBn p{margin:0;}#mermaid-svg-qXC2rkYN87w0fbBn .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-qXC2rkYN87w0fbBn .cluster-label text{fill:#333;}#mermaid-svg-qXC2rkYN87w0fbBn .cluster-label span{color:#333;}#mermaid-svg-qXC2rkYN87w0fbBn .cluster-label span p{background-color:transparent;}#mermaid-svg-qXC2rkYN87w0fbBn .label text,#mermaid-svg-qXC2rkYN87w0fbBn span{fill:#333;color:#333;}#mermaid-svg-qXC2rkYN87w0fbBn .node rect,#mermaid-svg-qXC2rkYN87w0fbBn .node circle,#mermaid-svg-qXC2rkYN87w0fbBn .node ellipse,#mermaid-svg-qXC2rkYN87w0fbBn .node polygon,#mermaid-svg-qXC2rkYN87w0fbBn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qXC2rkYN87w0fbBn .rough-node .label text,#mermaid-svg-qXC2rkYN87w0fbBn .node .label text,#mermaid-svg-qXC2rkYN87w0fbBn .image-shape .label,#mermaid-svg-qXC2rkYN87w0fbBn .icon-shape .label{text-anchor:middle;}#mermaid-svg-qXC2rkYN87w0fbBn .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qXC2rkYN87w0fbBn .rough-node .label,#mermaid-svg-qXC2rkYN87w0fbBn .node .label,#mermaid-svg-qXC2rkYN87w0fbBn .image-shape .label,#mermaid-svg-qXC2rkYN87w0fbBn .icon-shape .label{text-align:center;}#mermaid-svg-qXC2rkYN87w0fbBn .node.clickable{cursor:pointer;}#mermaid-svg-qXC2rkYN87w0fbBn .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qXC2rkYN87w0fbBn .arrowheadPath{fill:#333333;}#mermaid-svg-qXC2rkYN87w0fbBn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qXC2rkYN87w0fbBn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qXC2rkYN87w0fbBn .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qXC2rkYN87w0fbBn .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qXC2rkYN87w0fbBn .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qXC2rkYN87w0fbBn .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qXC2rkYN87w0fbBn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qXC2rkYN87w0fbBn .cluster text{fill:#333;}#mermaid-svg-qXC2rkYN87w0fbBn .cluster span{color:#333;}#mermaid-svg-qXC2rkYN87w0fbBn 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-qXC2rkYN87w0fbBn .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qXC2rkYN87w0fbBn rect.text{fill:none;stroke-width:0;}#mermaid-svg-qXC2rkYN87w0fbBn .icon-shape,#mermaid-svg-qXC2rkYN87w0fbBn .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qXC2rkYN87w0fbBn .icon-shape p,#mermaid-svg-qXC2rkYN87w0fbBn .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qXC2rkYN87w0fbBn .icon-shape .label rect,#mermaid-svg-qXC2rkYN87w0fbBn .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qXC2rkYN87w0fbBn .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qXC2rkYN87w0fbBn .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qXC2rkYN87w0fbBn :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 循环结束 / 被 abort 中断
检查 abort 标志位未触发
是
否
融合成功 n_fused > 0
无法融合 n_fused == 0
是
否
是
否
- 函数入口: ggml_graph_compute_thread
- 线程环境配置
set_numa_thread_affinity
3. 构造计算参数 struct ggml_compute_params
提取线程编号 ith / 总线程数 nth / 缓存区
4. for 循环遍历节点
node_n 从 0 到 n_nodes - 1
9. 退出循环: 终点 Barrier 同步
ggml_barrier
5. 取出当前算子节点
node = cgraph->nodes.node_n
6. 节点校验
是否为 NOP 空算子 或
无 COMPUTE 标志?
节点递增 node_n++
7.1 尝试算子融合
ggml_cpu_try_fuse_ops
跳过已融合节点
node_n += n_fused
7.2 执行前向算子计算
ggml_compute_forward
按 ith 分摊计算自己的矩阵切片
8.1 是否为主线程 ith == 0
且用户触发了 abort_callback?
标记 tp->abort 与 GGML_STATUS_ABORTED
8.2 是否还有下一个节点?
node_n + 1 < cgraph->n_nodes
齐步走屏障同步
ggml_barrier
所有线程在此互相等待
10. 清理/恢复 NUMA 状态
11. 函数返回 0
退回外层 while 循环等待下一张图
4. ggml_barrier:高并发无锁 Spinlock 屏障深度解析
为了确保多线程在算子之间不发生数据依赖紊乱(Race Condition),ggml 没有直接采用 POSIX pthread_barrier,而是精心设计了一套无锁(Lock-free)Spinlock 屏障。
4.1 屏障流程图
#mermaid-svg-JCXitDqMRVrWiwlu{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-JCXitDqMRVrWiwlu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JCXitDqMRVrWiwlu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JCXitDqMRVrWiwlu .error-icon{fill:#552222;}#mermaid-svg-JCXitDqMRVrWiwlu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JCXitDqMRVrWiwlu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JCXitDqMRVrWiwlu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JCXitDqMRVrWiwlu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JCXitDqMRVrWiwlu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JCXitDqMRVrWiwlu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JCXitDqMRVrWiwlu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JCXitDqMRVrWiwlu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JCXitDqMRVrWiwlu .marker.cross{stroke:#333333;}#mermaid-svg-JCXitDqMRVrWiwlu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JCXitDqMRVrWiwlu p{margin:0;}#mermaid-svg-JCXitDqMRVrWiwlu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-JCXitDqMRVrWiwlu .cluster-label text{fill:#333;}#mermaid-svg-JCXitDqMRVrWiwlu .cluster-label span{color:#333;}#mermaid-svg-JCXitDqMRVrWiwlu .cluster-label span p{background-color:transparent;}#mermaid-svg-JCXitDqMRVrWiwlu .label text,#mermaid-svg-JCXitDqMRVrWiwlu span{fill:#333;color:#333;}#mermaid-svg-JCXitDqMRVrWiwlu .node rect,#mermaid-svg-JCXitDqMRVrWiwlu .node circle,#mermaid-svg-JCXitDqMRVrWiwlu .node ellipse,#mermaid-svg-JCXitDqMRVrWiwlu .node polygon,#mermaid-svg-JCXitDqMRVrWiwlu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JCXitDqMRVrWiwlu .rough-node .label text,#mermaid-svg-JCXitDqMRVrWiwlu .node .label text,#mermaid-svg-JCXitDqMRVrWiwlu .image-shape .label,#mermaid-svg-JCXitDqMRVrWiwlu .icon-shape .label{text-anchor:middle;}#mermaid-svg-JCXitDqMRVrWiwlu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-JCXitDqMRVrWiwlu .rough-node .label,#mermaid-svg-JCXitDqMRVrWiwlu .node .label,#mermaid-svg-JCXitDqMRVrWiwlu .image-shape .label,#mermaid-svg-JCXitDqMRVrWiwlu .icon-shape .label{text-align:center;}#mermaid-svg-JCXitDqMRVrWiwlu .node.clickable{cursor:pointer;}#mermaid-svg-JCXitDqMRVrWiwlu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-JCXitDqMRVrWiwlu .arrowheadPath{fill:#333333;}#mermaid-svg-JCXitDqMRVrWiwlu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JCXitDqMRVrWiwlu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JCXitDqMRVrWiwlu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JCXitDqMRVrWiwlu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JCXitDqMRVrWiwlu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JCXitDqMRVrWiwlu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-JCXitDqMRVrWiwlu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JCXitDqMRVrWiwlu .cluster text{fill:#333;}#mermaid-svg-JCXitDqMRVrWiwlu .cluster span{color:#333;}#mermaid-svg-JCXitDqMRVrWiwlu 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-JCXitDqMRVrWiwlu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JCXitDqMRVrWiwlu rect.text{fill:none;stroke-width:0;}#mermaid-svg-JCXitDqMRVrWiwlu .icon-shape,#mermaid-svg-JCXitDqMRVrWiwlu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JCXitDqMRVrWiwlu .icon-shape p,#mermaid-svg-JCXitDqMRVrWiwlu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-JCXitDqMRVrWiwlu .icon-shape .label rect,#mermaid-svg-JCXitDqMRVrWiwlu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JCXitDqMRVrWiwlu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-JCXitDqMRVrWiwlu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-JCXitDqMRVrWiwlu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
是 (最后一个线程)
否 (先到的线程)
否 (轮次未变)
是 (已被最后一个线程唤醒)
是
否
线程进入 ggml_barrier
线程数 n_threads == 1?
直接返回 / 无需同步
#ifdef GGML_USE_OPENMP ?
调用 #pragma omp barrier
退出屏障
- 读取当前屏障通过轮次 n_passed
- 原子递增 n_barrier,获取到达序号
当前线程是最后一个到达的?
n_barrier == n_threads - 1
重置计数器 n_barrier = 0
增加通过轮次 n_barrier_passed + 1
自旋等待 while 轮次未变
调用 ggml_thread_cpu_relax 降低功耗
n_barrier_passed != n_passed ?
开启了 TSAN 检测?
执行虚拟原子 RMW 操作
fetch_add 0
执行内存屏障
atomic_thread_fence
4.2 核心设计哲学
- 用户态 Spinlock 替代 Kernel 休眠
POSIXmutex/cond会导致系统频繁发生用户态到内核态的上下文切换(毫秒级开销)。对于微秒级的张量计算,ggml 在用户态使用忙等待(Spin wait),配合 CPU 指令ggml_thread_cpu_relax()(在 x86 上为PAUSE,ARM 上为YIELD),降低功耗并避免管线堵塞,实现亚微秒级的屏障唤醒。 - 双计数器(Two-Counter)状态机制
n_barrier(打卡器):记录当前轮次已到达的线程数。n_barrier_passed(发令枪/轮次号):记录历史已通过的轮次。
先到的线程记录当前轮次n_passed后自旋死盯发令枪。只有最后一个到达的线程 负责重置n_barrier = 0并递增n_barrier_passed,一次性广播释放所有自旋线程。
4.3 为什么末尾必须执行 atomic_thread_fence(memory_order_seq_cst)?
源码实现片段:
cpp
// 先到的线程自旋等待(使用 relaxed 读取,追求极致的速度,无内存屏障 semantics)
while (atomic_load_explicit(&tp->n_barrier_passed, memory_order_relaxed) == n_passed) {
ggml_thread_cpu_relax();
}
// 核心:强内存屏障
atomic_thread_fence(memory_order_seq_cst);
在结尾处加入 atomic_thread_fence(memory_order_seq_cst),是出于以下底层硬件层面的严苛考量:
- 阻止指令乱序重排序(Out-of-Order Execution)
自旋循环中的读取使用的是memory_order_relaxed,它只保证对n_barrier_passed变量本身的原子性,不提供任何同步屏障约束 。现代弱内存模型 CPU(如 ARM64)或编译器可能会把屏障之后 算子的内存读取指令,重排序到while循环结束前 执行。加入了atomic_thread_fence,就构建了一道不可逾越的"单向防火墙"。 - 硬件 Store/Load Buffer 刷新(Cache Coherency)
当线程 A 在屏障前计算完数据时,修改可能仍滞留在其 CPU 核心的 Store Buffer 或 L1 Cache 中。当线程 B 被唤醒跳出屏障时,atomic_thread_fence(memory_order_seq_cst)在硬件层面会被翻译为:
- x86/x64 :
mfence指令(或带有LOCK前缀的指令),强行刷空 Store Buffer。 - ARM64 :
dmb ish(Data Memory Barrier) 指令,确保跨 Core 的内存写操作对所有 Core 完全可见。
- 高性能读写解耦
之所以不直接在while循环里写atomic_load_explicit(..., memory_order_acquire),是为了避免在几千次高频轮询中每次都触发昂贵的硬件级 Acquire 检查 。用relaxed快速轮询 + 跳出循环后单次fence,是极致性能优化的体现。
5. ggml_compute_forward:算子分发与架构理念
ggml_compute_forward 是计算图真正执行时的核心算子分发器(Op Dispatcher) ,它将枚举类型 tensor->op 映射到具体的 C/C++ 内核函数上。
从该函数的架构设计中可以总结出 4 个关键特点:
1. 内存视图操作的 0 成本 NOP 化
cpp
case GGML_OP_RESHAPE:
case GGML_OP_PERMUTE:
case GGML_OP_VIEW:
case GGML_OP_TRANSPOSE:
// NOP (No Operation)
对于 Reshape、Permute、View、Transpose 等内存视角操作,ggml 只在建图阶段修改指针步长(strides)与形状维度(ne),在 compute_forward 中直接当作 NOP(无操作) 处理,省去了昂贵的内存拷贝。
2. 多 Backend 扩展拦截机制
cpp
// 优先检测硬件加速/外部扩展路径
if (ggml_cpu_extra_compute_forward(params, tensor)) {
return;
}
提供插件式拦截机制,若第三方加速库或硬件 Backend 注册了专用内核,该逻辑将优先接管计算,避免了核心代码库的膨胀。
3. 数据与执行上下文完全解耦
内核函数统一接收 struct ggml_compute_params * params(包含线程编号 ith、总线程数 nth、Work Buffer 等)和 struct ggml_tensor * tensor(包含 Shape、Stride、Data 指针)。这种解耦使内核函数编写者只需根据 params->ith 即可轻松计算属于当前线程的 Slice 偏移量。
4. 覆盖主流与前沿 LLM 算子集
算子分发不仅支持传统的 MUL_MAT、RMS_NORM、ROPE、FLASH_ATTN_EXT,还全面支持了新型非 Attention 架构(如 RWKV 的 WKV6/WKV7、Mamba/SSM 的 SSM_CONV/SSM_SCAN),以及反向传播与优化器算子(OPT_STEP_ADAMW),具备完整的轻量级微调与推理能力。
6. 总结
ggml 计算引擎的核心设计范式可以概括为:
- 静态规划 :通过
ggml_graph_plan事先确定峰值内存(work_size),实现运行期零动态内存申请。 - 绑核与 SPMD 数据并行 :通过 CPU Affinity 绑定物理核心,以
ith分片形式协作计算单个算子。 - 极致无锁屏障:基于 Spinlock、双计数器与 Memory Fence 实现超低延迟的线程同步。
