Blog / Engineering deep dives

131,072 hops deep: Semurg walks a social graph up to 1,099 times faster than the incumbents

18 October 2026 · Semurg · 4 min read · Markdown

benchmarksgraphtraversalneo4jarcadedbfalkordbsurrealdbperformance

How deep can a database follow relationships before it gives up? We took one person in a public social graph of 117.2 million relationships[14] and asked every engine for everyone it could reach at each depth, from 2 hops all the way to 131,072, checking every answer against two independent reference implementations. Inside your application, one walk to 131,072 hops took Semurg 96.88 ms, ArcadeDB's embedded mode 459.74 ms[5], FalkorDB (no in-process mode: its fastest connection) 412.67 ms[6] and Neo4j's embedded mode 15,757.31 ms[4]. SurrealDB's embedded mode needed 109,350.14 ms for a single walk to 256 hops, its deepest, where Semurg needed 99.54 ms[8].

The test

The same data, machine and engines as our 4-hop comparison: SNAP's com-Orkut social graph, 3.07 million people[14], on one bare-metal AMD EPYC server, every engine in Docker with every thread and the same data disk, each through its fastest connection. Every depth is its own query from the same person: 2, 4, 8 and every doubling after that up to 131,072 hops. Each engine runs the whole ladder cold, warm and warm again; the figures are the warm medians.

Why the time jumps by 8 hops and then stays flat

Semurg's own times show the shape every engine shares: 0.135 ms at 2 hops[1], 3.515 ms at 4 hops[2], 95.946 ms at 8 hops[3], and 96.884 ms at 131,072 hops[4].

Semurg's time for one walk at each depth, inside the application: tiny at 2 and 4 hops, then the same height at 8 hops and at the deepest depth.2 hops: 0.135 ms2 hops0.135 ms4 hops: 3.515 ms4 hops3.515 ms8 hops: 95.946 ms8 hops95.946 ms131,072 hops: 96.884 ms131,072 hops96.884 ms
Source: [1] [2] [3] [4].
The numbers
PartValueSource
2 hops0.135 ms[1]
4 hops3.515 ms[2]
8 hops95.946 ms[3]
131,072 hops96.884 ms[4]

The jump is the graph, not the engine. A social network is small: from one person, 4 hops already reach 455,604 people[14], and by 8 hops the walk has reached nearly everyone it ever will. From there every deeper query walks the same whole graph again, so the time per depth stops growing: a walk to 131,072 hops costs Semurg the same as a walk to 8. Every engine bends at the same place; Neo4j's embedded mode goes from 17.108 ms at 2 hops to 15,594.362 ms at 8[1][3].

Inside your application

At full depth Semurg is 4.7 times faster than ArcadeDB's embedded mode[5] and 163 times faster than Neo4j's embedded mode[4]. FalkorDB and FalkorDBLite have no in-process mode, so in this column each runs through its fastest connection, as on the benchmark page: Semurg is 4.3 times faster than FalkorDB[6] and 4.4 times faster than FalkorDBLite[7].

One walk to the deepest depth, inside the application: Semurg's bar is the shortest, three engines take several times longer, and Neo4j's bar runs far to the right.Semurg: 96.884 msSemurg96.884 msFalkorDB: 412.67 msFalkorDB412.67 msFalkorDBLite: 430.788 msFalkorDBLite430.788 msArcadeDB: 459.735 msArcadeDB459.735 msNeo4j: 15,757.31 msNeo4j15,757.31 ms
Source: [4] [6] [7] [5].
The numbers
PartValueSource
Semurg96.884 ms[4]
FalkorDB412.67 ms[6]
FalkorDBLite430.788 ms[7]
ArcadeDB459.735 ms[5]
Neo4j15,757.31 ms[4]

SurrealDB's embedded mode is in a class of its own: 103,220.11 ms for one walk to 8 hops against Semurg's 95.95 ms[9], and 109,350.14 ms at 256 hops against 99.54 ms[8], which is 1,099 times slower. It did not go deeper in this run.

One walk to the same depth inside the application: Semurg's bar is too short to see beside SurrealDB's.Semurg: 99.54 msSemurg99.54 msSurrealDB: 109,350.14 msSurrealDB109,350.14 ms
Source: [8].
The numbers
PartValueSource
Semurg99.54 ms[8]
SurrealDB109,350.14 ms[8]

As a server

Through each engine's own server connection, one walk to 131,072 hops took Semurg 95.22 ms, ArcadeDB 113.71 ms[10], FalkorDB 412.67 ms[11] and Neo4j 14,033.83 ms[12].

One walk to the deepest depth, as a server: Semurg's bar is the shortest, ArcadeDB's is close behind, FalkorDB's is several times longer and Neo4j's runs far to the right.Semurg: 95.22 msSemurg95.22 msArcadeDB: 113.71 msArcadeDB113.71 msFalkorDB: 412.67 msFalkorDB412.67 msNeo4j: 14,033.828 msNeo4j14,033.828 ms
Source: [10] [11] [12].
The numbers
PartValueSource
Semurg95.22 ms[10]
ArcadeDB113.71 ms[10]
FalkorDB412.67 ms[11]
Neo4j14,033.828 ms[12]

ArcadeDB's server is the closest anyone comes: Semurg is ahead at the deepest depth[10], and at 8 hops the two are published as level[13]. That is the gap we are closing next.

Faster every release

Semurg ships continuously. Before any release is published it must pass a performance gate on the core operations (writing, reading, traversing and scanning), and a release that would make them slower does not ship. This ladder is re-run as the engine improves, and each re-run replaces the figures here and on the benchmark page.

Deeper, and bigger than memory

This graph fits in memory. The next ladder runs on a social graph many times larger than the memory each engine is given, at the same extreme depths, where a database has to keep walking while most of the graph sits on disk. Its results go on the benchmark page and into a post of their own when the run finishes.

Run it yourself

The kit behind every figure here is published; one command runs the whole graph comparison, this ladder included, on a Linux machine with Docker: curl -fsSL https://one.semurg.io/bench | sudo bash -s -- --graph. Read the script first with curl -fsSL https://one.semurg.io/bench | less. Every depth for every engine, in both columns, is in the ladder tables on the benchmark page.

Evidence

  1. Benchmark Walk from 2 to 131,072 hops: 2 hops: Semurg 0.135, neo4j 17.108 (r11.108) (bench:graph/t4_h2/embedded/neo4j@r11.108; the record as JSON)
  2. Benchmark Walk from 2 to 131,072 hops: 4 hops: Semurg 3.515, neo4j 653.257 (r11.108) (bench:graph/t4_h4/embedded/neo4j@r11.108; the record as JSON)
  3. Benchmark Walk from 2 to 131,072 hops: 8 hops: Semurg 95.946, neo4j 15594.362 (r11.108) (bench:graph/t4_h8/embedded/neo4j@r11.108; the record as JSON)
  4. Benchmark Walk from 2 to 131,072 hops: 131072 hops: Semurg 96.884, neo4j 15757.31 (r11.108) (bench:graph/t4_h131072/embedded/neo4j@r11.108; the record as JSON)
  5. Benchmark Walk from 2 to 131,072 hops: 131072 hops: Semurg 96.884, arcadedb 459.735 (r11.108) (bench:graph/t4_h131072/embedded/arcadedb@r11.108; the record as JSON)
  6. Benchmark Walk from 2 to 131,072 hops: 131072 hops: Semurg 96.884, falkordb 412.67 (r11.108) (bench:graph/t4_h131072/embedded/falkordb@r11.108; the record as JSON)
  7. Benchmark Walk from 2 to 131,072 hops: 131072 hops: Semurg 96.884, falkordblite 430.788 (r11.108) (bench:graph/t4_h131072/embedded/falkordblite@r11.108; the record as JSON)
  8. Benchmark Walk from 2 to 131,072 hops: 256 hops: Semurg 99.54, surrealdb 109350.14 (r11.108) (bench:graph/t4_h256/embedded/surrealdb@r11.108; the record as JSON)
  9. Benchmark Walk from 2 to 131,072 hops: 8 hops: Semurg 95.946, surrealdb 103220.106 (r11.108) (bench:graph/t4_h8/embedded/surrealdb@r11.108; the record as JSON)
  10. Benchmark Walk from 2 to 131,072 hops: 131072 hops: Semurg 95.22, arcadedb 113.71 (r11.108) (bench:graph/t4_h131072/server/arcadedb@r11.108; the record as JSON)
  11. Benchmark Walk from 2 to 131,072 hops: 131072 hops: Semurg 95.22, falkordb 412.67 (r11.108) (bench:graph/t4_h131072/server/falkordb@r11.108; the record as JSON)
  12. Benchmark Walk from 2 to 131,072 hops: 131072 hops: Semurg 95.22, neo4j 14033.828 (r11.108) (bench:graph/t4_h131072/server/neo4j@r11.108; the record as JSON)
  13. Benchmark Walk from 2 to 131,072 hops: 8 hops: Semurg 108.554, arcadedb 113.489 (r11.108) (bench:graph/t4_h8/server/arcadedb@r11.108; the record as JSON)
  14. Benchmark One query, 4 hops: Semurg 3.83, arcadedb 73.14 (r11.108) (bench:graph/t2/server/arcadedb@r11.108; the record as JSON)

Related