只改一个函数,性能提升10倍?解读一个 Rust 性能 PR
摘要
一篇深度博文,通过基准测试复现和代码分析,解读 GreptimeDB 中让 Prometheus 读取转换速度提升 10 倍的 Rust 性能 PR。
<p><a href="https://lobste.rs/s/sv9hwi/just_one_function_10x_faster_reading_rust">评论</a></p>
查看缓存全文
缓存时间: 2026/08/06 18:12
# 只改一个函数,就能快 10 倍?解读一个 Rust 性能优化 PR 源码:https://greptime.com/blogs/2026-07-31-prom-read-conversion-optimization
GreptimeDB 可以作为 Prometheus 的 remote read 后端。在这条链路的最后一步,查询引擎产生的列式 `RecordBatch` 会被转换成 Prometheus 协议中面向行的 `TimeSeries`:按标签集合对行分组,并将每个序列的样本收集到一起。
列式 RecordBatch 转换为面向行的 TimeSeries
*图 1:查询引擎产生的列式 `RecordBatch`,被转换成 Prometheus 协议中面向行的 `TimeSeries`*
这个函数(`recordbatches_to_timeseries`)在仓库里长期无人改动,直到我们的 committer @lyang24 (https://github.com/lyang24)(我们去年采访过他 (https://greptime.com/blogs/2025-04-10-greptimedb-committer-interview-lyang24))提交了 PR #8587 (https://github.com/GreptimeTeam/greptimedb/pull/8587) 并重写了它。该 PR 恰好只修改了一个文件:`src/servers/src/prom_store.rs` (https://github.com/GreptimeTeam/greptimedb/blob/32a6fc09157dff97c32297a7ad0cb04c14d0884f/src/servers/src/prom_store.rs)。
> i️**注意:** GreptimeDB v1.2.0-beta.1 (https://greptime.com/blogs/2026-08-04-greptimedb-v1-2-0-beta-1-release) 已发布,并包含此优化。
我之所以读这段代码,是因为这个 PR 出现在眼前。PR 描述里包含作者画的一张图,右下角是一份 CPU profile:在 30 万条序列、4 个并发读的情况下,`RecordBatch`→`TimeSeries` 这一步占用了 36.2% 的 CPU,仅字典物化(dictionary materialization)就占了 8.9%。一个既不解压也不碰磁盘、只在内存里搬运数据的函数,却烧掉三分之一的 CPU——这个数字不正常。profile 来自作者,我没有复现那个环境。为了弄清这个改动本身到底有多大收益,我需要一个能跑起来的 benchmark,而 PR 并没有附带基准测试代码。于是我在公共入口上重写了一个,并在改动前后各跑了一遍。改动前的数据同样不好看:10K 行每次转换需要 3.7 ms,100K 行需要 44 ms,也就是每秒不到 300 万行。改动之后,最好的情况快了超过 10 倍。
这篇博文是对该 PR 的逐步解读,走的是和《比 Go 慢 5 倍?优化 Rust Protobuf 解码性能》(https://greptime.com/blogs/2024-04-09-rust-protobuf-performance) 相同的路线:先写 benchmark 复现,再逐段阅读代码。Benchmark 代码在这个 gist 里 (https://gist.github.com/killme2008/d279cbaf38aa195237419474a1686e24)。
## 第 1 步:复现问题
首先,确定改动之前的状态。bench 通过 criterion (https://bheisler.github.io/criterion.rs/book/index.html) 直接驱动公共入口 `recordbatches_to_timeseries`:
```rust
fn bench_prom_read_convert(c: &mut Criterion) {
let mut group = c.benchmark_group("prom_read_convert");
group.measurement_time(Duration::from_secs(5));
// (series count, samples per series)
let sizes = [(10, 1000), (100, 100), (1000, 100), (5000, 20)];
for encoding in [Encoding::Dictionary, Encoding::Utf8] {
for ordering in [Ordering::Adjacent, Ordering::Interleaved] {
for (series, samples) in sizes {
let rows = series * samples;
let (schema, batch) = build_recordbatch(series, samples, ordering, encoding);
group.throughput(Throughput::Elements(rows as u64));
group.bench_with_input(
BenchmarkId::new(
format!("{}/{}", encoding.name(), ordering.name()),
format!("{series}x{samples}"),
),
&(schema, batch),
|b, (schema, batch)| {
b.iter(|| {
let batches = RecordBatches::try_new(schema.clone(), vec![batch.clone()])
.unwrap();
black_box(
recordbatches_to_timeseries("bench_metric", batches).unwrap(),
)
});
},
);
}
}
}
group.finish();
}
```
循环遍历的三个维度都来自真实场景。encoding 方面,`Utf8` 是普通字符串列,`Dictionary` 是 PromQL 读路径实际返回的标签布局。ordering 方面,`adjacent` 表示一个序列的样本在结果中是
相似文章
@Greptime: 关于Prometheus远程写入,瓶颈并非网络或memtable——而是Region Worker在dec…时持有&mut
GreptimeDB v1.0引入了Pending Rows Batcher,这是一个三阶段流水线,将CPU密集型工作从Datanode的关键路径上移开,使Prometheus远程写入吞吐量从120万提升到217万points/sec,并将Datanode的CPU使用率降低20%。
@Greptime: GreptimeDB v1.1.0 发布,PromQL rate/increase 查询速度提升高达97%,整体查询时间降低20-40%,TS… 上速度提升高达4.5倍
GreptimeDB v1.1.0 已发布,提供高达97%的PromQL查询加速,整体查询时间降低20-40%,在TSBS扫描密集型查询上性能提升高达4.5倍,并支持对现有表进行在线重分区。
@Greptime: 我们刚刚发布了 promql-parser v0.9.0,这是一个用 Rust 编写的 PromQL 解析器。亮点:• 函数的声明式重构 • fill*…
Greptime 发布了 promql-parser 0.9.0 版本,这是一款基于 Rust 的 PromQL 解析器,具有声明式函数重构功能,并支持 limit 函数。
@ErickSky:他们用 Rust 完全重写了 PostgreSQL……而且已经 100% 通过了官方的 Postgres 测试!注意,这是……
从头用 Rust 重新实现 PostgreSQL,已 100% 通过官方测试,磁盘兼容,并声称在开发中版本中性能大幅提升(事务型负载快 50%,分析型负载快约 300 倍)。
Rust语言的性能
本次演讲分析了Rust相较于C++的性能优势与劣势,提供了基准测试和最佳实践。附有幻灯片和阅读材料。