# Engines

> Geolith Search measured against Pelias, Photon and Nominatim on an India extract.

## Conditions

- One machine: Apple M2 Max, 96 GB RAM, Docker Desktop with 12 CPUs and 36 GB.
- **All four engines run in Docker** on the same machine, each from its own image and data volume. One engine runs at a time; the others are stopped. Compose file and scripts: `packages/bench/docker`.
- geolith-search answers from one `.glx` file on a Docker volume. There is no database.
- Accuracy: 200 queries per class, one request each, after a warm-up pass. A top-1 result counts as correct when it lies within the gold radius of the gold point: 10 km for a town and 25 km for a city in the OSM places class, 0.5 km for reverse, and 1 km for every other class. Top-5 counts a result within the same radius in the first five.
- Load: [autocannon](https://github.com/mcollina/autocannon) from the host, 10 seconds, 100 connections.
- Load and latency measured on 19 Oct 2026 with the 530 MB India file. Accuracy for geolith re-measured on 20 Oct 2026 with the current 543 MB file (name initials, synonyms and the town-reading fixes since); the other engines' rows are unchanged.
- Single run on a local machine: treat the ratios as the result, not the absolute numbers.

## Test data

- **India** as the area. OpenStreetMap extract `india-latest.osm.pbf` (1.7 GB, 5 Oct 2026) and Overture release `2026-09-23.1`.
- geolith indexes OSM and Overture together: settlements, venues and addresses from OSM, deduplicated against Overture, on top of the Overture places. Pelias indexes OSM with Who's on First admin data, plus street polylines and address interpolation. Photon and Nominatim index OSM.
- The Overture download box reaches past the Indian border, so the `.glx` file also holds places in neighbouring countries. Every geolith request sends `boundary.country=IN`, so all four engines answer from India only.
- The gold sets are sampled from both sources. The Overture POI class is sampled from Overture, which only geolith indexes, so read that row as coverage, not ranking.

## Summary

| Engine | Top-1 wins | Search load | Peak memory under load | Index on disk |
| --- | --- | --- | --- | --- |
| **geolith** | **5 of 7 classes** | **1,518 req/s** | **0.74 GB** | 543 MB, one file |
| Photon 1.3.0 | 1 of 7 (OSM POIs) | 489 req/s | 3.2 GB | 810 MB |
| Nominatim 5.3 | 1 of 7 (reverse) | 774 req/s | 3.1 GB | 45 GB |
| Pelias (master) | 0 of 7 | 371 req/s | 11.6 GB across six services | 1.0 GB + supporting stores |

## Accuracy

Top-1 / top-5 within the gold radius of each class (see Conditions), 200 queries per class.

| Query class | geolith | Pelias | Photon | Nominatim |
| --- | --- | --- | --- | --- |
| OSM places (cities, towns) | **95.0 / 97.5** | 77.0 / 86.0 | 80.5 / 96.5 | 85.0 / 96.0 |
| OSM addresses | **95.0 / 98.5** | 61.5 / 63.0 | 88.5 / 91.0 | 50.5 / 52.5 |
| OSM POIs | 84.5 / **91.5** | 61.0 / 72.5 | **85.5** / 89.5 | 68.5 / 72.5 |
| Overture POIs | **93.0 / 97.5** | 7.0 / 14.0 | 12.5 / 16.5 | 6.0 / 6.5 |
| Autocomplete | **82.5 / 88.5** | 32.5 / 36.5 | 42.5 / 48.5 | not supported |
| Search near a point | **96.0 / 97.0** | 39.0 / 42.5 | 52.0 / 53.5 | 44.5 / 47.0 |
| Reverse | 99.5 / 99.5 | 98.0 / 98.0 | 98.5 / 98.5 | **100.0 / 100.0** |

Percentages are of all queries in the class, so a query that returns nothing counts as a miss.

## Feature probes

25 single queries on the same India data, each testing one capability the three other engines document (neighbourhood and postcode on a result, `addr:place` addresses, house number with an area, bounding boxes, de-duplication, category filter and output, localized output, accents, apostrophes, interpolation). Script: `ml/parity_probe.py`.

| geolith | Nominatim | Photon | Pelias |
| --- | --- | --- | --- |
| **24 / 25** | 21 / 25 | 19 / 25 | 16 / 25 |

The probe geolith fails, an interpolated house number at the end of a range, fails on all four engines.

## Address parser

With no comma, the reader finds the town at the end of a query by trying readings against the file, as Nominatim does. See [Reading a town without a comma](/search/benchmarks/town-reading).

## Beyond parity: name initials

People write many places by their initials, and Google Maps finds them: "SIMS, Seawoods" for the Sterling Institute of Management Studies, "SBI Nerul" for the State Bank of India, "JNU" for Jawaharlal Nehru University. Nominatim, Photon and Pelias find a place by its initials only when OpenStreetMap tags them as `short_name`. geolith indexes the initials of every venue and area named in three to six words, so all three queries above return the right place first. The initials add 12 MB to the India file and change no gold-set answer.

## Latency, one request

Median / p95 in ms, one request per query, warm engines.

| Query class | geolith | Pelias | Photon | Nominatim |
| --- | --- | --- | --- | --- |
| Autocomplete | 5.8 / 54.7 | 4.9 / **9.8** | **4.0** / 14.8 | — |
| Search near a point | 7.1 / 22.2 | 9.0 / 18.8 | **3.1 / 14.5** | 6.5 / 18.1 |
| OSM addresses | 7.2 / **17.5** | 7.5 / 17.9 | **2.2** / 17.6 | 11.4 / 32.6 |
| OSM places | **2.5** / 5.7 | 4.0 / 35.4 | 3.7 / **5.0** | 5.4 / 10.7 |
| OSM POIs | 5.7 / 18.7 | 8.9 / 18.7 | **2.2** / 17.1 | 6.8 / **14.2** |
| Overture POIs | **5.9 / 11.4** | 7.7 / 12.7 | 10.0 / 18.8 | 6.5 / 13.0 |
| Reverse | **0.7 / 1.2** | 4.3 / 6.7 | 1.9 / 2.4 | 6.2 / 8.8 |

## Load

10 seconds, 100 connections: requests per second, p50 ms, p99 ms, errors.

| Query class | geolith | Pelias | Photon | Nominatim |
| --- | --- | --- | --- | --- |
| Search | **1,518** · 68 · 109 · 0 | 371 · 242 · 539 · 0 | 489 · 151 · 484 · 0 | 774 · 80 · 662 · 0 |
| Autocomplete | 668 · 142 · 306 · 0 | **980** · 92 · 217 · 0 | 593 · 167 · 313 · 0 | — |
| Search near a point | **994** · 98 · 145 · 0 | 414 · 226 · 1152 · 0 | 592 · 161 · 485 · 0 | 894 · 38 · 540 · 0 |
| Reverse | **21,860** · 4 · 8 · 0 | 1,842 · 52 · 88 · 0 | 3,132 · 30 · 43 · 0 | 1,118 · 41 · 420 · 0 |

Every run was clean. geolith runs one index read per core and queues the rest, which keeps its memory at 0.74 GB under load.

## Storage and setup

| Engine | Data store | Index on disk | Setup |
| --- | --- | --- | --- |
| **geolith** | one `.glx` file | 543 MB, 6,382,829 places | one geolith run from Overture + OSM: 15 min |
| Pelias | OpenSearch 7.17.27, 2.2M documents | 1.01 GB index | WOF, OSM, polylines and interpolation; the final pass ran ≈ 10 min after downloads |
| Photon | OpenSearch inside the Photon process | 810 MB | published dump, no build step here |
| Nominatim | PostgreSQL 16 | 45 GB | osm2pgsql import |

Pelias also needs libpostal, the placeholder service, the point-in-polygon service and the interpolation SQLite store; those services are running and are counted in its memory, not in the 1.01 GB.

## The `.glx` file

What the file format itself costs per query, read with `glx-query` from local disk with no server and no network. The file is 530 MB (19 Oct 2026).

| Query class | Top-1 | Top-5 | Read rounds p50 / p95 | Bytes p50 / p95 | CPU p50 / p95 |
| --- | --- | --- | --- | --- | --- |
| OSM places | 93.0 | 96.5 | 2 / 2 | 0.5 / 1.9 MB | 1 / 2 ms |
| OSM addresses | 95.5 | 98.0 | 2 / 3 | 0.6 / 1.9 MB | 7 / 18 ms |
| OSM POIs | 80.5 | 88.5 | 3 / 3 | 0.5 / 3.2 MB | 5 / 17 ms |
| Overture POIs | 90.5 | 96.0 | 2 / 3 | 0.5 / 9.4 MB | 4 / 26 ms |
| Autocomplete | 82.5 | 89.0 | 3 / 5 | 0.5 / 13.7 MB | 5 / 53 ms |

`glx-query` sends no country filter, so these top-1 numbers can differ from the API table by a point or two.

## Analysis

- **geolith takes top-1 on five of seven classes** and leads search, point-focused search and reverse throughput, from one 543 MB file with no database, using under 1 GB of memory under load.
- **OSM POIs are the one class Photon leads**, by 1.0 point (85.5 vs 84.5); geolith leads its top-5. Built from OSM alone, the data Photon indexes, geolith scores 86.5 on this class. In the full build, Overture adds more same-name branches in the same town (several shops called Burberry or Amul), and the gold set expects one of them.
- **Nominatim owns reverse geocoding** (100%), with a 45 GB database.
- **Pelias wins no class on this dataset.** It has the fastest autocomplete under load and the lowest autocomplete p95.
- **geolith's slowest single requests are long autocomplete prefixes** (p95 54.7 ms), within its 100 ms autocomplete target but well above Photon and Pelias.
- **The data asymmetry cuts both ways.** geolith is the only engine here that indexes both OSM and Overture, which is why it takes the Overture POI class outright.

## Reproduce

```bash
# From a checkout of the geolith search repository:
cd packages/bench
pnpm install

# Fill the Docker volumes, import Pelias and Nominatim, build the image:
docker/setup.sh
# Measure each engine alone (accuracy, load, memory):
docker/run.sh
# glx-query is the geolith-glx reader's command-line tool (feature `build`).
node src/glx.ts queries/india ~/india.glx ~/.cargo/bin/glx-query results/glx.json
```

The query sets are test fixtures sampled from OpenStreetMap (ODbL) and Overture Maps places on the dates above; the gold points are the sampled feature locations.
