DuckDB GPU Acceleration After 2.0: Where It Wins, Where It Doesn't, and Why the Mac Is Different

DuckDB 2.0 turned DuckDB into a server, AWS is buying the company behind it, and the GPU question is back. DuckDB 2.0 also changed the query format my GPU extension reads, and gpudb 0.7.1 adapts to it: on an Apple M4 Max, 18 of 22 TPC-H SF10 queries run on the GPU under DuckDB 2.0, 3.7x faster end to end, with every answer identical to DuckDB's. Here is where a GPU helps DuckDB, how gpudb decides, and why the Mac is the interesting case.
DuckDB 2.0 shipped on 21 October as a long-term support release, and the headline feature is that DuckDB can now be a server. Any DuckDB process can serve its databases over the new Quack protocol, and a new CONNECT statement can switch to a remote database, including PostgreSQL and MySQL. Two months earlier, AWS announced it is acquiring DuckLabs, the company behind DuckDB; DuckDB itself stays MIT-licensed under the non-profit DuckDB Foundation. The in-process database that people ran on laptops is turning into shared infrastructure, and that brings an old question back: should that shared box be using its GPU?
I have a stake in the answer. gpudb is my Apache-2.0 extension that runs plain DuckDB SQL on the GPU, Apple silicon through Metal first and NVIDIA through CUDA. DuckDB 2.0 changed the query format gpudb reads, and version 0.7.1 adapts to it. This post covers where a GPU helps DuckDB, what changed in 2.0, and what the numbers look like now.
Measured 29 October 2026 with the released wheels from PyPI: TPC-H SF10 (60,000,000-row lineitem), an Apple M4 Max with 64 GB of unified memory, warm, execute() path, minimum of 5 runs, every row compared with native before any time was counted.
DuckDB stopped being a laptop tool
The adoption numbers are the reason any of this matters. At DuckCon in June 2026, the DuckDB team put downloads at about 40 million a month, up from about 4 million in August 2024. In the 2025 Stack Overflow developer survey, DuckDB's usage rose from 1.4% to 3.3% of respondents, and it ranked fourth for admiration, 0.2 points behind SQLite. A year ago its own estimate was 20 or more Fortune 100 companies using it.
2.0 is built for that audience. Besides the server mode, it adds asynchronous I/O for Parquet and CSV, a first-class VARIANT type and a new SQL parser that extensions can hook into, and it is supported until at least October 2027. It also got faster at the plain work: on my TPC-H SF10 runs below, the 22 queries take 993 ms on DuckDB 2.0.0 against 1,236 ms on 1.5.6, about 20% less, without changing a line of SQL. Any GPU story about DuckDB has to beat that moving target.
Where a GPU helps DuckDB, and how gpudb decides
A GPU shines at doing the same arithmetic over millions of values that are already in memory. The skill is sending it exactly those queries and nothing else, and that is what gpudb is built around.
- Storage-bound queries stay storage-bound. If a query spends its time pulling Parquet from S3 or a disk, the network or the drive sets the pace, for a CPU and a GPU alike. The GPU's work starts once the data is in memory.
- It warms up in the background. The first time you query a table, DuckDB answers immediately while gpudb copies the columns it needs into GPU memory. Once they are in place, the GPU takes over. You never wait on the copy.
- It leaves DuckDB's fastest cases to DuckDB. Some work is already fast on the CPU: a GROUP BY over a few hundred distinct values fits in the CPU's fastest cache, and DuckDB answers it in milliseconds. gpudb hands those straight to DuckDB, along with single-table GROUP BYs under 1,000 groups, window functions and sums over
DOUBLE, so they run at full DuckDB speed. - It takes the heavy lifting. Large GROUP BYs, joins feeding an aggregation, and top-k queries over tens of millions of rows in memory run on the GPU. That is most of TPC-H, and it is where the 1.7x to 33.9x speed-ups below come from.
And then there is the copy. On a PC or a server, the GPU is a separate card with its own memory, and data reaches it over the PCIe bus, a cost every NVIDIA-based engine has to work around. On Apple silicon, the CPU and GPU sit on the same chip and share one pool of memory. gpudb still builds GPU-resident copies of the columns it uses, but no bus sits between the data and the GPU. That is the main reason the Mac is a different case, and why I started there.
What changed in DuckDB 2.0
On DuckDB 2.0.0, the released gpudb 0.7.0 installed from the community registry and loaded fine, with its Metal backend registered. Then I ran TPC-H at SF10 through its plain-SQL path, and 0 of 22 queries reached the GPU. Every one came back marked "shape": not a form the rewrite recognised, so DuckDB answered it. The same wheel on DuckDB 1.5.6, on the same data the same evening, put 19 of 22 on the GPU.
The cause was one layer down. DuckDB's stable C API has no hook that sees a statement before it is planned, so gpudb's Python wrapper reads each statement through DuckDB's own parser, json_serialize_sql, and decides from that tree whether there is a GPU rewrite. DuckDB 2.0 changed the shape of that tree:
-- the same statement, serialized by DuckDB's own parser
SELECT json_serialize_sql('SELECT k, sum(v) FROM t GROUP BY k');
-- DuckDB 1.5.6: sum's input is a child
"function_name": "sum",
"children": [ { "class": "COLUMN_REF", "column_names": ["v"] } ]
-- DuckDB 2.0.0: sum's input moved, and gained a wrapper
"function_name": "sum",
"arguments": [ { "name": "",
"expression": { "class": "COLUMN_REF", ... } } ]
Function inputs moved from children into arguments, each wrapped as a name and an expression. Constants changed format too, and a few nodes gained fields. gpudb's matcher saw sum() with no input, decided it did not understand the statement, and handed it to DuckDB. Until 0.7.1 shipped on 29 October, a fresh install paired with DuckDB 2.0 ran every query on DuckDB's CPU engine.
What did not happen matters most. Not one answer was wrong, and nothing was slower than DuckDB alone, because of the two rules gpudb is built on: never a different answer from DuckDB, and never slower than DuckDB. When the wrapper does not understand a statement, it does not guess. It hands the statement to DuckDB and DuckDB answers it. A format change that could have produced wrong results produced a missing speed-up instead.
The fix in 0.7.1
DuckDB 2.0 still accepts a tree in the old 1.5 shape when it turns one back into SQL. So the fix converts a 2.0 tree to the 1.5 shape once, at the single place the wrapper serialises a statement, and every reader downstream, including the C++ rewrite function in the extension, works unchanged. That made it a Python-only release: duckdb-gpudb 0.7.1 on PyPI, with the extension binary unchanged at 0.7.0, so nothing in DuckDB's community registry had to be rebuilt. It ships with 115 fixtures taken from DuckDB 1.5.6, covering the 22 TPC-H queries and one statement for each shape the rewrite supports, and the tests run on both versions.
One shape now stays on DuckDB on 2.0 on purpose: avg over DECIMAL. DuckDB 2.0's optimizer finalises that average in one of two ways depending on the plan, and the two can differ in the last bit. Under the never-different rule the wrapper declines rather than guess which one DuckDB will choose, which is why TPC-H Q1, a GPU query on 1.5.6 at 7.5x, stays on DuckDB 2.0.
The numbers on DuckDB 2.0
All measured on 29 October with the released wheels from PyPI, in clean environments, on an Apple M4 Max with 64 GB of unified memory: TPC-H scale factor 10, a 60-million-row lineitem, the minimum of five warm runs per query, every row compared with DuckDB's own answer before any time was counted.
18 of 22 queries run on the GPU, and 0 rows differ from DuckDB on any of them. Seventeen are between 1.73x (Q15) and 33.9x (Q5) faster. The eighteenth is Q11, at 0.98x: a 6-millisecond query that sits right on the line. The gpudb project's own run of the same release measured it at 1.04x, and on 1.5.6 I measured 1.02x. This is exactly the case gpudb's per-process check is for. After a statement's first few GPU runs, the wrapper times DuckDB's own plan once on a side cursor, and hands the statement back to DuckDB if the GPU was not faster. Across all 22 queries, DuckDB 2.0 alone takes 993 ms and with gpudb takes 266 ms, 3.7x end to end.
| DuckDB | gpudb | on the GPU | rows different | speed-up on those queries | all 22, DuckDB alone | all 22, with gpudb |
|---|---|---|---|---|---|---|
| 1.5.6 | 0.7.0 | 19 of 22 | 0 | 1.10x to 41.7x | not totalled | not totalled |
| 2.0.0 | 0.7.0 | 0 of 22 | 0 | none | 977 ms | 977 ms |
| 1.5.6 | 0.7.1 | 19 of 22 | 0 | 1.02x to 23.1x | 1,236 ms | 229 ms |
| 2.0.0 | 0.7.1 | 18 of 22 | 0 | 0.98x to 33.9x | 993 ms | 266 ms |
DuckDB 2.0 is also a faster baseline, which makes the result stronger. Q1 takes 108 ms on DuckDB 1.5.6 and 56 ms on 2.0, Q19 63 ms and 32 ms, Q21 137 ms and 71 ms. Even against that quicker CPU engine, gpudb takes the whole suite from 993 ms to 266 ms, and the biggest wins are the same queries as on 1.5.6: Q5, Q9, Q18 and Q22, joins and large GROUP BYs over data in memory.
These numbers are the Apple silicon side. The fix sits in the shared Python layer, so it applies to the NVIDIA CUDA backend as well; I have not published CUDA numbers on DuckDB 2.0 yet.
gpudb and DuckDB 2.0's server mode
The new server mode is the most interesting part of DuckDB 2.0 for a GPU. gpudb's plain-SQL rewrite lives in the client wrapper, in Python, because DuckDB's C API offers no hook before planning, so a statement that reaches a DuckDB server over the network goes straight to the server's planner. A server that has loaded the extension still offers the explicit gpu_* functions to its clients; the automatic routing is a Python-client feature today. I have not yet tested gpudb behind duckdb -serve.
Who else is putting DuckDB on a GPU
The serious other project is Sirius, from the University of Wisconsin-Madison with support from NVIDIA engineers. It is a DuckDB extension that takes DuckDB's query plan and runs it on the GPU with NVIDIA's cuDF libraries, and in December 2025 NVIDIA published a ClickBench record for it on a GH200 instance. The team's CIDR 2026 paper reports large cost-efficiency gains on TPC-H and ClickBench. It is strong work with NVIDIA's libraries behind it.
| Sirius | gpudb | |
|---|---|---|
| Who builds it | University of Wisconsin-Madison, with support from NVIDIA engineers | an independent open-source project, with outside contributors |
| GPUs | NVIDIA, through cuDF and RMM | Apple silicon through Metal, and NVIDIA through CUDA |
| How it plugs into DuckDB | an extension that takes DuckDB's plan as Substrait and runs the operators on the GPU | an extension on DuckDB's stable C API, plus a Python wrapper that rewrites plain SQL before DuckDB plans it |
| What decides GPU or CPU | whether it supports the operators | bounds measured before a release, then a timing against DuckDB in your own process |
| Headline result | a ClickBench record on a GH200 at $1.5 an hour, at least 7.2x the cost efficiency of the top CPU systems (NVIDIA, December 2025) | 18 of 22 TPC-H SF10 queries on an M4 Max on DuckDB 2.0, 0 rows different (this post) |
| How you get it | build from source; no tagged release, not in DuckDB's community registry | pip install duckdb-gpudb, or INSTALL gpudb FROM community for the explicit functions |
| Licence | Apache-2.0 | Apache-2.0 |
On NVIDIA data-center hardware, Sirius has the most momentum. gpudb's distinctive ground is the Mac: Sirius, cuDF and HeavyDB all need an NVIDIA GPU, so on Apple silicon gpudb is the GPU option for DuckDB, and it runs on NVIDIA through CUDA as well.
So, is it a game changer?
For DuckDB on a Mac, yes. If you do analytics with DuckDB on Apple silicon, with data that fits in memory, there is a GPU on the same chip sitting idle while DuckDB works. One pip install puts it to work on the heavy queries: 18 of 22 TPC-H queries on the GPU and 3.7x end to end on DuckDB 2.0, with every answer identical to DuckDB's and everything else handed back to DuckDB at full speed.
Across DuckDB as a whole, the gains go where the work is in memory. Queries that spend their time reading files from S3 or disk are paced by storage, and the automatic path runs from Python rather than through 2.0's server mode. Those are the places with the most room to grow.
The DuckDB 2.0 episode is the best evidence I have that the design holds. A change in DuckDB's query format made the GPU disappear, the cost was speed and never a wrong answer, and a fix at a single point brought it back.
Try it
pip install -U duckdb-gpudb # 0.7.1: works with DuckDB 2.0 and 1.x
gpudb my.duckdb # the footer says where each ran
# Python: the same surface as duckdb.connect()
import gpudb
con = gpudb.connect("my.duckdb")
con.execute("SELECT ... GROUP BY ...").fetchall()
print(con.last_rewrite()["detail"]) # where it ran, and why
gpudb runs on Apple silicon Macs on macOS 15 or later, and on x86-64 Linux with an NVIDIA driver. From any DuckDB client, INSTALL gpudb FROM community; LOAD gpudb; gives you the explicit gpu_* functions; I checked that this loads on DuckDB 2.0.0. The install matrix is in the install guide, the design behind the two rules is in TRANSPARENT_DESIGN.md, and what stays on DuckDB, reason by reason, is in KNOWN_ISSUES.md. If you run it on a different Mac or GPU, the numbers from your machine are new information, and I would like to see them as an issue on GitHub.
FAQ: DuckDB and the GPU after 2.0
Does DuckDB 2.0 support GPU acceleration?
Not by itself: DuckDB 2.0 has no GPU backend. GPU acceleration comes from extensions. gpudb 0.7.1 runs plain DuckDB SQL on Apple silicon GPUs through Metal and on NVIDIA GPUs through CUDA, and works with DuckDB 2.0. Sirius, from the University of Wisconsin-Madison with NVIDIA, runs DuckDB plans on NVIDIA GPUs.
Does gpudb work with DuckDB 2.0?
Yes, from version 0.7.1. Version 0.7.0 loaded on DuckDB 2.0 but sent every statement back to DuckDB, because DuckDB 2.0 changed the shape of the query tree that json_serialize_sql produces. 0.7.1 converts the new shape back to the old one at a single point. On an Apple M4 Max at TPC-H scale factor 10, 18 of 22 queries run on the GPU with zero rows different from DuckDB.
Can DuckDB use the GPU on a Mac?
With gpudb, yes. It is an Apache-2.0 DuckDB extension with a Metal backend for Apple silicon Macs on macOS 15 or later, installed with pip install duckdb-gpudb. Sirius, cuDF and HeavyDB all require an NVIDIA GPU.
Will a GPU speed up DuckDB queries on Parquet files in S3?
The download and decode are paced by the network and the disk, for a CPU and a GPU alike. Once the data is in memory, the heavy work speeds up: large GROUP BYs, joins with aggregation, top-k. The first query over a table runs on DuckDB while gpudb copies the columns it needs to the GPU, so you never wait on the copy.
gpudb vs Sirius: which should I use?
Sirius targets NVIDIA data-center GPUs, with NVIDIA's libraries and a ClickBench record behind it. On a Mac, gpudb is the GPU option for DuckDB, because Sirius requires NVIDIA hardware. gpudb also runs on NVIDIA GPUs through CUDA.
Read next
References & Citations
- DuckDB — Announcing DuckDB 2.0.0 LTS, 21 October 2026: server mode (Quack), CONNECT, support window, new parser — https://duckdb.org/2026/10/21/announcing-duckdb-200
- DuckDB — DuckLabs to Join AWS, 26 August 2026: acquisition, MIT licence, DuckDB Foundation — https://duckdb.org/2026/08/26/ducklabs-to-join-aws
- DuckDB — Adoption Metrics and Benchmark Results for DuckDB v1.4 LTS: Stack Overflow 2025 usage and admiration, Fortune 100 estimate — https://duckdb.org/2025/10/09/benchmark-results-14-lts
- DuckCon #7 — State of the Duck slides, June 2026: monthly downloads — https://blobs.duckdb.org/events/duckcon7/hannes-muehleisen-mark-raasveldt-duckcon7-state-of-the-duck.pdf
- NVIDIA Technical Blog — Sirius ClickBench record, 15 December 2025: developers, integration, GH200 result — https://developer.nvidia.com/blog/nvidia-gpu-accelerated-sirius-achieves-record-setting-clickbench-record/
- CIDR 2026 — Rethinking Analytical Processing in the GPU Era (Sirius paper) — https://www.vldb.org/cidrdb/papers/2026/p12-yogatama.pdf
- Sirius — repository: licence, stars, releases — https://github.com/sirius-db/sirius
- gpudb — the DuckDB 2.0 fix, commit 566f0d1 — https://github.com/singhpratech/duckdbgpumetaldbram/commit/566f0d18cf2cf51bb718ac9fef9c9c018bb6bb1b
- gpudb — duckdb-gpudb 0.7.1 on PyPI — https://pypi.org/project/duckdb-gpudb/0.7.1/
- gpudb — TRANSPARENT_DESIGN.md: the two rules, the client-side rewrite, the per-process check — https://github.com/singhpratech/duckdbgpumetaldbram/blob/main/docs/TRANSPARENT_DESIGN.md
- gpudb — DuckDB community extension listing — https://duckdb.org/community_extensions/extensions/gpudb
Subscribe to new posts from theaivibe.org
Related Posts

Schema Evolution Is Still the Hardest Problem in Data Engineering
Adding a column is easy. Renaming one silently breaks three dashboards and a compliance report. Why schema evolution is data engineering's hardest problem.

DuckDB vs SQL Server vs PostgreSQL for Analytics: SQL Server Standard's Ceilings, What They Cost, and What a Lakehouse Does Instead
SQL Server ships a real analytical engine and then fences Standard edition off from it: batch mode capped at 2 threads, a 32 GB columnstore cache, no batch mode on rowstore, no SIMD, no adaptive joins, all Enterprise at 3.83x the price per core. On one Apple M4 Max, DuckDB 2.0 ran six TPC-H-style queries over 60 million rows 18x to 47x faster than PostgreSQL 18, and DuckDB held to 2 threads still beat PostgreSQL on 8 workers by 3.7x to 8.6x. Where PostgreSQL's row store runs out, how a DuckDB lakehouse starts, what MotherDuck costs, and where DuckDB sits in AWS.
Apache Arrow and Apache DataFusion Now List ArrowMetal: Arrow Compute and a DataFusion Optimizer Rule on the Apple Silicon GPU
ArrowMetal is now on Apache Arrow's Powered By page and on Apache DataFusion's integrations list. What the two listings mean, and what the listed thing does as of 0.5.0, with one real run on each side.