Skip to content

Benchmarks

There are two separate benchmark setups with different purposes:

Continuous benchmark — source and results at https://github.com/ringsaturn/tz-benchmark, visualized at https://ringsaturn.github.io/tz-benchmark/. Runs automatically in GitHub Actions on each release for cross-package comparison. Because GitHub Actions runners have different hardware than a developer machine, the absolute numbers differ from local runs, but the relative trends between packages are what matters here.

Local benchmark — the tables below were measured on an Apple MacBook Pro with Apple M3 Max. These give a more representative picture of real-world latency on modern hardware.

Methodology

Each finder is initialized once and reused for all queries, matching the recommended production pattern. Queries use a representative sample of global city coordinates plus intentional edge-case border points.

Go (tzf v1.1.0)

TargetDatasetScenarioMedian (ns)p99 (ns)Throughput (ops/s)Memory (MiB)
DefaultFindertopology-simplified + preindexedge case · GetTimezoneName3000.03000.0393.5K74.70
Findertopology-simplifiededge case · GetTimezoneName2000.03000.0470.4K66.00
FullFinderfull-precision + preindexedge case · GetTimezoneName3000.03000.0395.6K421.50
Finderfull-precisionedge case · GetTimezoneName2000.03000.0475.3K412.70
DefaultFindertopology-simplified + preindexrandom world cities · GetTimezoneName1000.04000.01162.4K74.70
FuzzyFinderpreindexrandom world cities · GetTimezoneName469.81000.02128.6K8.90
Findertopology-simplifiedrandom world cities · GetTimezoneName2000.04000.0531.6K66.00
FullFinderfull-precision + preindexrandom world cities · GetTimezoneName1000.04000.01143.1K421.50
Finderfull-precisionrandom world cities · GetTimezoneName2000.05000.0468.6K412.70
DefaultFindertopology-simplified + preindexrandom world cities · GetTimezoneNames5000.09000.0208.0K74.70
FuzzyFinderpreindexrandom world cities · GetTimezoneNames462.71000.02161.2K8.90
Findertopology-simplifiedrandom world cities · GetTimezoneNames5000.08000.0211.5K66.00
FullFinderfull-precision + preindexrandom world cities · GetTimezoneNames5000.09000.0192.8K421.50

Rust (tzf-rs v1.2.0 / v1.3.0)

Topology-Simplified (bundled default)

TargetDatasetScenarioMedian (µs)Throughput (ops/s)Memory (MiB)
Findertopology-simplifiedYStripes only1.2296813,273103.30
Findertopology-simplifiedNo index6.5402152,90151.68
DefaultFindertopology-simplified + preindexYStripes only1.1383878,503125.98
DefaultFindertopology-simplified + preindexNo index2.2514444,16877.79

Full-Precision (optional full feature)

TargetDatasetScenarioMedian (µs)Throughput (ops/s)Memory (MiB)
Finder (full)full-precisionYStripes only2.0852479,570561.08
Finder (full)full-precisionNo index37.698026,527252.54
DefaultFinder (full)full-precision + preindexYStripes only1.3488741,400584.30
DefaultFinder (full)full-precision + preindexNo index11.275088,692278.63

Python (tzfpy v1.2.0)

tzfpy is a PyO3 binding over tzf-rs. The benchmark uses pytest-benchmark and measures a single get_tz() call (random coordinate, topology-simplified dataset). Results from Apple MacBook Pro with Apple M3 Max.

Index modeMedian (µs)Mean (µs)Throughput (Kops/s)Memory
Default (YStripes enabled)1.79341.8321545.8~120 MB
No YStripes (_TZFPY_DISABLE_Y_STRIPES=1)2.52132.5338394.7Not measured

Per-call overhead is comparable to the raw Rust figures; the difference from tzf-rs numbers reflects the Python → Rust FFI cost via PyO3.

Key observations

  • YStripes index brings a dramatic improvement for full-precision Finder: from 37.7 µs (no index) to 2.1 µs — an ~18× speedup. The effect is smaller but still significant for the topology-simplified dataset (6.5 µs → 1.2 µs, ~5×).
  • DefaultFinder (preindex + polygon) consistently wins for general workloads: ~1 µs median with ~75–126 MB memory, regardless of dataset.
  • FuzzyFinder (preindex only) is the fastest at ~470 ns, but only covers tiles that lie entirely within a single timezone polygon. For points near borders or outside covered tiles it returns no result rather than guessing. Only use it standalone if your workload is known to be well away from timezone borders.
  • Python (tzfpy) adds ~0.5–1 µs of PyO3 FFI overhead on top of the Rust baseline. With YStripes enabled the median sits at ~1.8 µs — well within the range of most backend API budgets.
  • Memory scales with dataset: switching from topology-simplified to full-precision in Rust adds roughly 450 MB with YStripes enabled.

Running benchmarks yourself

The continuous benchmark repo also contains scripts for running the same suite locally:

git clone https://github.com/ringsaturn/tz-benchmark
cd tz-benchmark
# Follow the README for language-specific setup and run instructions

Note that absolute numbers will vary by machine. Use the relative differences between finder types and index modes as the meaningful signal.