- 编程语言
- AI Agent
- 编译器
- CLI
- 人工智能
【免费下载链接】baml
The programming language for agents
本指南以仓库中 split-long-literal-1k.md 为解剖对象,讲解 BAML 性能基准工具speedtest如何用一份 Markdown 同时定义 BAML / Python / TypeScript 三语言等价基准,验证string.split在长字符串、多分隔符场景下的行为与性能,并结合 bex_vm 的源码剖析 split 的零拷贝实现原理。读完本文,你将理解该 Workload 的模板变量机制、三端代码的等价关系、可预期的输出结果,以及如何用speedtestCLI 单独筛选、运行并对比这一基准。
一、这个 Workload 在测什么:基准设计的核心意图
string::split long literal 1k属于speedtest基准套件中string分类下的一个用例,其命名可以拆解为三部分:
string::split:被测对象是字符串内建方法split(delim);long literal:输入是一段接近 200 字符的自然语言长字面量,而不是"hello world"这类短串;1k:基准循环 1000 次,强调"长输入 × 高频调用"的组合。
与同目录下的姊妹基准对比,能更清楚它的定位:
| Workload | 输入规模 | 迭代次数 | 侧重 |
|---|---|---|---|
| string-split-100k.md | 短字面量"hello world foo bar baz qux" | 100000 | 短串高频 split 开销 |
| split-short-literal-100k.md | 短字面量 | 100000 | 短字面量场景 |
| split-medium-literal-10k.md | 约 200 字符、8 段、\|分隔 | 10000 | 中等字面量 |
| split-long-literal-1k.md(本文) | 约 200 字符 × 10 段、###分隔 | 1000 | 长字面量、多段分隔 |
该 Workload 的核心假设是:BAML 的split在长字符串上不应产生明显的分配开销——因为底层实现是零拷贝的。下一节先还原它的完整定义,再在第四节用源码验证这个假设。
二、文档结构剖析:一份 Markdown 如何驱动三语言基准
该 Workload 文件遵循speedtest套件的统一格式:一个#标题、一个可选的## eval-setup段、以及## BAML/## Python/## Typescript三个代码段。解析逻辑位于 loader.py:
- 正则
^#\s+(.+)提取标题作为 workload 名称(string::split long literal 1k); - 正则 `^##\s+([\w-]+)\s*\n```\w*\n(.*?)```` 切出各代码段;
- 若存在
eval-setup,通过exec在受限命名空间中执行,产出模板变量; - 各语言代码段经模板替换后得到可执行源码。
eval-setup:用 Python 生成三端共享的输入
eval-setup是一段在基准加载时执行的 Python 代码,负责构造输入数据并导出三种语言各自的字面量转义形式:
import json chunk = ( "the quick brown fox jumps over the lazy dog and runs through the meadow " "while the sun sets behind the distant mountains casting long shadows " "across the rolling hills and the gentle breeze carries the scent of " "wildflowers and freshly cut grass through the warm summer air as birds " "sing in the trees and the world feels at peace with itself in this " "perfect moment of stillness and beauty that seems to stretch on forever " "without end or care for the troubles of yesterday or tomorrow only the " "present moment matters now" ) src = "###".join([chunk] * 10) delim = "###" baml_src = json.dumps(src) baml_delim = json.dumps(delim) py_src = repr(src) py_delim = repr(delim) js_src = json.dumps(src) js_delim = json.dumps(delim)这里的关键设计是一份数据、三份转义:
src:把一段约 200 字符的自然语言chunk用###连接 10 次,得到约 2000 字符、含 9 个分隔符的长串;baml_src/baml_delim:JSON 转义(双引号字符串),供 BAML 代码段使用;py_src/py_delim:Pythonrepr转义,保证换行、引号等字符被安全编码;js_src/js_delim:JSON 转义,供 TypeScript/Node 使用。
模板变量机制:$$前缀的巧思
模板替换由 loader 内部的_DDTemplate完成,它继承自 Python 标准库string.Template并将定界符改为$$:
class _DDTemplate(Template): """Template using $$var instead of $var, so single $ is literal.""" delimiter = '$$'选择$$var而不是$var是有意为之:eval-setup 生成的字符串本身包含大量$(例如 JSON 转义内容),若沿用默认$定界符会产生误替换;改用$$后,只有$$baml_src、$$baml_delim这类显式占位符会被替换,其余$全部按字面保留。替换采用safe_substitute,未定义的$$xxx会原样保留而非抛错。
三、三语言代码对照:语义等价的可执行基准
模板替换完成后,三份代码在语义上完全等价,这是"跨语言对比"成立的前提。
BAML
function main() -> int { let s = $$baml_src; let delim = $$baml_delim; let count = 0; for (let i = 0; i < 1000; i += 1) { let parts = s.split(delim); count += parts.length(); }; return count; }要点:s.split(delim)返回一个数组,parts.length()取段数,累加 1000 次后作为int返回。src由 10 段连接而成,含 9 个###分隔符,因此每次 split 得到10 个部分,最终返回值是1000 × 10 = 10000。
Python
s = $$py_src delim = $$py_delim c = 0 for _ in range(1000): c += len(s.split(delim)) print(c)Python 的str.split(delim)同样把10 段 / 9 分隔符切成 10 个元素,print(c)输出 10000。
TypeScript
const s = $$js_src; const delim = $$js_delim; let c = 0; for(let i=0;i<1000;i++) c += s.split(delim).length; console.log(c);String.prototype.split对非空定界符同样返回 10 个元素,console.log(c)输出 10000。
三端在语义上等价(非空定界符、无尾随空段),因此基准框架可以在计时之前先做跨语言输出校验——见第五节。
四、底层实现验证:split 的零拷贝语义
基准之所以把"长字面量 + 高频调用"作为考察点,与底层实现方式直接相关。BAML 运行时中字符串方法由 package_baml/string.rs 提供,split的实现如下:
// Zero-copy: each segment is a zero-copy substring Slice into the original. fn split(string: &BexStr, delimiter: &BexStr) -> Vec<BexStr> { let s = string.as_str(); let d = delimiter.as_str(); if d.is_empty() { return char_substrings(string); } let base = s.as_ptr() as usize; s.split(d) .map(|part| { let start = part.as_ptr() as usize - base; string.substring(start, start + part.len()) }) .collect() }实现要点:
- 零拷贝切片:用 Rust 标准库
str::split(d)遍历分隔位置,对每个子串计算它在原字符串中的字节偏移start,再调用string.substring(start, start + len)生成切片。每个段都是指向原字符串内存的BexStr切片,不复制字符数据——这正是注释Zero-copy的含义; - 空定界符特判:
d.is_empty()时退化为char_substrings,即按 Unicode 码点逐字符切分,这与 JavaScript"abc".split("")返回单个字符数组的行为一致; - 共享 substr 基建:
lines(按行切分)等相邻方法也复用相同的指针偏移 +substring模式,说明该零拷贝思路在字符串族方法中是统一约定。
由此可以推断:本 Workload 中 1000 次对约 2000 字符长串的 split 主要开销来自分割遍历与结果数组构建,而非字符数据拷贝,这也是它与短串高频场景(如string-split-100k)在测量维度上的本质区别。
五、如何运行与复用这个 Workload
speedtest是仓库baml_language/tools/speedtest下的 Python 工具包(入口配置见 pyproject.toml,脚本入口为speedtest = "speedtest.cli:main"),提供run/compare/open/list/baselines五个子命令,参数定义见 cli.py。
单独跑长字面量 split 基准
# 构建 release 版 baml-cli 后,仅运行名称匹配 "split-long-literal" 的 workload speedtest run --build --filter split-long-literal常用参数:
| 参数 | 作用 |
|---|---|
--filter STR | 只跑名称包含该子串的 workload,可重复指定多个 |
--only-baml | 跳过 Python / Node / Bun,只测 BAML |
--runs N | 固定跑 N 次(默认走自适应计时) |
--measurement-time SECS | 自适应模式下每个 workload 的目标总时长,默认 5 秒 |
--tag NAME | 为本次运行打标签,便于后续对比 |
--profile | 用samply record采集 CPU profile |
--results-dir DIR | 结果保存目录(默认~/.speedtest/) |
run的执行流程(见 runner.py)分五步:
- 打包:调用
baml-cli pack main --file <w.baml> -o <w.packed>把该 Workload 的 BAML 源码编译为独立可执行文件; - 取基准输出:运行打包产物,取最后一行 stdout 作为期望结果(本 Workload 应为
10000); - 跨语言校验:依次运行
python3 -S、node、bun(可用时),若输出与期望不一致则标记mismatch——这正是三端语义等价性被自动验证的环节; - 计时:先做 3 次预热(丢弃、暖 OS 缓存),再按目标时长自适应采样(样本数限制在
[5, 100]),统计中位数、标准差、最小/最大值; - 落盘:结果保存到
results/与baselines/<branch>/latest,支持后续对比。
对比与查看
speedtest compare base new # 对比两个基线 speedtest compare base new --threshold 5 # 忽略变化幅度小于 5% 的项 speedtest list # 列出全部 workload 名称 speedtest open # 浏览器打开结果 UI复用为 Rust CodSpeed 基准
仓库还提供了 export_baml.py 作为桥接脚本:它复用loader.load_workloads,把每个 Workload 的展开后BAML 源码导出为 JSON 数组({"name", "category", "baml"}),供crates/baml_tests作为 CodSpeed 基准直接复用,避免在 Rust 侧重复实现.md解析、eval-setup 执行与$$模板替换逻辑。
六、围绕该 Workload 的扩展阅读路径
若想深入该基准所依赖的运行时能力,可按以下路径继续探索:
- split 运行时实现:bex_vm/src/package_baml/string.rs 中
split及lines、chars、slice等字符串族方法的零拷贝实现; - Workload 解析与模板:speedtest/loader.py 中
parse_workload_md、_DDTemplate与load_workloads; - 计时与打包:speedtest/runner.py 中自适应计时
time_command_adaptive与打包pack_baml; - 同类基准对照:
string分类下其余 7 个 Workload(workloads/string/),可用于横向对比不同输入规模下 split、contains、concat、substring、trim 的性能特征。
七、小结:本 Workload 验证了什么
string::split long literal 1k用一份 Markdown 同时刻画了三件事:
- 测试方法:通过
eval-setup+$$模板变量,在加载期为 BAML / Python / TypeScript 生成字节级一致的长输入,保证三端基准的公平性; - 语义契约:三端代码对同一输入必须产出同一结果(10000),并由 runner 在计时前自动校验,防止"测了不同的事";
- 实现特征:长字面量高频 split 是零拷贝切片路径的代表性负载,其性能特征与短串场景存在本质差异。
对于想要为 BAML 新增或修改字符串内建方法的开发者,这份 Workload 既是一份可复用的性能回归用例,也是一份可运行的语义验证样例。
- 编程语言
- AI Agent
- 编译器
- CLI
- 人工智能
【免费下载链接】baml
The programming language for agents
相关推荐
Roc 字符串前缀移除实战:从 Str.drop_prefix 快照测试看 REPL、底层实现与零拷贝切片
Roc 字符串前缀移除实战:从 Str.drop_prefix 快照测试看 REPL、底层实现与零拷贝切片 Str.drop_prefix 是 Roc 标准库中
Rakam-API高级功能:Webhook集成与实时事件处理
Rakam API高级功能:Webhook集成与实时事件处理 📈 在当今数据驱动的世界中, 实时事件处理 和 Webhook集成 已成为现代应用分析的核心需求
编程语言AI Agent编译器CLI人工智能从B树到字符串比较:db_tutorial中SQL字符串操作的底层实现
从B树到字符串比较:db_tutorial中SQL字符串操作的底层实现 在数据库应用中,字符串比较是最常用的操作之一,无论是用户登录时验证用户名,还是按邮箱地址
示例工程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考