MXFrame: Cross-vendor GPU DataFrames with Mojo AOT kernels

Hey everyone :waving_hand:

A 6-table join over 10 million rows in 0.7 ms. That’s 128× faster than Polars — and it’s running on hand-written Mojo kernels with zero CUDA anywhere in the stack.

I’ve been building MXFrame: a DataFrame query engine with a Polars-style lazy API, powered entirely by pre-compiled Mojo AOT kernels. One kernel source → PTX on NVIDIA, ROCm on AMD, Metal on Apple Silicon, all through DeviceContext.

:link: Repo: GitHub - abhisheksreesaila/mxframe: GPU charged dataframe built using max framework · GitHub · Apache 2.0 · on PyPI

The idea

cuDF proved GPU DataFrames work — then welded the architecture to CUDA. Mojo cuts that cable.

So MXFrame is the cuDF architecture rebuilt on Mojo: a lazy logical plan with filter pushdown and join reordering, dispatching straight into AOT-compiled Mojo kernels over ctypes.

The design decision everything hangs on: compile once at build time, never at query time. Kernels ship as .so files, load in ~1 ms at process start, and every query after that is pure dispatch — no warmup, no JIT tax, no per-query compile. The only JIT left in the entire system is MAX Graph for GPU hash joins, compiled once per shape and cached for the session.

Results — all 22 TPC-H queries

RTX 3090 + AMD 12-core, warm median of 3, vs Polars and Pandas:

1M rows 10M rows
CPU path beats Polars 21 / 22 18 / 22
GPU path beats Polars 16 / 22 15 / 22

The headliners at 10M rows:

  • Q9 — 6-table join → 128×
  • Q12 — join + agg → 89×
  • Q7 — shipping volume → 42×
  • Q8 — market share → 32×

All 22 verified correct against Polars output.

And the losses, because they matter more: Q4, Q6, Q13, Q21 still fall back to PyArrow compute and get beaten. That’s the next milestone, and it’s in the roadmap.

Every number above is reproducible on your own box — benchmark CSVs are committed in-repo, and official TPC-H data regenerates via DuckDB’s dbgen port:

pixi run python3 scripts/gen_tpch_parquet.py --sf 1
pixi run python3 scripts/bench_real_tpch.py --data-dir data/tpch_sf1 --runs 3

What Mojo and MAX made possible

  • 93% of the codebase is Mojo — group aggs, masked aggs, inner + left hash joins, filter/gather, sort, unique. All hand-written.
  • Writing a kernel once and getting PTX, ROCm, and Metal out of the same source still feels like cheating.
  • AOT is the whole reason the small fast queries beat a Rust engine. When the query itself takes 0.7 ms, compile time isn’t overhead — it’s the entire race.

:television: Video walkthrough

Architecture and benchmarks, if you’d rather watch than read:

Weekly Mojo/MAX kernel deep-dives on the channel → Mojo Monday AI

Stars, issues, and brutal benchmark scrutiny all welcome :fire:

That is so cool, thank you for your work, do you see MXFrame adopting the Array API and Arrow and such in the future and what would you say is the Roadmap of such a cool project?

Wow, this sounds incredible Abhishek, great work!

This is really cool.

It uses PyArrow to store user input. There are fallback implementations that uses PyArrow as well, but primarily used to store input.

You might be interested in using the Mojo Arrow implementation GitHub - kszucs/marrow: Arrow implementation in Mojo · GitHub

Thanks @Ehsan for the link. I will be considering that especially some of the implementation is polluted by PyArrow in the core engine.