GQE is a proof-of-concept SQL query engine for running data analytics queries on GPUs. Its purpose is to achieve speed-of-light performance and serve as a blueprint to inform your database engine design.
Single-GPU speedup against DuckDB. Total GQE execution time for all 22 queries at scale factor of 1000: 9.15 s.1
Please see the contributing guide section on setting up the build environment and building GQE and the SQL client.
Start the node manager (it spawns one task manager per GPU):
./build/src/node_manager/gqe_node_manager \
--address 127.0.0.1 \
--port 50051 \
--num-gpus 1 \
--task-manager-binary ./build/src/task_manager/gqe_task_managerTo use multiple GPUs, raise --num-gpus. The node manager spawns one task manager subprocess per GPU and distributes query execution across them:
./build/src/node_manager/gqe_node_manager \
--address 127.0.0.1 \
--port 50051 \
--num-gpus 8 \
--task-manager-binary ./build/src/task_manager/gqe_task_managerThe node manager opens its TCP port only after all task managers have started and NVSHMEM bootstrap has finished, so a successful TCP connect is a reliable readiness signal.
To drive the whole workflow from a single terminal, start the node manager in the background, redirect its logs to a file, and wait for the port to open before running clients:
./build/src/node_manager/gqe_node_manager \
--address 127.0.0.1 \
--port 50051 \
--num-gpus 1 \
--task-manager-binary ./build/src/task_manager/gqe_task_manager \
> /tmp/gqe_node_manager.log 2>&1 &
until nc -z 127.0.0.1 50051; do sleep 0.5; done
# ... run load_tpch.py, gqe-cli, etc. ...
kill %+ # stop the most recent background job (the node manager)kill %+ sends SIGTERM; the node manager handles it gracefully, terminating its task managers and cleaning up shared memory before exiting.
Use CREATE TABLE followed by COPY ... FROM '<directory>' (FORMAT parquet). The COPY source is a directory containing one or more Parquet files.
./rust/target/release/gqe-cli --server-url http://127.0.0.1:50051 --sql-file - <<'EOF'
CREATE TABLE sales (
region VARCHAR,
sale_date DATE,
amount BIGINT
);
COPY sales FROM '/data/sales' (FORMAT parquet);
EOFTables live in GPU memory until the node manager exits, so later queries run without reloading.
If desired (e.g. table size exceeds memory capacity), Parquet file-backed EXTERNAL tables can be created without occupying memory.
./rust/target/release/gqe-cli --server-url http://127.0.0.1:50051 --sql-file - <<'EOF'
CREATE EXTERNAL TABLE sales (
region VARCHAR,
sale_date DATE,
amount BIGINT
) STORED AS PARQUET LOCATION '/data/sales';
EOFQueries run against file-backed tables read the files directly from disk. Note that the EXTERNAL keyword is optional but is recommended for clarity.
From stdin:
echo 'SELECT region, sum(amount) FROM sales GROUP BY region' | \
./rust/target/release/gqe-cli \
--server-url http://127.0.0.1:50051 \
--sql-file -From a file, saving the result to Parquet:
./rust/target/release/gqe-cli \
--server-url http://127.0.0.1:50051 \
--sql-file queries/q1.sql \
--parquet q1_result.parquetscripts/load_tpch.py issues the schema and COPY statements for the eight TPC-H tables. It takes a data directory as its positional argument; the directory must contain ci_schema.sql and one Parquet subdirectory per table (customer/, lineitem/, nation/, orders/, part/, partsupp/, region/, supplier/).
./scripts/load_tpch.py \
--server-url http://127.0.0.1:50051 \
/path/to/tpch/sf1scripts/run_tpch.py runs one or more of the 22 TPC-H queries from a directory containing q1.sql ... q22.sql:
# Run a single query
./scripts/run_tpch.py --server-url http://127.0.0.1:50051 /path/to/tpch/queries 1
# Run several
./scripts/run_tpch.py --server-url http://127.0.0.1:50051 /path/to/tpch/queries 1 5 14
# Run all 22 queries
./scripts/run_tpch.py --server-url http://127.0.0.1:50051 /path/to/tpch/queries all
# Run all 22 queries and validate each against reference Parquet results
./scripts/run_tpch.py \
--server-url http://127.0.0.1:50051 \
--validate /path/to/tpch/reference_results/sf1 \
/path/to/tpch/queries allSee docs/customized_parquet_reader/README.md for the additional GQE_USE_CUSTOMIZED_IO and GQE_IO_* variables that tune the customized Parquet reader.
The GPUs GQE uses can be restricted by setting the CUDA_VISIBLE_DEVICES environment variable. The node manager spawns one task manager per GPU (up to --num-gpus) and assigns each task manager a distinct device from the visible set.
The NUMA affinity of a device depends on the system topology. When memory kind is specified as numa or numa_pinned, GQE allocates tables on the NUMA node affine to the device used for table registration. In multi-process execution, NUMA node selection occurs per process.
If a table is split into multiple Parquet files, the lexicographical sort order of the file names has to correspond to the sort oder of the rows in the table, for pruning to be effective. This means that numbers in the file names have to be padded with leading zeros.
Good: lineitem01.parquet, ... , lineitem09.parquet, lineitem10.parquet
Bad: lineitem1.parquet, ... , lineitem9.parquet, lineitem10.parquet
Optional components, each guarded by its own CMake flag. See the linked docs for build instructions and feature-specific environment variables.
- Offline Substrait plan producer — generates Substrait binary plans from SQL outside the client-server flow. See docs/substrait_producer/README.md.
- Customized Parquet reader — a prototype reader that is more performant but limited in functionality. See docs/customized_parquet_reader/README.md.
- Query compiler — MLIR-based JIT that compiles relational operators into used NVVM IR kernels. See docs/compiler/README.md for the design and test/compiler/README.md for building and running the MLIR tests.
- Bret Alfieri
- Clemens Lutz
- Daniel Juenger
- Dhruv Sundararaman
- Eric Schmidt
- Eyal Soha
- Hao Gao
- James Xia
- Jiachun Li
- Kate Cheng
- Lingyan Yin
- Miloni Dipak Atal
- Nico Iskos
- Nuttiiya Seekhao
- Rui Bao
- Siyuan Lin
- Tanmay Gujar
- Tyler Allen
- Viktor Rosenfeld
- Wei-Lun Wu
- Yadu Kiran
- Zhengru Wang
Footnotes
-
The test results in this repository are derived from TPC-H decision support benchmark and aren’t comparable to published TPC-H results, as the test results in this repository do not comply with the TPC-H specification. ↩

