We’re making graph queries something you can just run, on data and infrastructure you already have, with no database in the way, at any scale
GFQL just brought Cypher graph queries to Polars dataframes. GFQL, standing for “graph dataframe query language”, is an open-source embeddable Cypher property graph query engine that is fast, easy, and built for both data people and AI agents. Unique to GFQL, it runs directly on your dataframes, with no graph database infrastructure required, and runs efficiently vectorized with both CPU and GPU modes. This post brings a first look at what we improved, why, and where we are going.
TL;DR: GFQL’s new lazy-mode engine brings Cypher graph queries to Polars dataframes, including to Polars’ new NVIDIA RAPIDS GPU runtime. Combined with the new sub-millisecond LALR(1) parser and first-class graph indexes, the per-query overheads drop to 0-2ms, making GFQL the fastest open source Cypher engine for both CPU and GPU modes. For popular graph benchmarks like LDBC, when measured on the same hardware, GFQL is 1.7–85× faster than Neo4j and Memgraph on OLTP graph queries, and faster than Kuzu on 7 out of 9 graph OLAP queries, with one benchmark hitting 200×.
Quickstart. Install GFQL with
pip install graphistry
For those new to our community, our experience is that most interesting data has a secret graph laying in wait – transactions, logs, identities, progressions, dependencies, behavior, ownership – but most graph questions never get asked because that has meant standing up a graph database for everyone first. Nobody tolerates this for tables, so we’re building GFQL to end this anachronism for graphs: not another lightweight graph database, but the graph query engine that doesn’t need one. Cypher should be an easy verb for our dataframes, Parquets, CSVs, databases, and the lakehouses we already have, so we can just as easily run on our laptop’s CPU as we can on racks of GPUs in the cloud.
This release changes where GFQL is fast. Our original pandas and cuDF runtimes work well for queries that do enough computation to outweigh their dispatch overhead. The new Polars runtime also handles the small queries that should finish in a few milliseconds – the kind that power interactive investigations, real-time alerting, high query-per-second data pipelines, agent harnesses, and visual analytics.

Chart: New GFQL release before/after. For LDBC search queries in CPU mode, Pandas runs 2.3X faster, and the new lazy Polars mode runs 28X faster.
GFQL runs Cypher without requiring moving into a graph database
GFQL is Graphistry’s open-source, embeddable property graph query engine several years into the making – and the first to run on both CPUs and GPUs. It runs Cypher property graph queries and dataframe-native operations directly on data from pandas, Polars, cuDF, Apache Arrow, Parquet files, and database queries:
Python
g = graphistry.edges(db.sql("..."), 'src_col', 'dst_col')
g.gfql("""
MATCH (account)-[payment]->(merchant)
WHERE payment.amount > 10000
RETURN account, merchant, payment
""", engine="polars-gpu")
No loading data into a separate graph database first, no ETL pipeline, no rearchitecting around a vendor. GFQL builds on a decade of GPU graph computing at Graphistry – we were founding members of GOAI, the effort that became NVIDIA RAPIDS, and helped start what became Apache Arrow, and are native to them: their daily progress is our progress. Just as pandas and DuckDB make tables we can just use, GFQL is doing that for graphs, starting inside the Python and dataframe workflows teams already use for ETL, investigations, analytics, ML, and visualization.
The problem with eager execution
A short Cypher query can expand into tens or hundreds of dataframe calls. Under eager execution, Python submits each one separately: a pandas call crosses into NumPy or another compiled CPU library, and each cuDF will likely have to further schedule and synchronize with the GPU.
These overheads are negligible when a query is slowly scanning many records. However, when a graph traversal takes a fraction of a millisecond, these overheads dominate painfully. GFQL was architecturally inappropriate for workloads like millisecond-level graph decision pipelines, and on some of our longer interactive analytics pipelines, it was eating too much of our budget for experiences to feel instantaneous – until now.

Diagram: Optimizing Cypher with GFQL – Eager vs lazy on CPU and GPU
Lazy Polars expands the possibilities
GFQL’s new runtime builds a lazy plan for Polars to bulk execute. Instead of sending potentially hundreds intermediate operations from Python one at a time, GFQL gives the engine one bulk query plan, which Polars then optimizes for minimal intermediate materializations and runs in its fast native Rust runtime. Important to the Graphistry community, these benefits carry to GPU mode with Polars’ new NVIDIA RAPIDS cuDF support. As we discuss at the end, polars-gpu still suffers from per-operation CPU↔GPU overheads, and a big motivation for GFQL’s new lazy engine is our goal of ultimately moving to zero-CPU for an entirely on-GPU runtime.
Under the hood, GFQL’s execution model differs from Neo4j/Memgraph-style traversal-pipeline engines and Kuzu-style columnar graph engines. Rather than streaming tuples and vector batches through operators node by node, GFQL processes the entire traversal frontier as bulk set operations – a handful of columnar kernels like sort, join, gather, and mask over whole edge and node collections. The vectorization is delegated to whichever columnar engine you’re on, including on-GPU, which exposes to them much more room for bulk optimizations.
This is also why Polars came first: the first lazy release already supports both CPU and, through RAPIDS cuDF, GPU execution. Likewise, its Arrow-native memory means graph queries join the Parquet, lakehouse, RAPIDS, and visualization stack with zero unnecessary copies.
Amdahl’s Law means we had to eliminate two more big overheads for the critical path to be visibly faster.. First, GFQL’s new LALR(1) parser for Cypher reduces parsing to under a millisecond. Second, GFQL’s first-ever adjacency index accelerates seeded neighbor lookups: a native indexed g.hop() runs in ~0.16ms, and its search time holds flats as we scaled the graph size 100X+.
For many CPU and GPU queries, GFQL’s combined overhead has shrunk from 10-100ms to a few milliseconds. The default configuration runs LDBC seeded search lookups 26× faster (2,997ms → 117ms), with common fast-paths landing at 2.6-4.8ms.

Diagram: How GFQL and Polars split planning
Early benchmarks
Our early Polars numbers are promising across a variety of popular graph query benchmarks. The runs below are all from a DGX Spark, which is a desktop box common to affordable LLM home labs. Popular for graph benchmarks, we ran Graph Data Council’s LDBC SNB benchmark at scale factors 0.1 and 1, as well as the 30.6M-edge Pokec social graph. The following benchmark against Neo4j, Memgraph, and Kuzu.
| Engine | Seeded search (OLTP) | Whole-graph OLAP | Graph build + index (ETL) | Runs on |
| Neo4j | GFQL is 1.7–85× faster | — | — | CPU |
| Memgraph | GFQL 1.7–85× faster | GFQL 4–62× faster
(all 12 Pokec queries) |
GFQL ~96× faster
(≈5s vs ~8 min, Pokec) |
CPU |
| Kuzu | Kuzu wins some small
seeded projections |
GFQL wins 7 of 9
(up to 200×) |
— | CPU |
| GFQL | 1.7–85× vs Neo4j / Memgraph | 4–62× vs Memgraph; 7/9 vs Kuzu | ~5s Pokec build | CPU + GPU |

Chart: Simple search workloads are no longer than bane of columnar graph engines. GFQL beats Neo4j and Memgraph 1.7x – 85x, and closing in on Kuzu for this worst case.
GFQL for faster graph search & online transaction processing (OLTP)
The GFQL update delivers on a common case for investigations: Seeded graph searches.
Common tasks like pulling user 360 views and alerting on suspicious events will start by searching for unique identifiers like an account name, and then expanding a few hops out from them. For urgent latency-sensitive queries and high-QPS throughput systems, these queries should finish within milliseconds. LDBC SNB queries Q1-Q7 are representative of these tasks, and even in CPU mode on the same hardware, we see GFQL giving significantly more performance than Neo4j and Memgraph on the same hardware by 1.7x-85X.

Chart: Fast-path specializations bring GFQL close to hand-tuned code for common cases.
Search speed should scale to larger graphs. This is not easy for columnar graph engines, where unoptimized ones would wastefully scan every edge instead of precisely traversing only the involved adjacencies. GFQL’s new index support shows seeded traversal speeds properly staying relatively steady even as the edge count grows by several magnitudes.

Chart: GFQL seeded searches stay steady even when edge count scales multiple magnitudes.
GFQL for faster graph online analytical processing (OLAP) and graph ETL
Whole-graph aggregates, filters, and searches with deep expansions and supernodes are columnar execution’s home turf, and GFQL shines here:
- Pokec – winning the full sweep: Taking each query’s best GFQL engine against a warm Memgraph, GFQL won all 12 queries at both tested scales . GFQL wins by 5-18× at 8M edges and 4-62× at 30.6M edges, with the margin widening as the scale grows. The min/max/avg aggregate task is especially dramatic: 6.4ms vs 347ms.
- 96X Faster graph lifecycle: Modern workloads like agents, datalakes, and interactive dashboards require graph ETL and index building, not just the search and analytics steps on warm preindexed data. Building the graph and indexes for Pokec took Memgraph ~8 minutes, while GFQL only took ~5 seconds. The reason is GFQL was designed to integrate into modern data pipelines by running natively with modern IO and formats to leverage a mix of zero-copy, accelerated, and machine-aligned optimizations.
Kuzu – interesting enough that Apple acquired the team and lives on via community forks – currently beats GFQL on a small number of small seeded projections, while GFQL already typically wins on the OLAP cases both were initially designed for. On even a small/medium 9-query analytical suite for a 100k-person social graph (2.8M edges), GFQL-on-Polars wins 7 of 9 tasks against Kuzu, generally at over 2×. Notably, GFQL wins q9 at 14× and q8 at 200×, and the margin grows with scale (50× at the 20k size, 200× at 100k), showing the importance of architecture and sustained core innovation.

Per-query speedup vs warm Kuzu, GFQL-on-Polars (CPU — the GPU column is a deep-dive item). Red queries are small selective searches while green are larger OLAP-style.
GFQL for faster large graph searches
An interesting case sits between the two workloads: searches that expand.
A seeded query that fans out – deeper hops, or crossing a supernode’s huge neighborhood – grows its wavefront until a “point” query becomes effectively a scan. Traversal-pipeline engines pay per-tuple as that happens. GFQL’s whole-frontier set operations treat a big wavefront the same as a whole-graph pass, so queries gracefully flip from OLTP-style search into OLAP. Switching engine modes can also make sense. For example, on Pokec at 30.6M edges, pandas wins the small expansions (0.18ms at 1 hop, 2.6ms at 3), while cuDF wins for GPU at 4 hops (159ms vs Memgraph’s 898ms).

Chart: The same seeded expansion at growing depth on the 30.6M-edge graph. A search becomes a scan, and GFQL hands off from CPU to GPU mid-continuum. Best GFQL engine per query, matched warm runs.

Chart: Fast-path specializations bring GFQL close to hand-tuned code for common cases.
Which GFQL engine to use when
The following rule-of-thumb helps pick the engine mode based on the graph size and query type:
- Tiny graph tasks, especially < 2ms: Try pandas CPU before polars CPU
- Small-to-medium graphs, and small seeded-searches: Polars CPU before Polars GPU
- Big whole-graph tasks: Polars GPU before Polars CPU
The result is that GFQL is now a great starting choice for most graph projects compared to a graph database. We recommend starting with it when:
- Your data already lives in dataframes, Parquet, CSVs, Python, a database, or a lakehouse, and you don’t want to ETL it into a graph database
- You need millisecond seeded searches for interactive investigations, real-time alerting, or high-QPS agent and data pipelines.
- You’re running graph ETL and pipelines that rebuild graphs and indexes frequently or on-the-fly: GFQL’s ~96× faster lifecycle matters here.
- You want accelerated whole-graph OLAP, up to billion scale per task, and want to less effort and more cost savings
Traditional graph databases still have their place, such as long-lived, concurrently-written transactional graph service with many clients and in the middle ground where data is smaller than a data lake yet above billion-scale.
What comes next
The GFQL Polars runtime does not yet cover every GFQL and Cypher operation: Simply switch to pandas or cudf when a query is statically rejected. Meanwhile, we’re actively expanding the supported set of lazy-mode primitives.
This release largely puts the foundations in place for performance work: Eager and lazy execution, engine-portable lowering, first-class indexes, graph statistics, and a growing set of baseline optimizations. That moves GFQL into the measure-and-tune phase. Not discussed, we’re specializing especially common cases for Graphistry users specifically and the community in general, ensuring those flows are closer to the hand-tuned vectorized CPU and GPU code our team would naturally write.
This is also our invitation to the community. If there are query patterns you need fast – shapes from your own workloads, benchmarks you care about – please file an issue in the PyGraphistry repo or reach out in Discord. We’re maintaining a list of priority workloads, and would be happy to add yours.
One big architectural unlock is that indexes are now a first-class part of GFQL. We started with CSR adjacency for graph tasks, and the same architecture opens the door to text, vector, and GIS indexes, Kuzu’s worst-case-optimal join structures – and especially in our mind, GPU implementations of all of the above.
Lazy execution also sets up the three destinations on our roadmap: zero ETL, where lazy plans push graph queries down to the Parquet and lakehouse data where it already lives, zero CPU, where the pipeline — load, query, wrangle, ML, visualize — stays on the GPU end to end, and bigger-than-memory tiling, such as optimizing multi-GPU workloads and spilling to disks as we advance from billion-scale in-memory graphs to trillion-scale ones important to some of our bigger users.
Quickstart. Install GFQL with
pip install graphistry
- npm i @graphistry/node-api
- npm i @graphistry/client-api
- REST: https://hub.graphistry.com/docs/api/
- Skills files for coding agents: https://github.com/graphistry/graphistry-skills
You can start with CPU mode to go faster than most graph engines on most tasks and without the pains of new infrastructure complicating. When you need to go bigger and faster, try out the NVIDIA GPU mode that scales with your hardware and gives you more performance per $ than CPU-era software.
If you’re at Black Hat or BSides Las Vegas, come find the Graphistry and Louie.ai team. This article is #2 in our announcement week, so stay tuned for more.
Links: