
llama.cpp iOS 集成:Swift 开发指南
将 llama.cpp 集成到 iOS 应用的分步指南。项目设置、Metal GPU 加速、模型加载、token 流式输出和生产部署的内存管理。
llama.cpp 是在 Apple 硬件上运行 GGUF 语言模型的推理引擎。它使用 Metal 进行 GPU 加速,支持从 A14(iPhone 12)开始的所有 iPhone 机型,根据模型大小和设备不同,token 生成速度在每秒 20-50 个之间。
本指南涵盖从项目设置到生产部署的完整集成流程。
集成选项
选项 1:Swift Package(推荐)
llama.cpp 仓库包含一个 Swift Package,您可以直接添加到 Xcode 项目中:
- 在 Xcode 中,前往 File,Add Package Dependencies
- 输入 llama.cpp 仓库 URL
- 选择所需的版本或分支
- 在 Swift 文件中导入
llama模块
这是最简单的集成路径。该 package 将 llama.cpp 作为构建的一部分编译,并将 C API 暴露给 Swift。
选项 2:预编译 Framework
将 llama.cpp 编译为 XCFramework,作为二进制依赖项包含。这样可以避免在项目中编译 C++ 源代码:
# 构建 framework
mkdir build-ios && cd build-ios
cmake .. -G Xcode \
-DCMAKE_SYSTEM_NAME=iOS \
-DCMAKE_OSX_DEPLOYMENT_TARGET=15.0 \
-DLLAMA_METAL=ON \
-DBUILD_SHARED_LIBS=OFF
cmake --build . --config Release
选项 3:llama.swift 封装
社区维护的 Swift 封装在 C 绑定之上提供了更符合 Swift 风格的 API。这些封装处理了桥接样板代码并暴露了更简洁的接口。
项目设置
最低要求
- iOS 15.0+(Metal 计算着色器需要)
- Xcode 15+
- 测试需要物理设备(模拟器不支持 Metal 计算)
构建设置
将 Metal framework 添加到项目:
- 链接
Metal.framework和MetalKit.framework - 如需自定义着色器,设置
METAL_COMPILER_FLAGS
权限
无需特殊权限。llama.cpp 在应用的普通沙箱中运行。内存使用是主要关注点(见下文)。
加载模型
import llama
class LlamaEngine {
private var model: OpaquePointer?
private var context: OpaquePointer?
func loadModel(at path: String) throws {
// 模型参数
var modelParams = llama_model_default_params()
modelParams.n_gpu_layers = 99 // 将所有层卸载到 Metal
// 加载 GGUF 文件
model = llama_load_model_from_file(path, modelParams)
guard model != nil else {
throw LlamaError.modelLoadFailed
}
// 创建推理上下文
var ctxParams = llama_context_default_params()
ctxParams.n_ctx = 2048 // 上下文窗口大小
ctxParams.n_threads = 4 // CPU 线程数(用于非 Metal 操作)
ctxParams.n_batch = 512 // 提示处理的批次大小
context = llama_new_context_with_model(model, ctxParams)
guard context != nil else {
throw LlamaError.contextCreationFailed
}
}
func unload() {
if let ctx = context {
llama_free(ctx)
context = nil
}
if let mdl = model {
llama_free_model(mdl)
model = nil
}
}
deinit {
unload()
}
}
关键参数
n_gpu_layers: 设置为 99(或模型的实际层数)以将所有内容卸载到 Metal。这是最重要的性能设置。
n_ctx: 上下文窗口大小(以 token 为单位)。更大的窗口使用更多内存。2048 对大多数移动端用例来说是实用的。如需更长对话可设为 4096。
n_threads: 用于在 CPU 上运行操作的线程数。设置为设备的性能核心数量(iPhone 上通常为 2-4 个)。
n_batch: 提示评估期间每批处理的 token 数。更高的值加速提示处理但使用更多内存。512 是一个好的默认值。
生成文本
分词和提示处理
extension LlamaEngine {
func generate(
prompt: String,
maxTokens: Int = 256,
temperature: Float = 0.7,
onToken: @escaping (String) -> Void
) -> String {
guard let ctx = context, let mdl = model else { return "" }
// 对提示进行分词
let promptTokens = tokenize(prompt)
// 创建提示处理批次
var batch = llama_batch_init(Int32(promptTokens.count), 0, 1)
for (i, token) in promptTokens.enumerated() {
llama_batch_add(&batch, token, Int32(i), [0], i == promptTokens.count - 1)
}
// 处理提示
llama_decode(ctx, batch)
llama_batch_free(batch)
// 生成 token
var output = ""
for _ in 0..<maxTokens {
let logits = llama_get_logits(ctx)
// 采样下一个 token
let token = sampleToken(logits: logits!, temperature: temperature)
// 检查是否到达序列末尾
if llama_token_is_eog(mdl, token) { break }
// 将 token 解码为字符串
let piece = decodeToken(token)
output += piece
onToken(piece)
// 准备下一个批次
var nextBatch = llama_batch_init(1, 0, 1)
llama_batch_add(&nextBatch, token, Int32(promptTokens.count + output.count), [0], true)
llama_decode(ctx, nextBatch)
llama_batch_free(nextBatch)
}
return output
}
private func tokenize(_ text: String) -> [llama_token] {
let maxTokens = Int32(text.utf8.count + 16)
var tokens = [llama_token](repeating: 0, count: Int(maxTokens))
let count = llama_tokenize(model, text, Int32(text.utf8.count),
&tokens, maxTokens, true, false)
return Array(tokens.prefix(Int(count)))
}
private func decodeToken(_ token: llama_token) -> String {
var buf = [CChar](repeating: 0, count: 64)
let len = llama_token_to_piece(model, token, &buf, 64, 0, false)
return String(cString: Array(buf.prefix(Int(len))) + [0])
}
}
Metal GPU 加速
当设置了 n_gpu_layers 时,Metal 加速会自动启用。llama.cpp 在首次加载时编译 Metal 着色器(耗时 1-2 秒,之后会缓存)。
性能影响
| 配置 | iPhone 15 Pro, 3B Q4 | iPhone 14, 3B Q4 |
|---|---|---|
| 仅 CPU (n_gpu_layers = 0) | 8-12 tok/s | 6-10 tok/s |
| Metal (n_gpu_layers = 99) | 18-25 tok/s | 14-18 tok/s |
Metal 平均提供 2 倍加速。生产环境务必启用。
Metal 着色器缓存
llama.cpp 首次在设备上运行时会编译 Metal 着色器。这会在首次模型加载时增加 1-2 秒。后续加载是即时的(着色器由 iOS 缓存)。
内存管理
内存预算
iOS 大约给应用分配设备总 RAM 的 50-70%,超过后会触发 jetsam(强制终止):
| 设备 | 总 RAM | 应用预算 | 可用于模型 |
|---|---|---|---|
| iPhone 12 (4GB) | 4GB | 约 2.5GB | 约 1.5GB |
| iPhone 14 (6GB) | 6GB | 约 3.5GB | 约 2.5GB |
| iPhone 15 Pro (8GB) | 8GB | 约 5GB | 约 3.5GB |
3B Q4 模型在 RAM 中占用约 2.2GB。在 6GB 设备上,这为应用、iOS 和其他进程留下约 1.3GB。紧张但可行。
最佳实践
// 加载前检查可用内存
func canLoadModel(sizeBytes: Int) -> Bool {
let available = os_proc_available_memory()
// 为应用和系统预留 500MB
return available > sizeBytes + 500_000_000
}
// 处理内存警告
func didReceiveMemoryWarning() {
engine.unload()
// 显示"模型已卸载"消息,提供重新加载选项
}
- 始终在加载前检查可用内存
- 当 AI 功能不活跃时卸载模型
- 通过卸载模型来处理
didReceiveMemoryWarning - 应用在后台时绝不保持模型加载状态
模型分发
内置打包
将 GGUF 文件作为资源添加到 Xcode 项目中。通过 Bundle.main 访问:
let modelPath = Bundle.main.path(forResource: "model", ofType: "gguf")!
对于超过 200MB 的模型,考虑使用 On Demand Resources 以避免增大初始下载大小。
下载安装
安装后下载模型并存储在应用的 Documents 目录中:
let documentsURL = FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask)[0]
let modelURL = documentsURL.appendingPathComponent("model.gguf")
对大文件使用 URLSession 后台下载。支持中断后恢复下载。
生产清单
- 模型在目标设备上加载不崩溃(在最低 RAM 目标设备上测试)
- Metal 加速已启用(通过性能日志验证)
- 内存警告处理程序优雅地卸载模型
- 下载后验证模型文件完整性(SHA256)
- 流式 token 在 UI 中平滑显示
- 用户可以取消生成(中断生成循环)
- 应用切换到后台时卸载模型
- 模型未加载时应用正常运行(优雅降级)
微调后的 GGUF 模型是关键要素。基础模型生成通用回复。在您的领域数据上微调的模型(通过 Ertas 等平台)生成与应用目的和风格相匹配的回复。llama.cpp 的集成方式无论使用哪个模型都是相同的。
Ship AI that runs on your users' devices.
Free plan with 30 credits/mo, no card required. Paid plans from $25/mo USD.
Keep reading

iOS应用中的AI:CoreML、云API和端侧LLM对比
iOS应用中AI的三条路径。CoreML用于Apple生态系统,云API用于能力拓展,llama.cpp端侧LLM用于成本和隐私。面向Swift开发者的实用比较。

llama.cpp Android 集成:Kotlin 开发指南
将 llama.cpp 集成到 Android 应用的分步指南(Kotlin)。JNI 绑定、Vulkan GPU 加速、模型加载以及跨 Android 设备范围的内存管理。

LLM 真的能在 iPhone 上运行吗? 基准测试与实际表现
通过 llama.cpp 在 iPhone 上运行 LLM 的真实基准数据。从 iPhone 12 到 iPhone 16 Pro 各机型的 token 生成速度、内存使用和热量表现。