TuringDB Benchmark Report
Date: 2026-02-16 Author(s): TuringDB team
Executive Summary
TuringDB is a column-oriented, in-memory graph database engine designed for analytical and read-intensive workloads. This report presents benchmark results comparing TuringDB against Neo4j and Memgraph across multiple datasets and query categories. What We Tested Poledb (~61K nodes / 105K relationships) Reactome (~3M nodes / 11.5M relationships) 48 real-world Cypher queries Cold runs only (no caching). No indexes, no database tuning. Same hardware, same machine. Headline Numbers Across all queries: 51.8× faster than Neo4j on average Median: 19× Max: 488× 41.3× faster than Memgraph on average Median 11x Max 359x Won 48/48 queries vs Neo4j Won 45/47 queries vs Memgraph And remember: All of this is running over plain JSON/HTTP for now, while competitors used Bolt (binary protocol), which actually makes the benchmark conservative in our case. We’re also running completely out-of-the-box with no manual tuning, no custom configuration, no indexes on our side. So we’re actually not giving ourselves any unfair advantage here.1. Test Environment
Hardware
| Spec | Value |
|---|---|
| CPU | Intel(R) Xeon(R) Gold 5412U |
| Cores | 48 |
| RAM | 256G |
| Storage | SSD |
| OS | Ubuntu 24.04.3 LTS |
Software Versions
Database Engines| Database | Version |
|---|---|
| TuringDB | 1.0 |
| Neo4j | 5.26.19-SNAPSHOT |
| Memgraph | unknown |
| Component | Version |
|---|---|
| Python | 3.13.11 |
| turingdb (Python SDK) | 1.20.0 |
| neo4j (Python driver) | 6.1.0 |
| mgconsole | 1.4 |
--storage-mode=IN_MEMORY_ANALYTICAL). No additional engine-specific tuning was applied beyond what the import pipeline provides.
Client Protocol
TuringDB is queried over HTTP via its Python client, while Neo4j and Memgraph are queried over the Bolt binary protocol. Bolt is a more efficient wire protocol than HTTP for database communication, meaning TuringDB’s measured times include higher protocol overhead. This makes the benchmark conservative in TuringDB’s favor — with an equivalent binary protocol, TuringDB’s results would be even faster.2. Dataset
This section describes the datasets used in the benchmark. Each dataset was imported into all three engines using the same pipeline (see turing-bench for reproduction steps). The data is identical across all three databases.Poledb
| Metric | Count |
|---|---|
| Nodes | 61,521 |
| Relationships | 105,840 |
| Node Labels | 11 |
| Relationship Types | 17 |
| Benchmark Queries | 14 |
| Label | Count |
|---|---|
| Crime | 28,762 |
| Location | 14,904 |
| PostCode | 14,196 |
| Officer | 1,000 |
| Vehicle | 1,000 |
| PhoneCall | 534 |
| Person | 369 |
| Phone | 328 |
| 328 | |
| Area | 93 |
| Object | 7 |
| Type | Count |
|---|---|
| OCCURRED_AT | 28,762 |
| INVESTIGATED_BY | 28,762 |
| HAS_POSTCODE | 14,904 |
| LOCATION_IN_AREA | 14,904 |
| POSTCODE_IN_AREA | 14,196 |
| INVOLVED_IN | 985 |
| KNOWS | 586 |
| CALLER | 534 |
| CALLED | 534 |
| CURRENT_ADDRESS | 368 |
| HAS_PHONE | 328 |
| HAS_EMAIL | 328 |
| KNOWS_SN | 241 |
| FAMILY_REL | 155 |
| KNOWS_PHONE | 118 |
| KNOWS_LW | 80 |
| PARTY_TO | 55 |
Reactome
| Metric | Count |
|---|---|
| Nodes | 2,978,202 |
| Relationships | 11,537,843 |
| Node Labels | 108 |
| Relationship Types | 88 |
| Benchmark Queries | 34 |
| Label | Count |
|---|---|
| DatabaseObject | 2,978,202 |
| ReferenceEntity | 930,514 |
| ReferenceSequence | 927,102 |
| DatabaseIdentifier | 856,680 |
| ReferenceDNASequence | 617,302 |
| Deletable | 532,391 |
| Trackable | 524,558 |
| PhysicalEntity | 406,613 |
| GenomeEncodedEntity | 246,176 |
| EntityWithAccessionedSequence | 238,918 |
| ReferenceRNASequence | 204,186 |
| Person | 171,942 |
| InstanceEdit | 157,659 |
| Event | 117,945 |
| Complex | 110,048 |
| ReferenceGeneProduct | 105,614 |
| ReactionLikeEvent | 94,655 |
| Reaction | 83,459 |
| MetaDatabaseObject | 79,220 |
| Interaction | 77,995 |
| UndirectedInteraction | 77,995 |
| UpdateTracker | 60,837 |
| AbstractModifiedResidue | 56,756 |
| TranslationalModification | 50,733 |
| EntitySet | 43,526 |
| Publication | 42,286 |
| LiteratureReference | 42,098 |
| CatalystActivity | 40,297 |
| ModifiedResidue | 36,547 |
| DefinedSet | 34,937 |
| Pathway | 23,290 |
| Summation | 21,332 |
| GroupModifiedResidue | 13,057 |
| DeletedInstance | 10,573 |
| BlackBoxEvent | 10,435 |
| CandidateSet | 8,589 |
| Regulation | 7,833 |
| Deleted | 7,668 |
| GeneticallyModifiedResidue | 5,947 |
| PositiveRegulation | 4,590 |
| SimpleEntity | 3,897 |
| ReplacedResidue | 3,459 |
| ControlReference | 3,367 |
| NegativeRegulation | 3,243 |
| GO_Term | 3,198 |
| FragmentModification | 2,488 |
| ReferenceIsoform | 2,369 |
| FragmentReplacedModification | 2,259 |
| ReferenceMolecule | 2,183 |
| RegulationReference | 1,897 |
| GO_MolecularFunction | 1,665 |
| Polymer | 1,391 |
| NonsenseMutation | 1,331 |
| PositiveGeneExpressionRegulation | 1,308 |
| Drug | 1,203 |
| CrosslinkedResidue | 1,129 |
| ChemicalDrug | 1,106 |
| CatalystActivityReference | 1,105 |
| ReferenceTherapeutic | 1,087 |
| GO_BiologicalProcess | 1,082 |
| NegativePrecedingEvent | 1,065 |
| ExternalOntology | 1,008 |
| Figure | 882 |
| Requirement | 851 |
| Disease | 770 |
| InterChainCrosslinkedResidue | 747 |
| EntityFunctionalStatus | 698 |
| FailedReaction | 457 |
| GO_CellularComponent | 451 |
| TopLevelPathway | 415 |
| Taxon | 408 |
| IntraChainCrosslinkedResidue | 382 |
| MarkerReference | 365 |
| Affiliation | 348 |
| OtherEntity | 348 |
| NegativeGeneExpressionRegulation | 331 |
| Polymerisation | 253 |
| PsiMod | 175 |
| Compartment | 156 |
| FragmentInsertionModification | 145 |
| ReferenceGroup | 142 |
| Release | 142 |
| Book | 133 |
| ReferenceDatabase | 103 |
| Species | 95 |
| ProteinDrug | 95 |
| FragmentDeletionModification | 84 |
| ModifiedNucleotide | 76 |
| TranscriptionalModification | 76 |
| URL | 55 |
| CellType | 31 |
| Depolymerisation | 29 |
| FunctionalStatus | 27 |
| Cell | 24 |
| CellDevelopmentStep | 22 |
| ControlledVocabulary | 21 |
| SequenceOntology | 15 |
| Anatomy | 14 |
| CellLineagePath | 12 |
| DeletedControlledVocabulary | 7 |
| NegativePrecedingEventReason | 6 |
| ReviewStatus | 5 |
| FunctionalStatusType | 5 |
| EvidenceType | 3 |
| DrugActionType | 3 |
| ReactionType | 3 |
| RNADrug | 2 |
| DBInfo | 1 |
| Type | Count |
|---|---|
| created | 2,797,487 |
| referenceDatabase | 1,869,394 |
| crossReference | 1,094,497 |
| species | 739,423 |
| referenceGene | 639,701 |
| compartment | 560,432 |
| inferredTo | 455,759 |
| author | 422,897 |
| hasComponent | 274,487 |
| referenceEntity | 244,018 |
| referenceTranscript | 219,870 |
| summation | 210,761 |
| input | 186,017 |
| hasMember | 172,620 |
| modified | 165,988 |
| output | 158,432 |
| interactor | 155,990 |
| hasModifiedResidue | 124,194 |
| hasEvent | 120,933 |
| literatureReference | 106,182 |
| evidenceType | 96,169 |
| precedingEvent | 73,882 |
| release | 60,837 |
| updatedInstance | 60,837 |
| referenceSequence | 56,756 |
| psiMod | 56,312 |
| catalystActivity | 52,010 |
| activity | 40,699 |
| physicalEntity | 40,297 |
| hasCandidate | 35,687 |
| reviewed | 27,812 |
| authored | 23,491 |
| reviewStatus | 21,779 |
| edited | 21,549 |
| disease | 20,493 |
| goBiologicalProcess | 18,300 |
| modification | 14,050 |
| activeUnit | 11,790 |
| regulatedBy | 11,164 |
| deletedInstance | 10,591 |
| regulator | 7,833 |
| reason | 7,099 |
| includedLocation | 6,960 |
| hasEncapsulatedEvent | 6,524 |
| replacementInstances | 3,967 |
| relatedSpecies | 3,030 |
| structureModified | 2,943 |
| internalReviewed | 2,675 |
| regulationReference | 1,985 |
| regulation | 1,897 |
| previousReviewStatus | 1,795 |
| repeatedUnit | 1,432 |
| revised | 1,423 |
| catalystActivityReference | 1,402 |
| negativePrecedingEvent | 1,065 |
| functionalStatus | 1,061 |
| figure | 940 |
| entityFunctionalStatus | 798 |
| secondReferenceSequence | 747 |
| normalReaction | 722 |
| diseaseEntity | 698 |
| equivalentTo | 683 |
| normalEntity | 617 |
| goCellularComponent | 612 |
| entityOnOtherCell | 563 |
| instanceOf | 535 |
| affiliation | 494 |
| normalPathway | 469 |
| superTaxon | 404 |
| isoformParent | 384 |
| marker | 365 |
| cell | 365 |
| markerReference | 365 |
| componentOf | 272 |
| proteinMarker | 191 |
| surroundedBy | 189 |
| RNAMarker | 172 |
| cellType | 114 |
| reverseReaction | 112 |
| requiredInputComponent | 105 |
| publisher | 97 |
| hasPart | 33 |
| tissue | 30 |
| functionalStatusType | 27 |
| structuralVariant | 27 |
| reactionType | 15 |
| organ | 15 |
| tissueLayer | 5 |
3. Methodology
Benchmark Design
- Each query is executed once per engine (cold run, no prior caching)
- Timing is measured with nanosecond precision (
time.perf_counter_ns()) from the Python client side, including network round-trip - All engines use the same Cypher queries, with minor syntax adaptations where necessary
- Results include the full query execution and result materialization time
Why Cold Runs
Neo4j and Memgraph employ aggressive query result caching. After a first execution, subsequent runs of the same query return cached results in near-zero time, which does not reflect real-world analytical workload patterns where queries vary. We therefore report single cold-run timings to measure actual query processing performance.Execution Pipeline
For each dataset, the benchmark pipeline (run.sh):
- Stops all database engines
- Starts the first engine and loads the dataset
- Executes all queries sequentially, recording wall-clock time per query
- Stops the engine and repeats for the next engine
- Generates a summary report with speedup ratios
4. Results Overview
Poledb
| Query | TuringDB | Neo4j | Memgraph | Speedup vs Neo4j | Speedup vs Memgraph |
|---|---|---|---|---|---|
MATCH (n) RETURN n | 57ms | 3130ms | 1858ms | 55x | 33x |
MATCH (p:Person) RETURN p | 3ms | 134ms | 25ms | 45x | 8.3x |
MATCH (p:Person) RETURN count(p) | 1ms | 115ms | 8ms | 115x | 8.0x |
MATCH (c:Crime) RETURN c | 15ms | 842ms | 772ms | 56x | 51x |
MATCH (c:Crime) RETURN count(c) | 1ms | 45ms | 13ms | 45x | 13x |
MATCH ()-[r]->() RETURN r | 64ms | 2660ms | 2386ms | 42x | 37x |
MATCH ()-[r]->() RETURN count(r) | 9ms | 55ms | 28ms | 6.1x | 3.1x |
MATCH (p:Person {name: 'John'})-[:PARTY_TO]->(c:Crime) RETURN p, c | 4ms | 24ms | 0ms | 6.0x | - |
MATCH (p:Person)-[:PARTY_TO]->(c:Crime) RETURN p.name, p.surname, c.type | 1ms | 28ms | 11ms | 28x | 11x |
MATCH (p:Person {surname: 'Smith'})-[r]->(n) RETURN p | 3ms | 64ms | 2ms | 21x | 0.7x |
MATCH (p:Person)-[r]->(n) WHERE p.surname = 'Smith' RETURN p | 3ms | 60ms | 1ms | 20x | 0.3x |
MATCH (p1:Person)-[:PARTY_TO]->(c:Crime)<-[:PARTY_TO]-(p2:Person) WHERE p1 <> p2 RETURN p1.name, p2.name, c.type | 1ms | 126ms | 8ms | 126x | 8.0x |
MATCH (p1:Person)-[:KNOWS]->(p2:Person)-[:PARTY_TO]->(c:Crime) RETURN p1.name, p2.name | 1ms | 24ms | 11ms | 24x | 11x |
MATCH (c:Crime)-[:OCCURRED_AT]->(l:Location) RETURN l.postcode | 30ms | 553ms | 362ms | 18x | 12x |
Reactome
| Query | TuringDB | Neo4j | Memgraph | Speedup vs Neo4j | Speedup vs Memgraph |
|---|---|---|---|---|---|
MATCH (n:Drug) RETURN n | 2ms | 977ms | 371ms | 488x | 186x |
MATCH (n:ProteinDrug) RETURN n | 1ms | 221ms | 340ms | 221x | 340x |
MATCH (n:Drug:ProteinDrug) RETURN n | 1ms | 270ms | 359ms | 270x | 359x |
MATCH (n:Taxon)-->(m:Species) RETURN n,m | 1ms | 259ms | 301ms | 259x | 301x |
MATCH (n)-->(m:Interaction)-->(o) RETURN n,m,o | 707ms | 33117ms | 32609ms | 47x | 46x |
MATCH (n{displayName:"Autophagy"}) RETURN n | 283ms | 918ms | 629ms | 3.2x | 2.2x |
MATCH (n{displayName:"Autophagy"})-->(m) RETURN m | 216ms | 628ms | 540ms | 2.9x | 2.5x |
MATCH (n{displayName:"Autophagy"})-->(m)-->(p) RETURN p | 215ms | 622ms | 569ms | 2.9x | 2.6x |
MATCH (n{displayName:"Autophagy"})-->(m)-->(p)-->(q) RETURN q | 370ms | 878ms | 702ms | 2.4x | 1.9x |
MATCH (n{displayName:"Autophagy"})-->(m)-->(p)-->(q)-->(r) RETURN r | 236ms | 2776ms | 2595ms | 12x | 11x |
MATCH (n{displayName:"Autophagy"})-->(m)-->(p)-->(q)-->(r)-->(s) RETURN s | 296ms | 5784ms | 5012ms | 20x | 17x |
MATCH (n{displayName:"Autophagy"})-->(m)-->(p)-->(q)-->(r)-->(s)-->(t) RETURN t | 493ms | 17983ms | 17256ms | 36x | 35x |
MATCH (n{displayName:"Autophagy"})-->(m)-->(p)-->(q)-->(r)-->(s)-->(t)-->(v) RETURN v | 1149ms | 66876ms | 54252ms | 58x | 47x |
MATCH (n{displayName:"APOE-4 [extracellular region]"}) RETURN n | 351ms | 887ms | 705ms | 2.5x | 2.0x |
MATCH (n{displayName:"APOE-4 [extracellular region]"})-->(m) RETURN m | 215ms | 734ms | 567ms | 3.4x | 2.6x |
MATCH (n{displayName:"APOE-4 [extracellular region]"})-->(m)-->(p) RETURN p | 211ms | 616ms | 603ms | 2.9x | 2.9x |
MATCH (n{displayName:"APOE-4 [extracellular region]"})-->(m)-->(p)-->(q) RETURN q | 213ms | 613ms | 585ms | 2.9x | 2.7x |
MATCH (n{displayName:"APOE-4 [extracellular region]"})-->(m)-->(p)-->(q)-->(r) RETURN r | 213ms | 649ms | 560ms | 3.0x | 2.6x |
MATCH (n{displayName:"APOE-4 [extracellular region]"})-->(m)-->(p)-->(q)-->(r)-->(s) RETURN s | 211ms | 669ms | 537ms | 3.2x | 2.5x |
MATCH (n{displayName:"APOE-4 [extracellular region]"})-->(m)-->(p)-->(q)-->(r)-->(s)-->(t) RETURN t | 211ms | 644ms | 557ms | 3.1x | 2.6x |
MATCH (n{displayName:"APOE-4 [extracellular region]"})-->(m)-->(p)-->(q)-->(r)-->(s)-->(t)-->(v) RETURN v | 215ms | 11087ms | 465ms | 52x | 2.2x |
MATCH (n)-[e:release]->(m) RETURN n,m | 240ms | 4046ms | 3162ms | 17x | 13x |
MATCH (n)-[e:interactor]->(m) RETURN n,m | 318ms | 26174ms | 25184ms | 82x | 79x |
MATCH (n)-[e:surroundedBy]->(m) RETURN n,m | 202ms | 1548ms | 367ms | 7.7x | 1.8x |
MATCH (n)-[:hasEvent]->(m) RETURN n,m | 292ms | 11204ms | 11008ms | 38x | 38x |
MATCH (n:Pathway)-[:hasEvent]->(m:ReactionLikeEvent) RETURN n,m | 94ms | 8442ms | 8696ms | 90x | 93x |
MATCH (r:ReactionLikeEvent)-[:output]->(s:PhysicalEntity) RETURN r,s | 184ms | 13383ms | 13591ms | 73x | 74x |
MATCH (n:DatabaseObject{isChimeric:false}) RETURN n | 239ms | 2507ms | 1538ms | 10x | 6.4x |
MATCH (n:DatabaseObject{isChimeric:true}) RETURN n | 213ms | 799ms | 429ms | 3.8x | 2.0x |
MATCH (b)-->(a:Pathway) RETURN a | 324ms | 4447ms | 5917ms | 14x | 18x |
MATCH (c)-->(b)-->(a:Pathway) RETURN a, c | 2318ms | 35138ms | 34946ms | 15x | 15x |
MATCH (c)-->(b)-->(a:Pathway) RETURN b | 2109ms | 22699ms | 22932ms | 11x | 11x |
MATCH (c)-->(b)-->(a:Pathway) RETURN c | 1978ms | 18090ms | 17969ms | 9.1x | 9.1x |
MATCH (c)-->(b)-->(a:Pathway) RETURN a | 1977ms | 22424ms | 23598ms | 11x | 12x |
5. Results by Query Category
5.1 Label Scans
Poledb:| Query | TuringDB | Neo4j | Memgraph | Speedup vs Neo4j | Speedup vs Memgraph |
|---|---|---|---|---|---|
MATCH (p:Person) RETURN p | 3ms | 134ms | 25ms | 45x | 8.3x |
MATCH (c:Crime) RETURN c | 15ms | 842ms | 772ms | 56x | 51x |
| Query | TuringDB | Neo4j | Memgraph | Speedup vs Neo4j | Speedup vs Memgraph |
|---|---|---|---|---|---|
MATCH (n:Drug) RETURN n | 2ms | 977ms | 371ms | 488x | 186x |
MATCH (n:ProteinDrug) RETURN n | 1ms | 221ms | 340ms | 221x | 340x |
MATCH (n:Drug:ProteinDrug) RETURN n | 1ms | 270ms | 359ms | 270x | 359x |
5.2 Full Graph Scans
Poledb:| Query | TuringDB | Neo4j | Memgraph | Speedup vs Neo4j | Speedup vs Memgraph |
|---|---|---|---|---|---|
MATCH (n) RETURN n | 57ms | 3130ms | 1858ms | 55x | 33x |
MATCH ()-[r]->() RETURN r | 64ms | 2660ms | 2386ms | 42x | 37x |
5.3 Property-Based Filtering
Poledb:| Query | TuringDB | Neo4j | Memgraph | Speedup vs Neo4j | Speedup vs Memgraph |
|---|---|---|---|---|---|
MATCH (p:Person {name: 'John'})-[:PARTY_TO]->(c:Crime) RETURN p, c | 4ms | 24ms | 0ms | 6.0x | - |
MATCH (p:Person {surname: 'Smith'})-[r]->(n) RETURN p | 3ms | 64ms | 2ms | 21x | 0.7x |
MATCH (p:Person)-[r]->(n) WHERE p.surname = 'Smith' RETURN p | 3ms | 60ms | 1ms | 20x | 0.3x |
MATCH (p1:Person)-[:PARTY_TO]->(c:Crime)<-[:PARTY_TO]-(p2:Person) WHERE p1 <> p2 RETURN p1.name, p2.name, c.type | 1ms | 126ms | 8ms | 126x | 8.0x |
| Query | TuringDB | Neo4j | Memgraph | Speedup vs Neo4j | Speedup vs Memgraph |
|---|---|---|---|---|---|
MATCH (n{displayName:"Autophagy"}) RETURN n | 283ms | 918ms | 629ms | 3.2x | 2.2x |
MATCH (n{displayName:"Autophagy"})-->(m) RETURN m | 216ms | 628ms | 540ms | 2.9x | 2.5x |
MATCH (n{displayName:"APOE-4 [extracellular region]"}) RETURN n | 351ms | 887ms | 705ms | 2.5x | 2.0x |
MATCH (n{displayName:"APOE-4 [extracellular region]"})-->(m) RETURN m | 215ms | 734ms | 567ms | 3.4x | 2.6x |
MATCH (n:DatabaseObject{isChimeric:false}) RETURN n | 239ms | 2507ms | 1538ms | 10x | 6.4x |
MATCH (n:DatabaseObject{isChimeric:true}) RETURN n | 213ms | 799ms | 429ms | 3.8x | 2.0x |
5.4 Relationship Type Traversal
Poledb:| Query | TuringDB | Neo4j | Memgraph | Speedup vs Neo4j | Speedup vs Memgraph |
|---|---|---|---|---|---|
MATCH (p:Person)-[:PARTY_TO]->(c:Crime) RETURN p.name, p.surname, c.type | 1ms | 28ms | 11ms | 28x | 11x |
MATCH (p1:Person)-[:KNOWS]->(p2:Person)-[:PARTY_TO]->(c:Crime) RETURN p1.name, p2.name | 1ms | 24ms | 11ms | 24x | 11x |
MATCH (c:Crime)-[:OCCURRED_AT]->(l:Location) RETURN l.postcode | 30ms | 553ms | 362ms | 18x | 12x |
| Query | TuringDB | Neo4j | Memgraph | Speedup vs Neo4j | Speedup vs Memgraph |
|---|---|---|---|---|---|
MATCH (n)-[e:release]->(m) RETURN n,m | 240ms | 4046ms | 3162ms | 17x | 13x |
MATCH (n)-[e:interactor]->(m) RETURN n,m | 318ms | 26174ms | 25184ms | 82x | 79x |
MATCH (n)-[e:surroundedBy]->(m) RETURN n,m | 202ms | 1548ms | 367ms | 7.7x | 1.8x |
MATCH (n)-[:hasEvent]->(m) RETURN n,m | 292ms | 11204ms | 11008ms | 38x | 38x |
MATCH (n:Pathway)-[:hasEvent]->(m:ReactionLikeEvent) RETURN n,m | 94ms | 8442ms | 8696ms | 90x | 93x |
MATCH (r:ReactionLikeEvent)-[:output]->(s:PhysicalEntity) RETURN r,s | 184ms | 13383ms | 13591ms | 73x | 74x |
5.5 Multi-Hop Traversals
Reactome:| Query | TuringDB | Neo4j | Memgraph | Speedup vs Neo4j | Speedup vs Memgraph |
|---|---|---|---|---|---|
MATCH (n{displayName:"Autophagy"})-->(m)-->(p) RETURN p | 215ms | 622ms | 569ms | 2.9x | 2.6x |
MATCH (n{displayName:"Autophagy"})-->(m)-->(p)-->(q) RETURN q | 370ms | 878ms | 702ms | 2.4x | 1.9x |
MATCH (n{displayName:"Autophagy"})-->(m)-->(p)-->(q)-->(r) RETURN r | 236ms | 2776ms | 2595ms | 12x | 11x |
MATCH (n{displayName:"Autophagy"})-->(m)-->(p)-->(q)-->(r)-->(s) RETURN s | 296ms | 5784ms | 5012ms | 20x | 17x |
MATCH (n{displayName:"Autophagy"})-->(m)-->(p)-->(q)-->(r)-->(s)-->(t) RETURN t | 493ms | 17983ms | 17256ms | 36x | 35x |
MATCH (n{displayName:"Autophagy"})-->(m)-->(p)-->(q)-->(r)-->(s)-->(t)-->(v) RETURN v | 1149ms | 66876ms | 54252ms | 58x | 47x |
MATCH (n{displayName:"APOE-4 [extracellular region]"})-->(m)-->(p) RETURN p | 211ms | 616ms | 603ms | 2.9x | 2.9x |
MATCH (n{displayName:"APOE-4 [extracellular region]"})-->(m)-->(p)-->(q) RETURN q | 213ms | 613ms | 585ms | 2.9x | 2.7x |
MATCH (n{displayName:"APOE-4 [extracellular region]"})-->(m)-->(p)-->(q)-->(r) RETURN r | 213ms | 649ms | 560ms | 3.0x | 2.6x |
MATCH (n{displayName:"APOE-4 [extracellular region]"})-->(m)-->(p)-->(q)-->(r)-->(s) RETURN s | 211ms | 669ms | 537ms | 3.2x | 2.5x |
MATCH (n{displayName:"APOE-4 [extracellular region]"})-->(m)-->(p)-->(q)-->(r)-->(s)-->(t) RETURN t | 211ms | 644ms | 557ms | 3.1x | 2.6x |
MATCH (n{displayName:"APOE-4 [extracellular region]"})-->(m)-->(p)-->(q)-->(r)-->(s)-->(t)-->(v) RETURN v | 215ms | 11087ms | 465ms | 52x | 2.2x |
5.6 Complex Patterns
Reactome:| Query | TuringDB | Neo4j | Memgraph | Speedup vs Neo4j | Speedup vs Memgraph |
|---|---|---|---|---|---|
MATCH (n:Taxon)-->(m:Species) RETURN n,m | 1ms | 259ms | 301ms | 259x | 301x |
MATCH (n)-->(m:Interaction)-->(o) RETURN n,m,o | 707ms | 33117ms | 32609ms | 47x | 46x |
MATCH (b)-->(a:Pathway) RETURN a | 324ms | 4447ms | 5917ms | 14x | 18x |
MATCH (c)-->(b)-->(a:Pathway) RETURN a, c | 2318ms | 35138ms | 34946ms | 15x | 15x |
MATCH (c)-->(b)-->(a:Pathway) RETURN b | 2109ms | 22699ms | 22932ms | 11x | 11x |
MATCH (c)-->(b)-->(a:Pathway) RETURN c | 1978ms | 18090ms | 17969ms | 9.1x | 9.1x |
MATCH (c)-->(b)-->(a:Pathway) RETURN a | 1977ms | 22424ms | 23598ms | 11x | 12x |
5.7 Aggregations
Poledb:| Query | TuringDB | Neo4j | Memgraph | Speedup vs Neo4j | Speedup vs Memgraph |
|---|---|---|---|---|---|
MATCH (p:Person) RETURN count(p) | 1ms | 115ms | 8ms | 115x | 8.0x |
MATCH (c:Crime) RETURN count(c) | 1ms | 45ms | 13ms | 45x | 13x |
MATCH ()-[r]->() RETURN count(r) | 9ms | 55ms | 28ms | 6.1x | 3.1x |
6. Why TuringDB Is Faster: Architectural Deep Dive
Column-Oriented Storage vs. Row-Oriented
Traditional graph databases (Neo4j, Memgraph) use row-oriented storage: each node is stored as a self-contained record with all its properties and adjacency pointers. This is efficient for single-node lookups but wasteful for analytical queries that scan many nodes and only need a few properties. TuringDB uses column-oriented storage: each property is stored as a separate, contiguous array. This design, proven in analytical relational databases (ClickHouse, DuckDB), is adapted here for graph workloads. Impact on benchmarks:- Label scans touch only the label column, not entire node records
- Property filters scan a single column instead of deserializing full nodes
- Aggregations (COUNT, etc.) operate on dense integer arrays
Streaming Execution vs. Volcano Model
Neo4j and Memgraph use the Volcano (iterator) model: each operator in the query plan produces one row at a time, pulling from the operator below. This introduces per-row function call overhead and prevents vectorized processing. TuringDB uses a streaming columnar execution engine: each operator processes a batch of values (a column) at once. This enables:- SIMD vectorization — processing multiple values per CPU instruction
- Reduced function call overhead — one call per batch, not per row
- Better branch prediction — uniform operations on homogeneous data
Zero-Locking Snapshot Isolation
TuringDB’s immutable DataPart architecture means read queries never contend with writes. While this benchmark runs queries sequentially (no concurrent load), the zero-locking design means there is no lock acquisition overhead even for single queries — the engine skips the entire lock management code path.No Index Dependency
Neo4j and Memgraph rely on indexes to accelerate property lookups — and in this benchmark, they benefit from the indexes and constraints included in the original dataset dump. TuringDB does not use explicit index structures. Its columnar layout makes property scans inherently fast — the column itself acts as a natural “index” for scan-based access patterns. Despite the competitors having indexes available, TuringDB still outperforms them on most property filter queries.7. Where Competitors Win
The following 2 queries (4% of benchmark) show a competitor outperforming TuringDB:| Dataset | Query | Competitor | TuringDB | Competitor Time | Speedup |
|---|---|---|---|---|---|
| poledb | MATCH (p:Person {surname: 'Smith'})-[r]->(n) RETURN p | Memgraph | 3ms | 2ms | 0.7x |
| poledb | MATCH (p:Person)-[r]->(n) WHERE p.surname = 'Smith' RETURN p | Memgraph | 3ms | 1ms | 0.3x |
8. Limitations and Caveats
- Single-machine benchmark. All engines ran locally; distributed deployment characteristics are not measured.
- Cold-run only. Results reflect first-execution performance. Workloads with repeated identical queries would benefit from Neo4j/Memgraph’s query caching.
- Import-provided configuration. Neo4j and Memgraph use the indexes and constraints from the original dump, and Memgraph runs in in-memory analytical mode. No additional tuning was applied. Production deployments may include further optimizations.
- Client-side timing. Measurements include Python client overhead and network round-trip (localhost). TuringDB uses HTTP while Neo4j and Memgraph use the more efficient Bolt protocol, giving the competitors a wire-protocol advantage.
- Two datasets. Results may not generalize to all graph structures or query patterns. Additional datasets would strengthen conclusions.
- No concurrent load. The benchmark runs queries sequentially with no simulated concurrent users.
9. Reproducing the Benchmark
All benchmark code, queries, and dataset import scripts are open source: Repository: https://github.com/turing-db/turing-benchAppendix A: Full Query Listing
-
Click to expand
Poledb
Full Graph Scans
Label Scans
Property-Based Filtering
Aggregations
Relationship Type Traversal
Reactome
Label Scans
Property-Based Filtering
Multi-Hop Traversals
Relationship Type Traversal
Complex Patterns
Appendix B: Raw Timing Data
The raw benchmark output (per-query mean/min/max/median) is available in thereports/ directory of the repository after running the benchmark.
https://github.com/turing-db/turing-bench
