docs(gfql): reconcile the OLAP q9 figure and re-measure LadybugDB - #1826
Merged
Conversation
The page read "q9 is 14.2x". Reconciled the same way q8 was, and the answer is the same: our own number was right and the competitor was measured running a DIFFERENT query. The published 14.2x is 65.03 ms / 920.69 ms, and both halves are in pyg-bench's 0.58.0 tag sweep. The Kuzu artifact stores its query text verbatim, and it is the old harness's OPTIONAL MATCH degree-product rewrite -- not the canonical two-hop path enumeration GFQL ran. Re-run on dgx-spark under the host perf lock (pygraphistry master 1537e46, Kuzu 0.11.3, 30 timed runs after 5 warmups, slots interleaved K G G K, one session, host load 0.96 -> 4.53), every slot value-identical at 45,562,239: GFQL engine='polars', canonical q9 66.26 / 64.22 ms (65.03 published) Kuzu 0.11.3, canonical q9 83.93 / 83.13 ms Kuzu 0.11.3, q9_degprod rewrite 878.51 / 859.49 ms (920.69 published) So our side reproduces, the rewrite reproduces, and the handicap applied to the competitor is 10.3-10.5x. The honest same-query ratio is 1.27-1.29x this session, consistent with the 1.26x the matched lane already publishes with provenance (pyg-bench published/docs-numbers.json, graphbench.100k.q9.polars_vs_kuzu) -- which is the figure the page now quotes. The 20k cell behaves identically: published 14.17 vs 60.00 ms = 4.2x, where 60.00 is again the rewrite (reproduced at 56.76-62.54) and the canonical query makes that cell a loss, not a win. Raw artifacts and the runner: pyg-bench results/graphbench-q9-reconciliation-20260728/. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YYZRXegrALuXd3NHH5evqx
…eral
bench_ladybug_cypher.py carried
LADYBUG = {"full_scan": 3789, "range": 7.5, "point": 0.3, "count": 3.3,
"scan_rel_props": 15722, "scan_rel_rowid": 14562}
with no URL, no version, no hardware and no citation, and it was the only
competitor input to the published "~65x" and "~3.5-3.7x" ratios. So LadybugDB
was actually run.
MEASURED (dgx-spark, host perf lock held and verified released, load 1.53 ->
2.54, 2026-07-28): LadybugDB 0.18.1 embedded in a host venv against GFQL
engine='polars' on pygraphistry master 1537e46 in
graphistry/test-rapids-official:26.02-gfql-polars (polars 1.35.2), both arms in
ONE session on ONE generated 5M-node / 20M-edge graph (nodes.parquet sha256
22c8b239..., edges.parquet 80ededc5...), 2 warmups + 5 timed runs, slots
interleaved L G G L. Every op's result values are digest-identical across the
two engines on all four slots -- rows AND values, not rows alone.
Ladybug is timed at its FASTEST result-producing scope of four measured (Arrow,
polars, pandas, Python row iterator). Choosing a slower scope for a competitor
is exactly the defect being corrected here.
full_scan GFQL 58.95 ms Ladybug 364.28 ms GFQL 6.18x
range GFQL 5.06 ms Ladybug 7.57 ms GFQL 1.50x
scan_rel_props GFQL 3256.91 ms Ladybug 1925.61 ms -> no longer claimed
scan_rel_rowid GFQL 3157.46 ms Ladybug 1449.13 ms -> no longer claimed
So "~65x" becomes 6.2x, and the "~3.5-3.7x" relationship-scan claim is
withdrawn rather than restated -- measured fairly on one box those are not GFQL
wins.
The literal is RECONCILED, not merely contradicted: it is a drain-scope
measurement (rows pulled through the Python iterator, the protocol the upstream
LadybugDB/kuzu-ladybug-benchmark runner uses) on hardware ~1.5x slower. literal
/ drain-measured-here is 1.52 / 1.56 / 1.52 on the three scans and 0.98-1.06 on
the three ops where the scope is irrelevant. And our own side was right again:
"~65x" implies a 58.3 ms GFQL full scan; measured 58.95 ms.
engines.rst also loses "query a 1.8B-edge graph in <8 GB RAM", attributed to
LadybugDB. It is not on ladybugdb.com, blog.ladybugdb.com, docs.ladybugdb.com
or their GitHub README; LadybugDB documents only "on-disk databases allow you
to process larger-than-memory workloads", which is what the row now says. The
1.8B traces to our own commit e676d7c -- "GFQL is in-memory; single-node
ceiling at ~1.8B" -- a statement about GFQL and Friendster, transposed onto the
competitor.
Runner, raw JSON and full provenance: pyg-bench benchmarks/ladybug_scan/ and
results/ladybug-scan-20260728/.
Docs build on dgx: docs/ci.sh exit 0, build succeeded, 19 pre-existing warnings,
none from these pages.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YYZRXegrALuXd3NHH5evqx
lmeyerov
force-pushed
the
docs/reconcile-q9-and-remeasure-ladybug
branch
from
July 28, 2026 07:46
58bbc27 to
9d778fe
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Do not merge — for human review. Both figures here were defended-or-replaced, not deleted, per the rule that a published number is not withdrawn on the strength of a re-measurement alone.
Claim A — the OLAP q9 "14.2×": verdict (b), reproduces only under a competitor handicap
The published 14.2× is 65.03 ms / 920.69 ms, and both halves survive as raw artifacts in pyg-bench (
results/campaigns/2026-07-gfql-cypher-dominance/raw/tag-0.58.0-sweep/s3_100k_{tag0580_polars,kuzu_tag0580}.json). The Kuzu artifact records its query text verbatim, and it is not the query GFQL ran:That is the old harness's
OPTIONAL MATCHdegree-product rewrite; GFQL ran the canonical two-hop path enumeration. Exactly q8's defect, on the same page, in the same sentence.Re-run: dgx-spark, host perf lock held and verified released, master
1537e467, Kuzu 0.11.3, query text md56e7ae268…, 30 timed runs after 5 warmups, slots interleaved K G G K in one session, host load 0.96 → 4.53. Every slot value-identical (45,562,239 at 100k; 1,017,900 at 20k):engine='polars', canonical q9q9_degprodrewritegraphbench.100k.q9.polars_vs_kuzuAt 20k the published
14.17 vs 60.00 ms = 4.2×behaves identically: 60.00 is the rewrite (reproduced at 56.76–62.54), and against the canonical query that cell is a loss (0.62–0.67×).→ the page now publishes the measured 66.6 ms vs 84.1 ms (1.26×).
Claim B — LadybugDB was actually installed and run
benchmarks/gfql/bench_ladybug_cypher.py:52-53hardcodedLADYBUG = {"full_scan": 3789, "range": 7.5, "point": 0.3, "count": 3.3, "scan_rel_props": 15722, "scan_rel_rowid": 14562}— no URL, no version, no hardware, no citation, and the only competitor input to~65×/~3.5–3.7×. Verified by inspection.LadybugDB 0.18.1 (
pip install ladybug==0.18.1, cp312 aarch64 wheel) measured on dgx-spark under the perf lock, in one session, against GFQLengine='polars'(master1537e467,graphistry/test-rapids-official:26.02-gfql-polars, polars 1.35.2) on one generated 5M-node / 20M-edge graph read by both arms (nodes.parquetsha25622c8b239…,edges.parquet80ededc5…). 2 warmups + 5 timed runs, slots L G G L, host load 1.53 → 2.54 (the lane waited for a quiet host). Result values digest-identical across both engines on every op and every slot — rows and values, so no ratio here is over mismatched rows.Ladybug is timed at its fastest result-producing scope of four measured (Arrow / polars / pandas / Python row iterator). Choosing a slower scope for a competitor is the defect being corrected.
polars~65×GFQL ~3.5×GFQL ~3.7×The literal is reconciled, not merely contradicted. It is a drain-scope measurement — rows pulled through the Python iterator, the protocol the upstream LadybugDB/kuzu-ladybug-benchmark runner uses — taken on hardware ~1.5× slower:
The three ops where the scope is irrelevant reproduce to within 2–6%; the three scans carry one uniform factor. And our own side was right again —
~65×implies a 58.3 ms GFQL full scan; measured 58.95 ms. Effective handicap the literal applied to Ladybug: 10.4× / 8.2× / 10.0×.Docs consequences:
~65×→ 6.2× — still a GFQL win, an order of magnitude smaller~3.5–3.7×→ withdrawn; measured fairly on one box those ops are not GFQL wins, so the page stops claiming them rather than restating thembench_ladybug_cypher.pyis replaced by the measured values with provenance in the module docstring, so a fabricated competitor column cannot be republished from itThe
1.8B-edge graph in <8 GB RAMline is not LadybugDB's claimNot on ladybugdb.com, blog.ladybugdb.com, docs.ladybugdb.com, or their GitHub README. What LadybugDB documents is qualitative — "On-disk databases allow you to process larger-than-memory workloads" — which is what
engines.rstnow says. The1.8Btraces to our own commite676d7cbe, whose message reads "GFQL is in-memory; single-node ceiling at ~1.8B": a statement about GFQL and the Friendster graph, later transposed onto the competitor. The<8 GB RAMhalf has no located source at all. Not citable → the number goes, the citable qualitative claim stays.Provenance and gates
/tmp/pyg-bench-perf.lock, released and verified FREE after each lane. Nothing was measured on the dev box.benchmarks/ladybug_scan/,results/ladybug-scan-20260728/,results/graphbench-q9-reconciliation-20260728/) —./bin/lintclean,pytest460 passed / 26 skipped,smokeCI green.docs/ci.sh, Dockerised,--gpusnot required for the sphinx lane): exit 0, build succeeded; zero warnings referencegfql/performanceorgfql/engines(the log's warnings are pre-existing autodoc/notebook ones present on master).engine='polars'only. A pandas arm was started and abandoned mid-run for shared-host safety — another agent's unlocked job had the box at 3 GB free — so it is not reported rather than reported partially.manifests/docs_numbers_spec.json(needs a new exporter kind, and the consumer docs(gfql): print the benchmark numbers pyg-bench publishes, and re-verify them #1818 is unmerged), so the page quotes them with a pointer to the committed artifact.🤖 Generated with Claude Code
https://claude.ai/code/session_01YYZRXegrALuXd3NHH5evqx