ベンチマーク
目的の異なる 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-11-01 のスナップショットによるもので、公開された tzf v2.1.4、tzf-rs 2.1.4、tzfpy 2.1.2(lite と +full の wheel)を対象に、いずれも tzf-dist v0.0.2026-f 上で測定されています。
測定方法
各 Finder は一度初期化され、すべてのクエリで再利用されます。これはドキュメントに記載された本番環境のパターンと一致します。クエリは 2 つのデータセットをサンプルします。170,540 件の世界都市(gt_cities.csv)と、境界に隣接する 23,408 件の地点(gt_edges.csv)で、いずれも完全精度の 2026f 正解データと照合します。精度検証では、Go において 1,000,000 点の一様乱数データセットも実行します。
メモリは候補ごとに隔離された子プロセスで測定します。以下では 4 つの列が登場し、これらは互換ではありません。
| 列 | 意味 |
|---|---|
| ベースライン | 候補を構築する前の RSS |
| 初期化ピーク | ロード中に到達する高水位(ru_maxrss)。コンテナのメモリ上限はこの値を収容できる必要があります。そうでなければ、定常状態なら収まるはずのプロセスが起動時に kill されます |
| 常駐 | 候補がクエリを処理できる状態になった後に保持しているデータ量。言語ネイティブの計測(Go は強制 GC 後の HeapAlloc、Rust はカウント機能付きグローバルアロケータ)によります。差分ではなく絶対値です。Python は Python ヒープの外にデータを保持するため n/a です |
| ロード後 RSS | クエリを処理できる状態のプロセスについて OS が報告する値。常駐 より 初期化ピーク に近くなります。メモリを解放しても RSS は縮まず、アロケータがページをカーネルに返さず再利用のために保持し続けるためです |
すべての候補で 常駐 が 3 つの値のうち最小です。ロード後 RSS が 初期化ピーク を上回る幅は最大 0.5 MiB です。
クエリレイテンシ
Apple M3 Max、2026f データセット、2026-11-01 スナップショット。
Go (tzf v2)
| ベンチマーク | ns/op | p50 (ns) | p99 (ns) | B/op | allocs/op |
|---|---|---|---|---|---|
NewDefaultFinder、世界都市 | 350.0 | 208.0 | 1500 | 0 | 0 |
NewDefaultFinder、境界都市 | 556.5 | 500.0 | 1250 | 0 | 0 |
NewEmbeddedFinder、世界都市 | 535.8 | 333.0 | 2542 | 0 | 0 |
NewEmbeddedFinder、境界都市 | 1230 | 1042 | 2750 | 0 | 0 |
NewFullFinder、世界都市 | 354.5 | 208.0 | 1708 | 0 | 0 |
NewFullFinder、境界都市 | 591.8 | 500.0 | 1542 | 0 | 0 |
3 つの Finder はいずれもアロケーションなしでクエリを処理します。
Rust (tzf-rs 2.1.4)
| ベンチマーク | ns/iter | 標準偏差 (ns) |
|---|---|---|
DefaultFinder、ランダム都市 | 225.62 | 21.17 |
DefaultFinder、ランダム境界都市 | 489.33 | 10.13 |
EmbeddedFinder、ランダム都市 | 350.34 | 110.85 |
EmbeddedFinder、ランダム境界都市 | 686.94 | 8.99 |
Python (tzfpy 2.1.2)
pytest-benchmark を使用し、1 ラウンドあたり get_tz() を 1 回呼び出します。
| ベンチマーク | 中央値 (ns) | 平均 (ns) | OPS (Kops/s) |
|---|---|---|---|
| ランダム都市、lite | 666.0 | 745.4 | 1,341.6 |
| ランダム境界都市、lite | 875.0 | 958.8 | 1,042.9 |
ランダム都市、+full | 667.0 | 810.5 | 1,233.8 |
ランダム境界都市、+full | 1,208.0 | 1,313.5 | 761.4 |
呼び出しあたりのオーバーヘッドは Rust の数値と同程度であり、差分は PyO3 を経由した Python から Rust への呼び出しコストによるものです。+full の行は tzfpy 独自のインデックスから配布される実験的な完全精度 wheel で、tzf-rs の EmbeddedFinder で full.tzb を参照します。
精度
完全精度の 2026f 正解データに対する誤答率です。「オフセット一致」は、誤答のうち UTC オフセットが同じものの件数です。
| データセット | N | 候補 | 誤答 | 誤答率 % | オフセット一致 |
|---|---|---|---|---|---|
| cities | 170,540 | Go NewDefaultFinder(lite .tzm) | 2 | 0.0012 | 1 |
| cities | 170,540 | Go NewEmbeddedFinder(lite .tzb) | 2 | 0.0012 | 1 |
| cities | 170,540 | Go NewFullFinder(full .tzb) | 0 | 0.0000 | 0 |
| cities | 170,540 | Rust DefaultFinder | 2 | 0.0012 | 1 |
| cities | 170,540 | Rust EmbeddedFinder | 2 | 0.0012 | 1 |
| cities | 170,540 | tzfpy | 2 | 0.0012 | 1 |
| cities | 170,540 | 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) | 21 | 0.0021 | 14 |
| uniform | 1,000,000 | Go NewFullFinder(full .tzb) | 0 | 0.0000 | 0 |
lite と full の Finder が異なる結果を返すのは境界付近のみです。簡略化による変位の上限は 111.2 m です。変位の詳細な表はよくある質問を参照してください。
メモリ
単位は MiB です。
Go
| 候補 | ベースライン | 初期化ピーク | 常駐 | ロード後 RSS |
|---|---|---|---|---|
| Go ランタイムの下限値 | 5.1 | 5.1 | 0.3 | 5.4 |
NewDefaultFinder(lite .tzm) | 5.5 | 44.5 | 13.3 | 44.6 |
NewEmbeddedFinder(lite .tzb をインプレースで参照) | 5.7 | 9.6 | 0.4 | 10.1 |
NewFullFinder(full .tzb) | 5.7 | 266.7 | 148.9 | 266.9 |
Rust
| 候補 | ベースライン | 初期化ピーク | 常駐 | ロード後 RSS |
|---|---|---|---|---|
| Rust ランタイムの下限値 | 5.8 | 5.8 | 0.0 | 5.9 |
DefaultFinder | 5.8 | 44.5 | 23.1 | 44.3 |
EmbeddedFinder | 5.8 | 10.2 | 0.2 | 10.2 |
EmbeddedFinder の常駐は 0.2 MiB です。データは 'static の埋め込みスライスであり、カウント機能付きアロケータが検出するのは tzf-rs 2.1 がオープン時に構築するインデックス(チャンクのスキップブロックとグループごとの緯度ストライプ)のみです。
Python
| 候補 | ベースライン | 初期化ピーク | 常駐 | ロード後 RSS |
|---|---|---|---|---|
| Python インタプリタの下限値 | 22.5 | 22.5 | n/a | 22.5 |
| tzfpy(lite) | 22.5 | 61.3 | n/a | 61.2 |
tzfpy +full | 22.5 | 38.8 | n/a | 38.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
+fullwheel の境界都市の中央値は 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 のデフォルト Finder はピーク 44.5 MiB に対して常駐 13.3 MiB、完全精度 Finder はピーク 266.7 MiB に対して常駐 148.9 MiB です。コンテナのメモリはピークに合わせ、長期運用のコストは常駐値で見積もってください。
- 言語ランタイムの下限値は異なるため(Go 5.1 MiB、Rust 5.8 MiB、Python 22.5 MiB)、言語間で合計値を比較する際はこれらを差し引く必要があります。
- lite データセットが完全精度の正解データと異なる結果を返すのは 170,540 件の世界都市のうち 2 件で、ハーネスはそのうち 1 件を UTC オフセットが同じ結果として数えています。