# Run the graph database benchmark yourself

We have published the benchmark we use to compare Galactus DB with Neo4j and
Memgraph. It is one Python script and one Compose file, Apache-2.0, at
[github.com/galactusdb/galactus-db-benchmark](https://github.com/galactusdb/galactus-db-benchmark).
Clone it, run one command, and it measures all three engines on your machine
and writes the results as CSV and a single page of charts. Every number in this
post came from that code, and you can check every one of them.

```sh
git clone https://github.com/galactusdb/galactus-db-benchmark
cd galactus-db-benchmark
pip install -r requirements.txt
python bench.py && python report.py
```

## How the suite keeps it even

- **One engine at a time.** Each engine starts on a fresh volume, runs every
  workload and is removed before the next one starts. The order rotates on
  each of the three passes, and we report the median.
- **The same limits for everyone.** Two profiles, applied identically to every
  container: 8 CPUs with 4 GB of memory, and 2 CPUs with 1 GB.
- **Community editions on their defaults.** Galactus DB 2.1.8 runs its
  published image as it ships, including buffered durability with a 50 ms
  commit-log sync. Neo4j 5.26.32 Community gets the heap and page cache that
  Neo4j's own `neo4j-admin server memory-recommendation` prints for each limit
  (2 GB heap and 512 MB page cache at 4 GB; 307 MB and 52 MB at 1 GB), plus the
  Graph Data Science plugin for weighted paths. Memgraph 3.14.0 runs its
  defaults with telemetry switched off.
- **Same client, data and Cypher.** One Python process drives every engine
  through the official Neo4j Python driver 5.28.1. The graph comes from a fixed
  seed: 100,000 people, each knowing two others, with a weight from 1 to 10 on
  every relationship. Every engine runs the same statement wherever it accepts
  it. Memgraph has no `shortestPath()` or Graph Data Science, so it runs its own
  `*BFS`, `*ALLSHORTEST` and `*WSHORTEST`.
- **The same answers.** Every timed query's answers are fingerprinted. In both
  profiles, all four engines returned identical answers for every workload.
- **Failures count.** When an engine runs out of memory, the workload is
  recorded as did not finish, with the reason, and the run carries on.

Galactus DB runs twice. **Paged** is its default engine: it opens a database
straight from disk and keeps the pages in use in memory. **Resident** keeps the
whole graph in memory, the same model as Memgraph.

The machine: an Intel Core i9-13900K with 64 GB of RAM, Windows 11 with Docker
Desktop 28.0.1 on WSL2, Python 3.10. Images by digest: Galactus DB 2.1.8
`sha256:50b294cc…`, Neo4j 5.26 Community `sha256:249a1ec6…`, Memgraph 3.14.0
`sha256:a7324212…`. Measured on 01/11/2026.

## Shortest paths

Finding the route between two things is the heart of most graph work, from
GraphRAG to fraud rings to supply chains. The unweighted tests pick two people
up to 6 hops apart. The weighted test runs Dijkstra between two random people
over the relationship weights; Galactus DB and Neo4j answer it with the same
`gds.shortestPath.dijkstra.stream` call on a projection built once.

![Shortest path, all shortest paths and weighted shortest path latency at 8 CPUs and 4 GB](/blog/charts/bench-paths.svg)

| 8 CPUs, 4 GB, median latency | GDB Paged | GDB Resident | Neo4j | Memgraph |
|---|---|---|---|---|
| Shortest path, up to 6 hops | 0.62 ms | **0.53 ms** | 1.46 ms | 2.29 ms |
| All shortest paths, up to 6 hops | 0.59 ms | **0.54 ms** | 1.31 ms | 3.70 ms |
| Weighted shortest path (Dijkstra) | **23.5 ms** | 23.9 ms | 26.1 ms | 417 ms |

Galactus DB found the shortest path in about a third of Neo4j's time and a
quarter of Memgraph's, and all shortest paths in about 40% of Neo4j's time and
a seventh of Memgraph's. On weighted paths it finished in 23.5 ms against
26.1 ms for Neo4j and 417 ms for Memgraph. Galactus DB 2.1.8 stops a
single-target Dijkstra as soon as it reaches the target and keeps the weighted
adjacency with the projection, which took this call from 105 ms in 2.1.7 to
23.5 ms.

## Lookups and traversals

![Index lookup, friends of friends, filtered scan and concurrent reads at 8 CPUs and 4 GB](/blog/charts/bench-lookups.svg)

| 8 CPUs, 4 GB | GDB Paged | GDB Resident | Neo4j | Memgraph |
|---|---|---|---|---|
| Index lookup by id, median | 0.50 ms | 0.49 ms | 1.22 ms | 0.49 ms |
| Friends of friends, median | 0.53 ms | 0.51 ms | 1.13 ms | **0.48 ms** |
| Filtered scan of 100,000 people, median | 26.2 ms | **8.3 ms** | 14.9 ms | 10.4 ms |
| Index lookups, 16 clients, reads/s | **4,108** | 4,025 | 3,005 | 3,849 |
| Index lookups, 16 clients, median | **3.4 ms** | 3.4 ms | 5.0 ms | 3.6 ms |

Index lookups take half a millisecond on Galactus DB and Memgraph, and about
2.5 times as long on Neo4j. Memgraph answered friends of friends 0.03 ms
sooner than Resident. The filtered scan reads all 100,000 people: Resident did
it in 8.3 ms, Memgraph in 10.4 ms, Neo4j in 14.9 ms and Paged in 26.2 ms. With 16 clients reading together, Paged
served the most lookups per second, 37% more than Neo4j.

## Memory

![Memory at rest, while loading and under concurrent reads at 8 CPUs and 4 GB](/blog/charts/bench-memory.svg)

| 8 CPUs, 4 GB | GDB Paged | GDB Resident | Neo4j | Memgraph |
|---|---|---|---|---|
| At rest with the graph loaded | 164 MB | **89 MB** | 3,043 MB | 252 MB |
| Peak while loading | 217 MB | **96 MB** | 3,049 MB | 428 MB |
| Peak under 16-client reads | 612 MB | 524 MB | 3,174 MB | **415 MB** |

With the graph loaded, Resident used 89 MB: a thirty-fourth of Neo4j's memory
and about a third of Memgraph's. Paged used 164 MB. Under 16 clients reading at
once, Memgraph peaked lowest at 415 MB, against 524 MB for Resident, 612 MB for
Paged and 3,174 MB for Neo4j.

## Loading and start-up

![Nodes and relationships loaded per second, and restart time, at 8 CPUs and 4 GB](/blog/charts/bench-load.svg)

| 8 CPUs, 4 GB | GDB Paged | GDB Resident | Neo4j | Memgraph |
|---|---|---|---|---|
| Nodes loaded per second, batches of 1,000 | **70,177** | 68,706 | 38,109 | 63,341 |
| Relationships loaded per second | 55,285 | **82,156** | 36,457 | 73,854 |
| Restart to first correct answer | 0.78 s | **0.77 s** | 14.9 s | 1.08 s |
| Container image on disk | **12 MB** | **12 MB** | 587 MB | 653 MB |

Galactus DB loaded nodes faster than both, and Resident loaded 82,156
relationships a second, 2.3 times Neo4j's rate. Paged loaded relationships at 55,285 a second,
behind Memgraph's 73,854.
After a clean stop, Galactus DB answered its first query in under 0.8 s,
Memgraph in 1.1 s and Neo4j in 14.9 s.

## With 2 CPUs and 1 GB

The second profile gives every engine a quarter of the CPUs and a quarter of
the memory: the size of a small cloud instance or a database sharing a server
with its application.

![Shortest path latency at 2 CPUs and 1 GB](/blog/charts/bench-paths-1gb.svg)

| 2 CPUs, 1 GB | GDB Paged | GDB Resident | Neo4j | Memgraph |
|---|---|---|---|---|
| Shortest path, median | **0.58 ms** | 0.62 ms | 1.38 ms | 2.26 ms |
| All shortest paths, median | 0.60 ms | **0.53 ms** | 1.31 ms | 3.23 ms |
| Weighted shortest path, median | **23.5 ms** | 24.9 ms | did not finish: out of memory in 3 of 3 passes | 377 ms |
| Index lookup, median | 0.48 ms | **0.47 ms** | 1.21 ms | 0.50 ms |
| Friends of friends, median | 0.50 ms | **0.48 ms** | 1.19 ms | 0.49 ms |
| Filtered scan, median | 30.1 ms | **8.4 ms** | 17.7 ms | 10.5 ms |
| Index lookups, 16 clients, reads/s | 4,156 | 4,150 | did not finish: out of memory in 2 of 3 passes | **4,276** |
| Index lookups, 16 clients, median | 3.3 ms | 3.3 ms | did not finish | **3.3 ms** |
| At rest with the graph loaded | 165 MB | **89 MB** | 1,031 MB | 250 MB |
| Peak while loading | 207 MB | **92 MB** | 1,031 MB | 395 MB |
| Peak under 16-client reads | 610 MB | 512 MB | did not finish | **434 MB** |
| Nodes loaded per second | 70,472 | **74,644** | 33,564 | 70,803 |
| Relationships loaded per second | 55,576 | **87,665** | 33,053 | 78,607 |
| Restart to first correct answer | **0.75 s** | 0.78 s | 15.6 s | 1.13 s |

With a quarter of the resources, Galactus DB kept its memory use, its load
rates and its weighted paths where they were at 4 GB. Its largest changes were
Paged's filtered scan, from 26.2 ms to 30.1 ms, and Resident's shortest path,
from 0.53 ms to 0.62 ms. Neo4j sat at its 1 GB limit with the graph loaded and
was killed for running out of memory on every weighted path pass and on two of
the three concurrent read passes. Memgraph served the most concurrent reads at
1 GB, 3% more than Galactus DB.

![Index lookup, friends of friends, filtered scan and concurrent reads at 2 CPUs and 1 GB](/blog/charts/bench-lookups-1gb.svg)

![Memory at rest, while loading and under concurrent reads at 2 CPUs and 1 GB](/blog/charts/bench-memory-1gb.svg)

![Nodes and relationships loaded per second, and restart time, at 2 CPUs and 1 GB](/blog/charts/bench-load-1gb.svg)

## Run it on your own machine

The suite pins every image version in its Compose file, records each image
digest and each server's effective settings next to the results, and prints
every statement each engine ran in its report. Change the scale, the number of
passes or the limits with one option each, and send us what you find.

For the side-by-side detail on each engine, including the workloads this suite
does not cover, see [Galactus DB compared with Neo4j](/compare/neo4j) and
[Galactus DB compared with Memgraph](/compare/memgraph).
