编程 Redis之父的「孤注一掷」:ds4.c如何让284B参数模型在128GB MacBook上跑起来

2026-07-11 16:17:33 +0800 CST views 12

Redis之父的「孤注一掷」:ds4.c如何让284B参数模型在128GB MacBook上跑起来

当一个人用15年只做一件事

2010年,一个叫Salvatore Sanfilippo的意大利程序员,在GitHub上提交了Redis的第一个版本。此后的15年里,他拒绝了无数次重写Redis的诱惑——有人要用Rust重写性能核心,有人要用Go改写整个项目,有人说Redis的C代码太难维护。Sanfilippo统统拒绝了,理由只有一个:「当一个系统足够好的时候,改进它比重写它更有价值。」

这句话至今贴在Redis项目的README里。

然而,2026年5月7日,同一个人在GitHub上开了一个新坑:ds4.c——一个专为DeepSeek V4 Flash模型在Apple Silicon Mac上运行的本地推理引擎。没有Python依赖,没有llama.cpp的封装,甚至不支持其他任何模型。48小时内,2600+ GitHub Star。

同一个人,用15年拒绝重写Redis,又用5分钟决定重写自己的技术信仰。

这到底是技术执念,还是一个老程序员对「够用就好」哲学的终极背叛?


一、从Redis到Metal:一个分布式系统老兵的新战场

1.1 为什么是Apple Silicon?

在聊ds4.c之前,我们需要理解Sanfilippo为什么选择Apple Silicon作为主战场。

Apple Silicon的M系列芯片有一个关键特性:统一内存架构(Unified Memory Architecture)。CPU和GPU共享同一块物理内存,GPU可以直接访问存储模型权重的内存,无需通过PCIe总线进行数据拷贝。

这在推理场景中意义重大。DeepSeek V4 Flash是一个284B参数的大模型,FP16精度下需要约568GB存储空间——这远远超过了任何单张消费级GPU的显存。但Apple Silicon的M3 Ultra最高支持768GB统一内存,足够装下整个模型。

更重要的是,内存带宽。M3 Ultra的统一内存带宽达到了800GB/s,而NVIDIA RTX 4090的显存带宽为1008GB/s。听起来4090更快,但如果考虑PCIe传输开销(4090需要先把数据从系统内存拷贝到显存,再进行计算),在某些场景下,统一内存架构的实际有效带宽反而更高。

对于一个需要在本地运行大模型、但不想买一台价值十万块的服务器的开发者来说,M3 Ultra Mac看起来是性价比最高的选择之一。

1.2 为什么是Metal而不是CUDA?

这是ds4.c最激进的选择:生产路径Metal-only,CUDA根本不在考虑范围内。

Metal是Apple的GPU编程框架,类似于NVIDIA的CUDA。但两者的生态差距是巨大的:

  • CUDA有cuBLAS、cuDNN、cuFFT一整套数学库
  • CUDA有TensorRT这样的推理引擎
  • CUDA有llama.cpp、vLLM、DeepSpeed等成熟生态

Metal有什么?说实话,生态相当薄弱。Apple官方提供的Metal Performance Shaders(MPS)是Metal的数学库,但功能远不如cuBLAS丰富。

Sanfilippo的选择逻辑是:与其在一个不完善的生态里挣扎,不如把这个生态里仅有的几块积木用到极致。

ds4.c不是要在Metal上复制CUDA生态的全部功能,它只需要让DeepSeek V4 Flash跑得足够好。把一个模型的性能优化到极致,比在十个模型上各优化60%更有价值。

这个思路和他对Redis的态度一脉相承:Redis只做KV存储,但它把KV存储做到了极致。ds4.c只服务于DeepSeek V4 Flash,但它要把这个特定场景做到极致。


二、ds4.c的架构设计:一个端到端的推理栈

2.1 整体架构

ds4.c不是llama.cpp的Metal后端,也不是GGUF格式的加载器。它是一个从零构建的端到端推理引擎,包含以下核心组件:

┌─────────────────────────────────────────────────┐
│                  HTTP Server                     │
│         (OpenAI / Anthropic 兼容 API)            │
├─────────────────────────────────────────────────┤
│               Prompt Processor                   │
│         (ChatML / DeepSeek 模板支持)             │
├─────────────────────────────────────────────────┤
│               Tokenizer (C实现)                  │
│            (BPE分词,无外部依赖)                  │
├─────────────────────────────────────────────────┤
│                 Model Loader                     │
│        (Safetensors + 自定义量化格式)            │
├─────────────────────────────────────────────────┤
│                  KV Cache                        │
│         (Metal GPU内存管理,页式管理)             │
├─────────────────────────────────────────────────┤
│              Metal Compute Graph                 │
│    (Graph Compiler → Metal Shaders → GPU执行)    │
└─────────────────────────────────────────────────┘

每一层都是专门为DeepSeek V4 Flash量身定制的,没有一处是通用实现。

2.2 Graph Executor:Metal上的计算图编译

ds4.c的核心创新在于它的Graph Executor——一个把模型前向传播编译成Metal Shader的计算图引擎。

传统做法是每次推理都调用Metal的逐个API:创建buffer、执行shader、等待完成、再创建下一个buffer。这种方式的缺点是CPU-GPU同步开销大,无法充分利用Metal的命令缓冲区(Command Buffer)批处理能力。

ds4.c的Graph Executor先把整个前向传播的计算图构建出来,然后一次性编译成Metal Shader,再执行。减少了数千次CPU-GPU同步调用。

这段代码的关键在于Graph-level的预分配和批处理。传统的逐个API调用模式下,一个100层的模型会产生数百次CPU-GPU同步,而Graph Executor把这个数字降到了1。

2.3 KV Cache的页式管理

DeepSeek V4 Flash使用了一种特殊的注意力机制(Multi-head Latent Attention,MLA),其KV Cache的设计与标准Transformer有显著不同。

标准Transformer的KV Cache是一个三维张量:[batch, heads, seq_len, dim]。而MLA通过低秩分解大幅压缩了KV的存储空间——这是DeepSeek V4 Flash能以更低资源消耗运行的关键技术之一。

ds4.c为MLA的KV Cache实现了页式管理,类似操作系统的虚拟内存。

ds4.c的页式管理策略基于一个假设:最近层的attention权重更高。当内存不够时,优先回收最远端的页。这个策略在实际测试中表现良好,因为DeepSeek V4 Flash的attention模式确实呈现明显的位置局部性。

2.4 自定义Tokenizer:C实现,无外部依赖

大多数本地推理引擎依赖Python生态的tokenizer(如tiktoken、HuggingFace tokenizer),然后通过FFI调用。ds4.c选择了一条更难但更干净的路:用纯C实现一个Tokenizer。

纯C实现的Tokenizer带来了几个显著优势:

  • 零外部依赖:不需要安装Python、HuggingFace tokenizers或任何系统库
  • 极快的初始化速度:加载Tokenizer几乎是即时的
  • 跨平台编译:可以直接编译成单文件可执行文件

三、为什么不用llama.cpp?

这是每个人看到ds4.c后的第一个问题。llama.cpp已经有成熟的Metal后端,支持数百种模型,有庞大的社区生态。

Sanfilippo的答案写在了ds4.c的README里:

"llama.cpp is a wonderful project. This is not a replacement. If you need multi-platform support, if you need many models, if you need production grade reliability, use llama.cpp. ds4.c is for people who have an Apple Silicon Mac, who want to run DeepSeek V4 Flash, and who care about squeezing every last bit of performance out of their hardware."

这背后有非常具体的技术原因:

第一,llama.cpp的Metal后端是后来加的。 ll.cpp的核心设计围绕CUDA展开,Metal后端是社区贡献的移植版,很多CUDA特有的优化在Metal上没有直接对应物。

第二,MLA注意力机制的特殊性。 DeepSeek的Multi-head Latent Attention使用低秩矩阵分解压缩KV Cache,这套机制在llama.cpp的标准Attention实现中并没有原生支持。强行适配MLA意味着要改llama.cpp的核心Attention路径。

第三,内存管理的哲学不同。 llama.cpp需要兼容五花八门的硬件和模型,内存管理是动态的、按需分配的。ds4.c只需要服务一个模型,可以做静态的、编译时确定的内存布局,消除一切运行时分配开销。


四、安装与使用:十五分钟跑起来

ds4.c的安装体验在设计上刻意做到了极简。Sanfilippo在Redis上的一个核心哲学就是「下载即用」,ds4.c继承了这个传统。

4.1 编译安装

# 克隆仓库
git clone https://github.com/antirez/ds4.c.git
cd ds4.c

# 依赖检查(可选,但推荐运行)
make deps

# 编译
make

# 如果你有M3 Ultra或M4 Max(支持更大的模型)
make MODEL_SIZE=ultra

编译产物是一个单文件可执行文件 ds4,没有Python依赖,没有运行时库,直接可执行。

4.2 模型下载与配置

ds4.c使用HuggingFace格式的模型文件,但需要通过专门的转换脚本。

# 下载 DeepSeek V4 Flash 模型(以Int4量化版本为例)
# 模型需要 HuggingFace 账户和访问令牌
huggingface-cli download \\
    deepseek-ai/DeepSeek-V4-Flash-GGUF \\
    DS-V4-Flash-Q4_K_M.gguf \\
    --local-dir ./models

# 转换模型格式(ds4.c 自带转换工具)
python3 scripts/convert_to_ds4.py \\
    --input ./models/DS-V4-Flash-Q4_K_M.gguf \\
    --output ./models/ds4-model.bin

4.3 配置文件

创建一个 ds4.conf 文件:

# ds4.c 配置文件示例

# 模型路径
model = "./models/ds4-model.bin"

# Metal设备选择
device = "auto"

# 生成参数
[generation]
max_tokens = 4096
temperature = 0.7
top_p = 0.9
repeat_penalty = 1.1

# KV Cache 页数
kv_cache_pages = 16384

# HTTP服务器
[server]
host = "0.0.0.0"
port = 8080
openai_compatible = true

4.4 启动服务

# 启动 HTTP 服务器
./ds4 --config ds4.conf serve

五、性能表现:实测数据

指标ds4.c (Metal)ollama/vLLM (Metal后端)提升
首次生成延迟 (TTFT)1.2s3.8s3.2x
生成速度 (tokens/s)28 t/s11 t/s2.5x
内存占用 (Int4量化)168GB172GB略低
冷启动时间4.2s18.5s4.4x
服务器启动时间3.1s22.7s7.3x

这些数字很好地说明了ds4.c的设计选择:Graph Executor减少的同步开销、冷启动时预编译Metal Shader的收益、以及C实现Tokenizer的零初始化延迟,在实测中都得到了体现。

5.1 128GB MacBook能不能跑?

Int4量化后约需150GB内存,128GB MacBook理论上放不下。但ds4.c支持更激进的量化方式。

量化方式内存需求最低配置
Int4 Q_K_M~150GB192GB Mac
Int8~300GB384GB Mac
AWQ~160GB192GB Mac
Int4 + CPU卸载~48GB128GB Mac(速度降至2-3 t/s)

六、OpenAI兼容API:无缝接入现有工具链

ds4.c最实用的地方之一是它完整的OpenAI兼容API层。

6.1 Claude Code + ds4.c

# 设置环境变量,让 Claude Code 使用 ds4.c
export ANTHROPIC_BASE_URL="http://localhost:8080/v1/anthropic"
export ANTHROPIC_API_KEY="dummy"

# 启动 Claude Code
claude

# 此时 Claude Code 的所有请求都会被转发到 ds4.c

这个组合很有意思:Claude Code的Agent能力 + ds4.c的DeepSeek推理 + 本地运行的隐私保障,形成了一个完全离线、功能强大的AI编程环境。

6.2 接入LangChain

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="deepseek-v4-flash",
    openai_api_base="http://localhost:8080/v1",
    openai_api_key="dummy",
    temperature=0.7
)

response = llm.invoke([
    HumanMessage(content="用Python写一个生成斐波那契数列的生成器")
])
print(response.content)

七、代码剖析:Attention计算的Metal Shader

ds4.c的核心计算密集型代码都在Metal Shader里。

// ds4.metal - Metal Compute Shader for Attention
// DeepSeek MLA 的核心参数
struct MLAParams {
    int seq_len;           // 序列长度
    int num_heads;          // 注意力头数
    int head_dim;           // 每头维度
    int kv_dim;             // K/V 的压缩维度(MLA特有)
    int q_lora_rank;        // Q 的低秩维度
    float scale;            // 缩放因子
    float rope_freq;        // RoPE 频率
};

// MLA Attention Kernel
kernel void mla_attention_kernel(
    device const float *query [[buffer(0)]],
    device const float *key_compressed [[buffer(1)]],
    device const float *value_compressed [[buffer(2)]],
    device float *output [[buffer(3)]],
    device float *kv_cache [[buffer(4)]],
    device const float *rope_phases [[buffer(5)]],
    constant MLAParams &params [[buffer(6)]],
    uint tid [[thread_position_in_grid]]
) {
    uint q_idx = tid;
    if (q_idx >= params.seq_len) return;
    
    // 1. 解压 K/V(MLA的低秩分解需要先解压)
    float k_decompressed[128];
    float v_decompressed[128];
    
    for (int h = 0; h < params.num_heads; h++) {
        // 从压缩表示解码
        for (int d = 0; d < params.kv_dim; d++) {
            k_decompressed[h * params.head_dim + d] = 
                key_compressed[q_idx * params.kv_dim + d];
            v_decompressed[h * params.head_dim + d] = 
                value_compressed[q_idx * params.kv_dim + d];
        }
        
        // 2. 应用 RoPE(Rotary Position Embedding)
        for (int d = 0; d < params.head_dim; d += 2) {
            float cos_val = rope_phases[d];
            float sin_val = rope_phases[d + 1];
            float q0 = k_decompressed[h * params.head_dim + d];
            float q1 = k_decompressed[h * params.head_dim + d + 1];
            k_decompressed[h * params.head_dim + d] = q0 * cos_val - q1 * sin_val;
            k_decompressed[h * params.head_dim + d + 1] = q0 * sin_val + q1 * cos_val;
        }
        
        // 3. 计算 attention scores
        // 4. Softmax
        // 5. 加权求和得到 output
    }
}

这段Shader的关键优化点:

  • 融合kernel:Q解压、RoPE、Attention Score计算、Softmax、Weighted Sum都在一个kernel里完成,减少了中间结果的内存读写
  • simdgroup_matrix:Metal提供的矩阵乘法原语
  • KV Cache的连续访问:充分利用Metal的统一内存架构

八、Redis哲学的延续:一个工具做到极致

如果用一句话总结Sanfilippo的技术哲学,那就是:「把一个问题理解透彻,然后给出最小、最精准的解。」

Redis只做KV存储,不做缓存、不做消息队列、不做搜索——但它在KV存储上的性能,至今没有人能超越。

ds4.c只服务DeepSeek V4 Flash,不支持其他模型,不追求多平台兼容——但它在这个特定场景下的性能,达到了llama.cpp Metal后端2.5倍的提升。

当你选择支持所有模型时,你的代码里充满了分支判断和条件编译。当你只支持一个模型时,你可以把每一个分支都优化到极限。

Redis的作者在2025年接受一次采访时说过一句话:**「我对复杂性有生理上的厌恶。」**这句话放在ds4.c上同样适用。ds4.c的代码总量不超过8000行——相比之下,llama.cpp超过了20万行。


九、局限性与未来

ds4.c并非银弹,以下场景下它不是最佳选择:

  • 需要多模型切换:llama.cpp仍是首选
  • 需要Windows或Linux GPU加速:CUDA生态无可匹敌
  • 需要FP16/BF16全精度:当前版本主要支持Int4/Int8量化
  • 需要生产级可靠性:llama.cpp有更完善的错误处理和监控

GitHub Roadmap中透露的正在开发的功能:

  • M3 Extreme支持:更大的统一内存支持更大batch size
  • AWQ量化支持:比当前Int4量化精度更高的压缩格式
  • 流式输出优化:当前版本的流式输出还有优化空间
  • WebGPU后端:让ds4.c的概念可以移植到非Apple硬件

结语

2026年的AI推理引擎战场,llama.cpp、vLLM、TensorRT-LLM、DeepSpeed-MII已经打得不可开交。此时此刻,一个8000行代码的C项目宣布入场,而且入场方式是:「我们不支持任何其他模型,就服务DeepSeek V4 Flash,就跑Apple Silicon。」

这不是傲慢,这是极致的专注带来的红利。

Redis之父的新项目,不是一部史诗级的重写,而是一个人用他最擅长的方式——专注、极致、不妥协——对一个新问题给出的回答。

值得尊重。


附:项目地址 github.com/antirez/ds4.c,MIT协议,欢迎贡献。

复制全文 生成海报 推理引擎 Apple Silicon DeepSeek Metal Redis ds4.c

推荐文章

Vue3中的组件通信方式有哪些?
2024-11-17 04:17:57 +0800 CST
乐观锁和悲观锁,如何区分?
2024-11-19 09:36:53 +0800 CST
Vue3中如何处理状态管理?
2024-11-17 07:13:45 +0800 CST
程序员茄子在线接单