pg_orrery/bench/benchmark_spgist_only.sql
Ryan Malloy 845aeee3a5 Add pass prediction guide, operator reference, and benchmarks
New docs:
- guides/pass-prediction.mdx: two-stage workflow (SP-GiST filter
  then SGP4 propagation), query window comparison tabs, GiST/SP-GiST
  coexistence example
- reference/operators-gist.mdx: &? operator signature and description,
  observer_window type reference, SP-GiST operator class docs with
  eccentricity/HEO limitation aside

Benchmarks on 14,376 CelesTrak active satellites:
- SP-GiST index: 2,344 kB, builds in 19 ms
- GiST index: 2,904 kB, builds in 45 ms
- Consistency: 0 false negatives, 0 false positives
- At 14k catalog size, seqscan (~6 ms) still beats index scan (~8 ms)
  due to low page count; cross-over expected at ~100k objects
2026-02-17 21:30:57 -07:00

265 lines
8.0 KiB
SQL

-- ============================================================
-- SP-GiST Orbital Trie Benchmark (Phase 3)
-- CelesTrak active catalog, ~14k satellites
-- GiST comparison omitted (known crash in gist_tle_picksplit)
-- ============================================================
\timing on
-- ============================================================
-- 1. Catalog distribution analysis
-- ============================================================
SELECT
CASE
WHEN tle_perigee(tle) < 2000 THEN 'LEO (<2000km)'
WHEN tle_perigee(tle) < 20000 THEN 'MEO (2000-20000km)'
WHEN tle_perigee(tle) < 34000 THEN 'GEO-transfer'
ELSE 'GEO/HEO (>34000km)'
END AS regime,
count(*) AS n,
round(100.0 * count(*) / (SELECT count(*) FROM bench_catalog), 1) AS pct
FROM bench_catalog
GROUP BY 1
ORDER BY 2 DESC;
-- ============================================================
-- 2. Create SP-GiST index
-- ============================================================
\echo '--- CREATE SP-GiST INDEX ---'
CREATE INDEX bench_spgist ON bench_catalog USING spgist (tle tle_spgist_ops);
SELECT pg_size_pretty(pg_relation_size('bench_spgist'::regclass)) AS spgist_size;
-- ============================================================
-- 3. Benchmark: 2h window, Eagle Idaho (43.7N) — RAAN active
-- ============================================================
\echo '--- BENCHMARK 1: 2h window, Eagle Idaho, 10 deg min_el ---'
-- 3a. Sequential scan (baseline)
SET enable_indexscan = off;
SET enable_bitmapscan = off;
\echo 'Sequential scan:'
EXPLAIN ANALYZE
SELECT count(*) AS candidates
FROM bench_catalog
WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'),
'2026-02-17 02:00:00+00'::timestamptz,
'2026-02-17 04:00:00+00'::timestamptz,
10.0
)::observer_window;
RESET enable_indexscan;
RESET enable_bitmapscan;
-- 3b. SP-GiST index scan
SET enable_seqscan = off;
\echo 'SP-GiST index scan:'
EXPLAIN ANALYZE
SELECT count(*) AS candidates
FROM bench_catalog
WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'),
'2026-02-17 02:00:00+00'::timestamptz,
'2026-02-17 04:00:00+00'::timestamptz,
10.0
)::observer_window;
RESET enable_seqscan;
-- ============================================================
-- 4. Benchmark: 24h window, Eagle Idaho — RAAN bypassed
-- ============================================================
\echo '--- BENCHMARK 2: 24h window, Eagle Idaho, 10 deg min_el ---'
SET enable_indexscan = off;
SET enable_bitmapscan = off;
\echo 'Sequential scan:'
SELECT count(*) AS seqscan_24h
FROM bench_catalog
WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'),
'2026-02-17 00:00:00+00'::timestamptz,
'2026-02-18 00:00:00+00'::timestamptz,
10.0
)::observer_window;
RESET enable_indexscan;
RESET enable_bitmapscan;
SET enable_seqscan = off;
\echo 'SP-GiST index scan:'
SELECT count(*) AS spgist_24h
FROM bench_catalog
WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'),
'2026-02-17 00:00:00+00'::timestamptz,
'2026-02-18 00:00:00+00'::timestamptz,
10.0
)::observer_window;
RESET enable_seqscan;
-- ============================================================
-- 5. Benchmark: 2h window, Equatorial observer
-- ============================================================
\echo '--- BENCHMARK 3: 2h window, Equator, 10 deg min_el ---'
SET enable_indexscan = off;
SET enable_bitmapscan = off;
\echo 'Sequential scan:'
SELECT count(*) AS seqscan_equator
FROM bench_catalog
WHERE tle &? ROW(
observer('0.0N 0.0E 0m'),
'2026-02-17 02:00:00+00'::timestamptz,
'2026-02-17 04:00:00+00'::timestamptz,
10.0
)::observer_window;
RESET enable_indexscan;
RESET enable_bitmapscan;
SET enable_seqscan = off;
\echo 'SP-GiST index scan:'
SELECT count(*) AS spgist_equator
FROM bench_catalog
WHERE tle &? ROW(
observer('0.0N 0.0E 0m'),
'2026-02-17 02:00:00+00'::timestamptz,
'2026-02-17 04:00:00+00'::timestamptz,
10.0
)::observer_window;
RESET enable_seqscan;
-- ============================================================
-- 6. Benchmark: High min_el (45 deg)
-- ============================================================
\echo '--- BENCHMARK 4: 2h window, Eagle Idaho, 45 deg min_el ---'
SET enable_indexscan = off;
SET enable_bitmapscan = off;
\echo 'Sequential scan:'
SELECT count(*) AS seqscan_45deg
FROM bench_catalog
WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'),
'2026-02-17 02:00:00+00'::timestamptz,
'2026-02-17 04:00:00+00'::timestamptz,
45.0
)::observer_window;
RESET enable_indexscan;
RESET enable_bitmapscan;
SET enable_seqscan = off;
\echo 'SP-GiST index scan:'
SELECT count(*) AS spgist_45deg
FROM bench_catalog
WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'),
'2026-02-17 02:00:00+00'::timestamptz,
'2026-02-17 04:00:00+00'::timestamptz,
45.0
)::observer_window;
RESET enable_seqscan;
-- ============================================================
-- 7. Consistency check
-- ============================================================
\echo '--- CONSISTENCY CHECK ---'
SET enable_indexscan = off;
SET enable_bitmapscan = off;
CREATE TEMPORARY TABLE seq_results AS
SELECT norad_id FROM bench_catalog
WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'),
'2026-02-17 02:00:00+00'::timestamptz,
'2026-02-17 04:00:00+00'::timestamptz,
10.0
)::observer_window;
RESET enable_indexscan;
RESET enable_bitmapscan;
SET enable_seqscan = off;
CREATE TEMPORARY TABLE idx_results AS
SELECT norad_id FROM bench_catalog
WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'),
'2026-02-17 02:00:00+00'::timestamptz,
'2026-02-17 04:00:00+00'::timestamptz,
10.0
)::observer_window;
RESET enable_seqscan;
SELECT count(*) AS in_seq_not_idx FROM seq_results
WHERE norad_id NOT IN (SELECT norad_id FROM idx_results);
SELECT count(*) AS in_idx_not_seq FROM idx_results
WHERE norad_id NOT IN (SELECT norad_id FROM seq_results);
DROP TABLE seq_results, idx_results;
-- ============================================================
-- 8. Pruning summary
-- ============================================================
\echo '--- PRUNING SUMMARY ---'
SELECT
'2h/Eagle/10deg' AS scenario,
(SELECT count(*) FROM bench_catalog) AS catalog_size,
count(*) AS candidates,
round(100.0 * count(*) / (SELECT count(*) FROM bench_catalog), 1) AS candidate_pct,
round(100.0 * (1.0 - count(*)::numeric / (SELECT count(*) FROM bench_catalog)), 1) AS pruning_pct
FROM bench_catalog
WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'),
'2026-02-17 02:00:00+00'::timestamptz,
'2026-02-17 04:00:00+00'::timestamptz,
10.0
)::observer_window
UNION ALL
SELECT
'24h/Eagle/10deg',
(SELECT count(*) FROM bench_catalog),
count(*),
round(100.0 * count(*) / (SELECT count(*) FROM bench_catalog), 1),
round(100.0 * (1.0 - count(*)::numeric / (SELECT count(*) FROM bench_catalog)), 1)
FROM bench_catalog
WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'),
'2026-02-17 00:00:00+00'::timestamptz,
'2026-02-18 00:00:00+00'::timestamptz,
10.0
)::observer_window
UNION ALL
SELECT
'2h/Equator/10deg',
(SELECT count(*) FROM bench_catalog),
count(*),
round(100.0 * count(*) / (SELECT count(*) FROM bench_catalog), 1),
round(100.0 * (1.0 - count(*)::numeric / (SELECT count(*) FROM bench_catalog)), 1)
FROM bench_catalog
WHERE tle &? ROW(
observer('0.0N 0.0E 0m'),
'2026-02-17 02:00:00+00'::timestamptz,
'2026-02-17 04:00:00+00'::timestamptz,
10.0
)::observer_window
UNION ALL
SELECT
'2h/Eagle/45deg',
(SELECT count(*) FROM bench_catalog),
count(*),
round(100.0 * count(*) / (SELECT count(*) FROM bench_catalog), 1),
round(100.0 * (1.0 - count(*)::numeric / (SELECT count(*) FROM bench_catalog)), 1)
FROM bench_catalog
WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'),
'2026-02-17 02:00:00+00'::timestamptz,
'2026-02-17 04:00:00+00'::timestamptz,
45.0
)::observer_window;
-- ============================================================
-- Cleanup
-- ============================================================
DROP INDEX bench_spgist;
DROP TABLE bench_catalog;
\timing off