Appearance
Part 6: 代码走读
1. GPU 后端 + 总结
追踪 1:Metal 初始化与管线编译
GPU 初始化
c
// 行 2661: 单例初始化 Metal 设备
int ds4_gpu_init(void) {
g_device = MTLCreateSystemDefaultDevice(); // 获取 GPU 设备
g_queue = [g_device newCommandQueue]; // 命令队列
// 加载 Metal Shading Library(编译好的 .metallib)
// 从程序嵌入的 Metal 源码编译
g_library = [g_device newLibraryWithSource:metal_source
options:nil
error:&err];
// 初始化缓存字典
g_model_buffer_cache = [NSMutableDictionary new]; // 模型缓冲区
g_pipeline_cache = [NSMutableDictionary new]; // 管线状态
g_transient_buffers = [NSMutableArray new]; // 临时缓冲区
}管线状态缓存
c
// 行 624: 按名称获取或创建计算管线
static id<MTLComputePipelineState> ds4_gpu_get_pipeline(
const char *function_name) {
NSString *key = @(function_name);
// 查缓存
id<MTLComputePipelineState> pipe = g_pipeline_cache[key];
if (pipe) return pipe; // 命中
// 缓存未命中: 从库中查找函数并编译管线
id<MTLFunction> fn = [g_library newFunctionWithName:key];
pipe = [g_device newComputePipelineStateWithFunction:fn
error:&err];
g_pipeline_cache[key] = pipe; // 存入缓存
return pipe;
}Metal 初始化层次:
MTLDevice (GPU 硬件抽象)
└── MTLCommandQueue (命令提交队列)
└── MTLCommandBuffer (一次提交的命令批次)
└── MTLComputeCommandEncoder (计算命令编码器)
└── MTLComputePipelineState (着色器管线)
└── MTLFunction (从 .metal 源码编译)
管线缓存:
"matmul_q8_0" → pipeline_state_0
"flash_attn_prefill" → pipeline_state_1
"routed_moe" → pipeline_state_2
...
(~60 个推理内核)追踪 2:零拷贝模型映射与张量管理
模型映射
c
// 行 4399: 将 mmap 的模型文件映射到 Metal 缓冲区
int ds4_gpu_set_model_map(const void *model_map, uint64_t model_size) {
// 委托给 ds4_gpu_set_model_map_range
// 在 macOS 上使用 MTLResourceStorageModeShared
// GPU 和 CPU 共享同一块物理内存
// → 零拷贝: 权重数据不需要从 CPU 复制到 GPU
}张量分配
c
// 行 3767: 分配 GPU 张量
ds4_gpu_tensor *ds4_gpu_tensor_alloc(uint64_t bytes) {
// 创建 Metal Buffer(共享内存模式)
id<MTLBuffer> buffer = [g_device newBufferWithLength:bytes
options:MTLResourceStorageModeShared];
// 包装为 DS4MetalTensor 对象
DS4MetalTensor *obj = [[DS4MetalTensor alloc] init];
obj.buffer = buffer;
obj.offset = 0;
obj.bytes = bytes;
// 追踪分配统计
g_live_bytes += bytes;
g_peak_bytes = MAX(g_peak_bytes, g_live_bytes);
// __bridge_retained: Objective-C → C 所有权转移
return (__bridge_retained ds4_gpu_tensor *)obj;
}张量写入
c
// 行 3859: CPU → GPU 数据传输
int ds4_gpu_tensor_write(ds4_gpu_tensor *tensor, uint64_t offset,
const void *data, uint64_t bytes) {
DS4MetalTensor *obj = (__bridge DS4MetalTensor *)tensor;
// 验证边界
if (offset + bytes > obj.bytes) return -1;
// 直接 memcpy(共享内存模式,CPU/GPU 看到同一块内存)
memcpy((uint8_t *)[obj.buffer contents] + obj.offset + offset,
data, bytes);
return 0;
}零拷贝内存架构:
CPU 虚拟地址空间 GPU
┌─────────────────┐
│ mmap 区域 │ ← 81GB GGUF 文件
│ (权重数据) │
│ │ MTLResourceStorageModeShared
│ ─ ─ ─ ─ ─ ─ ─ ─│ ← 同一块物理内存
│ │
│ Metal Buffer │ ← ds4_gpu_tensor_alloc 分配
│ (激活值/输出) │
└─────────────────┘
CPU 通过指针直接读写 → GPU 通过同一物理地址访问
无需 cudaMemcpy / [buffer didModifyRange]追踪 3:GPU matmul 与 matvec
Q8_0 量化矩阵乘
c
// 行 4919: Q8_0 权重 × Q8_0 激活的矩阵乘
int ds4_gpu_matmul_q8_0_tensor(
ds4_gpu_tensor *out, // 输出张量
const void *model_map, // 模型 mmap 基地址
uint64_t model_size,
uint64_t weight_offset, // 权重在 mmap 中的偏移
uint64_t in_dim, uint64_t out_dim,
const ds4_gpu_tensor *x, // 输入激活张量
uint64_t n_tok) { // token 数量
// 要求 in_dim % 32 == 0(Q8_0 块大小对齐)
// 获取 Metal 缓冲区
id<MTLBuffer> out_buf = ...;
id<MTLBuffer> x_buf = ...;
id<MTLBuffer> w_buf = model_map + weight_offset; // 零拷贝权重
// 设置计算管线和参数
id<MTLComputePipelineState> pipe = ds4_gpu_get_pipeline("matmul_q8_0");
id<MTLComputeCommandEncoder> enc = [cb computeCommandEncoder];
[enc setComputePipelineState:pipe];
[enc setBuffer:out_buf offset:0 atIndex:0];
[enc setBuffer:w_buf offset:0 atIndex:1];
[enc setBuffer:x_buf offset:0 atIndex:2];
// ... 设置其他参数 ...
// 分发计算线程
[enc dispatchThreadgroups:threadgroups
threadsPerThreadgroup:threads];
[enc endEncoding];
}F16 矩阵乘
c
// 行 5141: FP16 权重矩阵乘(用于 embedding 等未量化的权重)
int ds4_gpu_matmul_f16_tensor(
ds4_gpu_tensor *out,
const void *model_map, uint64_t model_size,
uint64_t weight_offset,
uint64_t in_dim, uint64_t out_dim,
const ds4_gpu_tensor *x,
uint64_t n_tok) {
// 与 Q8_0 版本结构相同
// 但使用 "matmul_f16" 着色器管线
// 无 in_dim 对齐要求
}GPU matvec 线程分发:
权重矩阵 [out_dim × in_dim]
│
▼ 分割为 threadgroups
│
每个 threadgroup:
处理一个输出行的子范围
加载权重块到共享内存
与输入向量做分块点积
累加结果
threadgroup 大小: 例如 [64, 1, 1]
总 threadgroups: [ceil(out_dim/64), n_tok, 1]追踪 4:GPU Flash Attention
Prefill 注意力
c
// 行 10569: Prefill 阶段的 Flash Attention
int ds4_gpu_attention_prefill_raw_heads_tensor(
ds4_gpu_tensor *heads, // 输出: [n_head × head_dim]
const void *model_map,
uint64_t model_size,
uint64_t sinks_offset, // Sink 参数偏移
const ds4_gpu_tensor *q, // Query [n_tok × n_head × head_dim]
const ds4_gpu_tensor *raw_kv, // KV 缓存 [n_tokens × head_dim]
uint32_t n_tokens, // KV 缓存行数
uint32_t window, // SWA 窗口大小
uint32_t n_head, uint32_t head_dim) {
// 包装 Sink 参数(零拷贝)
ds4_gpu_wrap_model_range(model_map, sinks_offset, ...);
// 获取专用 Flash Attention 管线
ds4_gpu_encode_flash_attention_prefill_raw_heads(
cb, pipe, heads, q, raw_kv,
n_tokens, window, n_head, head_dim);
}Decode 注意力
c
// 行 10619: Decode 阶段的 Flash Attention
int ds4_gpu_attention_decode_raw_batch_heads_tensor(
ds4_gpu_tensor *heads,
const void *model_map, uint64_t model_size,
uint64_t sinks_offset,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *raw_kv,
uint32_t n_tokens,
uint32_t pos0, // 序列起始位置
uint32_t n_raw, // raw 缓存行数
uint32_t raw_cap, // raw 缓存容量
uint32_t raw_start, // raw 缓存起始索引
uint32_t window,
uint32_t n_head, uint32_t head_dim) {
// 额外参数管理 KV 缓存窗口:
// pos0: 当前 token 在完整序列中的位置
// n_raw: 实际有效的 raw KV 行数
// raw_cap: raw 缓存的总容量
// raw_start: 循环缓冲区的起始索引
ds4_gpu_encode_flash_attention_decode_raw_batch_heads(
cb, pipe, heads, q, raw_kv,
n_tokens, pos0, n_raw, raw_cap, raw_start, ...);
}Flash Attention GPU 实现:
标准注意力: Flash Attention:
1. Q×K^T → scores 1. 分块加载 Q, K, V 到共享内存
2. softmax(scores) 2. 在线 softmax(分块计算最大值和指数和)
3. × V → output 3. 分块累加输出
内存: O(n²) 4. 不显式存储完整的 attention 矩阵
内存: O(n)
Prefill: 所有 prompt token 的 Q 对所有 KV 做注意力
Decode: 1 个新 token 的 Q 对所有 KV 做注意力追踪 5:GPU MoE 与 HC 操作
路由 MoE
c
// 行 12736: GPU 上的路由 MoE
int ds4_gpu_routed_moe_one_tensor(
ds4_gpu_tensor *out,
ds4_gpu_tensor *gate, ds4_gpu_tensor *up,
ds4_gpu_tensor *mid, ds4_gpu_tensor *experts,
const void *model_map, uint64_t model_size,
uint64_t gate_offset, uint64_t up_offset,
uint64_t down_offset,
uint32_t gate_type, uint32_t down_type,
uint64_t gate_expert_bytes, uint64_t gate_row_bytes,
uint64_t down_expert_bytes, uint64_t down_row_bytes,
uint32_t expert_in_dim, uint32_t expert_mid_dim,
uint32_t out_dim,
const ds4_gpu_tensor *selected, // 6 个专家索引
const ds4_gpu_tensor *weights, // 6 个专家权重
uint32_t n_expert, // 6
float clamp,
const ds4_gpu_tensor *x) { // 输入
// 对齐要求:
// expert_in_dim % 256 == 0
// expert_mid_dim % 256 == 0
// 验证 8 个 Metal 缓冲区大小
// gate, up, mid, out, experts, selected, weights, x
// GPU 内核执行:
// 1. 6 个专家的 gate+up 投影(并行)
// 2. SwiGLU 激活
// 3. 6 个专家的 down 投影(并行)
// 4. 加权累加
}HC Split Sinkhorn
c
// 行 13503: GPU 上的 HC 分解
int ds4_gpu_hc_split_sinkhorn_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *mix, // 混合系数
const void *model_map,
uint64_t model_size,
uint64_t scale_offset, // 缩放参数偏移
uint64_t base_offset, // 基础偏置偏移
uint32_t n_hc, // HC 维度
uint32_t sinkhorn_iters, // Sinkhorn 迭代次数
float eps) {
// 计算输出大小
uint32_t mix_hc = 2 * n_hc + n_hc * n_hc; // pre + post + comb
// out 大小 = mix_hc × sizeof(float)
// 验证 n_hc ∈ [1, 16]
// 包装模型范围(零拷贝)
ds4_gpu_wrap_model_range(model_map, scale_offset, ...);
ds4_gpu_wrap_model_range(model_map, base_offset, ...);
// 获取管线并分发
id<MTLComputePipelineState> pipe =
ds4_gpu_get_pipeline("hc_split_sinkhorn");
}追踪 6:GPU 命令提交与同步
命令缓冲区管理
c
// 行 3903: 开始新的命令批次
int ds4_gpu_begin_commands(void) {
g_batch_cb = [g_queue commandBuffer]; // 从队列获取命令缓冲区
return 0;
}
// 行 3910: 刷新(提交当前批次,立即开始新批次)
int ds4_gpu_flush_commands(void) {
[g_batch_cb commit]; // 提交到 GPU
[g_pending_cbs addObject:g_batch_cb]; // 追踪待完成命令
g_batch_cb = [g_queue commandBuffer]; // 立即创建新批次
return 0;
}
// 行 3929: 结束命令批次
int ds4_gpu_end_commands(void) {
[g_batch_cb commit];
[g_pending_cbs addObject:g_batch_cb];
g_batch_cb = nil;
return 0;
}
// 行 3937: 等待所有 GPU 操作完成
int ds4_gpu_synchronize(void) {
if (g_batch_cb) ds4_gpu_end_commands(); // 先结束当前批次
for (id<MTLCommandBuffer> cb in g_pending_cbs)
[cb waitUntilCompleted]; // 等待每个命令完成
[g_pending_cbs removeAllObjects];
return 0;
}GPU 命令流水线:
CPU 时间线:
begin → 编码命令 → flush → 编码更多 → flush → end → synchronize
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
GPU 时间线:
[执行批次1] [执行批次2] [执行批次3] [等待完成]
flush 的作用:
- 提交已编码的命令,让 GPU 立即开始执行
- 同时创建新的命令缓冲区继续编码
- CPU/GPU 重叠工作,隐藏延迟
synchronize:
- 等待所有批次完成
- 通常在需要读取 GPU 输出时调用
- 例如: 采样前需要 logits 已经写好追踪 7:CUDA Prefill 性能优化
宽 WMMA 评分 Kernel
CUDA 后端使用 NVIDIA WMMA(Warp Matrix Multiply-Accumulate)张量核心进行压缩索引评分:
c
// 原有: 16 压缩 token/tile (16x16 WMMA)
indexer_scores_wmma_kernel
// 新增变体(默认使用 128 宽):
indexer_scores_wmma32_kernel // 32/tile, 64 threads
indexer_scores_wmma64_kernel // 64/tile, 128 threads
indexer_scores_wmma128_kernel // 128/tile, 256 threads
// 优化: 压缩索引预加载到 shared memory,只加载一次
// 然后迭代所有注意力头,避免每头重复读全局内存CUB BlockRadixSort Top-K
c
// 替代手写 bitonic sort,处理 n_comp <= 8192
// 512 threads × 16 items = 8192 items per block
indexer_topk_8192_cub_kernel
// Float → uint64_t key packing 实现降序排序:
// key = (topk_float_ordered_key(v) << 32) | (0xffffffff - idx)
// 整数降序 = float 降序
// 动态 shared memory:
cudaDevAttrMaxSharedMemoryPerBlockOptin // 查询设备上限
cudaFuncSetAttribute(..., shared_size) // 请求额外 shared memoryTop-K 预排序优化访存
c
// 将 512 个 top-k 索引按升序排列后再送入注意力 kernel
indexed_topk_sort_512_asc_kernel
// 随机访问 → 顺序访问,改善压缩 KV cache 的内存合并实测:32K prefill 255→346 tok/s(+36%),全曲线均值 316→370 tok/s(+17%)。
追踪 8:Metal NAX 加速
Neural Accelerator 检测
c
// ds4_metal.m — M5 NAX 检测
// 启动时检查系统属性中的 M5 标志
g_metal4_m5_neural_accelerators_hint = ...; // 读取设备属性
// NAX kernel 选择逻辑:
if (g_metal4_m5_neural_accelerators_hint && n_tokens >= 16) {
// 使用 NAX 加速路径
// indexer_scores: 卸载到 Neural Accelerator
// MoE expert matmul: Q8_0 → NAX tensor matmul
} else {
// 标准金属计算路径
}NAX kernel 自动在 M5 设备上启用,无需用户配置。在非 M5 设备上编译安全(条件分支指向标准路径)。
总结:ds4 代码架构全景
ds4.c (18189 行) — 推理引擎核心
├── 量化内核 (158-3990) 量化与矩阵运算
│ ├── block_q2_K/q4_K/q8_K/iq2_xxs
│ ├── matvec_q8_0 / matvec_q2_k / matvec_iq2_xxs
│ └── f16_to_f32 / fp8 量化
├── 线程池 (612-770) 推理流程
├── 注意力机制 (2700-7092) 模型架构
│ ├── RMS Norm / RoPE 推理流程
│ ├── Q/KV 投影 / Flash Attention
│ ├── KV 缓存管理
│ └── 混合注意力 (raw + comp)
├── MoE / FFN (5012-6031) 模型架构
│ ├── SwiGLU 激活
│ ├── Hash/Top-K 路由
│ └── 共享专家 + 路由专家
├── 推理循环 (6055-7919) 推理流程
│ ├── Prefill (层主序)
│ └── Decode (逐 token)
├── 分词器 (13438-14165) 分词与采样
├── 采样 (14874-15083) 分词与采样
└── 公共 API (15716-17440) 模型架构
ds4_server.c (10349 行) — HTTP 服务
├── JSON 解析器 (141-437) 服务层
├── HTTP/SSE (3080-3180) 服务层
├── API 兼容层 (735-4259) 服务层
├── KV 持久化 (5138-6195) 服务层
├── SHA-1 (5192-5270) 服务层
└── 服务器架构 (4717-8100) 服务层
ds4_metal.m (14647 行) — GPU 后端
├── 初始化与管线 (624-2661) GPU 加速
├── 张量管理 (3767-3937) GPU 加速
├── 矩阵运算 (4919-5160) GPU 加速
├── Flash Attention (10569+) GPU 加速
├── MoE / HC (12736+) GPU 加速
└── Metal Shader (.metal) 编译到 .metallib