优化和收尾
This commit is contained in:
1 parent
7f5bcc289e
commit
9e8c5f1ef0
33 files changed
+3332
-598
No files matched your search
Generated
+334
@@ -0,0 +1,334 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
|
||||
|
||||
[[package]]
|
||||
name = "chusql-storage"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"interprocess",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chusql-storage-bench"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chusql-storage",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "doctest-file"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2db04e74f0a9a93103b50e90b96024c9b2bdca8bce6a632ec71b88736d3d359"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "interprocess"
|
||||
version = "2.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6cb02ffbf25e8ef0f4a95bff054eef75f6d1bf16919474cf2caa63b21997478a"
|
||||
dependencies = [
|
||||
"doctest-file",
|
||||
"libc",
|
||||
"recvmsg",
|
||||
"widestring",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.189"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||
|
||||
[[package]]
|
||||
name = "recvmsg"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.151"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "3.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_write",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_write"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
|
||||
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "chusql-storage-bench"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
# 存储层基准单独成包,不混进功能代码(见同目录 README)。
|
||||
[dependencies]
|
||||
chusql-storage = { path = "../../chusql-storage" }
|
||||
serde_json = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3"
|
||||
|
||||
[[bench]]
|
||||
name = "storage"
|
||||
harness = false
|
||||
@@ -1,36 +0,0 @@
|
||||
# chusql-storage 基准测试(预留)
|
||||
|
||||
这个目录预留给 **Rust 存储层** 的基准测试代码,目前 `chusql-storage/` 还没有开工,
|
||||
所以这里先占位。
|
||||
|
||||
## 计划放什么
|
||||
|
||||
`chusql-storage` 做的是磁盘那一层的事情,性能指标和查询层完全不同 ——
|
||||
查询层关心"少算几行",存储层关心"少读几次盘"。所以基准要分开测:
|
||||
|
||||
| 待测模块 | 关心的指标 |
|
||||
| -------- | ---------- |
|
||||
| 页管理 | 单页读写延迟、顺序读 vs 随机读吞吐 |
|
||||
| B+ 树 | 插入/查找/范围扫描的耗时随数据量增长的趋势 |
|
||||
| 缓冲池 | 命中率、缺页次数、替换策略(LRU 等)的对比 |
|
||||
| WAL | 追加写吞吐、日志落盘延迟、恢复耗时 |
|
||||
| 崩溃恢复 | 从日志重放的时间、恢复后数据一致性 |
|
||||
|
||||
## 用什么
|
||||
|
||||
Rust 侧的基准测试一般用 [criterion](https://github.com/bheisler/criterion.rs):
|
||||
|
||||
```
|
||||
benchmark/chusql-storage/
|
||||
├── Cargo.toml
|
||||
└── benches/
|
||||
├── page.rs
|
||||
├── btree.rs
|
||||
├── buffer_pool.rs
|
||||
└── wal.rs
|
||||
```
|
||||
|
||||
跑法:`cargo bench`
|
||||
|
||||
另外还要和 Haskell 层对齐口径:查询层的基准是"整条查询的耗时",
|
||||
存储层的基准是"单次 IO / 单次查找的耗时",两边不要混在一起比。
|
||||
@@ -0,0 +1,353 @@
|
||||
//! 存储层基准:页 IO、B+ 树、堆表、WAL 与服务端写入路径。
|
||||
//!
|
||||
//! 用 `std::time::Instant` 计时,不引第三方基准框架,离线也能跑。
|
||||
//! 每项跑 `rounds` 轮取最快一轮:存储层的抖动主要来自 fsync 与调度,
|
||||
//! 取最快一轮最接近"这套代码本身要花多久"。
|
||||
//!
|
||||
//! 跑法:`cargo bench`(可选环境变量 `CHUSQL_BENCH_N` 改规模,默认 2000)
|
||||
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use chusql_storage::btree::DiskBTree;
|
||||
use chusql_storage::config::{DEFAULT_BTREE_ORDER, DEFAULT_PAGE_SIZE};
|
||||
use chusql_storage::heap::HeapTable;
|
||||
use chusql_storage::page::{Page, PageFile};
|
||||
use chusql_storage::protocol::Row;
|
||||
use chusql_storage::wal::{Wal, WalOp};
|
||||
use serde_json::json;
|
||||
|
||||
// 工具
|
||||
/// 跑一个操作 `ops` 次,量总耗时并折算每次耗时
|
||||
fn bench<F: FnMut()>(label: &str, ops: usize, rounds: usize, mut f: F) {
|
||||
let mut best = Duration::MAX;
|
||||
for _ in 0..rounds {
|
||||
let t0 = Instant::now();
|
||||
f();
|
||||
let d = t0.elapsed();
|
||||
if d < best {
|
||||
best = d;
|
||||
}
|
||||
}
|
||||
let per = best.as_secs_f64() * 1e6 / ops as f64;
|
||||
println!(
|
||||
"{:<38} {:>9.2} ms {:>10.3} us/次",
|
||||
label,
|
||||
best.as_secs_f64() * 1e3,
|
||||
per
|
||||
);
|
||||
}
|
||||
|
||||
/// 只量一次(fsync 类操作太慢,不适合反复跑)
|
||||
fn bench_once<F: FnOnce()>(label: &str, ops: usize, f: F) {
|
||||
let t0 = Instant::now();
|
||||
f();
|
||||
let d = t0.elapsed();
|
||||
let per = d.as_secs_f64() * 1e6 / ops as f64;
|
||||
println!(
|
||||
"{:<38} {:>9.2} ms {:>10.3} us/次",
|
||||
label,
|
||||
d.as_secs_f64() * 1e3,
|
||||
per
|
||||
);
|
||||
}
|
||||
|
||||
/// 造一行
|
||||
fn row(id: i64) -> Row {
|
||||
let mut m = Row::new();
|
||||
m.insert("id".to_string(), json!(id));
|
||||
m.insert("name".to_string(), json!(format!("user{}", id)));
|
||||
m
|
||||
}
|
||||
|
||||
/// 小随机数发生器,避免引 rand
|
||||
fn next_rand(state: &mut u64) -> u64 {
|
||||
*state = state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
|
||||
*state >> 33
|
||||
}
|
||||
|
||||
// 页 IO
|
||||
/// 顺序写 / 顺序读 / 随机读
|
||||
fn bench_page(dir: &std::path::Path, n: usize) {
|
||||
let path = dir.join("page.db");
|
||||
let mut pf = PageFile::with_options(&path, DEFAULT_PAGE_SIZE, 1024).unwrap();
|
||||
|
||||
bench("page 顺序写(含 flush)", n, 3, || {
|
||||
pf.truncate().unwrap();
|
||||
for i in 0..n {
|
||||
pf.write_page(&Page::new(i as u64, DEFAULT_PAGE_SIZE)).unwrap();
|
||||
}
|
||||
pf.flush().unwrap();
|
||||
});
|
||||
drop(pf);
|
||||
|
||||
// 池子故意开小,让顺序读真的去读盘
|
||||
let mut pf = PageFile::with_options(&path, DEFAULT_PAGE_SIZE, 16).unwrap();
|
||||
bench("page 顺序读(池 16,基本都缺页)", n, 3, || {
|
||||
for i in 0..n {
|
||||
pf.read_page(i as u64).unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
let mut pf = PageFile::with_options(&path, DEFAULT_PAGE_SIZE, 16).unwrap();
|
||||
let mut state = 12345u64;
|
||||
bench("page 随机读(池 16)", n, 3, || {
|
||||
for _ in 0..n {
|
||||
let id = next_rand(&mut state) % n as u64;
|
||||
pf.read_page(id).unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
// 池子开大、工作集比池子还大:每次缺页都要挑一个牺牲页
|
||||
let mut pf = PageFile::with_options(&path, DEFAULT_PAGE_SIZE, 1024).unwrap();
|
||||
let mut state = 999u64;
|
||||
bench("page 随机读(池 1024,工作集 > 池)", n, 3, || {
|
||||
for _ in 0..n {
|
||||
let id = next_rand(&mut state) % n as u64;
|
||||
pf.read_page(id).unwrap();
|
||||
}
|
||||
});
|
||||
println!(
|
||||
" (池 1024:命中 {}/{})",
|
||||
pf.hits(),
|
||||
pf.hits() + pf.misses()
|
||||
);
|
||||
}
|
||||
|
||||
// B+ 树
|
||||
/// 插入、点查、全量遍历
|
||||
fn bench_btree(dir: &std::path::Path, n: usize) {
|
||||
let path = dir.join("bt.idx");
|
||||
let _ = std::fs::remove_file(&path);
|
||||
let mut bt = DiskBTree::open(&path, DEFAULT_PAGE_SIZE, DEFAULT_BTREE_ORDER, 1024).unwrap();
|
||||
|
||||
bench("btree 插入(含 flush)", n, 2, || {
|
||||
bt.clear().unwrap();
|
||||
for i in 0..n {
|
||||
bt.insert(i as i64, i as u64).unwrap();
|
||||
}
|
||||
bt.flush().unwrap();
|
||||
});
|
||||
|
||||
bench("btree 点查(全部命中)", n, 3, || {
|
||||
for i in 0..n {
|
||||
bt.get(i as i64).unwrap().expect("key must exist");
|
||||
}
|
||||
});
|
||||
|
||||
bench("btree 全量遍历", n, 3, || {
|
||||
let v = bt.iter_all().unwrap();
|
||||
assert_eq!(v.len(), n);
|
||||
});
|
||||
}
|
||||
|
||||
// 堆表
|
||||
/// 插行、全表扫描、按索引取行
|
||||
fn bench_heap(dir: &std::path::Path, n: usize) {
|
||||
let path = dir.join("heap.db");
|
||||
let idx = dir.join("heap.idx");
|
||||
let _ = std::fs::remove_file(&path);
|
||||
let _ = std::fs::remove_file(&idx);
|
||||
let mut t = HeapTable::open(&path, DEFAULT_PAGE_SIZE, 1024).unwrap();
|
||||
|
||||
bench("heap 插入(只在缓冲池,不 flush)", n, 2, || {
|
||||
t.replace_all(&[]).unwrap();
|
||||
for i in 0..n {
|
||||
t.insert_row(&row(i as i64)).unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
bench("heap 插入 + flush", n, 2, || {
|
||||
t.replace_all(&[]).unwrap();
|
||||
for i in 0..n {
|
||||
t.insert_row(&row(i as i64)).unwrap();
|
||||
}
|
||||
t.flush().unwrap();
|
||||
});
|
||||
|
||||
bench("heap 批量插入 + flush(一次落盘)", n, 2, || {
|
||||
t.replace_all(&[]).unwrap();
|
||||
let rows: Vec<Row> = (0..n as i64).map(row).collect();
|
||||
t.insert_rows(&rows).unwrap();
|
||||
t.flush().unwrap();
|
||||
});
|
||||
|
||||
bench("heap 全表扫描", n, 3, || {
|
||||
let rows = t.scan().unwrap();
|
||||
assert_eq!(rows.len(), n);
|
||||
});
|
||||
}
|
||||
|
||||
// WAL
|
||||
/// 追加写与清空
|
||||
fn bench_wal(dir: &std::path::Path, n: usize) {
|
||||
let path = dir.join("wal.log");
|
||||
let _ = std::fs::remove_file(&path);
|
||||
let wal = Wal::new(&path);
|
||||
let op = WalOp::Insert {
|
||||
table: "t".to_string(),
|
||||
row: row(1),
|
||||
};
|
||||
|
||||
bench_once("wal 追加(每条都 fsync)", n, || {
|
||||
for _ in 0..n {
|
||||
wal.append(&op).unwrap();
|
||||
}
|
||||
});
|
||||
wal.clear().unwrap();
|
||||
|
||||
bench_once("wal 清空(每条都 fsync)", n, || {
|
||||
for _ in 0..n {
|
||||
wal.clear().unwrap();
|
||||
}
|
||||
});
|
||||
wal.clear().unwrap();
|
||||
|
||||
bench_once("wal 截断(不落盘)", n, || {
|
||||
for _ in 0..n {
|
||||
wal.truncate().unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
// 拆分:一次 fsync 本身多贵,一次 open 多贵
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
// 大小和一条真实的 WAL 记录同量级(~200 字节)
|
||||
let bytes = vec![0u8; 200];
|
||||
bench_once(" └ open + write + fsync", n, || {
|
||||
for _ in 0..n {
|
||||
let mut f = OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(&path)
|
||||
.unwrap();
|
||||
f.write_all(&bytes).unwrap();
|
||||
f.sync_data().unwrap();
|
||||
}
|
||||
});
|
||||
let mut open_file = OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(&path)
|
||||
.unwrap();
|
||||
bench_once(" └ 复用句柄:write + fsync", n, || {
|
||||
for _ in 0..n {
|
||||
open_file.write_all(&bytes).unwrap();
|
||||
open_file.sync_data().unwrap();
|
||||
}
|
||||
});
|
||||
bench_once(" └ only fsync(不开不写)", n, || {
|
||||
for _ in 0..n {
|
||||
open_file.sync_data().unwrap();
|
||||
}
|
||||
});
|
||||
bench_once(" └ only open/close", n, || {
|
||||
for _ in 0..n {
|
||||
let _ = OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(&path)
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
wal.clear().unwrap();
|
||||
}
|
||||
|
||||
// 服务端写入路径
|
||||
/// 复刻 server.rs 里 write_via_wal 的顺序:清 WAL -> 写 WAL -> 改数据 -> flush -> 清 WAL
|
||||
fn bench_write_path(dir: &std::path::Path, n: usize) {
|
||||
let path = dir.join("wp.db");
|
||||
let idx = dir.join("wp.idx");
|
||||
let wal_path = dir.join("wp.wal");
|
||||
let _ = std::fs::remove_file(&path);
|
||||
let _ = std::fs::remove_file(&idx);
|
||||
let _ = std::fs::remove_file(&wal_path);
|
||||
|
||||
let mut t = HeapTable::open_indexed(
|
||||
&path,
|
||||
&idx,
|
||||
DEFAULT_PAGE_SIZE,
|
||||
DEFAULT_BTREE_ORDER,
|
||||
1024,
|
||||
)
|
||||
.unwrap();
|
||||
let wal = Wal::new(&wal_path);
|
||||
|
||||
bench_once("写入路径 单条 INSERT(端到端)", n, || {
|
||||
for i in 0..n as i64 {
|
||||
wal.truncate().unwrap();
|
||||
let op = WalOp::Insert {
|
||||
table: "t".to_string(),
|
||||
row: row(i),
|
||||
};
|
||||
wal.append(&op).unwrap();
|
||||
t.insert_row(&row(i)).unwrap();
|
||||
t.flush().unwrap();
|
||||
wal.clear().unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
// 拆开量,好定位时间花在哪一步
|
||||
bench_once(" └ wal.truncate()(不落盘)", n, || {
|
||||
for _ in 0..n {
|
||||
wal.truncate().unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
let op = WalOp::Insert {
|
||||
table: "t".to_string(),
|
||||
row: row(1),
|
||||
};
|
||||
bench_once(" └ wal.append()(含 fsync)", n, || {
|
||||
for _ in 0..n {
|
||||
wal.append(&op).unwrap();
|
||||
}
|
||||
});
|
||||
wal.clear().unwrap();
|
||||
|
||||
bench_once(" └ wal.clear()(含 fsync)", n, || {
|
||||
for _ in 0..n {
|
||||
wal.clear().unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
bench_once(" └ heap.insert_row()(含写索引)", n, || {
|
||||
// 主循环已经插过 0..n 了,这里换个号段,免得撞上"索引列唯一"
|
||||
for i in n as i64..2 * n as i64 {
|
||||
t.insert_row(&row(i)).unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
bench_once(" └ heap.flush()", n, || {
|
||||
for _ in 0..n {
|
||||
t.flush().unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let n: usize = std::env::var("CHUSQL_BENCH_N")
|
||||
.ok()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.unwrap_or(2000);
|
||||
|
||||
println!("chusql-storage 基准:n = {},页大小 = {} 字节,btree order = {}",
|
||||
n, DEFAULT_PAGE_SIZE, DEFAULT_BTREE_ORDER);
|
||||
println!("每项取最快一轮;带 fsync 的项只跑一次(跑多了太慢)\n");
|
||||
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
|
||||
println!("[页 IO]");
|
||||
bench_page(dir.path(), n);
|
||||
|
||||
println!("\n[B+ 树]");
|
||||
bench_btree(dir.path(), n);
|
||||
|
||||
println!("\n[堆表]");
|
||||
bench_heap(dir.path(), n);
|
||||
|
||||
println!("\n[WAL]");
|
||||
bench_wal(dir.path(), n.min(200));
|
||||
|
||||
println!("\n[服务端写入路径]");
|
||||
bench_write_path(dir.path(), n.min(200));
|
||||
}
|
||||
Reference in new issue
Block a user