NornicDB vs Neo4j — Northwind Benchmark Comparison¶
- Products seeded: 48,000, Orders seeded: 48,000
- Iterations per query: 10 (after 2 warmup iterations)
Summary¶
| Metric | NornicDB | Neo4j | Delta | Ratio |
|---|---|---|---|---|
| Overall mean latency (ms) | 0.23 | 98.64 | -99.8% | 432.38× |
| Overall throughput (ops/sec) | 17.70 | 7.76 | +128.1% | 2.28× |
| Seed duration (ms) | 6,795.99 | 5,750.07 | +18.2% | 0.85× |
| Avg CPU power (mW) | 8,923.47 | 8,608.14 | +3.7% | 0.96× |
| Avg GPU power (mW) | 39.93 | 95.37 | -58.1% | 2.39× |
| Avg package power (mW) | 8,963.41 | 8,703.51 | +3.0% | 0.97× |
| Energy during benchmark (J) | 118.00 | 264.87 | -55.5% | 2.24× |
| Benchmark wall-clock (s) | 14.00 | 31.63 | -55.7% | 2.26× |
| Peak memory used (bytes) | 32.5 GiB | 33.4 GiB | -2.8% | 1.03× |
| Raw data files (bytes) | 139,575,296 | 53,207,040 | +162.3% | 0.38× |
Delta = (NornicDB − Neo4j) / Neo4j. Ratio compares Neo4j to NornicDB for metrics where lower is better (latency, energy, disk), and NornicDB to Neo4j for throughput (higher is better).
Per-Query Latency¶
| Query | NornicDB mean (ms) | Neo4j mean (ms) | Delta | NornicDB P95 | Neo4j P95 | NornicDB ops/s | Neo4j ops/s |
|---|---|---|---|---|---|---|---|
products_per_category | 0.23 | 9.34 | -97.6% | 0.26 | 12.82 | 4,022.12 | 106.74 |
customer_category_distinct_orders | 0.25 | 226.67 | -99.9% | 0.27 | 259.13 | 3,946.72 | 4.41 |
optional_match_orders_count | 0.20 | 72.58 | -99.7% | 0.24 | 84.98 | 4,467.36 | 13.77 |
revenue_by_product | 0.24 | 85.96 | -99.7% | 0.33 | 93.62 | 4,170.79 | 11.63 |
Correctness¶
Seed verification. Post-seed counts reported by each database (via MATCH (n:Label) RETURN count(n) and equivalent edge queries).
| Entity | NornicDB | Neo4j | Match |
|---|---|---|---|
| Category | 96 | 96 | ✅ |
| Supplier | 144 | 144 | ✅ |
| Customer | 1,200 | 1,200 | ✅ |
| Product | 48,000 | 48,000 | ✅ |
| Order | 48,000 | 48,000 | ✅ |
| PART_OF | 48,000 | 48,000 | ✅ |
| SUPPLIES | 48,000 | 48,000 | ✅ |
| PURCHASED | 48,000 | 48,000 | ✅ |
| ORDERS | 168,050 | 168,050 | ✅ |
Per-query result fingerprints. Each engine runs the query on the first (warmup) iteration, canonicalises the full result set, and hashes it with SHA-256. A matching row_count + matching hash means the engines returned the same data.
| Query | NornicDB rows | Neo4j rows | NornicDB hash | Neo4j hash | Match |
|---|---|---|---|---|---|
products_per_category | 96 | 96 | 91e9f1f06368… | 91e9f1f06368… | ✅ |
customer_category_distinct_orders | 10 | 10 | 5da36214d516… | 5da36214d516… | ✅ |
optional_match_orders_count | 100 | 100 | 8950fcdaab16… | 8950fcdaab16… | ✅ |
revenue_by_product | 10 | 10 | 60b64c678f4c… | 60b64c678f4c… | ✅ |
Intra-run stability. Every iteration of each query re-fingerprints its result set; a mismatch within a single engine's run is flagged below.
- No intra-run mismatches on either engine.
✅ All correctness checks passed — both engines seeded identically and returned identical result sets (by row count and canonical SHA-256 fingerprint) for every benchmark query.
Storage¶
Raw data files only (preallocated scratch, WAL, and indexes excluded from the headline):
| Bucket | NornicDB | Neo4j |
|---|---|---|
| Raw data | 133.1 MiB (139,575,296 B) | 50.7 MiB (53,207,040 B) |
| Indexes / stats | 0 B (0 B) | 7.5 MiB (7,823,360 B) |
| Write-ahead logs | 260.0 KiB (266,240 B) | 144.4 MiB (151,363,584 B) |
| Metadata | 8.0 KiB (8,192 B) | 1.1 MiB (1,191,936 B) |
| Scratch (excluded) | 1.0 MiB (1,048,576 B) | 4.0 KiB (4,096 B) |
| Unclassified | 0 B (0 B) | 0 B (0 B) |
Total du | 134.4 MiB | 203.7 MiB |
- Raw data ratio: 2.62× Neo4j (larger)
- Full-dir ratio (includes scratch/WAL): 0.66× Neo4j
Power¶
| NornicDB | Neo4j | |
|---|---|---|
| Samples | 13 | 30 |
| Duration (s) | 13.16 | 30.43 |
| CPU avg (mW) | 8,923.5 | 8,608.1 |
| GPU avg (mW) | 39.9 | 95.4 |
| Package avg (mW) | 8,963.4 | 8,703.5 |
| Energy (J) | 118.00 | 264.87 |
Memory Pressure¶
System-wide memory during each engine's full lifecycle (startup → benchmark → shutdown).
| NornicDB | Neo4j | |
|---|---|---|
| Samples | 16 | 34 |
| Avg used (active+wired+compressor) | 31.8 GiB | 32.5 GiB |
| Peak used | 32.5 GiB | 33.4 GiB |
| Avg free | 13.1 GiB | 12.0 GiB |
| Min free | 12.3 GiB | 10.9 GiB |
| Avg compressed (logical) | 17.1 GiB | 17.1 GiB |
| Peak compressed | 17.1 GiB | 17.1 GiB |
Notes¶
- Power figures are Apple
powermetricsestimates; treat as directional, not absolute. Apple's own docs note that reported averages are approximate. - Both databases were freshly initialized before each run; Neo4j was stopped during the NornicDB run, and vice versa, to isolate measurements.
- Benchmarks ran over the Bolt protocol using the neo4j-go-driver.
- Storage classification: NornicDB raw data =
*.sst+*.vlog(LSM records + value log). Neo4j raw data =neostore*store.db*(record stores). Preallocated scratch files — Badger's 8 MiB memtable (*.mem) and 1 MiB discard log (DISCARD), and Neo4j empty*.idallocation files — are excluded because their size is fixed/preallocated and does not scale with the dataset.