跳到主要内容

基准测试套件

Goose 提供了完善的基准测试套件。 当你做出可能影响性能的改动时,务必运行这些基准测试以发现潜在性能回退。

快速开始

Goose 仓库中构建基准测试套件:

BUILD_BENCHMARK=1 BUILD_EXTENSIONS='tpch' make

列出基准测试

列出全部可用基准测试:

build/release/benchmark/benchmark_runner --list

运行基准测试

运行单个基准测试

运行单个基准测试可执行:

build/release/benchmark/benchmark_runner benchmark/micro/nulls/no_nulls_addition.benchmark

输出会以 CSV 形式打印到 stdout,格式如下:

name	run	timing
benchmark/micro/nulls/no_nulls_addition.benchmark 1 0.121234
benchmark/micro/nulls/no_nulls_addition.benchmark 2 0.121702
benchmark/micro/nulls/no_nulls_addition.benchmark 3 0.122948
benchmark/micro/nulls/no_nulls_addition.benchmark 4 0.122534
benchmark/micro/nulls/no_nulls_addition.benchmark 5 0.124102

你也可以通过 --out 指定输出文件。该文件只会写入耗时数据(按换行分隔)。

build/release/benchmark/benchmark_runner benchmark/micro/nulls/no_nulls_addition.benchmark --out=timings.out

文件内容示例:

0.182472
0.185027
0.184163
0.185281
0.182948

使用正则运行多个基准测试

你也可以通过正则表达式指定要运行的基准测试。 注意 shell 对部分正则字符的展开(如 * 常会被 shell 展开),因此需要正确引用或转义。

build/release/benchmark/benchmark_runner "benchmark/micro/nulls/.*"

运行全部基准测试

不传任何参数时将运行全部基准测试。

build/release/benchmark/benchmark_runner

其他选项

--info 可输出该基准测试的额外信息。

build/release/benchmark/benchmark_runner benchmark/micro/nulls/no_nulls_addition.benchmark --info
display_name:NULL Addition (no nulls)
group:micro
subgroup:nulls

--query 会打印该基准测试实际执行的查询。

SELECT min(i + 1) FROM integers;

--profile 会输出查询树。

创建基准测试

许多开发工作都与性能相关。 在其他测试之外加入基准测试,不仅能验证性能优化,也能防止该功能未来出现性能回退。

基准测试示例

下面以 FILL 窗口函数为例说明如何创建 benchmark 文件。 (FILL 会在有序分区内对缺失值做线性插值。)

benchmark 文件与单元测试文件类似,头部格式也相同。

# name: benchmark/micro/window/window_fill.benchmark
# description: Measure the performance of FILL
# group: [window]

可使用 make format-head 确保头部结构符合预期并避免 tidy 检查报错。

头部下方是一组描述 benchmark 的关键字。

name FillPerformance
group micro
subgroup window

有些 benchmark 只运行一条查询,但更常见的是通过 argument 关键字对 benchmark 做参数化。 这样可用不同配置(如数据规模)重复运行。 FILL 示例中有三个参数:

argument sf 10
argument errors 0.1
argument keys 4

FILL 中分别表示:

  • scale factor(每个分区的百万行数)
  • error rate(缺失值占比)
  • 分区数量

benchmark 通常在运行查询前需要准备数据。 数据准备写在 benchmark 文件的 load 段。 FILL 示例中我们使用参数与随机数生成器构造数据表。

load
select setseed(0.8675309);
create or replace table data as (
select
k::TINYINT as k,
(case when random() > ${errors} then m - 1704067200000 else null end) as v,
m,
from range(1704067200000, 1704067200000 + ${sf} * 1_000_000 * 10, 10) times(m)
cross join range(${keys}) keys(k)
);

查询中会展开 argument 参数,方式类似单元测试里 foreach 的值展开。 注意 load 段可以包含多条 SQL 语句。

数据准备完毕后,就可以在 run 段定义要测的查询。 其限制与单元测试相同(例如不能有空行等)。 在 FILL 基准中,我们要找出插值失败的位置:

run
SELECT
m,
k,
fill(v) OVER (PARTITION BY k ORDER BY m) as v
FROM
data
qualify v <> m - 1704067200000;

如果插值正确,则无论 scale 如何都不应有输出。 我们通过最后的 result 子句进行校验, 其语法与单元测试一致:

result III

通过“无输出行”这一断言,我们既能验证查询正确性,也能评估其性能。

顶层 benchmark/ 目录下还有许多其他示例, 建议查看以了解更多技巧。