Execution
GPU Execution (NVIDIA GQE)
The gpu_gqe route — running accelerated scans on NVIDIA GPUs through the RAPIDS GQE engine, with soft fallback everywhere.RVBBIT can execute accelerated analytical queries on an NVIDIA GPU through
GQE, the RAPIDS GPU Query Engine built on
libcudf. NVIDIA's post
Designing GPU-Accelerated Query Engines with NVIDIA GQE
is the reference for the engine itself — the three-layer query/data/execution
design on cuDF, nvCOMP, and NVSHMEM — and is what RVBBIT's gpu_gqe bridge
is built against. In routing terms it is simply one more candidate —
gpu_gqe — beside Duck/Vortex, DataFusion, native, and the Postgres
rowstore. The same contract applies: the heap stays the source of truth, and
the route is only taken when it can preserve SQL semantics.
The design goal is that GPU support is inert everywhere it can't run. On a box without a GPU (or without the GQE build), the candidate self-gates as unavailable and the router behaves exactly like the plain image. You do not need to configure anything to not use GQE.
What Runs On The GPU#
gpu_gqe serves SELECTs over accelerated (Parquet-backed) tables — large
scans, filters, and aggregations are the shapes where a GPU wins. The bridge
gates unsupported query shapes and falls back rather than guessing; shapes
that are legal but historically risky can be force-enabled with
rvbbit.gqe_allow_risky_shapes if you want to experiment.
Requirements:
- an NVIDIA GPU with a current driver,
nvidia-container-toolkiton the Docker host (thenvidiaruntime),- a GQE build available to the container (see Deployment).
How The Pieces Fit#
The extension launches a small binary chain, every link of which fails soft:
pg_rvbbitinvokes/usr/local/bin/rvbbit-gqe— a shim that probes for a GPU (nvidia-smi,/dev/nvidia*). No GPU or no GQE install → it reports{"status": "unavailable"}and the router skips the candidate.- The shim
execs the real bridge,rvbbit-gqe-bridge(the same codebase as thervbbit-duckworker, compiled for GQE). It prepares a Parquet-backed catalog for the referenced tables and talks to the GQE server over Arrow Flight (defaulthttp://127.0.0.1:50051), auto-starting it on first use. - Results come back as Arrow and land in the normal executor path, with the same telemetry as any other route.
Availability is probed and cached (rvbbit.gqe_probe_cache_ttl_ms, default
10s), so an unavailable GQE costs almost nothing per query.
Routing And Warm Priors#
Both the backend and its routing gate default on precisely because they self-gate on runtime availability:
| Setting | Env fallback | Default | Meaning |
|---|---|---|---|
rvbbit.gpu_gqe_backend |
RVBBIT_GPU_GQE_BACKEND |
true |
Enable the GQE backend at all. |
rvbbit.route_gpu_gqe |
RVBBIT_ROUTE_GPU_GQE |
true |
Let the router consider the gpu_gqe candidate. |
rvbbit.route_gpu_gqe_prior |
— | off |
Warm-prior routing (below). |
rvbbit.route_gpu_gqe_prior_min_rows |
— | — | Minimum table size before the prior applies. |
Set RVBBIT_ROUTE_GPU_GQE=0 to force the route off on a GPU host.
A GPU engine has a cold-start problem: the first query pays for server
startup, which poisons naive routing decisions. The warm prior solves
this: when rvbbit.route_gpu_gqe_prior is enabled, the freshness heartbeat
(accel_tick) periodically calls
rvbbit.warm_gpu_gqe(), which runs a trivial forced-GQE query and records
success in rvbbit.gqe_warm_state. The router's GQE prior only fires while
that warm state is fresh — so no user query ever pays the cold start.
SELECT rvbbit.warm_gpu_gqe(); -- best-effort warm probe (no-op when prior is off)
SELECT * FROM rvbbit.gqe_warm_state; -- singleton: last confirmed-warm timestamp
Like every candidate, gpu_gqe participates in
route training: force it per-session with
SET rvbbit.route_force_candidate = 'gpu_gqe', include it in
route_train_query(...) candidate lists, and inspect decisions with
rvbbit.route_explain(...).
Direct Passthrough#
For debugging, the raw entry point the router uses is callable directly:
SELECT rvbbit.gpu_gqe_query_json(
'SELECT count(*) FROM events',
'["count"]'::jsonb,
1000
);
Observability#
rvbbit.doctor(false) includes a gpu_gqe block in its accelerator / runtime row — binary path, config state, probe result, and what happens if
the route is unavailable:
SELECT detail->'gpu_gqe'
FROM rvbbit.doctor(false)
WHERE name = 'runtime';
Route-level decisions land in the same rvbbit.route_decisions /
rvbbit.route_executions telemetry as every other engine, and
rvbbit.route_explain('<query>') shows whether gpu_gqe was eligible and why
or why not.
Deployment#
The GPU image is prebuilt and published —
ghcr.io/ryrobes/rvbbit-postgres-gqe (~9GB) — so a GPU deployment is a pull,
not a compile. It is the standard image plus the GQE runtime, and one image
covers every CUDA compute-capability 8.0+ GPU:
| GPU | Covered by |
|---|---|
| A100, RTX 30-series, RTX 40-series, Jetson Orin | sm_80 SASS (8.x cards run it natively) |
| H100 / H200 | sm_90 SASS |
| B100 / B200 / GB200 | sm_100 SASS |
| RTX 50-series, RTX PRO Blackwell, DGX Spark | sm_120 SASS + PTX (forward-compatible) |
Turing (RTX 20-series) and older are not supported — cudf 26.x dropped them. On such hosts the plain image behaves identically (the route self-gates).
Start it with the GPU overlay on top of the standard compose:
curl -fsSL https://rvbbit.ai/docker-compose.yml -o docker-compose.yml
curl -fsSL https://rvbbit.ai/docker-compose.gqe.yml -o docker-compose.gqe.yml
docker compose -f docker-compose.yml -f docker-compose.gqe.yml up -d
The overlay sets the GPU-specific runtime pieces (gpus: all, shared memory
and memlock limits, NVIDIA_VISIBLE_DEVICES). Everything else — the
ensemble, ports, volumes — matches the standard
quickstart stack.
Building from source instead (custom GQE/cudf refs or arch lists) remains
supported: Dockerfile.rvbbit-gqe builds the full toolchain image on the GPU
box (one-time, ~2h on 48 cores), and Dockerfile.rvbbit-gqe-runtime extracts
the slim runtime image from it. The dev overlays
(docker-compose.gqe-image.yml, docker-compose.gqe-host.yml) cover
build-from-source and host-mounted GQE trees.
GPU host preflight#
Before first start, make sure the host is actually GPU-ready — "GPU instance" does not mean drivers are installed:
nvidia-smi -L # if missing: install the driver, then REBOOT
# (modprobe alone leaves "Driver/library version mismatch")
docker info --format '{{json .Runtimes}}' | grep nvidia # nvidia-container-toolkit
docker run --rm --gpus all ghcr.io/ryrobes/rvbbit-postgres:latest nvidia-smi -L
Hardware notes#
- Blackwell-class GPUs (compute capability 12.x): set
NVSHMEM_DISABLE_CUDA_VMM=0for the GQE service. The default (1) makes NVSHMEM initialization fail on these cards and every GPU-routed query burns the sidecar timeout before falling back to a CPU engine. - Very large scans on cloud block storage: GQE reads parquet with GPU-direct I/O (KvikIO); on VMs without GPUDirect Storage this can fail at large row-group counts. The router's fallback (and its learned latency models) handle this automatically — CPU engines serve those queries.
Advanced env knobs for nonstandard layouts: RVBBIT_GQE_BIN,
RVBBIT_GQE_BRIDGE_BIN, RVBBIT_GQE_CLI, RVBBIT_GQE_SERVER_URL,
RVBBIT_GQE_AUTO_START, RVBBIT_GQE_NUM_GPUS, RVBBIT_GQE_CLIENT_MODE,
RVBBIT_GQE_FLIGHT_FALLBACK, and RVBBIT_GQE_ASSUME_GPU (skip the GPU probe
in unusual container setups).
Fallback Behavior#
Failure at any layer degrades to the next-best route, never to an error:
- no GPU / no GQE build → shim reports unavailable, candidate skipped,
- GQE server unreachable → probe fails, candidate skipped (cached briefly),
- unsupported query shape → bridge refuses, router uses Duck/DataFusion/native,
- mid-query failure → normal route fallback with telemetry.
A non-GPU deployment behaves identically to one where GQE was never built.