-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbench.ts
More file actions
95 lines (77 loc) · 3.64 KB
/
Copy pathbench.ts
File metadata and controls
95 lines (77 loc) · 3.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#!/usr/bin/env tsx
/**
* Simple benchmark to measure query performance with and without indexes.
*
* Usage:
* npx tsx bench.ts # default queries on cloud-infra.json
* npx tsx bench.ts -g examples/social-graph.json
* npx tsx bench.ts -q 'MATCH (s:Service) RETURN s' 'MATCH (s:Service)-[r:DEPENDS_ON*1..2]->(d) RETURN s, d'
*/
import { readFileSync } from 'fs';
import { resolve } from 'path';
import { createGraph, buildGraphIndexes, GraphEngine, parseCypher } from './src/lib';
import type { GraphologyFile } from './src/lib';
import type { GraphIndexes } from './src/types/cypher';
// ── Helpers ──────────────────────────────────────────────────────────────────
function loadGraph(path: string): { data: GraphologyFile; graph: ReturnType<typeof createGraph> } {
const raw = readFileSync(resolve(path), 'utf-8');
const data = JSON.parse(raw) as GraphologyFile;
return { data, graph: createGraph(data) };
}
async function benchmark(query: string, graph: ReturnType<typeof createGraph>, indexes?: GraphIndexes): Promise<{ ms: number; rows: number }> {
const engine = new GraphEngine(graph, indexes);
const ast = parseCypher(query);
const iterations = 50;
const start = performance.now();
let lastRows = 0;
for (let i = 0; i < iterations; i++) {
const results = await engine.execute(ast);
lastRows = results.length;
}
const elapsed = performance.now() - start;
return { ms: elapsed / iterations, rows: lastRows };
}
// ── CLI arg parsing ──────────────────────────────────────────────────────────
let graphPath = 'examples/bench-graph.json';
const defaultQueries = [
'MATCH (s:Service) RETURN s',
'MATCH (s:Service)-[r:RPC*1..2]->(d) RETURN s.name, d.name',
'MATCH (n) RETURN count(n) AS total',
'MATCH (s:Service) RETURN s ORDER BY s.name SKIP 2 LIMIT 5',
'MATCH (s:Service {type: "RPC"}) RETURN s.name',
'MATCH (s:Service)-[r:HTTP*1..3]-(t) RETURN s.name, t.name',
'MATCH (s:Service) WITH s, count((s)-[]->()) AS outDegree RETURN s.name, outDegree ORDER BY outDegree DESC LIMIT 10',
];
let queries: string[] = [];
const args = process.argv.slice(2);
for (let i = 0; i < args.length; i++) {
const arg = args[i]!;
if (arg === '-g' && args[i + 1]) {
graphPath = args[++i]!;
} else if (arg === '-q') {
queries = args.slice(i + 1);
break;
}
}
if (!queries.length) queries = defaultQueries;
// ── Run ──────────────────────────────────────────────────────────────────────
const { data, graph } = loadGraph(graphPath);
const indexes = buildGraphIndexes(graph);
console.log(`Graph: ${data.nodes.length} nodes, ${data.edges.length} edges`);
console.log('');
const header = `${'Query'.padEnd(65)} | ${'No index'.padEnd(12)} | ${'Indexed'.padEnd(12)} | Speedup`;
console.log(header);
console.log('\u2500'.repeat(header.length));
for (const q of queries) {
const short = q.length > 63 ? q.slice(0, 60) + '...' : q;
const noIndex = await benchmark(q, graph);
const withIndex = await benchmark(q, graph, indexes);
const speedup = noIndex.ms > 0 ? (noIndex.ms / withIndex.ms).toFixed(1) : '\u221e';
const label = `${noIndex.rows} rows`;
console.log(
`${short.padEnd(65)} | ` +
`${noIndex.ms.toFixed(2)}ms (${label}) | ` +
`${withIndex.ms.toFixed(2)}ms (${label}) | ` +
`${speedup}x`
);
}