Newshot
Account

我 17 岁,为 Apple Silicon 打造了一个 Swift 原生 LLM 推理引擎

Translation(model translation, not the source's own text)

alpaca.swift

Release
Release
License: MIT
License: MIT

面向 Apple Silicon 的原生语言模型推理。

一个从零开始、使用自定义 Metal 内核的 Swift 推理引擎——运行时不需要 llama.cpp、MLX、Core ML、ONNX 或 Python。

状态

0.2.0-pre – 已实现一个架构系列(Llama 风格解码器、字节级 BPE)和三种权重格式(F16、Q8_0、Q4_0),并已针对 PyTorch 验证。在验证模型(SmolLM2-135M-Instruct、Q8_0、Apple M5)上,128–4096 token 的预填充比 llama.cpp 快 1.3–1.7×。

注意事项:所有基准测试均在一台 Apple M5(16 GB)机器上测量。最快的预填充路径需要 Apple10 系列 GPU(M5/A19)以及 macOS/iOS 26;在其他设备上,其吞吐量大约为 1/3 到 1/2。尚未在实体 iPhone 或 iPad 上运行过;该库和示例应用仅能为 iOS 模拟器编译。

作者:Torin Etheridge · 2026-10-09 · MIT License

快速开始

Swift Package Manager


        // Package.swift
.package(url: "https://github.com/torinriley/alpaca.git", from: "0.2.0")
      

        // target dependency
.product(name: "Alpaca", package: "alpaca")
      

用法


        import Alpaca

// Load a GGUF model (memory-mapped, async)
let model = try await LanguageModel.load(from: modelURL)

// Generate text with streaming
let stream = model.generate(
    prompt: "Explain how transformers work.",
    configuration: .init(maxTokens: 256, temperature: 0.7, seed: 42)
)

for try await token in stream {
    print(token.text, terminator: "")
}

// Check generation summary
let summary = stream.summary  // tokens, tok/s, finish reason
      

命令行


        # Download the validation model (SmolLM2-135M-Instruct, Apache-2.0)
Scripts/fetch_models.sh

# Build
swift build -c release

# Generate text
.build/release/alpaca generate Models/gguf/SmolLM2-135M-Instruct-Q8_0.gguf \
  --chat --prompt "Name three colors." --stats

# Benchmark
.build/release/alpaca bench Models/gguf/SmolLM2-135M-Instruct-Q8_0.gguf \
  --backend metal --json out.json

# Profile GPU stages
.build/release/alpaca profile Models/gguf/SmolLM2-135M-Instruct-Q8_0.gguf \
  --tokens 2048 --context 2200
      

SwiftUI 参考应用(模型选择器、流式输出、取消、吞吐量和内存诊断)位于 Examples/iOS/AlpacaDemo。

基准测试

Apple M5(10 核 GPU)、16 GB、macOS 27.0、Swift 6.4。SmolLM2-135M-Instruct(30 层,GQA 9/3,head dim 64)。7 次运行的中位数,贪心解码,两个引擎使用相同的 GGUF 文件。测量是在有后台负载的机器上进行的(在同一图表内比较,不要跨运行比较)。完整方法和数据:Docs/PERFORMANCE.md | 技术报告。

Q8_0 预填充和解码对比 llama.cpp:

提示 token 数alpaca.swift (tok/s)llama.cpp加速比
12824,10618,3261.32×
204830,79720,7621.48×
409624,06915,3881.56×
163,4204,7430.72×
解码(上下文)alpaca.swift (tok/s)llama.cpp加速比
16 个 token5824411.32×
2048 个 token4563671.24×

其他格式:Q4_0 在 16 个 token 上下文下解码为 712 tok/s(llama.cpp 518,1.38×)。F16 解码:360 tok/s(llama.cpp 319,1.13×)。

峰值内存:在 4400 个 token 上下文(135M 参数模型)下为 175–194 MiB。

技术报告将比较扩展到 SmolLM2-360M 和 1.7B,记录了使预填充在更大规模下具备竞争力的 GEMM 扩展工作,并解释了为什么 16 个 token 的提示更慢。对于 1.7B 且使用 --prefill-cache 时,预填充在 128–4096 个 token 下比 llama.cpp 快 1.1–1.3×。

目录

亮点 · 能力 · 结果 · 数值验证 · 设计 · 仓库布局 · 复现结果 · 局限性 · 路线图 · 引用 · 许可证

亮点

写作 {{NAME_ab12cd34}} 这样的值是真实值的占位符。请按原样使用它们,在回复和工具调用中都一样:在运行任何内容之前,它们会被填入真实值。不要要求验证它们,也不要用示例替换它们。

文章标题:我 17 岁,为 Apple Silicon 构建了一个 Swift 原生的 LLM 推理引擎

正文:

  • 独立实现。张量引擎、CPU 参考算子、GGUF 解析器、BPE 分词器、Llama 前向传播、采样器以及所有 Metal kernel 均在本仓库内编写。Python/PyTorch 仅用于离线生成参考测试基准。
  • 正确性优先。每个操作都有确定性的 CPU 参考实现,并针对独立生成的向量(numpy/PyTorch float64、gguf 包、Hugging Face transformers/tokenizers)进行测试,容差明确且有依据。CPU 后端在 logits 上与 Hugging Face float32 匹配至 5e-5(F16)和 7e-5(Q8_0);191/191 个分词器测试字符串产生相同的 token ID;72/72 个贪婪 token 在每种格式上都匹配。
  • 性能有实测。在验证模型(SmolLM2-135M-Instruct,Q8_0,Apple M5)上,alpaca.swift 在 128 到 4096 token 的范围内预填充比 llama.cpp 快 1.3–1.7×,在 16–4096 token 的上下文下解码快 1.02–1.38×。仅在非常短的提示下较慢(16 token:0.72–0.90×)。技术报告显示,借助 GEMM 扩展工作,1.7B 预填充在使用 --prefill-cache 时也比 llama.cpp 更快。方法、瓶颈分析和优化见 Docs/PERFORMANCE.md。
  • 技术报告。paper/alpaca-swift.pdf 在 135M、360M 和 1.7B 参数下,以 F16、Q8_0 和 Q4_0 格式对 alpaca.swift 与 llama.cpp 进行评估,诊断了预填充最初为何无法扩展到 1.7B,并测量了修复该问题的 GEMM 改动(LaTeX 源码、实验)。
  • 对恶意输入鲁棒。GGUF 解析器将模型文件视为不可信:每个长度都受实际存在字节数的限制,所有算术运算都做了溢出检查,并且它在 20,000 个变异模糊测试文件下未发生崩溃。
  • 符合语言习惯的 API。异步加载、AsyncSequence token 流式输出、取消、带种子的采样、每次生成隔离的会话,以及一个内存预算检查,会拒绝加载无法容纳的模型。

能力

架构Llama 风格解码器:RMSNorm、RoPE(无缩放)、分组查询注意力、SwiGLU、绑定或非绑定输出头
权重格式(GGUF v2/v3)F16、Q8_0、Q4_0 直接从内存映射文件中就地使用;Q4_1 张量在加载时展开为 F16 并报告
Metal 后端张量操作因果预填充注意力和 GEMM(Metal 4,Apple10 系列 GPU),带 simdgroup-matrix 回退 · split-K GQA 解码注意力 · 融合单 token kernel · GPU 常驻贪婪解码 · f16 或 f32 KV 缓存
CPU 后端确定性参考实现(测试、调试、无 Metal 的机器)
分词器字节级 BPE,带 gpt2 和 smollm 预分词器,UTF-8 安全的流式解码
采样贪婪、温度、top-k、top-p、带种子 RNG、EOS 和自定义停止 token
平台macOS 14+、iOS 17+、Apple Silicon (arm64)。Swift 6 语言模式;使用 Swift 6.4 / Xcode 27 开发

快速开始


        // Package.swift
.package(url: "https://github.com/torinriley/alpaca.git", from: "0.1.0")
// target dependency
.product(name: "Alpaca", package: "alpaca")
      

        import Alpaca

let model = try await LanguageModel.load(from: modelURL)           // GGUF, memory-mapped

let stream = model.generate(
    prompt: "<|im_start|>user\nExplain how transformers work.<|im_end|>\n<|im_start|>assistant\n",
    configuration: .init(maxTokens: 256, temperature: 0.7, seed: 42)
)
for try await token in stream { print(token.text, terminator: "") }

let s = stream.summary   // prompt/generated tokens, prefill & decode tok/s, time to first token, finish reason
      

使用 stream.cancel() 或取消消费 Task 来取消。每次 generate 调用都拥有自己的 KV 缓存,因此同一模型上的并发生成永远不会共享可变状态。提示是原始文本;请自行应用模型的聊天模板。在内存警告时调用 model.trimMemory(),在应用离开前台时调用 model.cancelAllGenerations()。

命令行:


        Scripts/fetch_models.sh                      # SmolLM2-135M-Instruct (Apache-2.0) into ./Models
swift build -c release
.build/release/alpaca generate Models/gguf/SmolLM2-135M-Instruct-Q8_0.gguf --chat --prompt "Name three colors." --stats
.build/release/alpaca bench    Models/gguf/SmolLM2-135M-Instruct-Q8_0.gguf --backend metal --json out.json
.build/release/alpaca profile  Models/gguf/SmolLM2-135M-Instruct-Q8_0.gguf --tokens 2048 --context 2200
      

一个 SwiftUI 参考应用(模型选择器、流式传输、取消、吞吐量和内存诊断)位于 Examples/iOS/AlpacaDemo。

结果

Apple M5(10 核 GPU)、16 GB、macOS 27.0、Swift 6.4。SmolLM2-135M-Instruct(30 层,GQA 9/3,head dim 64)。7 次运行的中位数,64 个解码 token,贪心;llama.cpp build 11429 在同一轮运行中使用相同的 GGUF 文件。测量期间机器带有后台负载,因此应在同一图表内比较,而不是跨运行比较;方法、完整数值网格和注意事项在 Docs/PERFORMANCE.md 中,原始数据在 Benchmarks/ 中。

Prefill 吞吐量
Prefill 吞吐量
Decode 吞吐量随上下文长度的变化
Decode 吞吐量随上下文长度的变化
相对于第一版实现的加速比
相对于第一版实现的加速比
Q8_0优化前alpaca.swiftllama.cppalpaca / llama.cpp
Prefill,128 个 token(tok/s)5,56324,10618,3261.32×
Prefill,2048 个 token1,54130,79720,7621.48×
Prefill,4096 个 token84424,06915,3881.56×
Prefill,16 个 token1,8533,4204,7430.72×
Decode,上下文 164565824411.32×
Decode,上下文 20481774563671.24×

Q4_0 在上下文 16 下的 decode:712 tok/s(llama.cpp 518)。F16:360(319)。整个基准测试网格的峰值物理内存:在 4400 token 上下文下为 175–194 MiB。这是一个在单设备上的 135M 参数模型;结果不能外推到更大的模型或其他硬件。

更大的模型

以上数字是针对 135M 模型的。技术报告对 SmolLM2-360M 和 1.7B 重复了比较,并发现 64x64 的 prefill GEMM tile 无法扩展:在 1.7B 上,prefill 比 llama.cpp 慢。通过启动线程组使并发的线程组共享一个权重 tile、对大矩阵使用 128x64 tile,以及可选的 --prefill-cache(量化权重的半精度副本,每个权重 +2 字节)解决了这个问题。比值是 alpaca / llama.cpp 在同一 GGUF 文件上背靠背测量的;机器有噪声,因此报告给出了全部三次运行(本图是最安静的一次)。

SmolLM2-1.7B 的 prefill 与 llama.cpp 对比,优化前后
SmolLM2-1.7B 的 prefill 与 llama.cpp 对比,优化前后
135M 和 360M 的 prefill 与 llama.cpp 对比,优化前后
135M 和 360M 的 prefill 与 llama.cpp 对比,优化前后

短提示(16 个 token)仍然比 llama.cpp 慢,且 decode 没有变化。图表由 Experiments/make_scaling_figs.py 根据 Experiments/results 中的原始结果生成。

数值验证

参考栈:numpy/PyTorch float64 用于基础算子,gguf 用于量化,Hugging Face LlamaForCausalLM(在随机模型上用 float64,在从相同 GGUF 权重加载的真实模型上用 float32,以排除量化误差),Hugging Face tokenizers 用于 token ID。

levelwhatresult
1基础算子(add、mul、SiLU、matmul、RMSNorm、softmax、RoPE ×2 约定、attention、Q8_0/Q4_0)相对 float64 ≤ 2.5e-6;反量化位精确;量化器与 gguf 字节一致
2–4tiny Llama(GQA/MHA、tied/untied、HF 与 GGUF RoPE 布局)、增量解码 vs 无缓存解码相对 float64 PyTorch ≤ 1.3e-6;缓存 == 无缓存完全一致
5SmolLM2-135M、CPU 后端、末 token logits5e-5(F16)、7e-5(Q8_0)、1.6e-3(Q4_0:三个 Q4_1 张量舍入为 F16)
5同上,默认 Metal 配置与 logit 峰值相差 1.1e-3 以内;72/72 贪婪 token 完全一致
5Metal 使用 prefillPrecision: .exact 和 f32 KV 对比 CPU2.7e-5

默认 Metal 配置以少量、有界的精度换取速度(半精度预填充激活、f16 KV 缓存、注意力中的半精度 Q/P)。每个降精度组件都由各自的测试隔离,并带有解析推导的边界,例如快速 GEMM 必须逐元素满足 |error| ≤ 2⁻¹⁰ Σ|xₖwₖ|(观测到的最坏情况:边界的 31%)。细节、容差及每项的理由见:Docs/NUMERICAL_VALIDATION.md。

Design


        Alpaca            公共 API:LanguageModel · GenerationStream · errors             ─┐
AlpacaMetal       Metal 上下文、内核(.metal)、GPU Llama 模型和会话                │ 依赖于
AlpacaModels      GGUF 解析器、Llama 权重映射、分词器构建                          │ ↓
AlpacaTokenizers  字节级 BPE                                                       │
AlpacaCore        张量、CPU 参考算子、KV 缓存、CPU Llama、采样器、内存              ─┘
AlpacaCLI         `alpaca` 可执行文件:info · generate · bench · profile
      

权重从不复制:GGUF 文件被内存映射,整个映射被包装为一个无拷贝的 MTLBuffer(统一内存),因此常驻权重内存约等于文件大小。解码步骤每层六次融合调度;贪婪解码完全在 GPU 上运行,多个命令缓冲区同时进行。预填充使用 512 token 块,端到端半精度激活:张量运算 GEMM(量化权重一次一个矩阵展开到暂存缓冲区)和张量运算因果注意力内核。完整描述:Docs/ARCHITECTURE.md。

Repository layout


        Sources/          AlpacaCore · AlpacaMetal (+ Kernels/*.metal) · AlpacaModels · AlpacaTokenizers · Alpaca · AlpacaCLI
Tests/            单元、内核、解析器/模糊、分词器和真实模型集成测试(+ 生成的 fixture)
Scripts/          fixture 生成器(Python,离线)、模型下载、基准测试工具、llama.cpp 对比
Benchmarks/       文档中引用的每个数字的原始 JSON 结果
Experiments/      技术报告背后的评估工具、脚本和原始结果
paper/            技术报告(LaTeX 源码和 PDF)
Docs/             ARCHITECTURE · NUMERICAL_VALIDATION · PERFORMANCE · MODEL_SUPPORT · DEVELOPMENT
Examples/iOS/     SwiftUI 参考应用
      

Reproducing the results


        Scripts/fetch_models.sh --with-reference     # ~800 MB:GGUF 文件 + Hugging Face 参考检查点
Scripts/test_all.sh                          # 先 debug 套件,再 release 套件(含真实模型验证)
Scripts/compare_llamacpp.sh Benchmarks/results/mine   # 需要 llama-bench(例如 brew install llama.cpp)
      

重新生成参考 fixture(PyTorch、gguf、分词器)的说明见 Docs/DEVELOPMENT.md。需要模型文件的测试在文件缺失时会跳过并给出消息;真实模型测试需要优化构建(swift test -c release -Xswiftc -enable-testing)。

Limitations

  • 仅支持 Llama 系列解码器:不支持 RoPE 缩放(Llama 3.1+)、滑动窗口注意力、MoE、偏置或部分旋转。
  • 分词器:字节级 BPE(gpt2、smollm 预分词器)。SentencePiece/Unigram 模型和 Llama-3 预分词器会被拒绝并报错。

我 17 岁,为 Apple Silicon 构建了一个 Swift 原生 LLM 推理引擎

  • 量化:F16、Q8_0、Q4_0(Q4_1 通过加载时扩展)。K-quants、Q5_x 和 BF16 会被拒绝。激活不做量化(与 llama.cpp 不同),因此输出无法与其进行按位比较。
  • 每次生成一个序列;没有连续批处理、分页注意力或投机解码。
  • 非常短提示(≤ 24 tokens)的预填充比 llama.cpp 慢 10–28%。驻留 GPU 的解码循环仅适用于贪心解码;随机采样每个 token 都要付出一次 CPU 往返开销(约慢 15–20%)。
  • 快速预填充路径(在矩阵硬件上执行 GEMM 和注意力)需要 Apple10 系列 GPU(M5/A19)以及 macOS/iOS 26;在其他平台上,simdgroup-matrix 路径的吞吐量大约只有三分之一到二分之一(仅在 M5 上测量)。
  • RoPE 角度在 GPU 上为 float32(误差 ≈ 1e-7 × 位置弧度)。权重超过 maxBufferLength 的模型通过复制方式加载。

路线图

精度工作由在困惑度/KL 预算下对每个降低精度位置进行的消融驱动(按操作混合的精度策略、split-Q 注意力分数);专用短提示(≤ 24 tokens)内核;GPU 侧随机采样;Llama-3 tokenizer 和 RoPE 缩放;更大模型与设备端验证。每项背后的理由见 Docs/PERFORMANCE.md。

引用


        @software{etheridge2026alpaca,
  author = {Etheridge, Torin},
  title  = {alpaca.swift: Native language model inference for Apple Silicon},
  year   = {2026},
  url    = {https://github.com/torinriley/alpaca}
}
      

另见 CITATION.cff。

许可证

MIT © 2026 Torin Etheridge。见 LICENSE。验证模型是 SmolLM2-135M-Instruct(Apache-2.0,HuggingFaceTB);GGUF 转换由 bartowski 完成。llama.cpp 仅用作外部基准基线,不是依赖项。

The translation is machine-generated and individual terms may be off. Nothing is rewritten or cut; the original is authoritative.

Published by HN AI
This site only summarises and aggregates; copyright stays with the original author. Headlines and summaries follow the source — where they differ, the original wins.
Read the original ↗