跳至内容
基准测试

基准测试

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

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

按日期归档的快照:snapshot/ 下的目录在 Apple M3 Max 上本机取得。本页全部数据来自 2026-11-01 快照,针对已发布的 tzf v2.1.4、tzf-rs 2.1.4 和 tzfpy 2.1.2(lite 与 +full 两种 wheel)测得,三者均基于 tzf-dist v0.0.2026-f。

测试方法

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

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

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

每个候选项的 常驻 都是三个数值中最小的;加载后 RSS 比 初始化峰值 最多高 0.5 MiB。

查询延迟

Apple M3 Max,2026f 数据集,2026-11-01 快照。

Go (tzf v2)

基准项ns/opp50 (ns)p99 (ns)B/opallocs/op
NewDefaultFinder,世界城市350.0208.0150000
NewDefaultFinder,边界城市556.5500.0125000
NewEmbeddedFinder,世界城市535.8333.0254200
NewEmbeddedFinder,边界城市12301042275000
NewFullFinder,世界城市354.5208.0170800
NewFullFinder,边界城市591.8500.0154200

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

Rust (tzf-rs 2.1.4)

基准项ns/iter标准差 (ns)
DefaultFinder,随机城市225.6221.17
DefaultFinder,随机边界城市489.3310.13
EmbeddedFinder,随机城市350.34110.85
EmbeddedFinder,随机边界城市686.948.99

Python (tzfpy 2.1.2)

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

基准项中位数 (ns)平均 (ns)OPS (Kops/s)
随机城市,lite666.0745.41,341.6
随机边界城市,lite875.0958.81,042.9
随机城市,+full667.0810.51,233.8
随机边界城市,+full1,208.01,313.5761.4

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

精度

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

数据集N候选项错误数错误率 %偏移量相同
cities170,540Go NewDefaultFinder(lite .tzm)20.00121
cities170,540Go NewEmbeddedFinder(lite .tzb)20.00121
cities170,540Go NewFullFinder(full .tzb)00.00000
cities170,540Rust DefaultFinder20.00121
cities170,540Rust EmbeddedFinder20.00121
cities170,540tzfpy20.00121
cities170,540tzfpy +full00.00000
edges23,408Go NewDefaultFinder(lite .tzm)10.00431
edges23,408Go NewFullFinder(full .tzb)00.00000
edges23,408Rust DefaultFinder10.00431
edges23,408tzfpy10.00431
edges23,408tzfpy +full00.00000
uniform1,000,000Go NewDefaultFinder(lite .tzm)210.002114
uniform1,000,000Go NewFullFinder(full .tzb)00.00000

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

内存

数值单位为 MiB。

Go

候选项基线初始化峰值常驻加载后 RSS
Go 运行时基线5.15.10.35.4
NewDefaultFinder(lite .tzm)5.544.513.344.6
NewEmbeddedFinder(lite .tzb 原地查询)5.79.60.410.1
NewFullFinder(full .tzb)5.7266.7148.9266.9

Rust

候选项基线初始化峰值常驻加载后 RSS
Rust 运行时基线5.85.80.05.9
DefaultFinder5.844.523.144.3
EmbeddedFinder5.810.20.210.2

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

Python

候选项基线初始化峰值常驻加载后 RSS
Python 解释器基线22.522.5n/a22.5
tzfpy(lite)22.561.3n/a61.2
tzfpy +full22.538.8n/a38.8

观察结果

  • 原地机制以查询延迟换取内存。在 Go 中,初始化峰值从 44.5 MiB 降到 9.6 MiB,同时世界城市的中位延迟从 208 ns 升到 333 ns,边界城市的中位延迟从 500 ns 升到 1,042 ns。
  • 与 2026-09-14 快照(tzf v2.1.1、2026c)相比,Go 世界城市的中位延迟仍为 208、333、208 ns,Go 边界城市的中位延迟相差最多 42 ns。Rust EmbeddedFinder 随机城市的平均值从 292.68 ns 升到 350.34 ns,本次运行的标准差为 110.85 ns。Go NewFullFinder 的初始化峰值从 315.5 MiB 降到 266.7 MiB,常驻堆保持在约 148 MiB;初始化峰值变化的原因未查明。
  • tz-benchmark 于 2026-09-20 更新了基准数据文件,世界城市数据集从 154,694 个增加到 170,540 个,因此精度计数不能与 2026-09-14 的表格直接比较。
  • tzfpy +full wheel 的边界城市中位延迟为 1,208 ns(lite wheel 为 875 ns),初始化峰值 38.8 MiB(lite wheel 为 61.3 MiB)。
  • 完整精度数据集增加的是内存而非延迟。Go 的初始化峰值从 44.5 MiB 升到 266.7 MiB,世界城市的中位延迟保持在 208 ns。
  • 初始化峰值高于稳定状态的开销。Go 默认查找器峰值 44.5 MiB,常驻 13.3 MiB;完整精度查找器峰值 266.7 MiB,常驻 148.9 MiB。容器内存按峰值规划,长期运行成本按常驻值估算。
  • 各语言的运行时基线不同(Go 5.1 MiB、Rust 5.8 MiB、Python 22.5 MiB),跨语言比较总量前需要先减去这部分。
  • lite 数据集在 170,540 个世界城市中与完整精度基准有 2 处不一致,测试工具将其中 1 处计为 UTC 偏移量相同。
最后更新于