groundy
infrastructure & runtime

Triton Kernels Pass Tests but Run Slow: The GPU Kernel Eval Gap

A July 2026 preprint shows Triton and TileLang kernels can pass correctness checks yet run hundreds of times slower than library baselines, moving validation cost to adopters.

9 min···3 sources ↓

A kernel that compiles and gives the right answer is not the same as a kernel that runs fast. That distinction is the entire finding of Correct but Slow (arXiv:2607.04454), a July 2026 preprint from Tingxi Li, Ravishka Rathnasuriya, and Wei Yang (v1 submitted 5 July 2026, v2 7 July 2026) that studies 22 Triton and TileLang kernels across five operator categories on NVIDIA A100 and GH200 GPUs. The reference-based numerical checks most kernels are validated against say nothing about whether the replacement matches the throughput of the tuned library operator it is supposed to displace.

What does “correct but slow” actually mean?

The phrase names a specific failure mode: a domain-specific-language kernel passes a correctness suite and still regresses badly on latency relative to the hand-tuned library baseline it was written to replace.

The most striking exhibit is a TileLang LayerNorm implementation. It is idiomatic, it passes KernelBench’s correctness check, and it runs more than 300× slower than the PyTorch baseline, according to the preprint’s abstract (accessed 2026-07-09). Three hundred times. This is not a 15% miss a profiler would flag at a glance. It is a kernel that, by every test in the standard harness, is a valid drop-in replacement, and in production is unusably slow.

The gap matters because the DSLs in question are not toy languages. Triton describes itself as a language and compiler for writing highly efficient custom deep-learning primitives, aiming for higher productivity than CUDA with higher flexibility than other DSLs. It is a frequent target for projects that want Python-level kernel authoring without dropping all the way to CUDA. TileLang sits in the same category, and Apache TVM is the older compilation framework in this lineage. When the validation methodology for kernels in these languages systematically misses performance regressions, the cost does not stay with the DSL authors. It lands on every team that adopts the DSL and trusts its passing tests.

Why does a kernel that passes correctness checks run slow?

Reference-based numerical validation compares a kernel’s output against a trusted reference within a tolerance. It is a soundness check: does this produce the right numbers? It does not exercise the dimensions that decide whether a kernel is worth shipping, because correctness is a property of the result and performance is a property of the path to the result.

Two kernels that return identical outputs can differ enormously in how they get there. One may launch enough threads to saturate the GPU; the other may serialize work across a single dimension. One may keep data in registers and shared memory; the other may round-trip through global memory. One may use a fused, tensor-core-friendly reduction; the other may do a sequential pass. None of these choices change the numerical answer, so none are caught by comparing outputs.

KernelBench, the correctness harness the study examines, was built to test whether generated kernels are functionally correct. It does that job. The problem the paper identifies is not that KernelBench is broken. It is that the ecosystem has been treating correctness as a proxy for replacement-readiness, and the data shows the proxy fails. A passing correctness check is the floor, not the finish line.

Which slowdowns can you fix, and which are structural?

One of the more useful contributions of the paper is that it refuses to lump all slow kernels together. The causes of the regressions differ by kernel family, and that difference changes what you can do about them.

TileLang normalization and reduction kernels are slow mainly for repairable authoring reasons. The preprint points to sequential reductions and unnecessary dtype conversions as the typical culprits. These are mistakes a kernel author can find and fix: collapse the sequential reduction into a parallel one, drop the conversion that pushes data through a wider type than the computation needs. Once fixed, these kernels recover most of the gap.

Convolution and large GEMM are different. After optimization, they retain residual gaps that the author cannot close by editing the kernel, because the bottleneck has moved out of the author’s code and into the toolchain. The paper attributes these residuals to code-generation limits and autotuning-coverage limits in the DSL compilers themselves. Notably, vendor-library algorithm selection contributes only marginally to the remaining difference, which means swapping in a cleverer algorithm is not the lever here. The lever is the compiler, and the compiler is what the author cannot touch.

How do you catch a slow-but-correct kernel without exhaustive benchmarks?

The paper’s constructive contribution is a pair of lightweight screening checks that, taken together, flagged every functionally valid but inefficient kernel in the study suite.

The first is library-relative efficiency: compare the kernel’s throughput against the tuned library operator it is meant to replace, not just against correctness. If your LayerNorm runs at a fraction of the library baseline, that is the signal, regardless of whether the outputs match. The second is roofline utilization: measure how close the kernel comes to the hardware’s achievable bandwidth or compute ceiling for its arithmetic intensity. A kernel running far below its roofline is leaving performance on the table for reasons correctness testing will never surface.

The two checks are complementary, per the preprint. Library-relative efficiency tells you whether the kernel is competitive in absolute terms. Roofline utilization tells you whether the kernel is extracting what the hardware can deliver. Used together, they also do the diagnostic work the paper values most: they separate the repairable authoring defects from the structural residuals. A kernel far from the library baseline but close to its roofline is probably bottlenecked on something the compiler controls. A kernel far from both is more likely something the author can fix.

The appeal of this pair is that neither requires the exhaustive sweep the paper sets out to avoid. You do not need to benchmark every shape, every dtype, every batch. You need a baseline number and a roofline number, both cheap to obtain relative to a full perf-equivalence matrix.

What does this cost a CUDA-to-Triton migration?

This is where the finding stops being academic. The implicit promise of a GPU kernel DSL like Triton, which bills itself as offering higher productivity than CUDA, is that you trade some control for faster development without giving up much performance. That trade is only honest if “passes our tests” means “performs like the thing it replaces.” The paper shows it does not.

Every migration that moves a CUDA kernel into Triton or TileLang and treats the passing correctness check as evidence of success is now on the hook for something the original CUDA workflow may not have required: a performance-equivalence suite. The cost of that suite is not trivial. You need a representative set of production-shaped workloads, the library baselines to compare against, the instrumentation to measure throughput and roofline, and the discipline to run all of it before declaring the migration done.

The broader effect is that the benchmarking burden shifts. Where it used to sit with the DSL, which could point to passing tests as evidence of readiness, it now sits with the kernel author and the adopting team. The DSL sold you a language and a compiler. Proving the output is fast enough to ship is your job.

What should a pre-production validation workflow look like?

Start from the assumption that correctness is a precondition, not a conclusion. A kernel that fails correctness is rejected outright; a kernel that passes correctness enters the performance pipeline, which is where the actual shipping decision happens.

The minimum viable workflow, assembled from the paper’s recommendations, has three gates. First, the reference-based correctness check, identical to what KernelBench and similar harnesses already do. Second, the library-relative efficiency check, comparing against the tuned operator the kernel replaces. Third, the roofline utilization check, to localize where the lost performance is going. Run all three before a kernel is merged, and treat the efficiency and roofline numbers as gating, not informational.

The honest framing is that this is not extra work the DSLs should have saved you from. It is work that was always necessary and that the field has been quietly skipping, because the correctness check was cheap and the regressions were easy to miss until production hit them. The contribution of Correct but Slow is not the discovery that slow kernels exist. It is the demonstration that the standard validation methodology cannot tell a fast one from a slow one, plus a cheap pair of checks that can.

Frequently Asked Questions

Does the correctness-versus-performance gap apply outside Triton and TileLang?

Yes. The study uses Triton and TileLang as examples, but the underlying issue is reference-based numerical validation. Any DSL or handwritten CUDA kernel validated only on output correctness can ship with severe regressions. Apache TVM and similar Python-first compilers face the same risk, because functional equivalence does not exercise memory traffic, occupancy, or autotuning choices.

How does the proposed two-check screening differ from a full autotuning sweep?

Library-relative efficiency and roofline utilization are diagnostic filters, not a search. They flag every slow kernel in the suite without enumerating every tile size, dtype, or batch shape. A full autotuning sweep tries to find the fastest configuration, while the two-check workflow first tells you whether you need to keep searching and whether the bottleneck sits in the author’s code or the compiler.

What is the minimum artifact set a team needs to add these checks to CI?

A tuned library baseline for each operator, a throughput measurement for the candidate kernel, and a roofline model for the target GPU. For an A100 or GH200 the roofline numbers are published; the main work is wiring the candidate kernel and baseline into the same benchmark loop so the comparison runs on production-shaped shapes and dtypes. This is cheaper than a full perf-equivalence matrix, but it still requires CI minutes and stable GPU runners.

When could the two-screening method fail to catch a bad kernel?

It assumes the chosen library baseline and roofline model match the production workload. If the reference operator itself is suboptimal for a niche dtype or nonstandard tensor layout, the kernel may look efficient while still regressing against the real target. The method also does not catch correctness bugs that only appear on shapes outside the test rig.

What would make this evaluation gap disappear?

Not better autotuning alone. The paper shows residuals persist in convolution and large GEMM even after optimization, so closing the gap would require the DSL compilers to expand autotuning coverage and improve code generation for those families. Until then, the gap is structural and teams should keep library fallbacks for those operators.

sources · 3 cited