GeoSQLite/Benchmark report

MEASURED, NOT PROMISED.

The native PoC, against Redis.

The complete one-million-point benchmark, from latency to disk flushes.
Recorded native results, not browser WASM timings.

Completed run · September 16, 2026 · Apple M4
Download report
00

THE SHORT VERSION

Fast nearby searches. Different tradeoffs.

NEARBY CITY SEARCH · 5 KM

The PoC had lower median latency.

SQLite FULL 0.163ms
Redis 2.997ms

Nearest 20 with distances, one TCP client.

See all TCP workloads
SPARSE GLOBAL SEARCH · 100 KM

Redis was faster in this workload.

SQLite FULL 0.087ms
Redis 0.060ms

Warm reads are not a universal engine ranking.

Understand the comparison
How to read this report

p50 is the median; p95 and p99 describe the slower tail. Lower latency is better; higher throughput is better. Read-only Redis had persistence disabled. The TypeScript client and WASM build were added later and are not measured here.

01

Native engine

Engine-only latency: no socket or Python overhead. All three methods use the same dataset.

Native engine (no socket or Python). Latency is in milliseconds. Values are from the recorded run.
WorkloadMethodSamplesp50 msp95 msp99 msMean candidate points
City · 5 kmBest-first R*Tree20000.1122.6983.90020.0
City · 5 kmBounding cube2008.56210.77812.4209925.6
City · 5 kmFull scan20133.435149.922174.7591000000.0
City · 50 kmBest-first R*Tree20000.1170.2050.31320.0
City · 50 kmBounding cube20078.01188.72792.84899988.8
City · 50 kmFull scan20131.632141.903148.9011000000.0
Global · 100 kmBest-first R*Tree20000.0290.0450.10912.4
Global · 100 kmBounding cube2000.0280.0380.04518.7
Global · 100 kmFull scan20126.380132.553141.3931000000.0
Global · 5,000 kmBest-first R*Tree20000.0520.1110.24920.0
Global · 5,000 kmBounding cube200113.656421.254457.218207425.5
Global · 5,000 kmFull scan20131.053139.749140.8911000000.0
02

TCP latency & throughput

One client, nearest 20 with distances. Both engines receive identical queries through the same Python TCP client.

TCP, one client, nearest 20 with distances. Latency is in milliseconds. Values are from the recorded run.
WorkloadEnginep50 msp95 msp99 msRequests/secMean matches
City · 5 kmSQLite FULL0.1630.2350.3085,85620.0
City · 50 kmSQLite FULL0.1770.2430.3355,55320.0
Global · 100 kmSQLite FULL0.0870.1370.19410,37712.5
Global · 5,000 kmSQLite FULL0.1110.1690.2448,43320.0
City · 5 kmRedis (no persistence)2.9974.0685.05633420.0
City · 50 kmRedis (no persistence)14.01014.94719.6257020.0
Global · 100 kmRedis (no persistence)0.0600.0820.11212,39712.5
Global · 5,000 kmRedis (no persistence)91.800108.087117.9941120.0
03

Pipeline throughput

16 requests per batch, same city_5km queries. Latency here is for a complete batch, not an individual request.

  • SQLite FULL: 6,910 requests/sec; batch p95 4.002 ms.
  • Redis (no persistence): 363 requests/sec; batch p95 47.790 ms.
04

Concurrent traffic

Durability settings matter. On macOS, Redis AOF always uses the stronger F_FULLFSYNC primitive. Compare it with SQLite FULL + F_FULLFSYNC, not ordinary FULL. NORMAL may lose acknowledged writes after a power failure.

Four reader connections and one writer connection; 250 requests per client. Updates change points in the queried collection. The PoC dispatches commands serially in one process. Values include client scheduling and queueing.

Concurrent traffic. Latency is in milliseconds. Values are from the recorded run.
Engine / durabilityRead p50 msRead p95 msWrite p50 msWrite p95 msTotal ops/sec
SQLite FULL0.3640.7770.5830.9139,214
Redis AOF always15.21218.12015.18818.052323
SQLite NORMAL0.3620.7530.5110.85810,447
SQLite FULL + F_FULLFSYNC4.6999.0196.0209.795776
05

Native updates

200 transactions per case, changing coordinates in a separate 1,000-member collection. Batch construction is excluded from transaction latency and included in throughput.

Native updates. Latency is in milliseconds. Values are from the recorded run.
DurabilityPoints/transactionp50 msp95 msPoints/sec
full10.0520.07216,059
full1001.0401.24493,613
normal10.0260.03527,743
normal1001.0011.29494,794
06

Storage & ingestion

  • Initial load: 121.03 seconds; 8,262 points/sec. Includes synthetic generation, FULL commits in 1,000-point batches, and final checkpoint.
  • Database after native update tests: 208.0 MiB.
  • Native benchmark process peak RSS: 83.6 MiB; this includes ingestion and all methods.
07

Redis result cross-check

200 shared queries; 0 top-20 membership differences. Maximum distance difference among common members: 0.301 m. Redis quantizes coordinates; the PoC preserves input doubles. Independent Haversine correctness tests are separate from this cross-check.

08

Methodology & limits

  • Reproducible synthetic dataset: 80% clustered around eight cities, 20% broadly distributed; seed 20260916. Not production traffic.
  • Native baselines share query prefixes. Best-first has 2,000 samples by default, bounding-cube 200, full scan 20; p99 from 20 samples is descriptive, not a reliable tail estimate.
  • Native and TCP workloads use different PRNG implementations. Both TCP engines receive identical commands and exact same source coordinates. Compare within each section.
  • Warm cache measurements; no result cache. Process/OS cold starts and sustained saturation are not measured. The full database need not fit in SQLite's configured cache.
  • TCP includes the identical Python client's encoding, socket I/O, and response decoding; this is end-to-end observed throughput, not maximum server capacity.
  • Redis read-only comparisons disable AOF and snapshots. SQLite remains disk-backed. Read results do not establish equivalent write durability.
  • On macOS, Redis AOF always uses F_FULLFSYNC; SQLite FULL uses ordinary fsync by default. Those default rows are NOT equivalent durability comparisons. The separately measured SQLite FULL + F_FULLFSYNC row, when present, enables fullfsync and checkpoint_fullfsync for the closer comparison. Neither is a hardware power-failure certification.
  • SQLite NORMAL can lose recent acknowledged writes after power loss. SIGKILL recovery was tested with FULL; actual power loss was not simulated.
  • Nearest 20 allows early stopping. Large returned result sets, polygons, arbitrary metadata filters, multiple hosts, and huge numbers of collections are outside this benchmark.
  • No CPU affinity, isolated machine, repeated-run confidence intervals, or open-loop arrival generator. Other applications and coordinated omission can affect the tails.

Raw timings and environment: benchmark.json and native.json beside this report.

09

Recorded validation

Checks recorded with the original benchmark, not a new test run.

Completed on 2026-09-16 with Apple clang 17, SQLite 3.51.3 and Redis 8.2.9.

  • make test: passed native and RESP2 integration suites.
  • Native correctness: 127 independent Haversine-oracle queries against each of three search implementations, over 12,004 points; date line, poles, boundaries, zero radius, ties, invalid-batch atomicity, updates/deletes and namespace isolation.
  • Concurrent WAL test: a writer atomically moved 100 points while another connection searched them. Readers observed complete before/after snapshots.
  • RESP2 tests: fragments, binary strings, pipelines, malformed requests, eight simultaneous clients and restart after SIGKILL with acknowledged FULL writes.
  • AddressSanitizer and UndefinedBehaviorSanitizer: native suite passed.
  • Full million-point run: SQLite integrity_check, R*Tree rtreecheck and point/index membership checks passed.
  • Redis cross-check: 200 queries, zero top-20 membership differences; maximum reported distance difference for common members was 0.301 m.
  • Result validation: all 12 native cases, 10 TCP/pipeline cases and four mixed traffic configurations present; percentile ordering checked.
  • Dedicated macOS F_FULLFSYNC measurement added after verifying that Redis uses this stronger flush primitive. Its source fingerprints and timestamp are in benchmark.json; no spatial search logic changed for this supplement.

No benchmark service is left running. Native and TCP measurements are different measurement layers; only compare equivalent rows. Power-failure behavior and production readiness are not established by these tests.

10

Environment, sources & reproduction

Every report row above comes directly from the checked-in Markdown report.

Run: 2026-09-16T17:59:22.621545+00:00

Machine: macOS-26.3.1-arm64-arm-64bit-Mach-O; Apple M4. SQLite 3.51.3; 1,000,000 points; 64 MiB SQLite page cache per engine.

Redis comparator: Redis server v=8.2.9 sha=00000000:1 malloc=libc bits=64 build=516af531ee24aeba

Run it yourself

make -C native -j4
python3 native/scripts/benchmark.py --points 1000000 --queries 2000 --tcp-queries 500

The runner uses isolated databases and listeners. Redis is optional; a missing comparator is explicitly skipped.

Setup & benchmark methodology