Skip to content

Add gpudb — GPU-accelerated DuckDB on CUDA + Apple Silicon Metal#1898

Open
singhpratech wants to merge 7 commits into
duckdb:mainfrom
singhpratech:add-gpudb-extension
Open

Add gpudb — GPU-accelerated DuckDB on CUDA + Apple Silicon Metal#1898
singhpratech wants to merge 7 commits into
duckdb:mainfrom
singhpratech:add-gpudb-extension

Conversation

@singhpratech

@singhpratech singhpratech commented May 10, 2026

Copy link
Copy Markdown

Adds the gpudb community extension (now at v0.1.3).

What it does

gpudb registers gpu_sum, gpu_min, gpu_max aggregate operators that DuckDB transparently dispatches to the GPU. Two backends share one codebase:

  • NVIDIA CUDA (Linux, sm_70+): on-device aggregation with PCIe-amortized transfer
  • Apple Silicon Metal (macOS, M1+): unified memory, zero-copy

A hybrid CPU/GPU planner picks the backend per cardinality regime. CPU fallback is automatic when no GPU is present.

What's new in v0.1.3 (since the original PR)

Hybrid Metal GROUP BY that auto-dispatches between two paths per query:

  • slot-lock path — 32,768 hash partitions × 1024 threadgroup-memory slots; 32-bit CAS protects non-atomic 64-bit sum updates (workaround for Apple Silicon's missing 64-bit atomic_fetch_add). Sweet spot: expected_groups ∈ [1024, 16M].
  • radix-opt path — vectorized 4× ulong4 loads, in-block 8-bit multi-split scatter via simdgroup prefix-sum. For very low or very high cardinality.

Env override: GPUDB_METAL_GROUPBY_PATH=slotlock|radix to force a path.

This flipped the most-attacked workload — TPC-H SF10 GROUP BY l_orderkey at 15M unique groups — from CPU 1.78× faster (v0.1.2) to Metal 1.30× faster.

Why it's interesting

  • First published SQL execution engine targeting Apple Silicon GPUs. Sirius (CIDR 2026, UW + NVIDIA) and cuDF are CUDA-only.
  • Hybrid planner with empirical cardinality thresholds — the open problem from Rosenfeld/Breß CSUR 2022 and Cao SIGMOD 2024.

Verified numbers — Apple Silicon (M4 Max) vs DuckDB CPU 16-thread

The DuckDB CLI uses 16 threads by default on M4 Max — these comparisons are vs that real default, not single-thread. Each row is pinned to a specific BENCHMARK.md cell; no cross-cell glue.

Workload DuckDB CPU mt Metal v0.1.3 Speedup
TPC-H SF10 multi-agg fusion l_quantity 27 ms 1.06 ms 25.5×
TPC-H SF10 multi-agg fusion l_extendedprice 26 ms 1.18 ms 22.0×
TPC-H SF10 multi-agg fusion l_orderkey 11 ms 1.13 ms 9.7×
SF10 SUM l_quantity HOT 5 ms 1.16 ms 4.3×
TPC-H SF10 GROUP BY l_extendedprice (1.35M unique) 113 ms 28.9 ms 3.9×
500M × 1M GROUP BY synthetic 820 ms 242 ms 3.4×
1B × 1M GROUP BY synthetic 2500 ms 770 ms 3.2×
1B int64 SUM HOT 40 ms 16.2 ms 2.6×
TPC-H SF1 GROUP BY l_orderkey (1.5M unique) 8 ms 5.71 ms 1.40×
TPC-H SF10 GROUP BY l_orderkey (15M unique) 56 ms 42.95 ms 1.30×
TPC-H SF10 GROUP BY l_quantity (50 unique) 26 ms 372 ms CPU 14× — structural

9 wins, 1 honest loss documented. The loss is structural — the CPU's hash table fits in L1 cache for ≤ 1K unique groups; no GPU implementation can beat it, and the auto-dispatcher routes those workloads to the radix path anyway.

Verified numbers — NVIDIA RTX 4090 (vs single-thread CPU baseline)

  • Peak SUM ratio: ~22.8× (size-dependent, resident column)
  • GROUP BY 50M rows × 10M unique groups: 21.8×
  • TPC-H SF1 GROUP BY (6M × 1.5M unique): 3.56× (re-bench post-launch — supersedes the original 4.8× claim)

Source / details

  • Repo: https://github.com/singhpratech/duckdbgpumetaldbram
  • Pinned ref: v0.1.3 = 3ebcd764d28d16848cb11c49ad85d2a93819747d
  • License: Apache-2.0
  • Tests: 96 C++ unit checks + 46 SQL queries, 0 fail / 0 xfail
  • Reproducibility entry: scripts/local_check.sh
  • BENCHMARK.md is append-only and includes cells where GPU loses to CPU (cold streaming SUM in particular)

Author note

Solo project; happy to iterate on description, build harness, or excluded_platforms list if anything in the standard CI workflow doesn't fit. The extension is built via CMake from src/extension/duckdb_loadable.cpp against pre-built libduckdb-libs/.

gpudb adds gpu_sum / gpu_min / gpu_max aggregate operators that DuckDB
transparently dispatches to the GPU when one is available, with a
hybrid CPU/GPU planner that picks the backend for each cardinality
regime.

Notable: this is the first published SQL execution engine that targets
Apple Silicon GPUs via Metal (Sirius CIDR 2026 and cuDF are CUDA-only).
The extension auto-selects backend at load time and falls back to CPU
cleanly when no GPU is present.

Source: https://github.com/singhpratech/duckdbgpumetaldbram
Pinned ref: v0.1.1 = f0be207946084505aaaadd6e08345b412e8e8eb7
License: Apache-2.0
singhpratech added a commit to singhpratech/duckdbgpumetaldbram that referenced this pull request May 10, 2026
Match the version submitted to duckdb/community-extensions#1898. Two
specific corrections versus the prior text:

1. CUDA 21.8× was listed for "500M × 1M groups" — that scale-cell
   actually shows Metal 4.89× and CUDA at ~4.44× (BENCHMARK.md L88).
   The 21.8× CUDA result lives at 50M × 10M groups (L1347).

2. CUDA 4.8× was listed for "TPC-H SF1 GROUP BY" — the BENCHMARK.md
   SF1 GROUP BY re-bench (L40) shows Metal 1.93× with no comparable
   CUDA cell at the same baseline. Removed pending re-bench.

Other numbers (Metal 5.28× SUM 1B, Metal 4.89× GROUP BY 500M×1M,
Metal 2.08× TPC-H SF1) all map to specific cells and are kept.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
singhpratech and others added 5 commits May 9, 2026 22:45
v0.1.1 had a launch-day install blocker: the C entry symbol was named
gpudb_duckdb_init_c_api but DuckDB looks up <extension_name>_init_c_api,
where extension name = "gpudb". This produced "did not contain function
gpudb_init_c_api: undefined symbol" on every LOAD.

v0.1.2 (sha 87e70adc) renames the entry to gpudb_init_c_api and wires
duckdb/extension-ci-tools' append_extension_metadata.py into the build
so the .duckdb_extension is produced with a proper footer.

Verified: `duckdb -unsigned -c "LOAD '...'"` now works on RTX 4090
(CUDA) and M4 Max (Metal); gpu_sum/min/max/GROUP BY/OVER() all match
native sum/min/max output.
Updates description.yml from v0.1.2 single-thread baseline to v0.1.3
multi-thread baseline (DuckDB CLI default, 16 threads on M4 Max).

The most-attacked workload — TPC-H SF10 GROUP BY l_orderkey at 15M
unique groups — was 1.78x slower than DuckDB CPU multi-thread in v0.1.2.
v0.1.3 ships a 32K-partition slot-lock hash aggregate that flips it to
1.30x faster. SF1 GROUP BY l_orderkey (1.5M unique) goes from a draw to
1.40x. Multi-aggregate fusion wins (25.5x / 22.0x / 9.7x) are unchanged.

Repo ref pinned to v0.1.3 = 3ebcd764d28d16848cb11c49ad85d2a93819747d.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…hon3 toolchain

- ref -> ae4bd9cd80e (feat/ext-community-ci-scaffold): adds the root Makefile
  (c_api_extensions template), vendored C-API headers, and a loadable-extension
  build that needs no libduckdb — fixes 'No rule to make target set_duckdb_version'
- requires_toolchains: python3 (c_api makefiles use a venv, capi_quack precedent)
- excluded_platforms: add windows_amd64 (Linux + macOS only for v0.1.x)
- restore hardware_requirements / known_limitations keys dropped in the
  original submission

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…mpty file)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@singhpratech

Copy link
Copy Markdown
Author

Hi maintainers 👋 — the earlier linux_arm64 failure (make: *** No rule to make target 'set_duckdb_version') is fixed: the extension repo was missing the extension-template build harness entirely. It's now scaffolded on the C-API template (extension-ci-tools c_api_extensions makefiles, vendored C API headers, stable C_STRUCT ABI — no libduckdb link), and ref is pinned to the commit where that landed on main.

This exact workflow (DuckDB v1.5.2, ci-tools v1.5-variegata) is green on all four platforms on our fork: https://github.com/singhpratech/community-extensions/actions/runs/28753457602

Could you approve the workflow run when you get a chance? Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant