Benchmarks run on 2026-02-22, comparing the Go port against the original Python (Skyfield) implementation.
- Machine: macOS (Darwin 25.3.0)
- Input data: 200-year time series centered on 2026-01-19
- Python: Anaconda Python with Skyfield 1.53, NumPy (vectorized, multi-core)
- Go: Compiled binary using goeph library, single-threaded
Small-scale test with ±10 day window around reference date.
- Past: 11 days (2026-01-08 to 2026-01-19) — 15,841 rows
- Future: 20 days (2026-01-09 to 2026-01-29) — 28,801 rows
- Total: 31 days, 44,642 rows, 42 columns each
| Metric | Go | Python | Ratio |
|---|---|---|---|
| Wall clock | 1.47s | 15.66s | 10.7x faster |
| User CPU | 1.41s | 53.94s | 38x less |
| System CPU | 0.19s | 51.54s | 271x less |
| Total CPU | 1.60s | 105.48s | 66x less |
| CPU utilization | 109% | 673% | — |
Go:
./generate_data_go 1.41s user 0.19s system 109% cpu 1.465 total
Python:
/opt/anaconda3/bin/python main.py 53.94s user 51.54s system 673% cpu 15.659 total
| Component | Max Error | Notes |
|---|---|---|
| Planets (Sun, Moon, Mercury–Pluto) | < 1.78e-7° | Moon lon worst; outer planets < 3e-10° |
| Galactic Center | < 1.7e-13° | Effectively exact |
| Lunar Nodes | < 2.5e-11° | Effectively exact |
| Ground Locations (all 6) | < 0.018° | IAU 2000A nutation + ΔT model |
| Satellites | up to 180° | SGP4 implementation differences |
Detailed column-level accuracy (Benchmark 1, 44,642 rows):
| Column | Max Error | Mean Error | Median Error |
|---|---|---|---|
| sun_lat_deg | 3.85e-13 | 9.20e-14 | 9.90e-14 |
| sun_lon_deg | 8.27e-09 | 3.85e-09 | 3.63e-09 |
| moon_lat_deg | 1.25e-08 | 3.31e-09 | 3.14e-09 |
| moon_lon_deg | 1.78e-07 | 4.95e-08 | 3.67e-08 |
| mercury_lat_deg | 2.72e-10 | 1.70e-10 | 1.85e-10 |
| mercury_lon_deg | 1.44e-08 | 6.23e-09 | 5.77e-09 |
| venus_lat_deg | 1.53e-10 | 1.02e-10 | 1.05e-10 |
| venus_lon_deg | 1.03e-08 | 4.75e-09 | 4.48e-09 |
| mars_lat_deg | 3.10e-11 | 1.81e-11 | 1.80e-11 |
| mars_lon_deg | 6.40e-09 | 2.93e-09 | 2.76e-09 |
| jupiter_lat_deg | 1.54e-11 | 7.96e-12 | 7.73e-12 |
| jupiter_lon_deg | 9.75e-10 | 4.93e-10 | 4.76e-10 |
| saturn_lat_deg | 1.86e-11 | 1.03e-11 | 1.01e-11 |
| saturn_lon_deg | 7.76e-10 | 3.10e-10 | 2.80e-10 |
| uranus_lat_deg | 2.57e-12 | 1.15e-12 | 1.07e-12 |
| uranus_lon_deg | 7.24e-11 | 5.52e-11 | 5.77e-11 |
| neptune_lat_deg | 3.79e-12 | 2.07e-12 | 2.02e-12 |
| neptune_lon_deg | 2.16e-10 | 8.05e-11 | 7.11e-11 |
| pluto_lat_deg | 1.25e-11 | 4.40e-12 | 3.84e-12 |
| pluto_lon_deg | 2.60e-10 | 1.20e-10 | 1.13e-10 |
| gc_lat_deg | 2.40e-14 | 2.04e-14 | 2.22e-14 |
| gc_lon_deg | 1.71e-13 | 1.11e-13 | 1.14e-13 |
| north_node_lon_deg | 2.47e-11 | 8.79e-12 | 5.68e-14 |
| south_node_lon_deg | 2.48e-11 | 8.79e-12 | 1.14e-13 |
| loc_ni_lat_deg | 1.91e-03 | 8.22e-04 | 6.97e-04 |
| loc_ni_lon_deg | 1.01e-02 | 4.58e-03 | 3.63e-03 |
| loc_chicago_lat_deg | 4.98e-03 | 2.35e-03 | 2.37e-03 |
| loc_chicago_lon_deg | 1.02e-02 | 6.51e-03 | 7.35e-03 |
| loc_london_lat_deg | 5.42e-03 | 2.76e-03 | 2.86e-03 |
| loc_london_lon_deg | 1.79e-02 | 7.99e-03 | 8.75e-03 |
| loc_cushing_lat_deg | 4.65e-03 | 2.06e-03 | 2.04e-03 |
| loc_cushing_lon_deg | 9.92e-03 | 5.95e-03 | 6.48e-03 |
| loc_ny_lat_deg | 4.92e-03 | 2.29e-03 | 2.30e-03 |
| loc_ny_lon_deg | 1.02e-02 | 6.39e-03 | 7.19e-03 |
| loc_mumbai_lat_deg | 3.46e-03 | 1.19e-03 | 1.01e-03 |
| loc_mumbai_lon_deg | 9.66e-03 | 5.01e-03 | 4.39e-03 |
Full 18-year historical range (2008–2026), ±365 day future window.
- Past: 18 years (2008-01-02 to 2026-01-19) — 9,492,481 rows
- Future: 730 days (2025-01-19 to 2027-01-19) — 1,051,201 rows
- Total: ~18.5 years of data, ~10.5M rows, 42 columns each
| Metric | Go | Python | Ratio |
|---|---|---|---|
| Wall clock | 3m 15s | 38m 37s | 11.9x faster |
| User CPU | 174.24s | 8,033.18s | 46x less |
| System CPU | 32.02s | 6,129.17s | 191x less |
| Total CPU | 206.26s | 14,162.35s | 69x less |
| CPU utilization | 105% | 611% | — |
Go:
./generate_data_go 174.24s user 32.02s system 105% cpu 3:15.04 total
Python:
/opt/anaconda3/bin/python main.py 8033.18s user 6129.17s system 611% cpu 38:37.11 total
| Metric | Go | Python |
|---|---|---|
| Rows/second (wall clock) | ~54,000 | ~4,500 |
| Rows/CPU-second | ~51,000 | ~740 |
Benchmark 2 covers near-present data (2008–2027), where ΔT and leap second corrections are small. Expected accuracy matches Benchmark 1:
| Component | Max Error | Notes |
|---|---|---|
| Planets (Sun, Moon, Mercury–Pluto) | < 1.8e-7° | Same as Benchmark 1 |
| Galactic Center | < 1.7e-13° | Effectively exact |
| Lunar Nodes | < 2.5e-11° | Effectively exact |
| Ground Locations (all 6) | < 0.018° | Near-present ΔT error is small |
| Satellites | varies | SGP4 divergence grows with time from TLE epoch |
200-year range at 1-hour intervals. This benchmark includes all 6 location columns.
- Range: 200 years (1926-02-13 to 2125-12-26) at 1-hour intervals
- Total: 1,752,001 rows, 42 columns each
| Metric | Go | Python | Ratio |
|---|---|---|---|
| Wall clock | 27.4s | 6m 31s | 14.3x faster |
| User CPU | 26.10s | 1,107.77s | 42x less |
| System CPU | 2.59s | 1,282.68s | 495x less |
| Total CPU | 28.69s | 2,390.45s | 83x less |
| CPU utilization | 104% | 611% | — |
Go:
./generate_data_go 26.10s user 2.59s system 104% cpu 27.365 total
Python:
python main.py 1107.77s user 1282.68s system 611% cpu 6:30.94 total
| Metric | Go | Python |
|---|---|---|
| Rows/second (wall clock) | ~63,900 | ~4,500 |
| Rows/CPU-second | ~61,100 | ~733 |
| Component | Max Error | Notes |
|---|---|---|
| Planets (Sun, Moon, Mercury–Pluto) | < 3.97e-7° | Moon lon worst; outer planets < 2e-9° |
| Galactic Center | < 2.8e-13° | Effectively exact |
| Lunar Nodes | < 2.5e-11° | Effectively exact |
| Ground Locations (all 6, incl. London & Cushing) | < 0.032° | IAU 2000A nutation + ΔT model |
| Satellites | up to 180° | Expected: SGP4 meaningless 100yr from TLE epoch |
Detailed column-level accuracy (Benchmark 3, 1,752,001 rows):
| Column | Max Error | Mean Error | Median Error |
|---|---|---|---|
| sun_lat_deg | 6.16e-12 | 1.13e-12 | 6.90e-13 |
| sun_lon_deg | 1.96e-08 | 1.20e-08 | 1.34e-08 |
| moon_lat_deg | 2.80e-08 | 9.57e-09 | 8.50e-09 |
| moon_lon_deg | 3.97e-07 | 1.63e-07 | 1.72e-07 |
| mercury_lat_deg | 6.84e-09 | 1.57e-09 | 1.29e-09 |
| mercury_lon_deg | 4.09e-08 | 1.49e-08 | 1.33e-08 |
| venus_lat_deg | 4.59e-09 | 6.90e-10 | 4.44e-10 |
| venus_lon_deg | 2.49e-08 | 1.27e-08 | 1.30e-08 |
| mars_lon_deg | 1.54e-08 | 6.91e-09 | 6.83e-09 |
| jupiter_lon_deg | 4.72e-09 | 1.63e-09 | 1.39e-09 |
| saturn_lon_deg | 2.45e-09 | 8.43e-10 | 7.22e-10 |
| uranus_lon_deg | 1.25e-09 | 4.09e-10 | 3.56e-10 |
| neptune_lon_deg | 7.37e-10 | 2.65e-10 | 2.32e-10 |
| pluto_lon_deg | 7.83e-10 | 2.14e-10 | 1.88e-10 |
| gc_lat_deg | 3.38e-14 | 1.74e-14 | 1.87e-14 |
| gc_lon_deg | 2.84e-13 | 1.80e-13 | 1.71e-13 |
| north_node_lon_deg | 2.51e-11 | 5.58e-12 | 0.00e+00 |
| loc_ni_lon_deg | 1.67e-02 | 6.74e-03 | 6.02e-03 |
| loc_chicago_lon_deg | 2.35e-02 | 7.50e-03 | 6.58e-03 |
| loc_london_lon_deg | 3.15e-02 | 8.49e-03 | 7.41e-03 |
| loc_cushing_lon_deg | 2.12e-02 | 7.20e-03 | 6.35e-03 |
| loc_ny_lon_deg | 2.30e-02 | 7.43e-03 | 6.53e-03 |
| loc_mumbai_lon_deg | 1.79e-02 | 6.83e-03 | 6.09e-03 |
Location summary: Max error dropped from 0.49° to 0.032° across the full 200-year range. Mean error is 0.005°. The residual comes from yearly ΔT interpolation vs Skyfield's daily IERS data.
248-year range at 1-week (168h) intervals, constrained by DE440s ephemeris limits (1849–2150).
- Past: 11 days (2026-01-08 to 2026-01-19) — 2 rows (weekly granularity)
- Future: 248 years (1902-02-19 to 2149-12-17) — 12,932 rows
- Total: 248 years of weekly data, 12,934 rows, 42 columns each
| Metric | Go | Python | Ratio |
|---|---|---|---|
| Wall clock | 1.50s | 7.06s | 4.7x faster |
| User CPU | 1.00s | 24.61s | 25x less |
| System CPU | 0.13s | 14.01s | 108x less |
| Total CPU | 1.13s | 38.62s | 34x less |
| CPU utilization | 75% | 547% | — |
Go:
./generate_data_go 1.00s user 0.13s system 75% cpu 1.504 total
Python:
python main.py 24.61s user 14.01s system 547% cpu 7.055 total
| Component | Max Error | Notes |
|---|---|---|
| Planets (Sun, Moon, Mercury–Pluto) | < 3.92e-7° | Consistent with Benchmark 3 |
| Galactic Center | < 2.8e-13° | Effectively exact |
| Lunar Nodes | < 4.8e-13° | Effectively exact |
| Ground Locations (all 6, incl. London & Cushing) | < 0.031° | IAU 2000A nutation + ΔT model |
| Satellites | up to 180° | Expected: SGP4 meaningless ±124yr from TLE epoch |
Detailed column-level accuracy (Benchmark 4, 12,932 rows):
| Column | Max Error | Mean Error | Median Error |
|---|---|---|---|
| sun_lat_deg | 6.96e-12 | 1.36e-12 | 8.43e-13 |
| sun_lon_deg | 1.94e-08 | 1.20e-08 | 1.34e-08 |
| moon_lat_deg | 2.72e-08 | 9.56e-09 | 8.43e-09 |
| moon_lon_deg | 3.92e-07 | 1.63e-07 | 1.72e-07 |
| mercury_lat_deg | 6.70e-09 | 1.56e-09 | 1.28e-09 |
| mercury_lon_deg | 4.02e-08 | 1.49e-08 | 1.33e-08 |
| venus_lat_deg | 4.45e-09 | 6.90e-10 | 4.45e-10 |
| venus_lon_deg | 2.46e-08 | 1.27e-08 | 1.30e-08 |
| mars_lon_deg | 1.53e-08 | 6.91e-09 | 6.84e-09 |
| jupiter_lon_deg | 4.68e-09 | 1.62e-09 | 1.39e-09 |
| saturn_lon_deg | 2.43e-09 | 8.47e-10 | 7.26e-10 |
| uranus_lon_deg | 1.24e-09 | 4.10e-10 | 3.57e-10 |
| neptune_lon_deg | 7.29e-10 | 2.59e-10 | 2.26e-10 |
| pluto_lon_deg | 7.75e-10 | 2.01e-10 | 1.73e-10 |
| gc_lat_deg | 3.38e-14 | 1.75e-14 | 1.87e-14 |
| gc_lon_deg | 2.84e-13 | 1.80e-13 | 1.71e-13 |
| north_node_lon_deg | 4.83e-13 | 5.81e-14 | 0.00e+00 |
| loc_ni_lat_deg | 2.14e-04 | 7.41e-05 | 7.45e-05 |
| loc_ni_lon_deg | 1.67e-02 | 7.31e-03 | 5.94e-03 |
| loc_chicago_lat_deg | 5.14e-03 | 3.32e-03 | 3.27e-03 |
| loc_chicago_lon_deg | 1.35e-02 | 6.22e-03 | 6.25e-03 |
| loc_london_lat_deg | 2.96e-03 | 1.54e-03 | 1.56e-03 |
| loc_london_lon_deg | 3.14e-02 | 1.00e-02 | 9.79e-03 |
| loc_cushing_lat_deg | 4.88e-03 | 2.95e-03 | 2.92e-03 |
| loc_cushing_lon_deg | 1.26e-02 | 6.16e-03 | 6.46e-03 |
| loc_ny_lat_deg | 5.01e-03 | 3.12e-03 | 3.01e-03 |
| loc_ny_lon_deg | 1.58e-02 | 6.38e-03 | 6.10e-03 |
| loc_mumbai_lat_deg | 3.69e-03 | 1.72e-03 | 1.60e-03 |
| loc_mumbai_lon_deg | 1.37e-02 | 6.21e-03 | 6.27e-03 |
Note: The reduced speedup (4.7x vs ~14x) at this small scale reflects Python's startup and initialization overhead becoming a larger fraction of total time. With only ~13k rows, both implementations finish quickly and the per-row advantage of Go is partially masked by fixed costs (ephemeris loading, file I/O).
200-year range at 1-day intervals.
- Past: 11 days (2026-01-08 to 2026-01-19) — 12 rows (daily granularity)
- Future: 200 years (1926-02-13 to 2125-12-26) — 73,001 rows
- Total: 200 years of daily data, 73,013 rows, 42 columns each
| Metric | Go | Python | Ratio |
|---|---|---|---|
| Wall clock | 2.52s | 21.0s | 8.3x faster |
| User CPU | 1.89s | 64.69s | 34x less |
| System CPU | 0.24s | 59.82s | 249x less |
| Total CPU | 2.13s | 124.51s | 58x less |
| CPU utilization | 84% | 592% | — |
Go:
./generate_data_go 1.89s user 0.24s system 84% cpu 2.523 total
Python:
python main.py 64.69s user 59.82s system 592% cpu 21.022 total
| Metric | Go | Python |
|---|---|---|
| Rows/second (wall clock) | ~28,900 | ~3,500 |
| Rows/CPU-second | ~34,300 | ~590 |
| Component | Max Error | Notes |
|---|---|---|
| Planets (Sun, Moon, Mercury–Pluto) | < 3.95e-7° | Consistent with Benchmarks 3–4 |
| Galactic Center | < 2.8e-13° | Effectively exact |
| Lunar Nodes | < 4.8e-13° | Effectively exact |
| Ground Locations (all 6) | < 0.031° | IAU 2000A nutation + ΔT model |
| Satellites | up to 180° | Expected: SGP4 meaningless ±100yr from TLE epoch |
Detailed column-level accuracy (Benchmark 5, 73,001 rows):
| Column | Max Error | Mean Error | Median Error |
|---|---|---|---|
| sun_lat_deg | 6.12e-12 | 1.13e-12 | 6.90e-13 |
| sun_lon_deg | 1.94e-08 | 1.20e-08 | 1.34e-08 |
| moon_lat_deg | 2.75e-08 | 9.57e-09 | 8.46e-09 |
| moon_lon_deg | 3.95e-07 | 1.62e-07 | 1.72e-07 |
| mercury_lat_deg | 6.79e-09 | 1.57e-09 | 1.29e-09 |
| mercury_lon_deg | 4.06e-08 | 1.49e-08 | 1.34e-08 |
| venus_lat_deg | 4.54e-09 | 6.90e-10 | 4.45e-10 |
| venus_lon_deg | 2.47e-08 | 1.27e-08 | 1.30e-08 |
| mars_lat_deg | 1.58e-09 | 1.84e-10 | 1.16e-10 |
| mars_lon_deg | 1.53e-08 | 6.91e-09 | 6.83e-09 |
| jupiter_lat_deg | 6.71e-11 | 2.37e-11 | 2.07e-11 |
| jupiter_lon_deg | 4.69e-09 | 1.63e-09 | 1.39e-09 |
| saturn_lat_deg | 7.22e-11 | 2.31e-11 | 1.93e-11 |
| saturn_lon_deg | 2.43e-09 | 8.43e-10 | 7.23e-10 |
| uranus_lat_deg | 1.25e-11 | 3.46e-12 | 2.73e-12 |
| uranus_lon_deg | 1.24e-09 | 4.09e-10 | 3.56e-10 |
| neptune_lat_deg | 1.68e-11 | 4.99e-12 | 4.05e-12 |
| neptune_lon_deg | 7.31e-10 | 2.65e-10 | 2.32e-10 |
| pluto_lat_deg | 1.47e-10 | 3.73e-11 | 3.07e-11 |
| pluto_lon_deg | 7.76e-10 | 2.14e-10 | 1.88e-10 |
| gc_lat_deg | 3.38e-14 | 1.75e-14 | 1.87e-14 |
| gc_lon_deg | 2.84e-13 | 1.80e-13 | 1.71e-13 |
| north_node_lon_deg | 4.83e-13 | 5.12e-14 | 0.00e+00 |
| south_node_lon_deg | 4.83e-13 | 5.13e-14 | 0.00e+00 |
| loc_ni_lat_deg | 2.15e-04 | 7.39e-05 | 7.37e-05 |
| loc_ni_lon_deg | 1.67e-02 | 7.44e-03 | 6.22e-03 |
| loc_chicago_lat_deg | 5.14e-03 | 3.32e-03 | 3.27e-03 |
| loc_chicago_lon_deg | 1.36e-02 | 6.30e-03 | 6.36e-03 |
| loc_london_lat_deg | 2.96e-03 | 1.54e-03 | 1.56e-03 |
| loc_london_lon_deg | 3.15e-02 | 1.02e-02 | 1.00e-02 |
| loc_cushing_lat_deg | 4.88e-03 | 2.95e-03 | 2.92e-03 |
| loc_cushing_lon_deg | 1.27e-02 | 6.22e-03 | 6.54e-03 |
| loc_ny_lat_deg | 5.01e-03 | 3.12e-03 | 3.01e-03 |
| loc_ny_lon_deg | 1.59e-02 | 6.49e-03 | 6.26e-03 |
| loc_mumbai_lat_deg | 3.69e-03 | 1.72e-03 | 1.60e-03 |
| loc_mumbai_lon_deg | 1.37e-02 | 6.31e-03 | 6.41e-03 |
Location summary: Max location error 0.031° (London longitude), mean 0.005°. Consistent with Benchmarks 3–4 — the ΔT residual is stable regardless of sampling interval.
Two fixes were applied to the Go implementation (reflected in Benchmark 3–5 numbers above):
- IAU 2000A nutation (30 terms default; full 678 luni-solar + 687 planetary terms available via
NutationFull) — adds GAST (true sidereal time) and the nutation rotation matrix N^T to the geodetic→ICRF pipeline. - ΔT time scale model — proper UTC → TT (via leap second table) → UT1 (via Skyfield-extracted ΔT table with linear interpolation) conversion, replacing the previous UT1 ≈ UTC approximation.
The 0.77° location error from Benchmark 4 was not from missing nutation (which contributes only ~0.002°) but from a time scale mismatch:
- Go treated UT1 ≈ UTC — the Julian Date was passed directly as UT1
- Skyfield converts UTC → TT (via leap seconds + 32.184s) → UT1 (via ΔT model)
The difference grows with time distance from the present:
| Date | ΔT (TT-UT1) | UT1-UTC | Resulting rotation error |
|---|---|---|---|
| 1902 | 0.8s | 41.4s | ~0.17° |
| 2000 | 63.8s | 0.4s | ~0.002° |
| 2026 | 69.1s | 0.1s | ~0.0004° |
| 2100 | 94.9s | -25.7s | ~0.11° |
| 2149 | 144.7s | -75.5s | ~0.32° |
The Go implementation now uses:
- Leap second table: 28 entries (1972-01-01 to 2017-01-01) from IERS Bulletin C
- ΔT table: 401 entries at yearly intervals (1800–2200), extracted from Skyfield (IERS data + Morrison-Stephenson-Hohenkerk 2020 model)
- Linear interpolation between yearly ΔT entries, flat extrapolation beyond table bounds
No measurable overhead from the ΔT table lookup or nutation computation. Benchmark 3 actually got faster (28.1s vs 32.2s previously), likely due to reduced system CPU from other improvements.
Benchmark 3 (200yr hourly, 1,752,001 rows):
| Component | Before (no ΔT) | After (with ΔT) | Improvement |
|---|---|---|---|
| Planets | < 4.81e-3° | < 3.97e-7° | ~12,000x |
| Galactic Center | < 2.8e-13° | < 2.8e-13° | Same (exact) |
| Lunar Nodes | < 1.65e-5° | < 2.5e-11° | ~660,000x |
| Ground Locations | < 0.49° | < 0.032° | 15x |
| Satellites | up to 180° | up to 180° | N/A |
Benchmark 4 (248yr weekly, 12,932 rows):
| Component | Before (no ΔT) | After (with ΔT) | Improvement |
|---|---|---|---|
| Planets | < 4.81e-3° | < 3.92e-7° | ~12,000x |
| Galactic Center | < 2.8e-13° | < 2.8e-13° | Same (exact) |
| Lunar Nodes | < 1.65e-5° | < 4.8e-13° | ~34,000,000x |
| Ground Locations | < 0.77° | < 0.031° | 25x |
| Satellites | up to 180° | up to 180° | N/A |
Location error breakdown by site (Benchmark 4, longitude — the dominant component):
| Location | Max lon error (before) | Max lon error (after) |
|---|---|---|
| Null Island (0°N, 0°E) | ~0.77° | 0.017° |
| Chicago (41.9°N) | ~0.63° | 0.014° |
| London (51.5°N) | ~0.75° | 0.031° |
| Cushing (36.0°N) | ~0.60° | 0.013° |
| NY (40.7°N) | ~0.65° | 0.016° |
| Mumbai (19.1°N) | ~0.63° | 0.014° |
The residual 0.031° (at London, the highest-latitude location) comes from yearly ΔT interpolation vs Skyfield's daily IERS data. Mean location error is 0.005°.
| Scale | Rows | Go | Python | Speedup |
|---|---|---|---|---|
| Small | 44,642 | 1.5s | 15.7s | 10.7x |
| Full | ~10,500,000 | 3m 15s | 38m 37s | 11.9x |
| 200yr hourly | 1,752,001 | 27.4s | 6m 31s | 14.3x |
| 200yr daily | 73,013 | 2.5s | 21.0s | 8.3x |
| 248yr weekly | 12,934 | 1.5s | 7.1s | 4.7x |
The speedup is ~11–14x at scale (>44k rows) and narrows to ~4.7–8.3x for smaller datasets where fixed costs dominate.
Nutation mode note: These benchmarks use the default NutationStandard (30 terms). Skyfield defaults to the full IAU 2000A series (678 luni-solar + 687 planetary terms) but also provides an iau2000b() variant (77 luni-solar, 0 planetary) for faster computation. With goeph's NutationFull (matching Skyfield's default precision), end-to-end workloads that include altaz/geodetic computations are ~5-7x slower, reducing the speedup to ~2-3x vs Skyfield. Position-only workloads (Observe, Apparent, coordinate conversions) are unaffected by the nutation setting and retain the full ~14x speedup. The ~1 arcsec difference between the two modes is negligible for most applications — see Nutation precision.
- ~12x wall-clock speedup across all benchmarks — Go consistently finishes in a fraction of Python's time.
- ~70x less total CPU time — Python uses ~600% CPU (spreading work across ~6 cores via NumPy), yet Go on a single core (~105%) finishes 12x faster wall-clock.
- System CPU: Python consumes disproportionate system time — reflecting heavy overhead from NumPy's memory management, multiprocessing coordination, and Skyfield's internal data handling.
- No interpreter overhead — Go compiles to native machine code; Python has per-operation interpreter dispatch.
- In-memory ephemeris — The Go SPK reader loads all Chebyshev coefficients into memory once at startup. Skyfield also caches, but through more abstraction layers.
- Minimal allocation — Go uses fixed-size arrays (
[3]float64) for positions; Python/NumPy creates intermediate array objects. - No multi-core coordination overhead — Go runs single-threaded with no synchronization cost. Python's multi-core approach (via NumPy) adds coordination overhead that exceeds the parallelism benefit for this workload.
- Direct binary I/O — Go reads the DAF file with
encoding/binary; Python goes through multiple library layers (Skyfield → jplephem → NumPy → file I/O).
The speedup comes with no meaningful loss in accuracy. Core position computations (SPK Chebyshev evaluation, apparent positions) are identical in both Go and Python — the same math, same polynomials, same coefficients. Nutation mode does not affect these computations.
| Component | Max Error (Bench 1–2, near present) | Max Error (Bench 3–5, 200–248yr) |
|---|---|---|
| Planets (Sun, Moon, Mercury–Pluto) | < 1.78e-7° | < 3.97e-7° |
| Galactic Center | < 1.7e-13° (exact) | < 2.8e-13° (exact) |
| Lunar Nodes | < 2.5e-11° (exact) | < 2.5e-11° (exact) |
| Ground Locations | < 0.018° | < 0.032° |
| Satellites | varies | up to 180° |
Note: Full row-by-row verification was performed across all overlapping timestamps with zero timestamp mismatches. The Go implementation uses IAU 2000A nutation (30 terms default, full 1365 terms available via NutationFull) and proper ΔT time scale conversion (leap second table + Skyfield-extracted ΔT table). The core math (Chebyshev evaluation, coordinate transforms, precession, nutation) matches Skyfield exactly. The remaining location error (0.018° near-present, 0.032° over 200+ years) is from yearly ΔT interpolation vs Skyfield's daily IERS data.