mxframe 0.3.0__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mxframe-0.5.0/CHANGELOG.md +160 -0
- mxframe-0.5.0/PKG-INFO +188 -0
- mxframe-0.5.0/README.md +153 -0
- {mxframe-0.3.0 → mxframe-0.5.0}/__init__.py +35 -6
- {mxframe-0.3.0 → mxframe-0.5.0}/aot_kernels.py +271 -13
- {mxframe-0.3.0 → mxframe-0.5.0}/custom_ops.py +155 -45
- {mxframe-0.3.0 → mxframe-0.5.0}/kernels_aot/libmxkernels_aot.so +0 -0
- mxframe-0.5.0/kernels_aot/libmxkernels_aot_gpu.so +0 -0
- mxframe-0.5.0/mxframe.egg-info/PKG-INFO +188 -0
- {mxframe-0.3.0 → mxframe-0.5.0}/mxframe.egg-info/requires.txt +2 -0
- {mxframe-0.3.0 → mxframe-0.5.0}/pyproject.toml +65 -63
- mxframe-0.3.0/CHANGELOG.md +0 -199
- mxframe-0.3.0/PKG-INFO +0 -152
- mxframe-0.3.0/README.md +0 -119
- mxframe-0.3.0/kernels_aot/libmxkernels_aot_gpu.so +0 -0
- mxframe-0.3.0/mxframe.egg-info/PKG-INFO +0 -152
- {mxframe-0.3.0 → mxframe-0.5.0}/MANIFEST.in +0 -0
- {mxframe-0.3.0 → mxframe-0.5.0}/_modidx.py +0 -0
- {mxframe-0.3.0 → mxframe-0.5.0}/compiler.py +0 -0
- {mxframe-0.3.0 → mxframe-0.5.0}/docs/vision-and-architecture.md +0 -0
- {mxframe-0.3.0 → mxframe-0.5.0}/kernels.mojopkg +0 -0
- {mxframe-0.3.0 → mxframe-0.5.0}/kernels_aot/__init__.py +0 -0
- {mxframe-0.3.0 → mxframe-0.5.0}/lazy_expr.py +0 -0
- {mxframe-0.3.0 → mxframe-0.5.0}/lazy_frame.py +0 -0
- {mxframe-0.3.0 → mxframe-0.5.0}/mxframe.egg-info/SOURCES.txt +0 -0
- {mxframe-0.3.0 → mxframe-0.5.0}/mxframe.egg-info/dependency_links.txt +0 -0
- {mxframe-0.3.0 → mxframe-0.5.0}/mxframe.egg-info/top_level.txt +0 -0
- {mxframe-0.3.0 → mxframe-0.5.0}/optimizer.py +0 -0
- {mxframe-0.3.0 → mxframe-0.5.0}/plan_validation.py +0 -0
- {mxframe-0.3.0 → mxframe-0.5.0}/setup.cfg +0 -0
- {mxframe-0.3.0 → mxframe-0.5.0}/setup.py +0 -0
- {mxframe-0.3.0 → mxframe-0.5.0}/sql_frontend.py +0 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# 📋 Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to MXFrame are documented here.
|
|
4
|
+
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## [0.5.0] — 2026-07-11
|
|
9
|
+
|
|
10
|
+
Full narrative: [v0.5.0 release notes](RELEASE_NOTES_0.5.0.md).
|
|
11
|
+
|
|
12
|
+
### ⚡ Native GPU UTF-8 predicates
|
|
13
|
+
- Added AOT `utf8_startswith_mask_gpu`, `utf8_contains_mask_gpu`, `utf8_equal_mask_gpu`, and packed-literal `utf8_isin_mask_gpu` kernels over Arrow validity, offsets, and byte buffers.
|
|
14
|
+
- Supports sliced arrays, nulls, empty strings/patterns, duplicate literals, and multibyte UTF-8 while matching Arrow filter semantics.
|
|
15
|
+
- GPU expression dispatch covers string `==`, `!=`, `startswith`, literal `contains`, and non-null literal `isin`; unsupported/null-membership cases retain Arrow fallback.
|
|
16
|
+
- Added permanent CPU/GPU Phase 6 tests and `pixi run bench-utf8` comparisons against PyArrow and Polars.
|
|
17
|
+
- At 1M rows, packed `isin` and `contains` beat local Arrow and Polars; exact equality remains faster in Polars' optimized CPU path, so no universal string-performance claim is made.
|
|
18
|
+
|
|
19
|
+
### ⚡ Weak-query GPU fusion
|
|
20
|
+
- Q6 dispatches `range5_sum_product_f32_gpu` instead of materializing five masks and a product array.
|
|
21
|
+
- Q4 uses `semi_range_group_count_i32_gpu` for fused qualifying-key marking, semi-join probing, and grouped counts.
|
|
22
|
+
- Q13 uses `key_match_counts_i32_gpu` to emit one compact count per customer without materializing a left join.
|
|
23
|
+
- Q21 uses `single_late_supplier_counts_i32_gpu` for exact `(order, supplier)` deduplication and deterministic compact supplier counts.
|
|
24
|
+
- Representative 1M/10M GPU results versus Polars: Q4 **3.4/28.6 vs 20.2/105.4 ms**; Q13 **1.8/28.5 vs 26.5/442.0 ms**; Q21 **6.6/49.8 vs 30.2/99.1 ms**.
|
|
25
|
+
|
|
26
|
+
### 📊 Bounded 22-query benchmark
|
|
27
|
+
- Every query and engine now runs in its own process, releasing Arrow, Pandas, and CUDA allocations between measurements.
|
|
28
|
+
- Added per-engine timeouts, five-query progress groups, merged CSV output, and optional synchronized `cudf.pandas` workers.
|
|
29
|
+
- MX CPU beat Polars on **22/22 at 1M** and **18/22 at 10M**.
|
|
30
|
+
- MX GPU beat Polars on **15/17 comparable paths at 1M** and **14/17 at 10M**.
|
|
31
|
+
- Q8 GPU remains `N/A:JIT`; Q15/Q17/Q18/Q20 GPU workers exceeded the bounded timeout.
|
|
32
|
+
- cuDF was unavailable locally, so RAPIDS remains `N/A` and no RAPIDS parity claim is made.
|
|
33
|
+
|
|
34
|
+
### 🎬 Visualization and identity
|
|
35
|
+
- Q4, Q6, Q12, Q13, Q14, and Q21 visualize their logical rewrite, dispatch boundary, and step-by-step Mojo kernel algorithm.
|
|
36
|
+
- Added a cool-gray MX mark whose rising X stroke is shaped as orange lightning.
|
|
37
|
+
- Verified responsive rendering and zero horizontal overflow in Playwright.
|
|
38
|
+
|
|
39
|
+
### Remaining performance-parity work
|
|
40
|
+
- Replace Q8's JIT path and fix Q15/Q17/Q18/Q20 GPU timeouts.
|
|
41
|
+
- Improve Q1 and Q14 GPU pipelines at 10M.
|
|
42
|
+
- Run synchronized 1M/10M benchmarks on a compatible RAPIDS/cuDF environment.
|
|
43
|
+
|
|
44
|
+
### Remaining feature-parity work
|
|
45
|
+
- Native GPU UTF-8 dictionary construction and string/date gather.
|
|
46
|
+
- GPU windows and additional semi/anti/cross/as-of/range joins.
|
|
47
|
+
- Broader regex/string, datetime, nested-type, reshape, SQL, streaming, and I/O pushdown support.
|
|
48
|
+
- Cross-generation NVIDIA testing plus AMD and Apple Silicon validation.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## [0.4.0] — 2026-07-11
|
|
53
|
+
|
|
54
|
+
### 🔗 String and composite joins
|
|
55
|
+
- Join keys from both tables now use one shared Arrow dictionary before dispatch, so equal strings receive identical dense `int32` IDs regardless of encounter order.
|
|
56
|
+
- Multi-column joins jointly densify shared per-column codes, avoiding mixed-radix overflow and supporting mixed string/integer composite keys.
|
|
57
|
+
- SQL null semantics are preserved: null keys receive side-specific IDs and never match, including left joins.
|
|
58
|
+
- Compact nonnegative integer joins retain their direct `int32` fast path; sparse and non-integer keys are safely densified.
|
|
59
|
+
- Fixed GPU upload-cache aliasing by retaining each cached NumPy host owner, preventing recycled host pointers from returning stale device buffers during joined-table gather.
|
|
60
|
+
- String encoding runs in Arrow on CPU; resulting IDs use the Mojo AOT CPU/GPU join kernels. Native GPU UTF-8 dictionary construction remains future work.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## [0.3.0] — 2026-07-10
|
|
65
|
+
|
|
66
|
+
### 📚 Docs & Benchmarks
|
|
67
|
+
- README restructured: 543 lines → 119 lines. Intro, quick start, and 4-row teaser only — all detail moved to dedicated docs.
|
|
68
|
+
- New `docs/benchmarks.md`: full 22-query TPC-H tables (1 M + 10 M rows), Mojo kernel catalogue, current limitations, roadmap, and reproduce instructions.
|
|
69
|
+
- New `docs/api.md`: LazyFrame method reference, expression syntax, SQL frontend, supported operations, project structure.
|
|
70
|
+
- `docs/vision-and-architecture.md`: fixed garbled sections 2.5/2.6/3; success criteria table repaired; all milestones marked ✅.
|
|
71
|
+
- `docs/archive/` added to `.gitignore` — old dev notes moved there, not published.
|
|
72
|
+
- All stale `Mojo 0.26.2` / `MAX Graph shape-cached join` references updated to reflect v0.2.3 reality (Mojo 26.4, AOT ctypes everywhere).
|
|
73
|
+
|
|
74
|
+
### 📊 Benchmark updates
|
|
75
|
+
- Q5/Q7/Q8/Q10/Q13/Q19 re-measured with per-run join-cache clearing (fresh-join). Previous v0.2.0 numbers for those queries were warm join-cache hits.
|
|
76
|
+
- Summary corrected: CPU 20/22 (Q13 loses fresh-join), GPU 12/22 (Q5/Q7 no longer win at 1 M without cache warmup).
|
|
77
|
+
- Q8 GPU marked `—` (MAX Graph `year()` groupby JIT too slow to benchmark; Phase 6 target).
|
|
78
|
+
|
|
79
|
+
### 🔨 Infrastructure
|
|
80
|
+
- `modular >= 26.4` requirement added to `pyproject.toml` runtime deps and Dependencies table in README.
|
|
81
|
+
- `docs/archive/` gitignored; previously committed archive files removed from history.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## [0.2.3] — 2026-07-09
|
|
86
|
+
|
|
87
|
+
### ⚡ Performance
|
|
88
|
+
- **Phase 4 — Device-resident GPU join pipeline** (`join_count_i32_gpu` + `join_scatter_i32_gpu`):
|
|
89
|
+
GPU inner joins now bypass MAX Graph JIT entirely. Cold-start join latency drops from ~300 ms (MAX session init + JIT) to ~70 ms. `AOTKernelsGPU.hash_join()` caches key arrays via `_cached_upload`; `AOTKernelsGPU.gather_table()` gathers all numeric columns with a single shared device-resident index upload.
|
|
90
|
+
- **Phase 5 — GPU sort + top-k** (`sort_topk_i32_gpu`): `ORDER BY … LIMIT k` fuses sort and limit in a single AOT bitonic sort kernel, downloading only `k × 4` bytes instead of `N × 4`. `_apply_post_ops_custom` peek-ahead passes `topk=k` directly to the sort kernel.
|
|
91
|
+
- **Phase 3 — Vectorised rank mapping**: `_encode_sort_key` replaces the per-unique-value Python `for` loop with a single vectorised NumPy scatter (`rank_of[sorted_order] = np.arange(n_unique)`). ~1.5× speedup for sort-key encoding at 5 M rows.
|
|
92
|
+
|
|
93
|
+
### 🛠 Fixed
|
|
94
|
+
- **GPU sort+limit crash**: `_apply_sort_custom` was calling `_gpu_gather_table` which referenced `gather_f32` as a MAX Graph custom op — that kernel was never registered in the mojopkg. Any `ORDER BY` on `device="gpu"` would crash. Now routes through `_aot_gpu.gather_table` (AOT ctypes, no MAX Graph).
|
|
95
|
+
- `_apply_sort_custom` CPU path now uses `np.argpartition` (O(N) partial sort) when `topk < N`, instead of a full O(N log N) argsort.
|
|
96
|
+
|
|
97
|
+
### 🔨 Infrastructure
|
|
98
|
+
- **Mojo/MAX 26.4 migration**: all `kernels/*.mojo` and `kernels_aot/*.mojo` migrated from 26.2 API (`fn` → `def`, `from tensor import` → `from extensibility import`, `DeviceContextPtr` → `DeviceContext`, `enqueue_function_experimental` → `enqueue_function`).
|
|
99
|
+
- **`kernels/_atomic.mojo`** (new): drop-in replacement for the missing `std.os.atomic.Atomic`. Implements `fetch_add`, `min`, `max`, `compare_exchange` via MLIR `llvm.atomicrmw` / `llvm.cmpxchg` instructions — portable across NVIDIA and AMD; works in both `mojo package` (JIT) and `mojo build --emit shared-lib` (AOT) contexts.
|
|
100
|
+
- All compiled artifacts rebuilt with Mojo 1.0.0b2 (modular 26.4): `kernels.mojopkg`, `libmxkernels_aot.so`, `libmxkernels_aot_gpu.so`.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## [0.2.2] — 2026-07-08
|
|
105
|
+
|
|
106
|
+
### 🔨 Infrastructure
|
|
107
|
+
- First release targeting **Mojo/MAX 26.4** (`modular = "26.4.*"` in `pixi.toml`).
|
|
108
|
+
- **`kernels/_atomic.mojo`** shim introduced to replace the removed `std.os.atomic` module.
|
|
109
|
+
- All kernel sources migrated to 26.4 syntax; compiled artefacts rebuilt.
|
|
110
|
+
- `pyproject.toml` `runtime` extra pinned to `modular>=26.4` to prevent 26.2 wheels from being installed by Colab/pip users.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## [0.2.1] — 2026-06-xx
|
|
115
|
+
|
|
116
|
+
### 🛠 Fixed
|
|
117
|
+
- TestPyPI re-upload to validate wheel packaging. No functional changes.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## [0.2.0] — 2026-05-xx
|
|
122
|
+
|
|
123
|
+
### ✨ Added
|
|
124
|
+
- **Phase 1 — GPU buffer cache + masked global min/max**:
|
|
125
|
+
- `masked_global_min_f32_gpu` / `masked_global_max_f32_gpu` Mojo AOT kernels.
|
|
126
|
+
- `AOTKernelsGPU` device-buffer cache (`_cached_upload`, eviction, `clear_buf_cache`) — eliminates redundant PCIe uploads on repeated hot queries.
|
|
127
|
+
- `group_mean_f32` GPU kernel (sum + count on GPU, divide on CPU).
|
|
128
|
+
- **Phase 2 — Masked global aggregation for any expression**:
|
|
129
|
+
- `_compute_masked_global_agg` extended to handle any inner expression via `_eval_expr_arrow` (e.g. `a*(1-b)`, `case_when`, `startswith`, `isin`) — SUM reduction stays GPU-resident.
|
|
130
|
+
- Join-only plans emit `"join_mojo_shortcut"` provenance (not `"pyarrow_shortcut"`) — Q19 restructured to single-pass join+filter+global_agg.
|
|
131
|
+
- **Phase 3 — GPU integer key encoding**:
|
|
132
|
+
- `group_encode_i32_gpu` open-addressing hash-table kernel for integer group keys; replaces PyArrow `dictionary_encode` for integer-keyed group-by on GPU.
|
|
133
|
+
- **Audit tool** (`scripts/audit_gpu_paths.py`): classifies each of 22 TPC-H queries as `GPU-CLEAN` / `MOJO-CPU` / `FALLBACK` via `last_compile_provenance["path"]`.
|
|
134
|
+
|
|
135
|
+
### ⚡ Performance
|
|
136
|
+
- **22/22 TPC-H queries GPU-CLEAN** (100% Mojo-backed on GPU, zero PyArrow fallbacks in the reduction and join hot paths).
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## [0.1.2] — 2026-04-22
|
|
141
|
+
|
|
142
|
+
### 🛠 Fixed
|
|
143
|
+
- `pip install "mxframe[runtime]"` now gives a working install. The `[runtime]` extra pulls `modular>=25.5`, which provides the `max.engine` / `max.driver` modules that `mxframe.compiler` imports at module load. (Pixi users get `modular` from the Modular conda channel and should install `mxframe` without the extra.)
|
|
144
|
+
- `sqlglot` import in `mxframe.sql_frontend` is now lazy: `import mxframe` no longer requires the `[sql]` extra. Calling `mxframe.sql(…)` without `sqlglot` installed raises a clear `ImportError` with the install hint.
|
|
145
|
+
- `__init__.py` `__version__` now matches `pyproject.toml`.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## [0.1.1] — 2026-04-22
|
|
150
|
+
|
|
151
|
+
### 🛠 Fixed
|
|
152
|
+
- Packaging: `readme` now points to `README.md` so PyPI shows the full project page with benchmarks and quickstart (was accidentally showing the internal vision/architecture doc).
|
|
153
|
+
- CI: `AOTKernelsGPU` init no longer hard-fails on CPU-only runners (`CUDA driver unavailable` is caught, `self._aot_gpu` falls back to `None`).
|
|
154
|
+
- CPU-only import and compute path validated in a clean venv without `modular`.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## [0.1.0] — 2026-04-21
|
|
159
|
+
|
|
160
|
+
Initial public release.
|
mxframe-0.5.0/PKG-INFO
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mxframe
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: GPU-accelerated DataFrame engine with Python ergonomics and Mojo AOT kernels.
|
|
5
|
+
Author-email: Abhishek Sreesaila <abhisheksreesaila@users.noreply.github.com>
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/abhisheksreesaila/mxframe
|
|
8
|
+
Project-URL: Repository, https://github.com/abhisheksreesaila/mxframe
|
|
9
|
+
Keywords: dataframe,gpu,mojo,arrow,analytics,tpch
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
16
|
+
Classifier: Topic :: Database
|
|
17
|
+
Requires-Python: >=3.12
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: pyarrow>=14.0
|
|
20
|
+
Requires-Dist: numpy>=1.24
|
|
21
|
+
Requires-Dist: pandas>=2.0
|
|
22
|
+
Provides-Extra: runtime
|
|
23
|
+
Requires-Dist: modular>=26.4; extra == "runtime"
|
|
24
|
+
Provides-Extra: polars
|
|
25
|
+
Requires-Dist: polars>=0.20; extra == "polars"
|
|
26
|
+
Provides-Extra: sql
|
|
27
|
+
Requires-Dist: sqlglot>=25.0; extra == "sql"
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
31
|
+
Requires-Dist: polars>=0.20; extra == "dev"
|
|
32
|
+
Requires-Dist: sqlglot>=25.0; extra == "dev"
|
|
33
|
+
Requires-Dist: duckdb>=0.9; extra == "dev"
|
|
34
|
+
Requires-Dist: twine>=6.0; extra == "dev"
|
|
35
|
+
|
|
36
|
+
<img src="visualize/mx-spark.svg" alt="MXFrame MX lightning mark" width="72">
|
|
37
|
+
|
|
38
|
+
# 🚀 MXFrame
|
|
39
|
+
|
|
40
|
+
> **GPU-accelerated DataFrames — Python ergonomics, Mojo AOT kernels.**
|
|
41
|
+
|
|
42
|
+
MXFrame is a DataFrame query engine that pairs a Polars-style Python API with pre-compiled Mojo AOT kernels. The current GPU build is validated on **NVIDIA**; AMD and Apple Silicon validation remain on the roadmap.
|
|
43
|
+
|
|
44
|
+
[](docs/benchmarks.md)
|
|
45
|
+
[](pyproject.toml)
|
|
46
|
+
[](pyproject.toml)
|
|
47
|
+
[](LICENSE)
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## ✨ Why MXFrame?
|
|
52
|
+
|
|
53
|
+
| | pandas | Polars | cuDF (RAPIDS) | **MXFrame** |
|
|
54
|
+
|---|---|---|---|---|
|
|
55
|
+
| GPU support | ❌ | ❌ | ✅ NVIDIA | ✅ **NVIDIA validated** |
|
|
56
|
+
| Compiled kernels | ❌ | ✅ Rust | ✅ CUDA | ✅ **Mojo AOT** |
|
|
57
|
+
| Install complexity | pip | pip | CUDA + RAPIDS stack | **pip or pixi** |
|
|
58
|
+
| TPC-H coverage | reference | ✅ | ✅ | ✅ **22/22** |
|
|
59
|
+
| Portable kernel design | ❌ | CPU | CUDA | ✅ NVIDIA today; AMD/Apple planned |
|
|
60
|
+
|
|
61
|
+
MXFrame uses a portable Mojo kernel architecture rather than CUDA-specific source. Kernels compile once to shared libraries and dispatch directly at runtime.
|
|
62
|
+
|
|
63
|
+
### What changed in v0.4 and v0.5
|
|
64
|
+
|
|
65
|
+
- **v0.4.0 — correct string/composite joins:** shared dictionaries assign identical dense IDs across both join sides; mixed composite keys and SQL null semantics are preserved.
|
|
66
|
+
- **v0.5.0 — native GPU UTF-8 predicates:** `startswith`, literal `contains`, string equality/inequality, and packed non-null literal `isin` operate directly on Arrow offsets and byte buffers.
|
|
67
|
+
- **v0.5.0 — weak-query fusion:** dedicated compact GPU paths remove expanded intermediates from Q4, Q6, Q13, and Q21.
|
|
68
|
+
- **v0.5.0 — bounded benchmarking and visualization:** every query/engine runs in an isolated process; Q4, Q6, Q12, Q13, Q14, and Q21 have step-by-step kernel visualizations.
|
|
69
|
+
|
|
70
|
+
See [v0.5.0 release notes](RELEASE_NOTES_0.5.0.md) for measured gains, limitations, and the parity roadmap.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## ⚡ Quick Start
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
# 1. Install pixi (Modular's package manager)
|
|
78
|
+
curl -fsSL https://pixi.sh/install.sh | bash
|
|
79
|
+
|
|
80
|
+
# 2. Clone and set up
|
|
81
|
+
git clone https://github.com/abhisheksreesaila/mxframe
|
|
82
|
+
cd mxframe
|
|
83
|
+
pixi install
|
|
84
|
+
|
|
85
|
+
# 3. Verify GPU is working
|
|
86
|
+
pixi run python3 scripts/_check_gpu.py
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
import pyarrow as pa
|
|
91
|
+
from mxframe import LazyFrame, Scan, col, lit
|
|
92
|
+
|
|
93
|
+
data = pa.table({
|
|
94
|
+
"dept": pa.array(["eng", "eng", "mkt", "mkt", "eng"]),
|
|
95
|
+
"salary": pa.array([120.0, 95.0, 80.0, 110.0, 130.0], pa.float32()),
|
|
96
|
+
"age": pa.array([32, 28, 35, 29, 40], pa.int32()),
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
result = (
|
|
100
|
+
LazyFrame(Scan(data))
|
|
101
|
+
.filter(col("age") > lit(28))
|
|
102
|
+
.groupby("dept")
|
|
103
|
+
.agg(
|
|
104
|
+
col("salary").sum().alias("total_salary"),
|
|
105
|
+
col("age").count().alias("headcount"),
|
|
106
|
+
)
|
|
107
|
+
.sort(col("total_salary"), descending=True)
|
|
108
|
+
.compute(device="gpu") # or "cpu"
|
|
109
|
+
)
|
|
110
|
+
print(result.to_pandas())
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
dept total_salary headcount
|
|
115
|
+
0 eng 345.0 3
|
|
116
|
+
1 mkt 110.0 1
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 📊 Performance — bounded v0.5.0 benchmark
|
|
122
|
+
|
|
123
|
+
RTX 3090, warm median of three runs. Every query and engine ran in its own process so Arrow, Pandas, and CUDA allocations were released between measurements.
|
|
124
|
+
|
|
125
|
+
- **1M:** MX CPU beat Polars on **22/22**; MX GPU beat Polars on **15/17 comparable** paths.
|
|
126
|
+
- **10M:** MX CPU beat Polars on **18/22**; MX GPU beat Polars on **14/17 comparable** paths.
|
|
127
|
+
- Q8 GPU remains `N/A:JIT`; Q15, Q17, Q18, and Q20 GPU workers exceeded the bounded timeout.
|
|
128
|
+
- cuDF was unavailable on this machine, so RAPIDS remains `N/A` and no RAPIDS parity claim is made.
|
|
129
|
+
|
|
130
|
+
### 1M representative queries
|
|
131
|
+
|
|
132
|
+
| Query | MX CPU | MX GPU | Polars | Best MX speedup |
|
|
133
|
+
|---|---:|---:|---:|---:|
|
|
134
|
+
| Q4 · Order priority | 12.8 ms | **3.4 ms** | 20.2 ms | **5.9× GPU** |
|
|
135
|
+
| Q6 · Discounted revenue | **6.7 ms** | 6.8 ms | 10.8 ms | **1.6× CPU** |
|
|
136
|
+
| Q12 · Shipping modes | **0.6 ms** | 6.1 ms | 26.6 ms | **44.3× CPU** |
|
|
137
|
+
| Q13 · Customer distribution | 20.4 ms | **1.8 ms** | 26.5 ms | **14.7× GPU** |
|
|
138
|
+
| Q21 · Waiting suppliers | 27.1 ms | **6.6 ms** | 30.2 ms | **4.6× GPU** |
|
|
139
|
+
|
|
140
|
+
### 10M representative queries
|
|
141
|
+
|
|
142
|
+
| Query | MX CPU | MX GPU | Polars | Best MX speedup |
|
|
143
|
+
|---|---:|---:|---:|---:|
|
|
144
|
+
| Q4 · Order priority | 265.1 ms | **28.6 ms** | 105.4 ms | **3.7× GPU** |
|
|
145
|
+
| Q9 · Product profit | **2.0 ms** | 16.5 ms | 109.7 ms | **54.9× CPU** |
|
|
146
|
+
| Q12 · Shipping modes | **1.5 ms** | 8.2 ms | 128.0 ms | **85.3× CPU** |
|
|
147
|
+
| Q13 · Customer distribution | 368.8 ms | **28.5 ms** | 442.0 ms | **15.5× GPU** |
|
|
148
|
+
| Q21 · Waiting suppliers | 1068.7 ms | **49.8 ms** | 99.1 ms | **2.0× GPU** |
|
|
149
|
+
|
|
150
|
+
Full matrices: [1M CSV](scripts/bench_results_1M.csv) · [10M CSV](scripts/bench_results_10M.csv) · [methodology and kernel catalogue](docs/benchmarks.md)
|
|
151
|
+
|
|
152
|
+
## What remains for parity
|
|
153
|
+
|
|
154
|
+
**Performance parity:** replace Q8's JIT path; fix Q15/Q17/Q18/Q20 GPU timeouts; improve Q1 and Q14 GPU execution; run synchronized 1M/10M measurements on a compatible RAPIDS/cuDF environment.
|
|
155
|
+
|
|
156
|
+
**Feature parity:** native GPU UTF-8 dictionary construction and string/date gather; GPU windows; semi/anti/as-of/range joins; broader string/regex, datetime, nested-type, reshape, SQL, streaming, and I/O pushdown coverage.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 📚 Docs
|
|
161
|
+
|
|
162
|
+
| | |
|
|
163
|
+
|---|---|
|
|
164
|
+
| [**v0.5.0 Release Notes**](RELEASE_NOTES_0.5.0.md) | v0.4/v0.5 gains, benchmark evidence, known gaps, and parity roadmap |
|
|
165
|
+
| [**API Reference**](docs/api.md) | LazyFrame, expressions, SQL frontend, supported operations, running tests |
|
|
166
|
+
| [**Benchmarks**](docs/benchmarks.md) | Full TPC-H tables, kernel catalogue, limitations, roadmap, reproduce instructions |
|
|
167
|
+
| [**Architecture**](docs/vision-and-architecture.md) | Design philosophy, internal layers, how MAX Graph fits in |
|
|
168
|
+
| [**Kernel Visualizer**](visualize/mxframe_pipeline.html) | Interactive plans, dispatch boundaries, GPU threads, and output masks |
|
|
169
|
+
| [**Contributing**](CONTRIBUTING.md) | Dev setup, writing Mojo kernels, adding queries |
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## 📦 Dependencies
|
|
174
|
+
|
|
175
|
+
| Package | Required | Purpose |
|
|
176
|
+
|---|---|---|
|
|
177
|
+
| `pyarrow >= 14` | ✅ | Column storage, zero-copy NumPy bridge |
|
|
178
|
+
| `numpy >= 1.24` | ✅ | Vectorized pre/post processing |
|
|
179
|
+
| `pandas >= 2.0` | ✅ | Reference implementations |
|
|
180
|
+
| `modular >= 26.4` | GPU only | MAX Engine runtime, Mojo GPU dispatch |
|
|
181
|
+
| `polars >= 0.20` | optional | Benchmark comparison |
|
|
182
|
+
| `sqlglot >= 25` | optional | SQL frontend parsing |
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 📄 License
|
|
187
|
+
|
|
188
|
+
Apache 2.0 — see [LICENSE](LICENSE).
|
mxframe-0.5.0/README.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
<img src="visualize/mx-spark.svg" alt="MXFrame MX lightning mark" width="72">
|
|
2
|
+
|
|
3
|
+
# 🚀 MXFrame
|
|
4
|
+
|
|
5
|
+
> **GPU-accelerated DataFrames — Python ergonomics, Mojo AOT kernels.**
|
|
6
|
+
|
|
7
|
+
MXFrame is a DataFrame query engine that pairs a Polars-style Python API with pre-compiled Mojo AOT kernels. The current GPU build is validated on **NVIDIA**; AMD and Apple Silicon validation remain on the roadmap.
|
|
8
|
+
|
|
9
|
+
[](docs/benchmarks.md)
|
|
10
|
+
[](pyproject.toml)
|
|
11
|
+
[](pyproject.toml)
|
|
12
|
+
[](LICENSE)
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## ✨ Why MXFrame?
|
|
17
|
+
|
|
18
|
+
| | pandas | Polars | cuDF (RAPIDS) | **MXFrame** |
|
|
19
|
+
|---|---|---|---|---|
|
|
20
|
+
| GPU support | ❌ | ❌ | ✅ NVIDIA | ✅ **NVIDIA validated** |
|
|
21
|
+
| Compiled kernels | ❌ | ✅ Rust | ✅ CUDA | ✅ **Mojo AOT** |
|
|
22
|
+
| Install complexity | pip | pip | CUDA + RAPIDS stack | **pip or pixi** |
|
|
23
|
+
| TPC-H coverage | reference | ✅ | ✅ | ✅ **22/22** |
|
|
24
|
+
| Portable kernel design | ❌ | CPU | CUDA | ✅ NVIDIA today; AMD/Apple planned |
|
|
25
|
+
|
|
26
|
+
MXFrame uses a portable Mojo kernel architecture rather than CUDA-specific source. Kernels compile once to shared libraries and dispatch directly at runtime.
|
|
27
|
+
|
|
28
|
+
### What changed in v0.4 and v0.5
|
|
29
|
+
|
|
30
|
+
- **v0.4.0 — correct string/composite joins:** shared dictionaries assign identical dense IDs across both join sides; mixed composite keys and SQL null semantics are preserved.
|
|
31
|
+
- **v0.5.0 — native GPU UTF-8 predicates:** `startswith`, literal `contains`, string equality/inequality, and packed non-null literal `isin` operate directly on Arrow offsets and byte buffers.
|
|
32
|
+
- **v0.5.0 — weak-query fusion:** dedicated compact GPU paths remove expanded intermediates from Q4, Q6, Q13, and Q21.
|
|
33
|
+
- **v0.5.0 — bounded benchmarking and visualization:** every query/engine runs in an isolated process; Q4, Q6, Q12, Q13, Q14, and Q21 have step-by-step kernel visualizations.
|
|
34
|
+
|
|
35
|
+
See [v0.5.0 release notes](RELEASE_NOTES_0.5.0.md) for measured gains, limitations, and the parity roadmap.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## ⚡ Quick Start
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
# 1. Install pixi (Modular's package manager)
|
|
43
|
+
curl -fsSL https://pixi.sh/install.sh | bash
|
|
44
|
+
|
|
45
|
+
# 2. Clone and set up
|
|
46
|
+
git clone https://github.com/abhisheksreesaila/mxframe
|
|
47
|
+
cd mxframe
|
|
48
|
+
pixi install
|
|
49
|
+
|
|
50
|
+
# 3. Verify GPU is working
|
|
51
|
+
pixi run python3 scripts/_check_gpu.py
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
import pyarrow as pa
|
|
56
|
+
from mxframe import LazyFrame, Scan, col, lit
|
|
57
|
+
|
|
58
|
+
data = pa.table({
|
|
59
|
+
"dept": pa.array(["eng", "eng", "mkt", "mkt", "eng"]),
|
|
60
|
+
"salary": pa.array([120.0, 95.0, 80.0, 110.0, 130.0], pa.float32()),
|
|
61
|
+
"age": pa.array([32, 28, 35, 29, 40], pa.int32()),
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
result = (
|
|
65
|
+
LazyFrame(Scan(data))
|
|
66
|
+
.filter(col("age") > lit(28))
|
|
67
|
+
.groupby("dept")
|
|
68
|
+
.agg(
|
|
69
|
+
col("salary").sum().alias("total_salary"),
|
|
70
|
+
col("age").count().alias("headcount"),
|
|
71
|
+
)
|
|
72
|
+
.sort(col("total_salary"), descending=True)
|
|
73
|
+
.compute(device="gpu") # or "cpu"
|
|
74
|
+
)
|
|
75
|
+
print(result.to_pandas())
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
dept total_salary headcount
|
|
80
|
+
0 eng 345.0 3
|
|
81
|
+
1 mkt 110.0 1
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 📊 Performance — bounded v0.5.0 benchmark
|
|
87
|
+
|
|
88
|
+
RTX 3090, warm median of three runs. Every query and engine ran in its own process so Arrow, Pandas, and CUDA allocations were released between measurements.
|
|
89
|
+
|
|
90
|
+
- **1M:** MX CPU beat Polars on **22/22**; MX GPU beat Polars on **15/17 comparable** paths.
|
|
91
|
+
- **10M:** MX CPU beat Polars on **18/22**; MX GPU beat Polars on **14/17 comparable** paths.
|
|
92
|
+
- Q8 GPU remains `N/A:JIT`; Q15, Q17, Q18, and Q20 GPU workers exceeded the bounded timeout.
|
|
93
|
+
- cuDF was unavailable on this machine, so RAPIDS remains `N/A` and no RAPIDS parity claim is made.
|
|
94
|
+
|
|
95
|
+
### 1M representative queries
|
|
96
|
+
|
|
97
|
+
| Query | MX CPU | MX GPU | Polars | Best MX speedup |
|
|
98
|
+
|---|---:|---:|---:|---:|
|
|
99
|
+
| Q4 · Order priority | 12.8 ms | **3.4 ms** | 20.2 ms | **5.9× GPU** |
|
|
100
|
+
| Q6 · Discounted revenue | **6.7 ms** | 6.8 ms | 10.8 ms | **1.6× CPU** |
|
|
101
|
+
| Q12 · Shipping modes | **0.6 ms** | 6.1 ms | 26.6 ms | **44.3× CPU** |
|
|
102
|
+
| Q13 · Customer distribution | 20.4 ms | **1.8 ms** | 26.5 ms | **14.7× GPU** |
|
|
103
|
+
| Q21 · Waiting suppliers | 27.1 ms | **6.6 ms** | 30.2 ms | **4.6× GPU** |
|
|
104
|
+
|
|
105
|
+
### 10M representative queries
|
|
106
|
+
|
|
107
|
+
| Query | MX CPU | MX GPU | Polars | Best MX speedup |
|
|
108
|
+
|---|---:|---:|---:|---:|
|
|
109
|
+
| Q4 · Order priority | 265.1 ms | **28.6 ms** | 105.4 ms | **3.7× GPU** |
|
|
110
|
+
| Q9 · Product profit | **2.0 ms** | 16.5 ms | 109.7 ms | **54.9× CPU** |
|
|
111
|
+
| Q12 · Shipping modes | **1.5 ms** | 8.2 ms | 128.0 ms | **85.3× CPU** |
|
|
112
|
+
| Q13 · Customer distribution | 368.8 ms | **28.5 ms** | 442.0 ms | **15.5× GPU** |
|
|
113
|
+
| Q21 · Waiting suppliers | 1068.7 ms | **49.8 ms** | 99.1 ms | **2.0× GPU** |
|
|
114
|
+
|
|
115
|
+
Full matrices: [1M CSV](scripts/bench_results_1M.csv) · [10M CSV](scripts/bench_results_10M.csv) · [methodology and kernel catalogue](docs/benchmarks.md)
|
|
116
|
+
|
|
117
|
+
## What remains for parity
|
|
118
|
+
|
|
119
|
+
**Performance parity:** replace Q8's JIT path; fix Q15/Q17/Q18/Q20 GPU timeouts; improve Q1 and Q14 GPU execution; run synchronized 1M/10M measurements on a compatible RAPIDS/cuDF environment.
|
|
120
|
+
|
|
121
|
+
**Feature parity:** native GPU UTF-8 dictionary construction and string/date gather; GPU windows; semi/anti/as-of/range joins; broader string/regex, datetime, nested-type, reshape, SQL, streaming, and I/O pushdown coverage.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## 📚 Docs
|
|
126
|
+
|
|
127
|
+
| | |
|
|
128
|
+
|---|---|
|
|
129
|
+
| [**v0.5.0 Release Notes**](RELEASE_NOTES_0.5.0.md) | v0.4/v0.5 gains, benchmark evidence, known gaps, and parity roadmap |
|
|
130
|
+
| [**API Reference**](docs/api.md) | LazyFrame, expressions, SQL frontend, supported operations, running tests |
|
|
131
|
+
| [**Benchmarks**](docs/benchmarks.md) | Full TPC-H tables, kernel catalogue, limitations, roadmap, reproduce instructions |
|
|
132
|
+
| [**Architecture**](docs/vision-and-architecture.md) | Design philosophy, internal layers, how MAX Graph fits in |
|
|
133
|
+
| [**Kernel Visualizer**](visualize/mxframe_pipeline.html) | Interactive plans, dispatch boundaries, GPU threads, and output masks |
|
|
134
|
+
| [**Contributing**](CONTRIBUTING.md) | Dev setup, writing Mojo kernels, adding queries |
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 📦 Dependencies
|
|
139
|
+
|
|
140
|
+
| Package | Required | Purpose |
|
|
141
|
+
|---|---|---|
|
|
142
|
+
| `pyarrow >= 14` | ✅ | Column storage, zero-copy NumPy bridge |
|
|
143
|
+
| `numpy >= 1.24` | ✅ | Vectorized pre/post processing |
|
|
144
|
+
| `pandas >= 2.0` | ✅ | Reference implementations |
|
|
145
|
+
| `modular >= 26.4` | GPU only | MAX Engine runtime, Mojo GPU dispatch |
|
|
146
|
+
| `polars >= 0.20` | optional | Benchmark comparison |
|
|
147
|
+
| `sqlglot >= 25` | optional | SQL frontend parsing |
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## 📄 License
|
|
152
|
+
|
|
153
|
+
Apache 2.0 — see [LICENSE](LICENSE).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""mxframe - GPU-accelerated DataFrames with MAX Engine"""
|
|
2
2
|
|
|
3
|
-
__version__ = "0.
|
|
3
|
+
__version__ = "0.5.0"
|
|
4
4
|
|
|
5
5
|
# Lazy expression layer
|
|
6
6
|
from .lazy_expr import Expr, col, lit, when, row_number
|
|
@@ -13,17 +13,41 @@ from .lazy_frame import (
|
|
|
13
13
|
DeviceType,
|
|
14
14
|
)
|
|
15
15
|
|
|
16
|
-
# Compiler
|
|
17
|
-
from .compiler import GraphCompiler
|
|
18
|
-
|
|
19
16
|
# Optimizer
|
|
20
17
|
from .optimizer import PlanOptimizer, OptimizationResult, optimize_plan
|
|
21
18
|
|
|
22
19
|
# Plan validation
|
|
23
20
|
from .plan_validation import PlanValidationError, validate_plan, validate_plan_or_raise
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
_RUNTIME_EXPORTS = {"GraphCompiler", "CustomOpsCompiler", "KERNELS_PATH"}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _runtime_import_error() -> ImportError:
|
|
26
|
+
return ImportError(
|
|
27
|
+
"MXFrame computation requires the Modular MAX runtime. "
|
|
28
|
+
"Install with: pip install 'mxframe[runtime]'"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def __getattr__(name: str):
|
|
33
|
+
if name not in _RUNTIME_EXPORTS:
|
|
34
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
35
|
+
try:
|
|
36
|
+
if name == "GraphCompiler":
|
|
37
|
+
from .compiler import GraphCompiler
|
|
38
|
+
return GraphCompiler
|
|
39
|
+
from .custom_ops import CustomOpsCompiler, KERNELS_PATH
|
|
40
|
+
return CustomOpsCompiler if name == "CustomOpsCompiler" else KERNELS_PATH
|
|
41
|
+
except ImportError as exc:
|
|
42
|
+
raise _runtime_import_error() from exc
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def clear_cache() -> None:
|
|
46
|
+
try:
|
|
47
|
+
from .custom_ops import clear_cache as _clear_cache
|
|
48
|
+
except ImportError as exc:
|
|
49
|
+
raise _runtime_import_error() from exc
|
|
50
|
+
_clear_cache()
|
|
27
51
|
|
|
28
52
|
# SQL frontend (optional; requires `sqlglot`).
|
|
29
53
|
# Imported lazily so `pip install mxframe` works without the sql extra.
|
|
@@ -59,6 +83,11 @@ def warmup(device: str = "auto") -> float:
|
|
|
59
83
|
import pyarrow as _pa
|
|
60
84
|
import numpy as _np
|
|
61
85
|
|
|
86
|
+
try:
|
|
87
|
+
from .custom_ops import CustomOpsCompiler
|
|
88
|
+
except ImportError as exc:
|
|
89
|
+
raise _runtime_import_error() from exc
|
|
90
|
+
|
|
62
91
|
t0 = _t.perf_counter()
|
|
63
92
|
|
|
64
93
|
# Step 1: session init (cached -- effectively free on repeat calls)
|