Summary (benchmark integrity — must fix before publishing "official" numbers)
benchmarks/gfql/graph_benchmark_q1_q9.py (GFQL vs Kuzu/Ladybug/Neo4j/Memgraph on the prrao87 graph-benchmark suite) gives GFQL two unfair advantages vs the competitor databases, which all run their own real Cypher:
1. Dataframe shortcuts bypass the GFQL engine entirely
For q1, q3, q4, q5, q6, q7 (query_variant="standard", the default — _uses_dataframe_shortcut, line 238-243), GFQL does not run its query engine. It runs hand-written pandas/cuDF groupby/merge code (_query{1,3,4,5,6,7}_dataframe_shortcut, plus _query5_polars_shortcut, _query6_cudf_shortcut, etc.). That measures hand-tuned dataframe code, not GFQL — while Kuzu/Neo4j/Memgraph parse+plan+execute real Cypher. Not apples-to-apples.
- Fix: run real GFQL (
g.gfql(<cypher>, engine=...)) for all queries. On pandas/cuDF this works for q1-q9 once the count(<other-alias>) lowering routing is fixed (see the lowering issue; q1 also works today via the count(*) form). On polars, traversal queries NIE (see the polars binding_ops issue) and count(*) is currently wrong (see the P0 count-broadcast issue).
2. Untimed lowercase precompute (both GFQL and the Memgraph runner)
graph_benchmark_q1_q9.py precomputes gender_lc/interest_lc at load (untimed) for the toLower() filters in q5/q6/q7. Measured impact when moved inside the timed region: <1ms (negligible) — but for symmetry it should be timed or removed.
graph_benchmark_memgraph_q1_q9.py similarly precomputes lowercased columns. The competitors (Kuzu/Ladybug/Neo4j) run raw tolower() inline. For official numbers, no side gets untimed precompute — everyone runs the raw query.
Data-scale caveat (already caught)
/tmp/graph-benchmark-gfql-memgraph = TINY (1k persons/10k edges). Full data (100k/2.42M) = /tmp/graph-benchmark-gfql-memgraph-full (repo data/ symlinks to it; the Docker mount does NOT follow the symlink — mount the target dir directly). An earlier Memgraph q1-q9 run on the tiny set produced invalid ~100× fast numbers.
Acceptance
- GFQL runs its real engine (no dataframe shortcuts) for every query it reports.
- No untimed precompute for any system.
- Every system runs the identical canonical query from the prrao87 repo (
neo4j/query.py etc.).
- Where GFQL can't yet run a query as real GFQL on an engine, the cell is marked honestly (NIE / in-progress), NOT shortcut-faked.
Summary (benchmark integrity — must fix before publishing "official" numbers)
benchmarks/gfql/graph_benchmark_q1_q9.py(GFQL vs Kuzu/Ladybug/Neo4j/Memgraph on the prrao87 graph-benchmark suite) gives GFQL two unfair advantages vs the competitor databases, which all run their own real Cypher:1. Dataframe shortcuts bypass the GFQL engine entirely
For q1, q3, q4, q5, q6, q7 (
query_variant="standard", the default —_uses_dataframe_shortcut, line 238-243), GFQL does not run its query engine. It runs hand-written pandas/cuDFgroupby/merge code (_query{1,3,4,5,6,7}_dataframe_shortcut, plus_query5_polars_shortcut,_query6_cudf_shortcut, etc.). That measures hand-tuned dataframe code, not GFQL — while Kuzu/Neo4j/Memgraph parse+plan+execute real Cypher. Not apples-to-apples.g.gfql(<cypher>, engine=...)) for all queries. On pandas/cuDF this works for q1-q9 once thecount(<other-alias>)lowering routing is fixed (see the lowering issue; q1 also works today via thecount(*)form). On polars, traversal queries NIE (see the polars binding_ops issue) and count(*) is currently wrong (see the P0 count-broadcast issue).2. Untimed lowercase precompute (both GFQL and the Memgraph runner)
graph_benchmark_q1_q9.pyprecomputesgender_lc/interest_lcat load (untimed) for the toLower() filters in q5/q6/q7. Measured impact when moved inside the timed region: <1ms (negligible) — but for symmetry it should be timed or removed.graph_benchmark_memgraph_q1_q9.pysimilarly precomputes lowercased columns. The competitors (Kuzu/Ladybug/Neo4j) run rawtolower()inline. For official numbers, no side gets untimed precompute — everyone runs the raw query.Data-scale caveat (already caught)
/tmp/graph-benchmark-gfql-memgraph= TINY (1k persons/10k edges). Full data (100k/2.42M) =/tmp/graph-benchmark-gfql-memgraph-full(repodata/symlinks to it; the Docker mount does NOT follow the symlink — mount the target dir directly). An earlier Memgraph q1-q9 run on the tiny set produced invalid ~100× fast numbers.Acceptance
neo4j/query.pyetc.).