--- warning: DO NOT CHANGE THIS MANUALLY, THIS IS GENERATED BY https://github.com/duckdb/community-extensions repository, check README there title: gpudb excerpt: | DuckDB Community Extensions GPU-accelerated plain DuckDB SQL on Apple Silicon Metal and NVIDIA CUDA - GROUP BY, joins, filters and top-k run on the GPU when measured faster, with the same answers. First SQL execution engine that targets Apple Silicon GPUs. # gpudb's entry for the DuckDB community-extensions registry, kept beside the # source so it can be diffed against the descriptor the registry serves. # Schema: https://github.com/duckdb/community-extensions/blob/main/CONTRIBUTING.md extension: name: gpudb description: GPU-accelerated plain DuckDB SQL on Apple Silicon Metal and NVIDIA CUDA - GROUP BY, joins, filters and top-k run on the GPU when measured faster, with the same answers. First SQL execution engine that targets Apple Silicon GPUs. version: 0.7.0 language: C++ build: cmake license: Apache-2.0 requires_toolchains: "python3;cuda" excluded_platforms: "wasm_mvp;wasm_eh;wasm_threads;windows_amd64;windows_amd64_rtools;windows_amd64_mingw" maintainers: - singhpratech repo: github: singhpratech/duckdbgpumetaldbram ref: b712f4b7624b83873ca9c221f70a4a9b5ce5f3f7 # the commit the v0.7.0 tag points at; rebuilt unchanged for DuckDB 1.5.6 (stable C API, no source change) docs: hello_world: | LOAD gpudb; -- which processor will run, and what this build can do SELECT gpu_build_info(); -- make a (key, value) pair resident on the GPU once ... CREATE TABLE sales AS SELECT (range % 1000)::BIGINT AS store, (range * 7 % 10007)::BIGINT AS amount FROM range(10000000); SELECT gpu_upload_pair('sales_by_store', store, amount) FROM sales; -- ... then ask as often as you like: GROUP BY, HAVING and top-k run on the device -- top 5 stores by sum(amount): (store, sum, count) SELECT * FROM gpu_groupby_sum_resident_topk('sales_by_store', 5, 'desc'); -- backend, rows, wall time and kernel time of the last call SELECT gpu_last_stats(); extended_description: | ## Plain DuckDB SQL on the GPU `gpudb` is DuckDB plus a GPU. You write the SQL you already write. A statement is answered by the GPU when that has been **measured** to be faster for its shape and size on your machine, and by DuckDB — untouched, at its usual speed — otherwise. Rows, column names and column types are identical either way. Two rules hold everywhere and are enforced by the test gates the project ships: 1. **Never slower than DuckDB.** Every rewritten shape is timed against native; a shape that loses is left to DuckDB, and the losing measurements stay published. 2. **Never a different answer.** Integer and `DECIMAL` aggregates are bit-exact (128-bit sums on the device). Anything DuckDB itself computes order-dependently — `sum(DOUBLE)` — is never rewritten. ### Three ways in The first two put **plain** SQL on the device and need the Python package as well as this extension, because DuckDB's stable C API has no hook that sees a statement before it is planned: `pip install duckdb-gpudb`. On Apple Silicon (macOS 15 or later) and x86-64 Linux (glibc 2.34 or newer) that wheel carries a matching extension binary inside it, which is the quickest way to plain SQL on the GPU; elsewhere it uses the extension DuckDB has installed from this registry. The third needs no wrapper: loaded in any DuckDB client, this extension gives the explicit `gpu_*` functions. **1. The `gpudb` shell** — a SQL shell whose footer says, under every result, where the statement ran and why. $ gpudb my.duckdb gpudb> SELECT l_partkey, sum(l_quantity) AS qty FROM lineitem GROUP BY l_partkey ORDER BY qty DESC LIMIT 5; ... GPU (topk: the resident GROUP BY) **2. Python — `gpudb.connect()`** — the same decision, for applications and notebooks. import gpudb con = gpudb.connect("my.duckdb") # same surface as duckdb.connect() con.sql("SELECT l_partkey, sum(l_quantity) AS qty FROM lineitem " "GROUP BY l_partkey ORDER BY qty DESC LIMIT 5") con.last_rewrite() # what ran where, and why Tables become resident in the background, in short segments taken only while the connection is idle. A memory budget decides what stays on the device by measured value; what does not fit simply runs on DuckDB. **3. Explicit `gpu_*` functions — any DuckDB client, including the CLI.** This is the only route that needs no wrapper, because DuckDB's stable C API has no hook that sees a statement before it is planned. `gpu_upload` / `gpu_upload_pair` make columns resident once; `gpu_sum_resident`, `gpu_groupby_sum_resident` (`_having`, `_topk`), `gpu_topk_resident`, `gpu_inner_join` and the exact `gpu_groupby_exact_*` family read them from GPU memory. `gpu_last_stats()` reports which processor ran and for how long. ### What runs on the device | SQL | On the GPU | |---|---| | `GROUP BY` with `sum` `count` `min` `max` `avg`, `count(DISTINCT)` | yes — integer, `DATE`, `TIMESTAMP`, `DECIMAL`, `VARCHAR` keys, several keys, `GROUP BY ALL` | | `WHERE` | yes — one fused pass on the device | | `HAVING`, `ORDER BY ... LIMIT k` | yes | | Joins | inner, left, right, many-to-many; several payload columns | | Subqueries | `EXISTS`, `IN`, scalar subqueries as `WHERE` terms; derived tables; CTEs; views | | Aggregates without `GROUP BY` | yes | | Window functions, `FULL` joins, `median` / `stddev` / quantiles, `sum(DOUBLE)`, prepared-statement parameters, statements inside an explicit transaction | stay on DuckDB | `sum(DOUBLE)` stays on DuckDB by design: DuckDB's own result depends on the order the values are added, so there is no single answer for a GPU to match. The explicit `gpu_sum` functions remain available for it, with a stated 1e-9 relative tolerance. ### Measured (TPC-H, every row compared with native DuckDB) Release build of 2026-09-20, default memory budget, N=5. A statement reaches the GPU through `execute()` or through `sql()` — two different code paths, timed separately: | | asked through | on the GPU | rows differing | speed-up on those queries | |---|---|---|---|---| | Apple M4 Max (Metal), SF1 | execute() | 17 of 22 | 0 | 1.52x - 15.32x | | Apple M4 Max (Metal), SF1 | sql() | 17 of 22 | 0 | 1.37x - 9.49x | | Apple M4 Max (Metal), SF10 | execute() | 19 of 22 | 0 | 1.06x - 48.10x | | Apple M4 Max (Metal), SF10 | sql() | 19 of 22 | 0 | 0.92x - 26.39x | | RTX 4090 Laptop (CUDA), SF1 | execute() | 17 of 22 | 0 | 1.21x (Q15) - 31.44x (Q9) | | RTX 4090 Laptop (CUDA), SF1 | sql() | 17 of 22 | 0 | 1.66x (Q15) - 16.27x (Q9) | Plain SQL runs on the GPU by default on both backends. One row is below 1.0x and is printed rather than dropped: TPC-H Q11 at SF10 on Metal, a 6-7 ms statement measuring 1.06x through execute() and 0.92x through sql(), which straddles parity - the per-process measured rule is what declines such a statement where it loses. Nothing on CUDA is below 1.0x in these runs: Q1 first measured 0.96x and 0.98x there on the release build, the cause was found (a few-group key was being answered by sorting the whole column), CUDA got a direct grouped reduce for such keys, and Q1 now measures 1.99x and 2.07x. The remaining queries are declined on purpose (correlated subqueries, or shapes where DuckDB was measured faster) and run on DuckDB unchanged. Every measurement, **including the shapes where the GPU loses**, is in `BENCHMARK.md`; every trade-off is in `KNOWN_ISSUES.md`; the dated engineering journal is `docs/RESEARCH_NOTES.md`. ### Platforms | Platform | From this registry | |---|---| | macOS, Apple Silicon | Metal GPU | | Linux x86_64 / arm64 | `SELECT gpu_build_info();` says which backends the binary you received carries and which one it chose. Where it is CPU-only, the SQL surface and the answers are the same. A CUDA build is also available from the project's releases and from source | | No GPU | CPU fallback | The extension is written in C++ and touches DuckDB through its stable C API only (the C_STRUCT ABI): it links no libduckdb, includes no DuckDB C++ headers and does no plan surgery. Guides: the `gpudb` shell https://github.com/singhpratech/duckdbgpumetaldbram/blob/main/docs/USING_THE_SHELL.md , `gpudb.connect()` https://github.com/singhpratech/duckdbgpumetaldbram/blob/main/docs/USING_PYTHON.md , installing both pieces https://github.com/singhpratech/duckdbgpumetaldbram/blob/main/docs/INSTALL.md Source, benchmarks and issues: https://github.com/singhpratech/duckdbgpumetaldbram hardware_requirements: | Apple Metal: an Apple Silicon Mac (M1 or later) on macOS 15 or later, which is the floor the shipped binaries declare. The binary compiles its shaders at run time as Metal shading language 3.2; building from source needs the macOS 15 SDK. NVIDIA CUDA: any GPU with sm_75 or later (Turing and newer). The driver a binary needs follows the CUDA toolkit it was built with: a CUDA 13 build needs R580 or newer, a CUDA 12.x build reaches the GPU on R525+. Falls back to CPU otherwise. Linux: the binaries this registry builds (x86-64 and arm64) need libgomp.so.1 present at load time (apt install libgomp1, dnf install libgomp) - the extension links OpenMP dynamically. It is on most images already; in a minimal container LOAD fails with "libgomp.so.1: cannot open shared object file". The project's own release binary and the pip wheel are x86-64 with glibc 2.34 or newer, and the wheel bundles its own libgomp.so.1. known_limitations: | * Window functions, FULL joins, median / stddev / quantiles, and sum / avg over DOUBLE or FLOAT are answered by DuckDB. sum(DOUBLE) is by design: native's own result depends on the order the values are added, so "the same as native" is not definable for it. * A Linux binary from this registry may report compiled=cpu: it offers the same gpu_* functions and returns the same results, with backend=CPU. SELECT gpu_build_info(); says which backends any given binary carries (compiled=) and which one it chose (runtime=). A CUDA build is available from the project's releases and from source. * On CUDA hardware, ordinary SQL is rewritten onto the GPU by default, and GPUDB_CUDA_EXACT=0 turns that off without a rebuild. The thresholds it decides with were measured on Apple Silicon and verified on one NVIDIA machine, not measured for every GPU. The gate run on that RTX 4090 Laptop before the path was switched on was 1630 cells, none slower than native and none differing; the same gate on the release build is 1631 cells - 1014 rewritten and passing, 616 declined on a threshold, none below 1.0x, none differing, exit 0. Both backends read one measured thresholds table; the rule that admits a key with few distinct values rests on a backend being able to answer it without sorting the whole column, which both now do. * Ordinary SQL is answered on the device only through a client that sees the statement before DuckDB plans it - the project's `gpudb` shell or gpudb.connect(), both from pip install duckdb-gpudb. LOAD gpudb in the stock CLI, or in any other client, gives the explicit gpu_* functions and the same answers, not the rewrite. * A copy of the extension already installed is kept by a plain INSTALL; FORCE INSTALL gpudb FROM community; or UPDATE EXTENSIONS; is what replaces it with the newest build this registry has, and neither reaches a process that has already loaded the old one. * GROUP BY at very low cardinality is a native win: DuckDB's streaming aggregate is scan-bound and very fast there, and the size thresholds leave those statements with it. * Whole-column min / max on a stored table stays a native win — DuckDB answers it from zonemap statistics without scanning. extension_star_count: 31 extension_star_count_pretty: 31 extension_download_count: 847 extension_download_count_pretty: 847 image: '/images/community_extensions/social_preview/preview_community_extension_gpudb.png' layout: community_extension_doc --- ### Compatibility Matrix This table covers all DuckDB releases that have not reached [end-of-life](https://duckdb.org/release_calendar), including LTS releases, and shows the 2 most recent patch releases of each. Older patch releases are not listed, but the extension may be available for them as well.
| Platform | 1.4.4 | 1.4.5 | 1.5.5 | 1.5.6 | 2.0.0 | | :--- | :---: | :---: | :---: | :---: | :---: | | Linux AMD64 | ❌ | ❌ | ✅ | ✅ | ✅ | | Linux ARM64 | ❌ | ❌ | ✅ | ✅ | ✅ | | macOS AMD64 | ❌ | ❌ | ✅ | ✅ | ❌ | | macOS ARM64 | ❌ | ❌ | ✅ | ✅ | ✅ | | Windows AMD64 | ❌ | ❌ | ❌ | ❌ | ❌ | | Windows ARM64 | ❌ | ❌ | ❌ | ❌ | ❌ | | WebAssembly | ❌ | ❌ | ❌ | ❌ | ❌ | ### Installing and Loading ```sql INSTALL {{ page.extension.name }} FROM community; LOAD {{ page.extension.name }}; ``` {% if page.docs.hello_world %} ### Example ```sql {{ page.docs.hello_world }}``` {% endif %} {% if page.docs.extended_description %} ### About {{ page.extension.name }} {{ page.docs.extended_description }} {% endif %} ### Added Functions | function_name | function_type | description | comment | examples | |-----------------------------------------|---------------|-------------|---------|----------| | gpu_agg_exact_global | table | NULL | NULL | | | gpu_anti_join_count_resident | scalar | NULL | NULL | | | gpu_anti_join_sum_resident | scalar | NULL | NULL | | | gpu_anti_join_sum_resident_f64 | scalar | NULL | NULL | | | gpu_assert_rows | scalar | NULL | NULL | | | gpu_avg_decimal | scalar | NULL | NULL | | | gpu_build_info | scalar | NULL | NULL | | | gpu_drop_column | scalar | NULL | NULL | | | gpu_drop_resident | scalar | NULL | NULL | | | gpu_groupby_count_resident | table | NULL | NULL | | | gpu_groupby_count_resident_having | table | NULL | NULL | | | gpu_groupby_count_resident_topk | table | NULL | NULL | | | gpu_groupby_exact_multi | table | NULL | NULL | | | gpu_groupby_exact_resident | table | NULL | NULL | | | gpu_groupby_exact_resident_having | table | NULL | NULL | | | gpu_groupby_exact_resident_topk | table | NULL | NULL | | | gpu_groupby_exact_resident_where | table | NULL | NULL | | | gpu_groupby_exact_resident_where_having | table | NULL | NULL | | | gpu_groupby_exact_resident_where_topk | table | NULL | NULL | | | gpu_groupby_sum_resident | table | NULL | NULL | | | gpu_groupby_sum_resident_f64 | table | NULL | NULL | | | gpu_groupby_sum_resident_f64_having | table | NULL | NULL | | | gpu_groupby_sum_resident_f64_topk | table | NULL | NULL | | | gpu_groupby_sum_resident_having | table | NULL | NULL | | | gpu_groupby_sum_resident_topk | table | NULL | NULL | | | gpu_inner_join | table | NULL | NULL | | | gpu_invalidate | scalar | NULL | NULL | | | gpu_join_count_resident | scalar | NULL | NULL | | | gpu_join_materialize | scalar | NULL | NULL | | | gpu_join_rows_resident | table | NULL | NULL | | | gpu_join_sum_resident | scalar | NULL | NULL | | | gpu_join_sum_resident_f64 | scalar | NULL | NULL | | | gpu_last_stats | scalar | NULL | NULL | | | gpu_left_join_count_resident | scalar | NULL | NULL | | | gpu_left_join_sum_resident | scalar | NULL | NULL | | | gpu_left_join_sum_resident_f64 | scalar | NULL | NULL | | | gpu_max | aggregate | NULL | NULL | | | gpu_max_resident | scalar | NULL | NULL | | | gpu_min | aggregate | NULL | NULL | | | gpu_min_resident | scalar | NULL | NULL | | | gpu_note_rows | scalar | NULL | NULL | | | gpu_prepare_resident | scalar | NULL | NULL | | | gpu_resident_dict_component | scalar | NULL | NULL | | | gpu_resident_dictionary | table | NULL | NULL | | | gpu_resident_info | scalar | NULL | NULL | | | gpu_residents | table | NULL | NULL | | | gpu_rewrite_ast | scalar | NULL | NULL | | | gpu_semi_join_count_resident | scalar | NULL | NULL | | | gpu_semi_join_sum_resident | scalar | NULL | NULL | | | gpu_semi_join_sum_resident_f64 | scalar | NULL | NULL | | | gpu_store_columns | table | NULL | NULL | | | gpu_sum | aggregate | NULL | NULL | | | gpu_sum_resident | scalar | NULL | NULL | | | gpu_sum_resident_f64 | scalar | NULL | NULL | | | gpu_topk_resident | table | NULL | NULL | | | gpu_topk_resident_f64 | table | NULL | NULL | | | gpu_upload | aggregate | NULL | NULL | | | gpu_upload_abort | scalar | NULL | NULL | | | gpu_upload_begin | scalar | NULL | NULL | | | gpu_upload_columns | aggregate | NULL | NULL | | | gpu_upload_finish | scalar | NULL | NULL | | | gpu_upload_pair | aggregate | NULL | NULL | | | gpu_upload_pair_exact | aggregate | NULL | NULL | | | gpu_upload_rows_exact | aggregate | NULL | NULL | | | gpu_upload_status | scalar | NULL | NULL | | ### Overloaded Functions This extension does not add any function overloads. ### Added Types This extension does not add any types. ### Added Settings | name | description | input_type | scope | aliases | typed_value | |----------------|---------------------------------------------------------|------------|--------|---------|-------------| | temp_directory | Set the directory to which temporary files are written. | VARCHAR | GLOBAL | [] | post.db.tmp |