ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

llama.cpp 之 llama.swiftui:在 iPhone 上本地运行大模型的 SwiftUI 示例工程全解

llama.cpp 之 llama.swiftui:在 iPhone 上本地运行大模型的 SwiftUI 示例工程全解 llama.cpp 之 llama.swiftui在 iPhone 上本地运行大模型的 SwiftUI 示例工程全解【免费下载链接】llama.cppLLM inference in C/C项目地址: https://gitcode.com/GitHub_Trending/ll/llama.cpp本篇围绕 llama.swiftui 示例 展开讲解如何在 Apple 平台上构建 llama.cpp 的 XCFramework、将其集成进 Xcode 工程并在 iPhone 真机/模拟器上跑通本地 LLM 推理并结合示例源码深入剖析 Swift 侧LlamaContext封装的模型加载、采样链构建、批解码循环与内置基准测试的实现细节。读完你可以独立完成「编译框架 → 集成工程 → 运行推理 → 查看性能」的全流程并理解这条链路与 llama.cpp C API 的对应关系。一、示例工程定位与目录结构llama.swiftui 是 llama.cpp 仓库中的一个 iPhone 本地推理示例 App官方 README 将其定位为「可以用作更高级项目起点的起始工程」a starting point for more advanced projects。它演示了三件事从 llama.cpp 源码构建出可在 iOS/macOS 等平台上使用的XCFramework将 XCFramework 作为二进制依赖集成进 SwiftUI 工程用纯 Swift 代码封装 llama.cpp 的 C API完成模型下载、加载、文本补全与性能基准。整个示例位于 examples/llama.swiftui 目录结构如下examples/llama.swiftui/ ├── llama.swiftui.xcodeproj/ # Xcode 工程文件 ├── llama.cpp.swift/ │ └── LibLlama.swift # 核心Swift 对 llama.cpp C API 的封装LlamaContext actor └── llama.swiftui/ ├── llama_swiftuiApp.swift # App 入口 ├── Models/ │ └── LlamaState.swift # 状态管理模型列表、下载、补全调度、bench 调用 └── UI/ ├── ContentView.swift # 主界面消息日志、输入框、Send/Bench/Clear/Copy 按钮 ├── DownloadButton.swift # 单模型下载/加载按钮下载进度、状态机 ├── InputButton.swift # 自定义模型 URL 输入 └── LoadCustomButton.swift # 加载已下载的自定义模型配套的构建脚本是仓库根目录的 build-xcframework.shXCFramework 的 Swift 包集成方式另见 docs/xcframework.mdApp Store 校验脚本在 scripts/apple/ 目录下。二、构建 XCFrameworkbuild-xcframework.sh 详解2.1 基本用法在 macOS 上需要 Xcode、Xcode Command Line Tools 与 CMake 3.28.0 或更高版本脚本启动时会检查cmake与xcrun是否存在$ ./build-xcframework.sh不加参数时默认构建全部平台变体也可以按需只构建指定目标$ ./build-xcframework.sh ios-sim ios-device从 build-xcframework.sh 的文件头注释与build_spec函数可以看出脚本支持 8 种构建目标默认系统版本如下构建目标说明最低系统版本架构ios-simiOS 模拟器iOS 16.4arm64 x86_64ios-deviceiOS 真机iOS 16.4arm64macosmacOSmacOS 13.3arm64 x86_64visionosvisionOS 真机visionOS 1.0arm64visionos-simvisionOS 模拟器visionOS 1.0arm64 x86_64tvos-simtvOS 模拟器tvOS 16.4arm64 x86_64tvos-devicetvOS 真机tvOS 16.4arm64脚本开头还允许通过环境变量覆盖部分默认值例如 Metal 库是否嵌入二进制GGML_METAL_EMBED_LIBRARY默认 ON并发构建数MAX_PARALLEL_BUILDS默认 1即串行构建各平台构建日志写入各目标.log。2.2 关键 CMake 配置脚本为所有平台统一传入一组 CMake 参数见 build-xcframework.sh 的COMMON_CMAKE_ARGS几个值得注意的开关-DGGML_METALON启用 Metal 后端这是 iPhone 上跑 GPU 推理的前提Apple GPU 通过 Metal Performance Shaders 执行矩阵运算-DGGML_METAL_EMBED_LIBRARYON将编译出的 Metal shader 库嵌入二进制免去运行时外挂.metallib文件-DGGML_BLAS_DEFAULTON默认链接 Accelerate/BLAS 提供 CPU 侧加速-DGGML_NATIVEOFF关闭针对宿主机的 SIMD 原生优化保证在目标设备上的可移植性-DLLAMA_BUILD_APP/COMMON/EXAMPLES/TOOLS/TESTS/SERVEROFF、-DLLAMA_BUILD_MTMDON只产出推理库本体与多模态MTMD库关闭与移动端无关的可执行目标-DLLAMA_OPENSSLOFF、-DMTMD_VIDEOOFFiOS/visionOS/tvOS 构建裁掉移动端不需要的依赖减小体积一组CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_*参数禁用代码签名使框架产物可被任意项目复用。2.3 从静态库到 XCFramework 的组装流程脚本的构建流程分三步源码中可以直接看到实现按平台多架构编译。每个目标如build_ios_device用cmake -G Xcode配置后以 Release 配置构建产出各平台、各架构的静态库libllama.a、libggml*.a、libggml-metal.a、libggml-blas.a、libmtmd.a、libvendor-hash.a。合并静态库并链接成动态库combine_static_libraries见 build-xcframework.sh先用xcrun libtool -static把 8 个静态库合并为一个combined.a再用clang -dynamiclib配合-Wl,-force_load链接出单一llama动态库并链接Foundation、Metal、Accelerate三个系统框架install_name设为rpath/llama.framework/llama。真机构建额外用xcrun vtool -set-build-version标记平台版本visionOS 会根据 Xcode 是否大于 16.2 自动选择visionos或xros标记这一步是 App Store 校验通过的必要条件。组装 framework 结构并打包 XCFrameworksetup_framework_structure按平台创建llama.framework目录macOS 使用带Versions/A的版本化结构iOS/visionOS/tvOS 使用扁平结构拷入 include/llama.h、ggml/include 下的公共头文件及 MTMD 头文件并生成module.modulemapframework module llama { umbrella Headers link c link framework Accelerate link framework Metal link framework Foundation export * }最后xcrun xcodebuild -create-xcframework把各平台的 framework 与对应 dSYM 打成统一产物build-apple/llama.xcframework。也就是说最终产物是一个同时覆盖 iOS 模拟器/真机、macOS、visionOS、tvOS 的单文件框架包Swift 工程只需import llama即可访问全部 C API。三、将 XCFramework 集成进 Xcode 工程README 给出的集成方式有两种任选其一将build-apple/llama.xcframework直接拖入 Xcode 工程导航器在 Target 设置的 Frameworks, Libraries, and Embedded Content 一节中手动添加该框架注意嵌入方式应选择 Embed Sign / Embed Without Signing因为它是动态库。本仓库示例工程 llama.swiftui.xcodeproj 已按此配置好打开后即可在模拟器或真机上构建运行。如果目标工程使用 Swift Package Manager则按 docs/xcframework.md 的方式把发布版 XCFramework 声明为binaryTarget// swift-tools-version: 5.10 import PackageDescription let package Package( name: MyLlamaPackage, targets: [ .executableTarget( name: MyLlamaPackage, dependencies: [ LlamaFramework ]), .binaryTarget( name: LlamaFramework, url: 发布版 llama-xcframework.zip 的下载地址, checksum: 对应 zip 的校验和 ) ] )使用时按发布版本修改 URL 与 checksum 即可。注意 XCFramework 本身只是「免编译的二进制库」——它不会在宿主机上执行仍需在 Apple 设备上运行推理。四、App 的功能与状态管理4.1 主界面入口 llama_swiftuiApp.swift 是标准 SwiftUI App 结构唯一视图是 ContentView.swift顶部ScrollView展示llamaState.messageLog模型输出与统计信息点击可收起键盘中部 80pt 高的TextEditor输入提示词底部一排按钮Send发起补全、Bench跑基准测试、Clear清空上下文与日志、Copy复制日志到剪贴板View Models导航进入DrawerView分三个 Section从 Hugging Face 下载模型InputButton可粘贴任意 GGUF 直链、已下载模型列表可左滑删除文件、默认模型列表。4.2 模型列表与下载状态集中在 LlamaState.swift它是MainActor的ObservableObject关键逻辑init时先loadModelsFromDisk()扫描 Documents 目录中已有的 GGUF 文件再生成默认模型列表。示例内置的默认模型按名称/量化包括TinyLlama-1.1BQ4_0 约 0.6 GiB、TinyLlama-1.1B ChatQ8_0 约 1.1 GiB、TinyLlama-1.1BF16 约 2.2 GiB、Phi-2Q4_0 约 1.6 GiB / Q8_0 约 2.8 GiB、Mistral-7B-v0.1Q4_0 约 3.8 GiB、OpenHermes-2.5-Mistral-7BQ3_K_M 约 3.5 GiB均为 Hugging Face 上的 GGUF 直链。DownloadButtonDownloadButton.swift实现三态状态机download→downloadingURLSessionDownloadTask下载用 KVO 观察fractionCompleted显示百分比可点击取消→downloaded点击改为「Load」调用llamaState.loadModel(modelUrl:)就地加载。loadModel拿到本地文件 URL 后调用LlamaContext.create_context(path:)加载成功则记录日志并更新状态若工程包内自带models/ggml-model.gguf资源Bundle.main子目录models启动时会直接加载作为默认模型。4.3 补全与统计的调度LlamaState.complete(text:)的流程记录起点 →completion_initprompt 处理计时得到 heat-up 时间→ 在Task.detached中循环completion_loop直到is_done每个新 token 通过MainActor.run追加到日志 → 结束后计算生成吞吐n_len / 生成耗时打印 Heat up took Xs / Generated Y t/s并调用clear()复位 KV 缓存。五、核心封装LlamaContext 的源码级剖析LibLlama.swift 是整个示例的技术核心一个约 340 行的文件完整演示了如何在 Swift 中直接驱动 llama.cpp 的 C APIimport llama后即可访问llama.h中全部符号。5.1 batch 辅助函数C 层的llama_batch是以裸数组表示的变长结构示例用两个自由函数封装其「添加 token」语义func llama_batch_clear(_ batch: inout llama_batch) { batch.n_tokens 0 } func llama_batch_add(_ batch: inout llama_batch, _ id: llama_token, _ pos: llama_pos, _ seq_ids: [llama_seq_id], _ logits: Bool) { batch.token[Int(batch.n_tokens)] id batch.pos[Int(batch.n_tokens)] pos batch.n_seq_id[Int(batch.n_tokens)] Int32(seq_ids.count) // ... 写入 seq_id 与 logits 标记 batch.n_tokens 1 }logits参数控制该位置是否参与采样——prompt 阶段只对最后一个 token 置 1生成阶段对每个新 token 置 1。5.2 上下文创建参数选择与动机LlamaContext.create_context(path:)是加载入口见 LibLlama.swift其中几个参数值得注意线程数max(1, min(8, processorCount - 2))即最多 8 线程并预留 2 个核心给系统/主线程——移动设备上 CPU 推理的典型取法上下文长度ctx_params.n_ctx 2048直接决定 KV 缓存大小是内存占用的主要开关模拟器强制 CPU#if targetEnvironment(simulator) model_params.n_gpu_layers 0 print(Running on simulator, force use n_gpu_layers 0) #endif因为模拟器上没有真机 GPUMetal 后端不可用必须把所有层放到 CPU 上跑真机上则使用默认值Metal 后端承担矩阵运算。初始化顺序为llama_backend_init()→llama_model_load_from_file()→llama_init_from_model()任一步失败返回nil即抛出LlamaError.couldNotInitializeContext。资源释放集中在deinit依次llama_sampler_free、llama_batch_free、llama_model_free、llama_free、llama_backend_free与 C API 的生命周期要求一一对应。5.3 采样链温度 分布LlamaContext.init中构建了一个两级采样链let sparams llama_sampler_chain_default_params() self.sampling llama_sampler_chain_init(sparams) llama_sampler_chain_add(self.sampling, llama_sampler_init_temp(0.4)) llama_sampler_chain_add(self.sampling, llama_sampler_init_dist(1234))即先把 logits 除以温度 0.4低温使分布更集中再按固定种子 1234 的分布采样器取 token。n_len最大生成长度默认 1024到达该长度或采样到 EOGend-of-generationtoken 时结束。5.4 两段式补全循环Prompt 阶段completion_init(text:)先做 KV 容量检查n_kv_req tokens (n_len - tokens)不能超过n_ctx否则打印错误然后llama_tokenizeadd_bos: true把输入切成 token整个 prompt 放入一个 batch、只对末尾 token 置 logits一次性llama_decode完成 prompt 处理。生成阶段completion_loop()每次调用产生一个 tokenllama_sampler_sample(sampling, context, batch.n_tokens - 1)从最后有效位置采样出新 token若为 EOG 或达到n_len置is_done true并返回将 token 转回字符串——这里有一段值得注意的健壮性处理llama_token_to_piece返回的字节序列在 token 边界处可能不是合法 UTF-8因此用temporary_invalid_cchars暂存只有当缓冲区可整体解析为 UTF-8或至少存在可解析的合法后缀时才产出字符串否则本轮返回空串。这避免了多字节汉字等被截断时出现乱码崩溃llama_batch_add(batch, new_token_id, n_cur, [0], true)后llama_decoden_cur/n_decode递增进入下一轮。5.5 内置基准测试benchLlamaContext.bench(pp:tg:pl:nr:)在 App 内实现了一个微型版llama-benchprompt 部分构造pp个占位 token 的 batch 一次解码用llama_synchronize保证 GPU 计时准确生成部分循环tg次、每次以pl条并行序列解码模拟多路生成每轮前后llama_memory_clear清 KV 缓存nr轮取平均并计算标准差最后用llama_model_desc、llama_model_size、llama_model_n_params输出与命令行工具风格一致的 Markdown 表格model / size / params / backend / test / t/s。LlamaState.bench()的调用策略是先以pp: 8, tg: 4做一轮热启动若热启动耗时超过 5 秒则判定设备太慢并中止正式测试才用pp: 512, tg: 128, pl: 1, nr: 3。这个 5 秒阈值是示例作者对「设备能否给出可信数字」的启发式判断读者可按需调整。六、从源码结构看整体调用链把上面各部分串起来这个示例的运行时调用链是DownloadButton / LlamaState.loadModel └─ LlamaContext.create_context ├─ llama_backend_init 注册 ggml CPU/Metal 后端 ├─ llama_model_load_from_file 解析 GGUF、加载权重 └─ llama_init_from_model 建上下文与 KV 缓存n_ctx2048 LlamaState.complete(text:) ├─ completion_initllama_tokenize → llama_batch_add ×N → llama_decodeprompt ├─ completion_loop循环 │ llama_sampler_sampletemp 0.4 → dist 1234 │ → llama_token_to_piece含 UTF-8 修复 │ → llama_batch_add → llama_decode单 token └─ clearllama_memory_clear 复位 KV 缓存 Bench 按钮bench() 走 llama_decode llama_synchronize 计时从源码结构看示例刻意保持了对 C API 的「薄封装」Swift 侧没有引入任何中间抽象层OpaquePointer直接持有llama_model/llama_context句柄actor隔离多线程访问——这意味着如果要扩展该示例例如改用 chat 模板 对话、接入 MTMD 多模态、或改用llama_context_params调大n_ctx都可以直接在LlamaContext内按 llama.cpp 的 C API 文档操作这也是 README 称其为「起点工程」的原因。七、验证构建产物scripts/apple 校验脚本除了本地运行仓库还在 scripts/apple/ 下提供了五个平台验证脚本validate-ios.sh、validate-macos.sh、validate-tvos.sh、validate-visionos.sh、validate-apps.sh。以 validate-ios.sh 为例它的流程是自动生成一个最小 SwiftUI 测试工程import llama并实例化llama_context_default_params()验证符号可用→ 把build-apple/llama.xcframework拷入工程 →xcodebuild archive打包 → 生成 IPA →xcrun altool --validate-app做 App Store 校验未提供 Apple ID 时退化为本地校验IPA 存在性、可执行二进制、llama.framework嵌入情况、lipo -info架构检查。这解释了前面构建脚本为何要花篇幅处理vtool平台标记、dSYM 与 strip——它们正是通过这类校验的必备环节。八、适用前提与限制构建必须在 macOS 上进行依赖 Xcode 工具链xcrun/libtool/dsymutil/vtool与 CMake ≥ 3.28.0各平台最低系统版本由构建脚本固定iOS 16.4、macOS 13.3、visionOS 1.0、tvOS 16.4模拟器上 Metal 后端不可用示例代码会强制n_gpu_layers 0因此模拟器仅适合功能验证性能数字请以真机 Bench 为准示例的默认采样链温度 0.4、种子 1234、n_ctx 2048与线程数策略都是演示配置生产项目应按模型与设备调整内置默认模型列表为较早的公开 GGUF 模型实际使用建议以 DownloadButton 的下载机制替换为自己的模型地址。可继续深入的仓库路径examples/llama.swiftui/README.md、build-xcframework.sh、docs/xcframework.md、include/llama.hC API 总入口、scripts/apple/validate-ios.sh、ggml/src/ggml-metalMetal 后端实现。【免费下载链接】llama.cppLLM inference in C/C项目地址: https://gitcode.com/GitHub_Trending/ll/llama.cpp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表