ik_llama.cpp 的 Swift 批处理生成示例 llama-batched-swift 完整指南

发布时间:2026/9/18 10:23:20
ik_llama.cpp 的 Swift 批处理生成示例 llama-batched-swift 完整指南 ik_llama.cpp 的 Swift 批处理生成示例 llama-batched-swift 完整指南【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址: https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp本指南围绕 ik_llama.cpp 仓库中的 Swift 批处理示例 examples/batched.swift/README.md 展开系统讲解如何使用 Swift 语言调用 llama 库实现「多序列并行生成」。读者将掌握该示例的构建方式、命令行用法、完整的源码执行流程从模型加载、prompt 分词、KV 缓存规划到多流采样生成以及它与 C 版 examples/batched/batched.cpp 的对应关系可直接据此在 macOS 上运行或二次开发自己的 Swift 推理程序。示例定位Swift 克隆的 batched 示例examples/batched.swift是 ik_llama.cpp 官方 C 示例examples/batched的 Swift 移植版。C 版batched用于演示从给定 prompt 进行批处理生成batched generation同一个 prompt 在同一个上下文中并行生成多条独立序列。Swift 版完整复刻了这一思路用 Swift 的 Foundation 与 llama 库的 C API 实现了同样的多流生成逻辑。两者在功能上完全对应C 版通过gpt_params命令行解析支持-m、-p、-n、-np等参数Swift 版采用更精简的CommandLine.arguments位置参数用法为MODEL_PATH [PROMPT] [PARALLEL]。从源码结构看Swift 版刻意保持了与 C 版一致的算法骨架先评估 prompt再把 KV 缓存复制到各并行序列最后逐 token 采样并组批解码。这使得它非常适合作为学习 llama 批处理 API 的 Swift 入门参考。目录结构与构建方式该示例共 4 个文件结构如下examples/batched.swift/ ├── README.md # 使用说明 ├── Makefile # xcodebuild 构建脚本 ├── Package.swift # Swift Package 定义 └── Sources/ └── main.swift # 全部实现源码Makefile一键构建examples/batched.swift/Makefile 的内容如下.PHONY: build build: xcodebuild -scheme llama-batched-swift -destination generic/platformmacOS -derivedDataPath build rm -f ./llama-batched-swift ln -s ./build/Build/Products/Debug/llama-batched-swift ./llama-batched-swift它使用xcodebuild以llama-batched-swiftscheme 构建 macOS 平台产物并把 Debug 目录下的可执行文件软链接到项目根目录。因此构建命令就是make构建产物即为根目录下的./llama-batched-swift。Package.swift依赖仓库根目录的 llama 包examples/batched.swift/Package.swift 声明了包元信息swift-tools-version: 5.5平台要求macOS(.v12)及以上依赖声明.package(name: llama, path: ../../)即把仓库根目录本身作为 Swift Package 依赖executableTarget名为llama-batched-swift源码路径为Sources链接 Foundation 与 AppKit 框架。值得注意的是仓库根目录的 Package.swift 将 llama 库组织为llama这个 library target其 sources 包含src/llama.cpp、src/llama-vocab.cpp、src/llama-sampling.cpp、ggml/src/ggml.c、ggml/src/ggml-backend.c等核心文件并在 Darwin 平台追加ggml/src/ggml-metal.m与 Metal 资源、启用GGML_USE_ACCELERATE与GGML_USE_METAL宏——这意味着 Swift 示例在 macOS 上可以自动获得 Metal GPU 加速与 Accelerate 矩阵运算支持。头文件通过根目录的spm-headers如 spm-headers/llama.h暴露给 Swift 模块。命令行用法与参数说明按 examples/batched.swift/README.md程序调用方式为./llama-batched-swift MODEL_PATH [PROMPT] [PARALLEL]三个位置参数的含义结合 Sources/main.swift 第 4-17 行的解析逻辑可以确认参数位置默认值说明MODEL_PATH第 1 个必填GGUF 模型文件路径缺失时打印用法并退出exit(1)PROMPT第 2 个Hello my name is生成使用的提示词PARALLEL第 3 个1并行序列数量n_parallel仅在可转为整数时生效否则取 1一个典型调用对应 C 版 README 中-np 4的效果./llama-batched-swift ./models/llama-7b-v2/ggml-model-f16.gguf Hello my name is 4与 C 版 examples/batched/README.md 中的./llama-batched -m ... -p Hello my name is -np 4相比Swift 版将参数从命令行选项简化为位置参数同时把n_len序列总长度含 prompt硬编码为 32。源码全流程解析Sources/main.swift 共约 260 行是整个示例的核心。下面按执行顺序逐段拆解。1. 参数解析与常量let modelPath: String arguments[1] let prompt: String arguments.count 2 ? arguments[2] : Hello my name is let n_parallel: Int arguments.count 3 Int(arguments[3]) ! nil ? Int(arguments[3])! : 1 let n_len: Int 32n_len为序列总长度含 prompt token硬编码为 32与 C 版params.n_predict 32对应。2. 后端初始化与模型加载llama_backend_init() defer { llama_backend_free() } let model_params llama_model_default_params() guard let model llama_load_model_from_file(modelPath.cString(using: .utf8), model_params) else { print(Failed to load model) exit(1) } defer { llama_free_model(model) }llama_backend_init()初始化 llama 后端配合defer在程序退出时llama_backend_free()释放体现 Swift 的资源管理风格llama_load_model_from_file使用默认模型参数加载 GGUF 模型失败即退出。3. Prompt 分词与 KV 缓存容量规划var tokens tokenize(text: prompt, add_bos: true) let n_kv_req UInt32(tokens.count) UInt32((n_len - Int(tokens.count)) * n_parallel)tokenize第 217-228 行封装了llama_tokenizeC API按 UTF-8 字节数分配 token 缓冲add_bos加首 tokenspecial tokens传false把 C 指针结果拷贝进 Swift 数组后释放内存。关键公式n_kv_req tokens (n_len - tokens) * n_parallel与 C 版 examples/batched/batched.cpp 第 57 行的n_kv_req tokens_list.size() (n_predict - tokens_list.size())*n_parallel完全一致。它估算出所有并行序列合计需要的 KV 缓存 token 数prompt 共享一次之后每个序列各占(n_len - tokens)个新位置。4. 上下文创建与容量校验var context_params llama_context_default_params() context_params.seed 1234 context_params.n_ctx n_kv_req context_params.n_batch UInt32(max(n_len, n_parallel)) context_params.n_threads 8 context_params.n_threads_batch 8 let context llama_new_context_with_model(model, context_params) let n_ctx llama_n_ctx(context) if n_kv_req n_ctx { print(error: n_kv_req (%d) n_ctx, the required KV cache size is not big enough\n, n_kv_req) exit(1) }seed 1234固定随机种子保证可复现n_ctx直接设为所需的n_kv_reqn_batch max(n_len, n_parallel)保证单次llama_decode能容纳整个 prompt 或一整轮的多序列 token线程数硬编码为 8C 版则走gpt_params默认值若估算的 KV 需求超过实际上下文容量则报错退出与 C 版的防御逻辑一致。5. 构造 batch 并评估 promptvar batch llama_batch_init(max(Int32(tokens.count), Int32(n_parallel)), 0, 1) batch.n_tokens Int32(tokens.count) for (i, token) in tokens.enumerated() { batch.token[i] token batch.pos[i] Int32(i) batch.n_seq_id[i] 1 if let seq_id batch.seq_id[i] { seq_id[0] 0 } batch.logits[i] 0 } batch.logits[Int(batch.n_tokens) - 1] 1 if llama_decode(context, batch) ! 0 { print(llama_decode() failed) exit(1) }这是理解批处理 API 的关键llama_batch_init(max(tokens, n_parallel), 0, 1)预分配 batch 槽位第三个参数1是每个 token 的序列 ID 数量上限每个 prompt token 填入token、pos位置、seq_id[0] 0归属序列 0只有最后一个 prompt token 的logits置 1llama_decode只为需要 logits 的 token 计算输出——这正是 C 版第 124-125 行注释llama_decode will output logits only for the last token of the prompt的机制源码第 84-88 行以 TODO 注释的形式演示了 Swift 中访问batch.seq_id[i]可选指针的写法C 里是直接的batch.seq_id[i][0]。6. 复制 KV 缓存到各并行序列for i in 1 .. n_parallel { llama_kv_cache_seq_cp(context, 0, Int32(i), 0, batch.n_tokens) }llama_kv_cache_seq_cp将序列 0prompt 计算得到的 KV 状态复制到序列i区间为[0, batch.n_tokens)。这样所有并行序列共享 prompt 的 KV 结果而无需重复计算。C 版第 132-136 行保留了同样的 API 调用注释掉的版本用-1, -1表示全区间两者互为印证。7. 生成主循环采样与组批var streams: [String] .init(repeating: , count: n_parallel) var i_batch Int32 var n_cur batch.n_tokens var n_decode 0 let t_main_start ggml_time_us() while n_cur n_len { batch.n_tokens 0 for i in 0 .. n_parallel { if i_batch[i] 0 { continue } // 该流已结束 let logits llama_get_logits_ith(context, i_batch[i]) var candidates: [llama_token_data] ... // top_k40, top_p0.9, temp0.4 llama_sample_top_k(context, candidates_p, top_k, 1) llama_sample_top_p(context, candidates_p, top_p, 1) llama_sample_temp(context, candidates_p, temp) let new_token_id llama_sample_token(context, candidates_p) if llama_token_is_eog(model, new_token_id) || n_cur n_len { i_batch[i] -1 continue } // 追加 token 到 batch准备下一轮解码 batch.token[Int(batch.n_tokens)] new_token_id batch.pos[Int(batch.n_tokens)] n_cur batch.n_seq_id[Int(batch.n_tokens)] 1 seq_id[0] Int32(i) batch.logits[Int(batch.n_tokens)] 1 i_batch[i] batch.n_tokens batch.n_tokens 1 n_decode 1 } if batch.n_tokens 0 { break } n_cur 1 if llama_decode(context, batch) ! 0 { ... } }这段循环是整个批处理的核心机制值得重点理解每轮先清空 batch再为每个活跃流采样一个 token因此一次llama_decode同时推进多条序列——这就是并行解码的效率来源采样链路为top_k(40) → top_p(0.9) → temp(0.4) → llama_sample_token与 C 版 batched.cpp 第 179-187 行的参数与顺序逐一对应注释中还保留了llama_sample_token_greedy的贪心采样替代方案i_batch[i]记录每个流最新 token 在 batch 中的索引下一轮据此取llama_get_logits_ith拿到该流的 logits遇到 EOGend of generationtoken 或达到n_len时将该流标记为-1结束单流模式n_parallel 1下逐 token 立即打印到 stdout多流模式则累积到streams[i]最后统一输出完整序列第 156 行llama_token_is_eog用于识别模型定义的结束 token如 EOS保证生成能自然终止。8. 多字节 UTF-8 的 token 解码缓冲token_to_piece第 230-261 行封装了llama_token_to_piece处理了 Swift 字符串拼接多字节字符的经典坑先用 8 字节小缓冲探测若返回负数说明实际字节数更多则按-nTokens重新分配精确缓冲当解码出的字节可能构成不完整的多字节 UTF-8 序列例如 CJK 字符被拆到相邻 token时先把字节暂存到buffer凑满 4 字节UTF-8 单字符最大长度或能组成合法字符串后再转为String从而避免乱码。这正是多流累积打印场景下保证中文等字符输出正确的关键实现也是相比 C 版common_token_to_piece需要多处理的 Swift 特有部分。9. 计时与统计let t_main_end ggml_time_us() print(decoded \(n_decode) tokens in ... s, speed: ... t/s) llama_print_timings(context)ggml_time_us()统计解码耗时并计算 tokens/sllama_print_timings(context)输出 load time、sample time、prompt eval time、eval time、total time 等详细分解与 C 版输出格式一致。运行效果示例参照 C 版 examples/batched/README.md 的运行输出Swift 版以-np 4等价参数第 3 参数传 4运行时会打印n_len 32, n_ctx ..., n_batch ..., n_parallel 4, n_kv_req ... generating 4 sequences ... sequence 0: Hello my name is ... sequence 1: Hello my name is ... sequence 2: Hello my name is ... sequence 3: Hello my name is ... decoded N tokens in X.XX s, speed: XX.XX t/s即同一个 prompt 派生出 4 条内容各异的续写因seed1234固定可重复复现并给出整体解码速度。C 版 README 中的示例输出展示了 n_parallel4 时全部 4 个流在约 3.57 秒内解码 108 token、约 30.26 t/s 的典型结果Swift 版输出结构与其一一对应。与 C 版的关键差异速览维度C 版 batchedSwift 版 llama-batched-swift参数形式-m/-p/-n/-np选项位置参数MODEL_PATH [PROMPT] [PARALLEL]模型/上下文参数走gpt_params默认值与命令行覆盖llama_model_default_params() 硬编码 seed/n_threadsbatch 填充common_batch_add辅助函数手动赋值token/pos/n_seq_id/seq_id/logits字段token 解码common_token_to_piece自实现token_to_piece含 UTF-8 缓冲构建CMake 目标make内部 xcodebuild SwiftPM目标平台全平台macOS 12底层可走 Metal扩展建议与注意事项更换模型把MODEL_PATH换成任意 GGUF 文件即可例如仓库 models 目录下的模板模型或自行量化的模型调整并行度PARALLEL参数越大单轮解码的 batch 越满理论上越能发挥 GPU/多核并行但需注意n_kv_req随n_parallel线性增长超出n_ctx会触发源码第 60-63 行的容量校验错误修改生成长度n_len 32硬编码于 main.swift 第 17 行二次开发时可改为从参数读取采样策略若想要确定性输出可参考注释改用llama_sample_token_greedy若要更高多样性可调大temp学习价值该示例是理解llama_batch结构与llama_decode批处理语义的最小完整 Swift 实现配合 C 版 batched.cpp 对照阅读可快速掌握 llama 库的底层解码流程。总结examples/batched.swift以不到 300 行的 Swift 代码完整复刻了 ik_llama.cpp 的批处理生成示例覆盖了后端初始化、模型加载、prompt 分词、KV 容量规划、batch 构造、KV 序列复制、多流采样组批、UTF-8 缓冲解码与计时统计的全链路。它既是 Swift 开发者上手 llama 库的最佳范例也是理解并行解码与 KV 缓存复用的直观教材。阅读本文后你可以直接make构建并运行./llama-batched-swift MODEL_PATH [PROMPT] [PARALLEL]并以此为骨架扩展出自己的 Swift 推理应用。【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址: https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询