ベンチマーク
目的の異なる 2 つのベンチマークセットアップがあります。
継続的ベンチマーク:ソースと結果は 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 上で測定されています。
測定方法
各 Finder は一度初期化され、すべてのクエリで再利用されます。これはドキュメントに記載された本番環境のパターンと一致します。クエリは 2 つのデータセットをサンプルします。154,694 件の世界都市(gt_cities.csv)と、境界に隣接する 23,408 件の地点(gt_edges.csv)で、いずれも完全精度の 2026c 正解データと照合します。精度検証では、Go において 1,000,000 点の一様乱数データセットも実行します。
メモリは候補ごとに隔離された子プロセスで測定します。以下では 4 つの列が登場し、これらは互換ではありません。
| 列 | 意味 |
|---|---|
| ベースライン | 候補を構築する前の RSS |
| 初期化ピーク | ロード中に到達する高水位(ru_maxrss)。コンテナのメモリ上限はこの値を収容できる必要があります。そうでなければ、定常状態なら収まるはずのプロセスが起動時に kill されます |
| 常駐 | 候補がクエリを処理できる状態になった後に保持しているデータ量。言語ネイティブの計測(Go は強制 GC 後の HeapAlloc、Rust はカウント機能付きグローバルアロケータ)によります。差分ではなく絶対値です。Python は Python ヒープの外にデータを保持するため n/a です |
| ロード後 RSS | クエリを処理できる状態のプロセスについて OS が報告する値。常駐 より 初期化ピーク に近くなります。メモリを解放しても RSS は縮まず、アロケータがページをカーネルに返さず再利用のために保持し続けるためです |
すべての候補で 常駐 <= ロード後 RSS <= 初期化ピーク の関係が成り立ちます。
クエリレイテンシ
Apple M3 Max、2026c データセット、2026-09-14 スナップショット。
Go (tzf v2)
| ベンチマーク | ns/op | p50 (ns) | p99 (ns) | B/op | allocs/op |
|---|---|---|---|---|---|
NewDefaultFinder、世界都市 | 345.8 | 208.0 | 1500 | 0 | 0 |
NewDefaultFinder、境界都市 | 543.7 | 459.0 | 1208 | 0 | 0 |
NewEmbeddedFinder、世界都市 | 533.7 | 333.0 | 2500 | 0 | 0 |
NewEmbeddedFinder、境界都市 | 1162 | 1000 | 2791 | 0 | 0 |
NewFullFinder、世界都市 | 347.9 | 208.0 | 1583 | 0 | 0 |
NewFullFinder、境界都市 | 606.7 | 500.0 | 1750 | 0 | 0 |
3 つの Finder はいずれもアロケーションなしでクエリを処理します。
Rust (tzf-rs 2.1)
| ベンチマーク | ns/iter | 標準偏差 (ns) |
|---|---|---|
DefaultFinder、ランダム都市 | 221.13 | 41.39 |
DefaultFinder、ランダム境界都市 | 474.83 | 52.12 |
EmbeddedFinder、ランダム都市 | 292.68 | 51.42 |
EmbeddedFinder、ランダム境界都市 | 666.11 | 48.17 |
Python (tzfpy 2.1.0b2)
pytest-benchmark を使用し、1 ラウンドあたり get_tz() を 1 回呼び出します。
| ベンチマーク | 中央値 (ns) | 平均 (ns) | OPS (Kops/s) |
|---|---|---|---|
| ランダム都市、lite | 625.0 | 718.7 | 1,391.4 |
| ランダム境界都市、lite | 834.0 | 907.0 | 1,102.5 |
ランダム都市、+full | 667.0 | 822.7 | 1,215.5 |
ランダム境界都市、+full | 1,167.0 | 1,292.3 | 773.8 |
呼び出しあたりのオーバーヘッドは Rust の数値と同程度であり、差分は PyO3 を経由した Python から Rust への呼び出しコストによるものです。+full の行は tzfpy 独自のインデックスから配布される実験的な完全精度 wheel で、tzf-rs の EmbeddedFinder で full.tzb を参照します。
精度
完全精度の 2026c 正解データに対する誤答率です。「オフセット一致」は、誤答のうち UTC オフセットが同じものの件数です。
| データセット | N | 候補 | 誤答 | 誤答率 % | オフセット一致 |
|---|---|---|---|---|---|
| cities | 154,694 | Go NewDefaultFinder(lite .tzm) | 1 | 0.0006 | 1 |
| cities | 154,694 | Go NewEmbeddedFinder(lite .tzb) | 1 | 0.0006 | 1 |
| cities | 154,694 | Go NewFullFinder(full .tzb) | 0 | 0.0000 | 0 |
| cities | 154,694 | Rust DefaultFinder | 1 | 0.0006 | 1 |
| cities | 154,694 | Rust EmbeddedFinder | 1 | 0.0006 | 1 |
| cities | 154,694 | tzfpy | 1 | 0.0006 | 1 |
| cities | 154,694 | tzfpy +full | 0 | 0.0000 | 0 |
| edges | 23,408 | Go NewDefaultFinder(lite .tzm) | 1 | 0.0043 | 1 |
| edges | 23,408 | Go NewFullFinder(full .tzb) | 0 | 0.0000 | 0 |
| edges | 23,408 | Rust DefaultFinder | 1 | 0.0043 | 1 |
| edges | 23,408 | tzfpy | 1 | 0.0043 | 1 |
| edges | 23,408 | tzfpy +full | 0 | 0.0000 | 0 |
| uniform | 1,000,000 | Go NewDefaultFinder(lite .tzm) | 19 | 0.0019 | 14 |
| uniform | 1,000,000 | Go NewFullFinder(full .tzb) | 0 | 0.0000 | 0 |
lite と full の Finder が異なる結果を返すのは境界付近のみです。簡略化による変位の上限は 111.2 m です。変位の詳細な表はよくある質問を参照してください。
メモリ
単位は MiB です。
Go
| 候補 | ベースライン | 初期化ピーク | 常駐 | ロード後 RSS |
|---|---|---|---|---|
| Go ランタイムの下限値 | 4.7 | 4.7 | 0.2 | 5.0 |
NewDefaultFinder(lite .tzm) | 5.1 | 41.5 | 13.1 | 41.5 |
NewEmbeddedFinder(lite .tzb をインプレースで参照) | 5.2 | 9.2 | 0.3 | 9.6 |
NewFullFinder(full .tzb) | 5.2 | 315.5 | 147.0 | 315.5 |
Rust
| 候補 | ベースライン | 初期化ピーク | 常駐 | ロード後 RSS |
|---|---|---|---|---|
| Rust ランタイムの下限値 | 5.8 | 5.9 | 0.0 | 5.9 |
DefaultFinder | 5.8 | 46.6 | 22.8 | 46.5 |
EmbeddedFinder | 5.8 | 10.3 | 0.2 | 10.4 |
EmbeddedFinder の常駐は 0.2 MiB です。データは 'static の埋め込みスライスであり、カウント機能付きアロケータが検出するのは tzf-rs 2.1 がオープン時に構築するインデックス(チャンクのスキップブロックとグループごとの緯度ストライプ)のみです。
Python
| 候補 | ベースライン | 初期化ピーク | 常駐 | ロード後 RSS |
|---|---|---|---|---|
| Python インタプリタの下限値 | 22.4 | 22.4 | n/a | 22.4 |
| tzfpy(lite) | 22.4 | 60.3 | n/a | 60.2 |
tzfpy +full | 22.4 | 38.2 | n/a | 38.2 |
観察結果
- インプレース方式はクエリレイテンシとメモリのトレードオフです。Go では初期化ピークが 41.5 MiB から 9.2 MiB に下がる一方、世界都市の中央値は 208 ns から 333 ns、境界都市の中央値は 459 ns から 1,000 ns に上がります。
- 2026-09-11 のスナップショットと比べると、インプレース方式の境界都市の値は Go の p50 で 8,959 ns から 1,000 ns、Rust の平均で 4,779.81 ns から 666.11 ns に下がりました。要因は tzf 2.1 と tzf-rs 2.1 のクエリ走査の書き直し、および
v0.0.2026-c-tzb2の 64 点チャンクです。展開方式の Finder はノイズの範囲内で変化なし、結果は同一です。 - 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 のデフォルト Finder はピーク 41.5 MiB に対して常駐 13.1 MiB、完全精度 Finder はピーク 315.5 MiB に対して常駐 147.0 MiB です。コンテナのメモリはピークに合わせ、長期運用のコストは常駐値で見積もってください。
- 言語ランタイムの下限値は異なるため(Go 4.7 MiB、Rust 5.8 MiB、Python 22.4 MiB)、言語間で合計値を比較する際はこれらを差し引く必要があります。
- lite データセットが完全精度の正解データと異なる結果を返すのは 154,694 件の世界都市のうち 1 件で、その結果も UTC オフセットは同じです。