Can LLMs optimize database queries on GPUs?
DataKernelBench evaluates LLMs on a novel task: optimizing analytical database queries as GPU kernels. It first represents each SQL query as a validated PyTorch program called a TorchPlan. It then evaluates LLMs by asking them to optimize either the tensor-intensive core (core) or the full query implementation (full) using CUDA or Triton, with execution-guided repair. The benchmark covers all 22 TPC-H queries.
On TPC-H SF10 using one NVIDIA H100, the strongest configuration—GPT-5.5 with CUDA at the full-query level—achieves 2.11× overall speedup over compiled TorchPlan with a 100% pass rate. The paper evaluates ten proprietary and open-weight models and analyzes how model strength, backend, optimization scope, and prompt context affect correctness and performance.
Links: Project page · Paper · Poster · Slides · Code · Data: Hugging Face · Zenodo
TPC-H SF10 (H100). Layout: artifacts/README.md.
Hands-on tutorial (self-contained, runs on Colab): artifacts/datakernelbench_tutorial.ipynb
- SQL queries — artifacts/sql/ (
q01.sql–q22.sql) - TorchPlans — artifacts/torchplans/ (
q01.py–q22.py) - CUDA kernel plans (GPT-5.5 generated) — artifacts/kernels/cuda-full/
- Prompts — artifacts/prompts/
Dask-cuDF / SF100 preview (data larger than one GPU; not a release): artifacts-dask/README.md.
- 🏆 Main leaderboard: results/leaderboards/tpch_sf10_h100.csv (SF10, H100)
- Compact view (one row per model): results/leaderboards-compact/tpch_sf10_h100.csv
- Other tracks (scale factor, hardware, benchmark): results/leaderboards/
- Per-query summary — key models vs baselines: results/baseline_db/baseline_db_tpch_h100_sf10.csv
- Per-query results for all experiments: results/query_timing_tpch.csv
Column definitions for both CSV types: results/README.md.
Regenerate: poetry run python kernel_plans/prepare_results.py
git clone <datakernelbench repo URL>
cd datakernelbench
curl -sSL https://install.python-poetry.org | python3 -
poetry installCopy .env.example as .env and fill the values.
utils/extract_benchmark.py uses DuckDB’s tpch / tpcds extensions (dbgen / dsdgen) to generate data at a chosen scale factor, export table CSVs, write query SQL, execute each query and save its result as CSV, emit logical plans as JSON (EXPLAIN (FORMAT JSON)), and save table metadata via SUMMARIZE and PRAGMA storage_info.
poetry run python utils/extract_benchmark.py
poetry run python utils/extract_benchmark.py scale_factor=0.01 benchmarks=[tpch]Defaults live in utils/conf/extract_benchmark.yaml (benchmarks: [tpch, tpcds], scale_factor: 1).
Output layout (under benchmarks/<tpch|tpcds>/)
benchmarks/<benchmark>/
├── data/
│ └── sf<scale>/ # one folder per scale factor
│ ├── <table>.csv
│ └── …
├── meta_data/
│ └── sf<scale>/
│ ├── summarize/ # per-table: <table>.csv
│ └── storage_info/ # per-table: <table>.csv
├── sql_queries/ # shared across scale factors
│ ├── q01.sql
│ └── …
├── sql_query_results/
│ └── sf<scale>/ # result rows from executing each SQL query
│ ├── q01.csv
│ └── …
└── json_plans/
└── sf<scale>/ # logical plan JSON for this SF’s statistics
├── q01.json
└── …
sf<scale> uses dots as underscores (e.g. 0.01 → sf0_01).
baseline/generate_torch_plans.py reads SQL from benchmarks/<benchmark>/sql_queries/, fills a prompt template, calls LiteLLM, and writes one .py plan per query under outputs/baseline/<benchmark>/ (shared across scale factors).
Defaults live in baseline/conf/generate_torch_plans.yaml. Logs: logs/baseline/<benchmark>/generate_torch_plans.log
poetry run python baseline/generate_torch_plans.py
poetry run python baseline/generate_torch_plans.py benchmark=tpcds scale_factor=1 max_llm_rounds=10
poetry run python baseline/generate_torch_plans.py prompt_path=prompts/baseline/base_with_json_plan.md| Setting | Meaning |
|---|---|
llm_provider |
litellm (default) or rits |
model |
Model id (LiteLLM or RITS, e.g. deepseek-ai/DeepSeek-V3.2) |
prompt_path |
Template (default prompts/baseline/base.md) |
max_llm_rounds |
Repair loop with DuckDB validation when > 1 |
benchmark, scale_factor |
JSON plans and validation data use benchmarks/.../<sf>/ |
Use prompts/baseline/base_with_json_plan.md to inline DuckDB JSON plans from benchmarks/<benchmark>/json_plans/<sf>/ when the template contains {{INSERT_JSON_PLAN_HERE}}.
LLM credentials in .env: LiteLLM (LITELLM_BASE_URL, LITELLM_API_KEY) or RITS (RITS_BASE_URL, RITS_API_KEY).
poetry run python baseline/generate_torch_plans.py llm_provider=rits model=deepseek-ai/DeepSeek-V3.2Validate and benchmark use the same Hydra keys for grouping artifacts: benchmark, scale_factor, gpu (auto-detected when null), and model (torch_eager or torch_compile). Use the same overrides on both commands so validation.csv and timing.csv land in one directory.
Per config slice:
results/<benchmark>/<sf>/<gpu>/baseline_<model>/
validation.csv # validate: id, pass, failure
timing.csv # benchmark: timings + source_path
metadata.json # benchmark run config
logs/baseline/<benchmark>/<sf>/<gpu>/baseline_<model>/
validate_torch_plans.log
Join on id (e.g. pandas merge). Plans live at outputs/baseline/<benchmark>/qNN.py (no SF in path); table data from benchmarks/<benchmark>/data/sf<tag>/.
Validate — baseline/validate_torch_plans.py compares DuckDB SQL to each torch plan. Defaults: baseline/conf/validate_torch_plans.yaml.
poetry run python baseline/validate_torch_plans.py
poetry run python baseline/validate_torch_plans.py model=torch_eager benchmark=tpch scale_factor=1 limit=5| Setting | Meaning |
|---|---|
model |
torch_eager or torch_compile (same execution mode as benchmark: compile wraps _query_core when set) |
benchmark, scale_factor |
Validation data from benchmarks/.../data/sf<tag>/ |
gpu |
Auto-detected when null (H100 / A100 / V100) |
max_diffs |
Max differing rows printed per failure |
Benchmark — baseline/benchmark_torch_plans.py times each plan (torch.compile when model=torch_compile). Defaults: baseline/conf/benchmark_torch_plans.yaml. No log files under results/; benchmark output is tqdm-only.
poetry run python baseline/benchmark_torch_plans.py
poetry run python baseline/benchmark_torch_plans.py model=torch_compile benchmark=tpch scale_factor=1
poetry run python baseline/benchmark_torch_plans.py model=torch_eager benchmark=tpcds scale_factor=10 limit=5| Setting | Meaning |
|---|---|
warmup, runs |
Timed repeats (defaults match kernel_plans) |
Each benchmark run replaces timing.csv and metadata.json for that slice and purges __pycache__ under the repo first. Each validate run replaces validation.csv for that slice.
Prompt templates: prompts/kernel_plans/ (composition details in that folder’s README).
Before a run on a new host, refresh environment versions (included in composed prompts when environment.md exists):
poetry run python utils/write_environment_versions.pyRequires validated baseline torch plans at outputs/baseline/<benchmark>/.
kernel_plans/generate_kernel_plans.py composes prompts from prompts/kernel_plans/, calls LiteLLM, and runs a repair loop per query: correctness vs the baseline torch plan, speedup vs baseline torch.compile(_query_core) at run_query level (median after warmup). GPU is auto-detected (H100 / A100 / V100).
Defaults live in kernel_plans/conf/generate_kernel_plans.yaml. Override on the CLI with Hydra:
poetry run python kernel_plans/generate_kernel_plans.py
poetry run python kernel_plans/generate_kernel_plans.py benchmark=tpcds scale_factor=1 framework=triton level=core
poetry run python kernel_plans/generate_kernel_plans.py limit=5 max_rounds=10 min_speedup=1.05 gpu=h100| Key | Default | Description |
|---|---|---|
framework |
triton |
triton or pytorch or cuda |
level |
core |
core or full |
benchmark |
tpch |
e.g. tpch, tpcds |
scale_factor |
1.0 |
dataset scale factor |
gpu |
auto | h100 / a100 / v100, or null to detect |
llm_provider |
litellm |
litellm or rits |
model |
(see yaml) | LLM model id (LiteLLM or RITS) |
max_rounds |
10 |
LLM repair rounds per query |
max_output_tokens |
16384 |
LLM response token limit (API default is 4096) |
min_speedup |
1.05 |
vs baseline torch.compile (in-loop bench during generate) |
warmup / runs |
2 / 5 |
timing repeats (in-loop bench during generate) |
load_baseline_timing |
true |
use results/.../timing.csv and validation.csv; fall back on demand if missing |
warmup_timeout_coef / warmup_timeout_floor_min |
20 / 1 |
correctness + bench warmup: max(1 min, 20× baseline_compile_ms); wall clock kills hung run_query |
run_timeout_coef |
3 |
bench timed runs: coef × baseline_compile_ms; -1 = off |
query_wall_timeout_min |
15 |
kill isolated per-query worker after this many minutes (LLM + nvcc + bench) |
resume |
false |
keep outputs/ and logs/; skip any query with terminal results.json (pass, no_speedup, fail, …); re-run only incomplete queries, append logs |
limit |
all | max queries (null = all) |
debug |
false |
tracebacks in terminal/log; per-round round_NN.debug.txt with LLM I/O |
isolate_queries |
true |
one subprocess per query (fresh CUDA after bad kernels) |
Only the fastest correct passing plan per query is written under outputs/kernel_plans/. Each generate run clears that run’s output and log directories first.
Per config slice (same layout as baseline results/, plus framework and level):
outputs/kernel_plans/<benchmark>/<sf>/<gpu>/<model>/<framework>/<level>/
qNN.py # best correct plan (or latest if none correct)
logs/kernel_plans/<benchmark>/<sf>/<gpu>/<model>/<framework>/<level>/
generate_kernel_plans.log # mirrors terminal output
generate_kernel_plans.json # run summary + per-query aggregates
queries/qNN/
results.json # round-wise status, tokens, speedup
round_01.py
round_01.debug.txt # debug=true: LLM input/output/status
<model> is the LiteLLM id with / → __ (e.g. aws/claude-opus-4-7 → aws__claude-opus-4-7). Use the same benchmark, scale_factor, gpu, model, framework, and level on generate and benchmark so paths align.
Re-benchmark existing kernel plans in the output folder (no LLM). Defaults: kernel_plans/conf/benchmark_kernel_plans.yaml. Use the same path keys as generate so it reads the right outputs/kernel_plans/.../ slice.
poetry run python kernel_plans/benchmark_kernel_plans.py
poetry run python kernel_plans/benchmark_kernel_plans.py benchmark=tpch scale_factor=1 framework=triton level=core
poetry run python kernel_plans/benchmark_kernel_plans.py limit=5 min_speedup=1.05 gpu=h100 model=aws/claude-opus-4-7| Key | Default | Description |
|---|---|---|
framework, level, benchmark, scale_factor, gpu, model |
(see yaml) | same run dimensions as generate |
load_baseline_timing |
true |
same as generate — read baseline medians from results/ when available |
min_speedup |
1.05 |
vs baseline torch.compile |
warmup / runs |
2 / 5 |
timing repeats |
max_diffs |
5 |
max cell diffs shown on correctness mismatch |
limit |
all | max queries (null = all) |
debug |
false |
verbose logging |
isolate_queries |
true |
one subprocess per query (fresh CUDA after bad kernels) |
Logs: logs/kernel_plans/<benchmark>/<sf>/<gpu>/<model>/<framework>/<level>/benchmark_kernel_plans.[json|log].
Results (same hierarchy as outputs/logs):
results/<benchmark>/<sf>/<gpu>/<model>/<framework>/<level>/
validation.csv # id, pass, failure (vs baseline torch plan)
timing.csv # candidate run_query medians + raw repeats
metadata.json # run config + resolved_gpu
kernel_plans/prepare_results.py merges baseline and kernel-plan runs into one query-level CSV per benchmark. Defaults: kernel_plans/conf/prepare_results.yaml.
poetry run python kernel_plans/prepare_results.py
poetry run python kernel_plans/prepare_results.py benchmarks=[tpch]
poetry run python kernel_plans/prepare_results.py benchmarks=[tpch,tpcds] output_dir=results results_prefix=query_timing| Setting | Meaning |
|---|---|
benchmarks |
Which benchmarks to export (default [tpch, tpcds]) |
output_dir |
Where CSVs are written (default results) |
results_prefix |
Output filename prefix (default query_timing → query_timing_tpch.csv) |
logs_dir |
Root for kernel-plan logs (default logs/kernel_plans) |
speedup_pass_levels |
Speedup thresholds in percent vs torch.compile for speedup_pass_rate_*pct columns (default [5, 10, 20]) |
Sources
- Baselines —
results/<benchmark>/<sf>/<gpu>/baseline_torch_*/(timing.csv+validation.csv); taggedrun_kind=baseline,framework=torch,level=full. - Model runs —
logs/kernel_plans/<benchmark>/.../generate_kernel_plans.json; taggedrun_kind=model.
Rows are sorted by hyperparameters then query_id. See results/README.md for column definitions.
Leaderboards — also writes output_dir/leaderboards/<benchmark>_sf<N>_<gpu>.csv (one file per benchmark × scale factor × gpu). Metrics are over queries where baseline torch.compile passed validation and has timing. Rows are ranked by sort_metric (default: overall_speedup_vs_torch_compile, higher is better).
If you use DataKernelBench, please cite:
@misc{datakernelbench,
title={DataKernelBench: Can LLMs Optimize Database Queries on GPUs?},
author={Gokul Karthik Kumar and Yotam Perlitz and Corey Lammie and Andrea Giovannini and Katja Hose},
year={2026},
eprint={2608.25061},
archivePrefix={arXiv},
primaryClass={cs.CL},
url={https://arxiv.org/abs/2608.25061},
}IBM Public Repository Disclosure All content in this repository including code has been provided by IBM under the associated open source software license and IBM is under no obligation to provide enhancements, updates, or support. IBM developers produced this code as an open source project (not as an IBM product), and IBM makes no assertions as to the level of quality nor security, and will not be maintaining this code going forward.