llama.cpp 在 iOS 上的應用:Swift 整合指南
將 llama.cpp 整合到 iOS 應用程式的分步指南。專案設定、Metal GPU 加速、模型載入、token 串流和記憶體管理,適用於正式環境部署。
llama.cpp 是在 Apple 硬體上運行 GGUF 語言模型的推論引擎。它使用 Metal 進行 GPU 加速,支援從 A14(iPhone 12)起的所有 iPhone 機型,並根據模型大小和裝置,以每秒 20-50 個 token 的速度生成。
本指南涵蓋從專案設定到正式環境部署的完整整合流程。
整合選項
選項 1:Swift Package(推薦)
llama.cpp 儲存庫包含一個 Swift Package,您可以直接新增到 Xcode 專案中:
- 在 Xcode 中,前往 File,Add Package Dependencies
- 輸入 llama.cpp 儲存庫 URL
- 選擇您想要的版本或分支
- 在 Swift 文件中匯入
llama模組
這是最簡單的整合路徑。該套件會在建構時編譯 llama.cpp,並將 C API 暴露給 Swift。
選項 2:預建置框架
將 llama.cpp 建構為 XCFramework 並作為二進位相依性包含。這可避免在專案中編譯 C++ 原始碼:
# 建構框架
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 框架新增到您的專案:
- 連結
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 運算的 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 在觸發 jetsam(強制終止)之前,會給應用程式約 50-70% 的裝置總 RAM:
| 裝置 | 總 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 $10/mo USD.
Keep reading
iOS 應用程式的 AI:CoreML、雲端 API 與裝置端 LLM 比較
iOS 應用程式的三條 AI 路徑。CoreML 用於 Apple 生態系統,雲端 API 用於能力,裝置端 LLM 透過 llama.cpp 實現成本與隱私。為 Swift 開發者提供的實用比較。
llama.cpp Android 整合:Kotlin 與 Vulkan 指南
將 llama.cpp 整合到 Android 應用程式,使用 Kotlin。涵蓋 JNI 繫結、Vulkan GPU 加速、模型載入、記憶體管理與裝置測試。
LLM 真的能在 iPhone 上運行嗎?效能基準與實際表現
透過 llama.cpp 在 iPhone 上運行 LLM 的真實基準資料。從 iPhone 12 到 iPhone 16 Pro 各機型的 token 生成速度、記憶體使用量和溫度表現。