跳至内容
基准测试

基准测试

项目有两套用途不同的基准测试。

持续基准测试:源代码及结果位于 https://github.com/ringsaturn/tz-benchmark,可视化展示在 https://ringsaturn.github.io/tz-benchmark/。它在每次发布时于 GitHub Actions 中运行,用于跨包对比。运行器硬件与开发机不同,因此绝对数值与本地运行有差异,包之间的相对次序可以对比。持续基准测试自 2026-09-11 起覆盖 tzf v2。

按日期归档的快照snapshot/ 下的目录在 Apple M3 Max 上本机取得。本页全部数据来自 2026-09-14 快照,针对已发布的 tzf v2.1.1、tzf-rs 2.1.1 以及 tzfpy 2.1.0b2 预发布版(lite 与 +full 两种 wheel)测得,三者均基于 tzf-dist v0.0.2026-c-tzb2

测试方法

每个查找器初始化一次并复用于所有查询,与文档中的生产环境用法一致。查询采样两个数据集:154,694 个世界城市(gt_cities.csv)和 23,408 个靠近边界的点(gt_edges.csv),均以完整精度的 2026c 数据作为基准。精度测试在 Go 中还运行了 1,000,000 个均匀随机点的数据集。

内存按候选项在独立的子进程中测量。下文出现四列,它们的口径互不相同:

含义
基线构造候选项之前的 RSS
初始化峰值加载过程中达到的高水位(ru_maxrss)。容器内存限制必须能容纳该值,否则进程会在启动阶段被杀死,即便它稳定运行时的占用完全放得下
常驻候选项准备好接受查询后实际持有的数据量,来自语言原生的内存统计:Go 为强制 GC 后的 HeapAlloc,Rust 为计数型全局分配器。Python 的数据保存在 Python 堆之外,因此为 n/a
加载后 RSS进程准备好接受查询时操作系统报告的值。它更接近初始化峰值而非常驻值,因为释放内存并不会让 RSS 缩小,分配器会保留这些页面以便复用

每个候选项都满足 常驻 <= 加载后 RSS <= 初始化峰值

查询延迟

Apple M3 Max,2026c 数据集,2026-09-14 快照。

Go (tzf v2)

基准项ns/opp50 (ns)p99 (ns)B/opallocs/op
NewDefaultFinder,世界城市345.8208.0150000
NewDefaultFinder,边界城市543.7459.0120800
NewEmbeddedFinder,世界城市533.7333.0250000
NewEmbeddedFinder,边界城市11621000279100
NewFullFinder,世界城市347.9208.0158300
NewFullFinder,边界城市606.7500.0175000

三个查找器的查询过程都不分配内存。

Rust (tzf-rs 2.1)

基准项ns/iter标准差 (ns)
DefaultFinder,随机城市221.1341.39
DefaultFinder,随机边界城市474.8352.12
EmbeddedFinder,随机城市292.6851.42
EmbeddedFinder,随机边界城市666.1148.17

Python (tzfpy 2.1.0b2)

使用 pytest-benchmark,每轮调用一次 get_tz()

基准项中位数 (ns)平均 (ns)OPS (Kops/s)
随机城市,lite625.0718.71,391.4
随机边界城市,lite834.0907.01,102.5
随机城市,+full667.0822.71,215.5
随机边界城市,+full1,167.01,292.3773.8

单次调用的开销与 Rust 的数值相当,差异来自通过 PyO3 从 Python 调用 Rust 的开销。+full 两行是 tzfpy 自有索引发布的实验性完整精度 wheel,它用 tzf-rs 的 EmbeddedFinder 原地查询 full.tzb

精度

与完整精度 2026c 基准对比的错误率。「偏移量相同」统计的是对应 UTC 偏移量相同的错误结果。

数据集N候选项错误数错误率 %偏移量相同
cities154,694Go NewDefaultFinder(lite .tzm10.00061
cities154,694Go NewEmbeddedFinder(lite .tzb10.00061
cities154,694Go NewFullFinder(full .tzb00.00000
cities154,694Rust DefaultFinder10.00061
cities154,694Rust EmbeddedFinder10.00061
cities154,694tzfpy10.00061
cities154,694tzfpy +full00.00000
edges23,408Go NewDefaultFinder(lite .tzm10.00431
edges23,408Go NewFullFinder(full .tzb00.00000
edges23,408Rust DefaultFinder10.00431
edges23,408tzfpy10.00431
edges23,408tzfpy +full00.00000
uniform1,000,000Go NewDefaultFinder(lite .tzm190.001914
uniform1,000,000Go NewFullFinder(full .tzb00.00000

lite 与完整精度查找器的差异只出现在边界附近。简化的位移上限为 111.2 m,完整的位移数据见常见问题

内存

数值单位为 MiB。

Go

候选项基线初始化峰值常驻加载后 RSS
Go 运行时基线4.74.70.25.0
NewDefaultFinder(lite .tzm5.141.513.141.5
NewEmbeddedFinder(lite .tzb 原地查询)5.29.20.39.6
NewFullFinder(full .tzb5.2315.5147.0315.5

Rust

候选项基线初始化峰值常驻加载后 RSS
Rust 运行时基线5.85.90.05.9
DefaultFinder5.846.622.846.5
EmbeddedFinder5.810.30.210.4

EmbeddedFinder 的常驻值为 0.2 MiB:其数据是 'static 的嵌入切片,计数型分配器记录到的只有 tzf-rs 2.1 在打开时构建的索引(chunk 跳过块和每个 group 的纬度条带)。

Python

候选项基线初始化峰值常驻加载后 RSS
Python 解释器基线22.422.4n/a22.4
tzfpy(lite)22.460.3n/a60.2
tzfpy +full22.438.2n/a38.2

观察结果

  • 原地机制以查询延迟换取内存。在 Go 中,初始化峰值从 41.5 MiB 降到 9.2 MiB,同时世界城市的中位延迟从 208 ns 升到 333 ns,边界城市的中位延迟从 459 ns 升到 1,000 ns。
  • 与 2026-09-11 快照相比,原地查找器的边界城市数值从 8,959 ns 降到 1,000 ns(Go p50)、从 4,779.81 ns 降到 666.11 ns(Rust 平均值)。变化来自 tzf 2.1 与 tzf-rs 2.1 重写的查询遍历,以及 v0.0.2026-c-tzb2 的 64 点 chunk;展开式查找器在噪声范围内不变,结果完全一致。
  • tzfpy +full 预发布版的边界城市中位延迟为 1,167 ns(lite wheel 为 834 ns),初始化峰值 38.2 MiB(lite wheel 为 60.3 MiB)。
  • 完整精度数据集增加的是内存而非延迟。Go 的初始化峰值从 41.5 MiB 升到 315.5 MiB,世界城市的中位延迟保持在 208 ns。
  • 初始化峰值高于稳定状态的开销。Go 默认查找器峰值 41.5 MiB,常驻 13.1 MiB;完整精度查找器峰值 315.5 MiB,常驻 147.0 MiB。容器内存按峰值规划,长期运行成本按常驻值估算。
  • 各语言的运行时基线不同(Go 4.7 MiB、Rust 5.8 MiB、Python 22.4 MiB),跨语言比较总量前需要先减去这部分。
  • lite 数据集在 154,694 个世界城市中与完整精度基准有 1 处不一致,且该结果对应的 UTC 偏移量相同。
最后更新于