Wren Core 基准测试指南:用 TPC-H 与复杂 SQL 量化 text-to-SQL 引擎的查询性能
【免费下载链接】WrenAIGenBI (Generative BI) for AI agents, an open-source, governed text-to-SQL through an open context layer that turns natural-language questions into trusted dashboards, charts, and SQL across 20+ data sources, such as BigQuery, Snowflake, PostgreSQL, ClickHouse, Amazon Redshift, Databricks and more.项目地址: https://gitcode.com/GitHub_Trending/wr/WrenAI
本篇指南围绕 WrenAI 开源仓库中wren-core的 benchmark crate(core/wren-core/benchmarks)展开,完整讲解其基准测试体系:基于行业标准 TPC-H 的 22 条决策支持查询、专为 Wren AI 复杂场景设计的 Wren 查询集,以及从一键脚本到分支对比、再到手动 Cargo 执行的全套运行方式。读完本文,你将掌握如何复现基准数据、如何在不同分支间做可重复的性能对比,以及基准结果背后从 SQL 解析、MDL 分析到 SQL 重写的核心调用链。
基准测试 crate 是什么
wren-core/benchmarks是 Wren core 库自带的基准测试 crate,其定位在官方文档中表述得非常明确:基于行业标准的开源基准(industry-standard open source benchmarks),用于帮助测量与持续改进 Wren core 的查询性能。它既不是连接外部数据库跑线上 SQL 的压测工具,也不是面向用户的性能指标展示页,而是面向核心引擎开发者与贡献者的内部性能回归工具——当你修改了wren-core中 SQL 解析、模型分析或 SQL 重写相关代码后,用它来确认性能没有回退。
从 Cargo.toml 可以看到该 crate 的依赖结构:核心依赖是工作区内的wren-core与datafusion(Wren 的 SQL 转换执行依赖 DataFusion 的SessionContext),配合structopt(命令行参数解析)、serde/serde_json(结果序列化)、tokio(异步运行时)、num_cpus(记录机器核数)与log/env_logger。也就是说,这个基准 crate 直接以wren-core为被测对象,每次运行都会真实走一遍 wren-core 的 SQL 处理管线。
支持的基准测试:TPC-H 与 Wren 专属查询
TPC-H:行业标准决策支持基准
TPC-H 是衡量查询性能的行业标准决策支持基准(decision support benchmark)。本仓库的 TPC-H 基准源自 TPC-H 规范 2.17.1 版本,测试数据与参考答案由tpch-gen(来自 databricks 的 tpch-dbgen)生成。数据集使用 Scale Factor 1(约 1GB),每个表对应单个 parquet 文件,采用 hash join 执行计划——这些细节在 bench.sh 的 usage 说明中也有印证。
仓库中提供了全部 22 条 TPC-H 查询,位于 queries/tpch 目录(q1.sql~q22.sql)。以 q1.sql 为例,它是经典的定价汇总查询:对lineitem表按l_returnflag与l_linestatus分组,计算数量、价格、折扣、税后金额的总和、均值与行数。这类查询覆盖了聚合、过滤、分组排序等常见分析场景。
TPC-H 基准在代码层面的核心是 src/tpch/mod.rs 中的tpch_manifest()函数。它用wren_core::mdl::builder的ManifestBuilder、ModelBuilder、ColumnBuilder以编程方式构造了 TPC-H 的 8 张表模型:customer、orders、lineitem、part、partsupp、supplier、nation、region,每张表都定义了列名、类型与主键,并映射到datafusion.public.xxx表引用。也就是说,基准并非直接对裸 SQL 计时,而是先构造一份 Wren 语义模型(MDL),再让 wren-core 对 TPC-H 查询做完整的分析、改写后再执行,这正是 Wren text-to-SQL 引擎的真实工作路径。
Wren 专属基准:面向复杂 SQL 的场景收集
Wren 基准专门用于收集 Wren AI 性能评估所需的复杂 SQL 测试用例,共 2 条查询,存放在 queries/q1.sql 与 queries/q2.sql:
q1:一条包含多个 CTE(Common Table Expressions)与子查询的复杂 SQL。以 queries/q1.sql 为例,它依次构造filtered_accounts、phase1_challenges、phase2_challenges、phase3_challenges等多个 CTE,每个 CTE 内又有NOT EXISTS子查询、LOWER()大小写归一化、JOIN与按日期分组的COUNT(DISTINCT ...),最后通过多层LEFT JOIN汇总。这类查询用于考验复杂查询规划与优化(complex query planning and optimization)能力。q2:与q1结构相似,但额外加入了UNION子句,将多个按结束时间与账户创建时间分组的 CTE 结果合并成all_dates全集后统一左连接,用于考验集合操作(set operations)下的查询改写与优化能力。
与 TPC-H 用代码构建 Manifest 不同,Wren 基准通过读取 JSON 格式的 MDL 文件来加载语义模型,见 src/wren/mod.rs 的get_manifest():它尝试从mdls/q{id}.json与benchmarks/mdls/q{id}.json两个候选路径读取并反序列化为Manifest。仓库中已提供 mdls/q1.json 与mdls/q2.json,其内容为标准的 Wren MDL 格式:声明dataSource(如 POSTGRES)、catalog/schema、每个模型的列定义(user_name、status_flag、account_id、created_date等)及底层tableReference映射。从源码结构看,这两条查询取自 Wren 产品中真实存在的业务分析场景(账户、挑战赛阶段通过率统计),因此能更贴近实际 text-to-SQL 工作负载。
快速开始:用 bench.sh 一键运行
最便捷的运行方式是 bench.sh 脚本。直接不带参数执行即可查看完整的 usage 帮助:
# 查看所有可用选项与用法 ./bench.shbench.sh支持三个子命令(run/compare/venv)与三类基准(all(默认)/tpch/wren),同时支持通过环境变量覆盖运行配置:
| 环境变量 | 说明 | 默认值 |
|---|---|---|
CARGO_COMMAND | 执行基准二进制文件的命令 | cargo run --release |
WREN_DIR | wren-core 所在目录 | 脚本所在目录的上一级 |
RESULTS_NAME | 结果子目录名称(默认取当前 git 分支名) | 当前分支名 |
RESULTS_DIR | 结果存放目录 | $SCRIPT_DIR/results/$RESULTS_NAME |
VIRTUAL_ENV | compare/venv 命令使用的 Python 虚拟环境 | $SCRIPT_DIR/venv |
例如显式指定被基准的 wren-core 目录运行:
WREN_DIR=/path/to/wren-core ./bench.sh run tpchrun子命令内部会自动读取当前 git 分支名作为结果目录名(分支名中的/会被替换为_),并在${WREN_DIR}/benchmarks下依次调用cargo run --release --bin tpch -- benchmark -i 10 -o <results>/tpch.json(或wren.json)。因此结果文件天然按分支隔离存放,这正是后续分支对比的数据基础。
分支间性能对比:完整工作流
基准测试最典型的用途是验证某个 feature 分支相对 main 分支的性能变化。官方工作流如下:
# 切到 main 分支,采集基线数据 git checkout main ./benchmarks/bench.sh run tpch # 切到你的 feature 分支,采集对比数据 git checkout mybranch ./benchmarks/bench.sh run tpch # 对比两个分支的结果 ./bench.sh compare main mybranch这里compare子命令会读取results/main/与results/mybranch/下同名的 JSON 文件,逐基准输出详细对比报告。需要说明的是,官方文档示例中git checkout main之后的命令写为./benchmarks/bench.sh run tpch(从仓库根目录视角执行),而compare使用./bench.sh compare main mybranch(从 benchmarks 目录视角执行)——两者均可,实际取决于你的当前目录与脚本路径。
对比报告会输出每个查询在两侧分支的耗时与变化结论,形如:
Comparing main and mybranch -------------------- Benchmark tpch.json -------------------- ┏━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━┓ ┃ Query ┃ main ┃mybranch ┃ Change ┃ ┡━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━┩ │ QQuery 1 │ 4.25ms │ 4.26ms │ no change │ │ QQuery 2 │ 11.25ms │ 11.68ms │ no change │ │ ... │ ... │ ... │ ... │ │ QQuery 22 │ 4.54ms │ 4.33ms │ no change │ └──────────────┴─────────┴─────────┴───────────┘ ┏━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓ ┃ Benchmark Summary ┃ ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━┩ │ Total Time (main) │ 133.16ms │ │ Total Time (mybranch) │ 132.92ms │ │ Average Time (main) │ 6.05ms │ │ Average Time (mybranch)│ 6.04ms │ │ Queries Faster │ 0 │ │ Queries Slower │ 0 │ │ Queries with No Change │ 22 │ └────────────────────────┴──────────┘对比报告的判定逻辑
上述报告的生成逻辑在 compare.py 中实现,其中有两个值得注意的设计:
- 单查询耗时取多次迭代的最小值:
QueryRun.execution_time取所有迭代elapsed的最小值(见compare.py中min(iteration.elapsed for iteration in self.iterations)),官方注释说明这是为了抵消系统负载波动等因素的影响。 - 噪声阈值判定:
--noise-threshold默认 0.05(±5%)。当comparison / baseline落在[1-0.05, 1+0.05]区间内时记为no change;小于下界记为+.x fast(报告中显示为+{1/change:.2f}x faster);大于上界记为{change:.2f}x slower,并分别统计 Faster / Slower / No Change 数量。
在运行compare之前需要先准备 Python 依赖,bench.sh提供了venv子命令一键完成:./bench.sh venv会创建venv虚拟环境并安装 requirements.txt 中声明的依赖(核心是rich,用于渲染表格)。你也可以直接手动执行:
python3 -m venv venv ./venv/bin/pip install -r requirements.txt ./venv/bin/python compare.py results/main/tpch.json results/mybranch/tpch.json手动运行基准:更精细的控制
当需要精确控制查询编号、迭代次数或输出文件时,可以直接用 Cargo 运行基准二进制:
# 运行 TPC-H 第 1 条查询,迭代 10 次,结果输出到 JSON cargo run --release --bin tpch -- benchmark --query 1 -i 10 -o result.json # 运行全部 TPC-H 查询,每条约 5 次迭代 cargo run --release --bin tpch -- benchmark --all-queries -i 5 # 运行 Wren 专属基准 cargo run --release --bin wren -- benchmark --query 1 -i 10tpch与wren两个二进制分别对应 TPC-H 与 Wren 两套基准。注意这里--all-queries实际是默认行为:从 src/tpch/run.rs 与 src/wren/run.rs 的实现看,RunOpt.query未指定时,TPC-H 会遍历1..=22(常量TPCH_QUERY_START_ID=1、TPCH_QUERY_END_ID=22),Wren 基准则遍历1..=2(常量QUERY_START_ID=1、QUERY_END_ID=2)。
命令行选项
| 选项 | 说明 | 默认值 |
|---|---|---|
--query <number> | 运行指定编号的查询(TPC-H:1–22,Wren:1–2) | 运行全部 |
-i, --iterations <number> | 每条查询的迭代次数 | 1(注:CLI 帮助中为 3,见下文) |
-o, --output <file> | 结果输出到 JSON 文件 | 仅打印到终端 |
--all-queries | 运行基准套件中的全部查询 | — |
关于迭代次数默认值有一个细节值得注意:官方 README 表格写默认值为 1,而 src/util/options.rs 中CommonOpt.iterations的实际默认值为3。另外bench.sh的run子命令固定以-i 10调用基准二进制。使用时应以实际代码为准,若追求与 README 一致,建议显式传入-i。
源码级原理:一条基准查询的完整执行链路
无论 TPC-H 还是 Wren 基准,核心执行逻辑高度一致,可以用 src/tpch/run.rs 的benchmark_query()概括为如下流程:
- 创建 DataFusion 的
SessionContext; - 加载语义模型并分析:
AnalyzedWrenMDL::analyze(tpch_manifest(), HashMap::default(), Mode::Unparse)——把 MDL Manifest 交给 wren-core 分析,使用Mode::Unparse(不解析模式); - 对每次迭代:读取查询 SQL(
get_query_sql(query_id)会按;拆分出多条语句),逐条调用transform_sql_with_ctx(&ctx, mdl, &[], HashMap::new().into(), query)完成 SQL 转换,随后用Instant::now()与elapsed()记录整批语句的总耗时; - 输出每次迭代耗时与平均值,并把结果写入
BenchmarkRun结构(见 src/util/run.rs)。
也就是说,被测的不是单纯执行 SQL 的快慢,而是wren-core 从 SQL 解析、语义模型(MDL)分析到 SQL 重写的完整转换管线。这与 WrenAI 的定位一致:Wren core 是面向 AI Agent 的开放上下文层,把自然语言问题转成受控的 SQL 与可视化,而基准测试保证这一转换链路的高性能与可回归。
SQL 文件加载策略
TPC-H 与 Wren 两套基准的 SQL 加载函数(get_query_sql)都采用了双路径回退策略:优先读取当前工作目录下的queries/...(或mdls/...),失败则回退到benchmarks/queries/...。这保证了无论从仓库根目录还是从benchmarks目录启动cargo run,都能正确定位查询文件。
结果 JSON 格式
当传入-o参数时,结果会序列化为 JSON(序列化逻辑在 src/util/run.rs),其结构正是compare.py解析所依赖的契约:
{ "context": { "benchmark_version": "<crate 版本>", "num_cpus": 8, "start_time": 1730000000, "arguments": ["benchmark", "--query", "1", "-i", "10"] }, "queries": [ { "query": "Query 1", "start_time": 1730000000, "iterations": [ { "elapsed": 4.25 }, { "elapsed": 4.18 } ] } ] }其中context自动记录 crate 版本(CARGO_PKG_VERSION)、CPU 核数(num_cpus::get())、起始时间与本次运行的完整命令行参数,为复现与审计提供元信息。
项目结构速览
core/wren-core/benchmarks/ ├── bench.sh # 主基准运行脚本(run / compare / venv) ├── compare.py # 结果对比与 rich 表格渲染 ├── requirements.txt # compare.py 依赖(rich) ├── data/ # 生成的基准数据(README 声明) ├── results/ # 基准结果与对比(按分支命名,运行时生成) ├── mdls/ # Wren 基准使用的 MDL JSON(q1.json、q2.json) ├── queries/ │ ├── tpch/ # 22 条 TPC-H 查询(q1.sql ~ q22.sql) │ └── q1.sql, q2.sql # Wren 专属复杂 SQL ├── src/ │ ├── tpch/ # TPC-H 基准实现(manifest 构建 + 执行) │ ├── wren/ # Wren 基准实现(MDL 加载 + 执行) │ └── util/ # 通用 CLI 选项与结果序列化 └── Cargo.toml # 依赖与构建配置需要说明的是,data/与results/目录不随仓库提交(前者由 tpch-dbgen 生成,后者在每次运行时自动创建),因此本地首次运行bench.sh run tpch前,需要按 TPC-H 规范用tpch-gen生成约 1GB(SF=1)的数据。Wren 基准不依赖外部数据文件,其 SQL 与 MDL 均内置于仓库中,开箱即可运行。
小结
Wren Core 的 benchmark crate 提供了一个结构清晰、可重复、支持分支对比的性能回归体系:TPC-H 提供行业标准基线,Wren 专属查询覆盖多 CTE、子查询与UNION等复杂分析场景;bench.sh一键编排数据采集,compare.py以 ±5% 噪声阈值给出可读的富文本对比报告;手动cargo run则把控制粒度下放到单条查询与迭代次数。对于任何希望为 wren-core 提交性能敏感改动的开发者而言,这既是衡量改动影响的标尺,也是理解 wren-core SQL 转换管线执行路径的最佳切入点。
【免费下载链接】WrenAIGenBI (Generative BI) for AI agents, an open-source, governed text-to-SQL through an open context layer that turns natural-language questions into trusted dashboards, charts, and SQL across 20+ data sources, such as BigQuery, Snowflake, PostgreSQL, ClickHouse, Amazon Redshift, Databricks and more.项目地址: https://gitcode.com/GitHub_Trending/wr/WrenAI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考