Files
ChuSQL/benchmark/README.md
T

94 lines
4.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ChuSQL 基准测试
这个目录放**性能基准测试**,和 `chusql-engine`、`chusql-storage` 的功能代码分开,
因为它回答的问题不一样:功能代码回答"对不对",基准测试回答"快不快、快多少"。
```
benchmark/
├── README.md 本文件:怎么跑、测什么、结果怎么解读
├── chusql-engine/ Haskell 查询层:优化器开/关的对比
│ ├── stack.yaml 把 chusql-engine 作为本地包引入
│ ├── chusql-bench.cabal
│ └── src/Main.hs
└── chusql-storage/ Rust 存储层:预留(见该目录下的 README)
```
---
## 怎么跑
```bash
cd benchmark/chusql-engine
stack run # 默认 users=500 行、orders=500 行
stack run -- 1000 1000 0.5 # 也可以自己指定:users 行数、orders 行数、每批目标秒数
```
第一次跑要编译,之后很快。整个默认跑完约 5 秒。
---
## 测什么
四个用例,**每个都跑两遍**:一遍走"翻译出来的原始工序单",一遍走"优化后的工序单"。
两次的结果行数会被打印出来互相对照,**不一致会当场标出来**。
| 用例 | 想看什么 |
| ---- | -------- |
| `JOIN` + `WHERE u.age > 90` | 谓词下推的主场:条件能筛掉约 90% 的行 |
| `JOIN`(无 WHERE) | 只剩投影下推在起作用,收益应该温和 |
| `JOIN` + `WHERE` + `ORDER BY` + `LIMIT` | 规则叠加时的综合效果 |
| 单表 `WHERE age > 90` | 对照组:没有下推空间,优化器只剩固定开销 |
---
## 计时怎么做(两个坑都踩过了)
**坑一:Haskell 是惰性的,不强制求值就等于没测。**
如果只写 `let r = evalRelOp db plan`,`r` 根本没被算出来,测到的是"定义一个变量"的时间。
所以每次必须把结果**算到底**(本项目里是逐行把列数累加起来当校验和)。
**坑二:Windows 上 CPU 时钟的精度只有约 15.6 毫秒。**
优化后的查询只要几毫秒,直接测会得到 `0.0 ms` 这种没意义的数字。
所以计时用的是**自适应批量**:把同一个用例连续跑 `k` 次测一整批,`k` 不断翻倍,
直到一批累计耗时超过目标值(默认 0.3 秒),再折算成单次耗时。
这样时钟精度的误差会被 `k` 摊薄,几毫秒的用例也能测准。
程序会打印实际用了多大的批次(例如"优化后 64 次/批")。
跑正式计时之前还会**预热两遍**,避免把首次构造表数据的开销算进去。
---
## 实测结果
**环境**:Windows,GHC 9.10.3,`-O2`
**数据**:`users` 500 行(`id / name / age`,`age` 只有 0-99 共 100 种取值),
`orders` 500 行(`id / user_id / product`),两表配对共 25 万对。
| 查询 | 结果行数 | 未优化 | 优化后 | 加速 |
| ---- | -------- | ------ | ------ | ---- |
| `JOIN` + `WHERE u.age > 90` | 45 | 125.0 ms | **7.1 ms** | **17.7x** |
| `JOIN`(无 WHERE) | 500 | 132.8 ms | 105.5 ms | 1.3x |
| `JOIN` + `WHERE` + `ORDER BY` + `LIMIT 10` | 10 | 171.9 ms | **10.3 ms** | **16.8x** |
| 单表 `WHERE age > 90` | 45 | 0.111 ms | 0.153 ms | 0.7x |
### 怎么解读
- **前两行的差距来自谓词下推。** 条件筛掉了约 90% 的用户,配对次数从 25 万降到约 2.25 万,
时间直接掉一个数量级。筛得越狠,省得越多。
- **第二行只有投影下推。** 配对次数一点没变(没有条件可下推),只是每一行变窄了,
所以只有 1.3 倍 —— 收益温和但确实是正的。
- **第四行反而略慢。** 单表查询本来就没有下推空间(条件早已贴着扫描),
而优化器自己要走一遍规则,有几十微秒的固定开销(0.153 − 0.111 ≈ 0.04 ms)。
**优化不是零成本的**:查询越大、配对越多,这笔账才越划算。
---
## 加新用例
在 `src/Main.hs` 的 `queries` 列表里加一条 SQL 字符串就行,其余全自动
(会自动翻译、优化、跑两遍、比结果、算加速比)。
想测别的规模,直接命令行传参;想在文档里引用数字,记得把**数据规模一起写上** ——
脱离规模谈"快了几倍"是没有意义的。