The First SQL Engine for Apple Silicon GPUs Is Now a DuckDB Community Extension
In May 2026 I shipped gpudb v0.1 — the first SQL execution engine targeting Apple Silicon GPUs, built as a DuckDB extension with a CUDA backend on Linux. Three releases later, the project crossed two lines at once. v0.3.0's streaming-aggregate rewrite reached parity with native DuckDB on end-to-end TPC-H queries — the worst cell improved roughly 100×, from 11.05 s to 0.109 s. And gpudb became an official DuckDB Community Extension: INSTALL gpudb FROM community now works in any DuckDB ≥ 1.5.5, signed, no flags. This is the full arc — what v0.1 proved, what v0.2 honestly lost, what v0.3 fixed, and why the next GPU frontier is joins.
TL;DR — gpudb, the first SQL execution engine for Apple Silicon GPUs, is now an official DuckDB Community Extension. In any DuckDB ≥ 1.5.5: INSTALL gpudb FROM community; — signed, no flags, full Metal backend on M-series Macs. v0.3.0's streaming rewrite reached parity with native DuckDB end-to-end (worst TPC-H cell improved ~100×), while the Metal operator kernels keep their 3–25× wins. Next up: a community-contributed GPU hash join, verified 9.9×.
In May 2026 I shipped something that, as far as I can tell, had never existed: a SQL execution engine that runs on Apple Silicon GPUs. Not a paper, not a prototype in a notebook — a loadable DuckDB extension with real Metal compute pipelines, benchmarked on TPC-H, with a CUDA backend riding the same codebase on Linux. That was gpudb v0.1.0, and the claim was narrow but real: nobody had wired a Mac's GPU into a database before.
Three months and three releases later, two lines crossed at once. The v0.3.0 rewrite reached parity with native DuckDB on end-to-end TPC-H queries — the worst cell improved by roughly 100×. And gpudb became an official DuckDB Community Extension, which means the entire install procedure is now:
INSTALL gpudb FROM community;
LOAD gpudb;
Any DuckDB ≥ 1.5.5, signed binaries, no flags, no downloads. This post is the story of how it got from first kernel to one-line install — including the release where the honest answer was "the SQL path loses by 109×," because that release is the reason the current one works.
Why an extension, and why Apple Silicon
The GPU-database idea is not new. It is, in fact, a graveyard. MapD became OmniSci became HEAVY.AI and was acqui-hired by NVIDIA in 2025. BlazingSQL went dormant in 2021. Voltron Data cut half its staff in 2025. Sirius — the strongest current academic entry, out of UW with NVIDIA backing at CIDR 2026 — is CUDA-only. Every standalone GPU database asked users to migrate their data and their queries to a new engine, and users kept declining.
gpudb makes two bets against that history. First: be an extension, not a database. DuckDB is already on the machines that matter — analysts' laptops, CI boxes, notebooks — and a loadable extension means zero migration: your files, your SQL, one LOAD. Second: target the hardware nobody else targets. Every engine in that graveyard, and every survivor, is CUDA-only. Meanwhile Apple ships up to 512 GB of unified memory at 819 GB/s on the M3 Ultra — a memory subsystem that would embarrass most server hardware — and no SQL engine had ever touched its GPU.
What v0.1 proved: the Apple Silicon GPU is a real analytics device
The first release was about existence proofs, and the numbers were better than I expected. On an M4 Max, against DuckDB's own CPU engine using all 16 threads — the honest baseline, not a strawman single-threaded loop — the Metal operator scorecard came back 9 wins, 1 loss on TPC-H lineitem cells.
The headline is the top of that chart: multi-aggregate fusion. Most real analytical queries don't compute one aggregate — they compute several over the same column (SELECT SUM(x), MIN(x), MAX(x), COUNT(x) is the median TPC-H pattern). Executed naively, that reads the column from DRAM once per aggregate. The fused Metal kernel reads it once and computes all four in a single pass, sustaining about 470 GiB/s on M4 Max — roughly 87% of the machine's memory bandwidth. Fusing four operations into one pass costs zero extra bandwidth, so per-op throughput effectively quadruples. That is where the 22–25× cells come from.
And the honest loss stays on the card: a GROUP BY with only 50 unique values loses to the CPU by 14×, because the CPU keeps its entire hash table in L1 cache and no GPU dispatch can pay for itself. That's not a bug to fix — it's a boundary to respect, which is why gpudb ships a hybrid planner that routes low-cardinality shapes to the CPU and keeps the GPU for the shapes it wins.
The Linux side: same codebase, opposite physics
The CUDA backend tells the same story from the other direction. On an RTX 4090, a resident-column SUM over 100 million rows sustains 1187 GiB/s — 17.9× over the CPU, finishing in 0.04 ms. But run it cold, and the PCIe transfer costs 80 ms before the 0.04 ms kernel even starts.
On a discrete GPU the transfer is the enemy, so the design wants resident columns. On unified memory the transfer is free — but so is the CPU's access to the same bytes, so the design wants compute-dense operators. One codebase, two backends, and the two lessons turn out to be mirror images of each other. Which brings us to the release where I had to learn that the hard way.
v0.2: the release that lost, on the record
The operator scorecard above is measured at operator level — the kernels against the CPU doing the same work. v0.2.0 asked the harsher question: what happens end-to-end, through the DuckDB CLI, on real rewritten TPC-H queries, extension versus native? The answer, recorded in BENCHMARK.md with a straight face: the extension lost every cell, by 3× to 109×.
The cause wasn't the GPU — it was the plumbing around it. The v0.2 aggregate path buffered every incoming value into per-state vectors and reduced at finalize. On unified memory, that copy is the whole cost: the query spends its time duplicating a column DuckDB already holds in memory, then a fast reduction at the end saves nothing. The 4-point analysis in the benchmark log predicted the loss was structural, not tunable. It was right.
v0.3: parity, by deleting work instead of adding it
The v0.3.0 rewrite replaced the buffered path with streaming running accumulators — the same algorithmic shape as a native DuckDB aggregate. No buffering, no copy, no finalize-time reduction. And on unified memory, one more consequence falls out: for a plain scalar aggregate over a column, the rewritten path doesn't dispatch to the GPU at all, because shipping bytes to a coprocessor that shares your memory just to add them up is pure overhead.
Every cell is now within 0–20% of native — Q6 at SF1 is a dead-even 1.00×, and the worst cell (SF1 GROUP BY) is 1.20×. The SF10 GROUP BY went from 11.05 seconds to 0.109 seconds. Correctness was gated before any timing counted: Q6 revenue totals match native to the printed digit, Q1 row sets match row-for-row, GROUP BY checksums are identical at both scale factors. The remaining 0–20% is recorded as unprofiled, because it is.
I want to be precise about what this means, because it's the most honest sentence in the release: v0.3's parity came from teaching the extension when not to use the GPU. The GPU's wins remain at operator level — the fused multi-aggregates, the large GROUP BYs, the shapes in Fig 4 — and the hybrid planner routes to them. What v0.3 fixed is that using gpu_* aggregates in an ordinary query is no longer a footgun. The extension now costs nothing where the GPU can't help, and pays off where it can.
Distribution: the part indie projects usually never solve
A database extension you have to compile from source, or load with security flags disabled, is a demo. The DuckDB Community Extensions registry is what turns it into software: the DuckDB team's infrastructure builds the extension from source on every platform, signs the binaries, and serves them to every DuckDB client. Two merged PRs later (#1898 for acceptance, #2404 for the version bump), the registry now ships gpudb v0.3.0 on all four platforms — with the full Metal backend in the Apple Silicon build, and a clean CPU fallback elsewhere. If you have DuckDB 1.5.5 or newer on an M-series Mac, you are one SQL statement away from Metal kernels. (The CUDA backend still needs a source build on Linux — toolchain packaging is a v0.4 item.)
What's next: joins, and a contributor I didn't recruit
The best signal a small open-source project can get is a stranger showing up with working code. gpudb's PR #43 is exactly that — a real Metal hash join with an on-device segment reduce and a gpu_inner_join surface, contributed from outside, verified at 9.9× on a 1M × 10M inner join on M4 Max. It's landing after a rebase pass, and it defines the v0.4 arc: joins are compute-dense in precisely the way scalar aggregates aren't, which makes them the right SQL-path GPU story on unified memory. The roadmap after that — resident-column SQL hooks, GPU window operators, and string operators on Metal where nothing like libcudf exists — is in the repo.
Try it
If you have an Apple Silicon Mac and DuckDB ≥ 1.5.5, the whole experiment is thirty seconds:
INSTALL gpudb FROM community;
LOAD gpudb;
SELECT gpu_sum(value::BIGINT) FROM range(1000000) AS t(value);
-- 499999500000, via a Metal streaming aggregate
The repo is github.com/singhpratech/duckdbgpumetaldbram — Apache-2.0, with every number in this post reproducible from BENCHMARK.md, losses included — and the extension's official home is its DuckDB Community Extensions page. The full technical write-up — architecture, kernel design, and the complete benchmark tables with an August 2026 addendum — is in the gpudb paper on this site. Thirteen years of GPU-database history says the standalone play doesn't survive. The bet here is different: ride an engine people already trust, target the hardware nobody else does, and report the 14× losses next to the 25× wins. So far, that bet is one merged registry and one external contributor ahead.
Subscribe to new posts from theaivibe.org
Related Posts

The Agent-Written Data Pipeline: The Review Bottleneck Nobody Priced In
AI agents can now write dbt models, SQL transforms, and backfills that pass CI and ship. The catch: a wrong number doesn't crash, it quietly poisons every dashboard downstream. The hard part moved from authoring to verification.

We Published Our 110× Loss. One Release Later, It Was Gone.
A reviewer on gpudb's DuckDB community-extensions PR asked the question every GPU project dreads: forget the kernel benchmarks — what does a user actually see end-to-end? We ran it honestly. Native DuckDB won every query shape, by 3× to 109×, against our own extension. We published those numbers in our own release notes — and the act of writing them down produced the structural diagnosis that closed the entire gap in the very next release. The fix was the opposite of what a GPU database is supposed to do: delete the GPU from the hot path. This is the full story, with every number.

The Inference Hardware Wars: Why Your Token Bill Is Decided in a Fab, Not a Prompt
The token-price crash everyone cheers isn't software magic. It's a hardware war: Cerebras and Groq attacking on speed, NVIDIA's Rubin counterpunching on cost-per-token. The winner of that fight, not your prompt, sets your inference bill and your latency floor.