Skip to content

conv2d_py (pmpp_v2): correctness gate rejects kernels more accurate than the fp32 cuDNN reference #164

Description

@shraderdm

conv2d_v2 correctness gate rejects kernels more accurate than its own reference

Board: conv2d_v2, ranked shape (256, k=32, C=128, batch=1) (seed 6252). Observed on
the L4 runner; the math is GPU-independent.

Summary

The correctness gate compares a submission against an fp32 cuDNN reference at
rtol=atol=1e-3. On this shape, fp32 cuDNN itself diverges ~3.76e-2 from the fp64
ground truth, because cuDNN selects an FFT-based convolution algorithm for the large
(k=32) kernel, and FFT-domain error scales with the global output magnitude
(sigma ~ sqrt(3232128) ~ 362), not each element's local value. At the many near-zero
output elements, that puts ~1e-2 of absolute error on the reference itself - 10x over the
gate's 1e-3 tolerance.

Consequence: a submission whose output is closer to the true fp64 result than cuDNN is
still gets flagged "incorrect" at those near-zero elements, because it differs from the
dirty reference by more than 1e-3. The gate rewards matching cuDNN's error, not
correctness.

Repro (attached conv2d_v2_reference_bug_repro.py, torch-only, ~15 lines)

fp32 cuDNN reference vs fp64 truth: max abs err = 3.762e-02

Deterministic mode (cudnn.deterministic=True, use_deterministic_algorithms(True)) gives the
bit-identical 3.762e-2 - it is the algorithm choice, not nondeterminism.

Suggested fixes (any one)

  1. Compute the reference in fp64, cast to fp32 for the comparison (ref_kernel runs
    conv2d(x.double(), w.double()).float()). fp64 conv is slower but the reference runs
    rarely; this makes "correct" mean "correct," and a genuinely accurate kernel passes.
  2. Loosen the tolerance on the affected large-kernel shapes to bound cuDNN's own algo error
    (less principled - it bakes the reference's dirt into the spec).
  3. Pin cuDNN to a non-FFT algorithm for the reference (fragile across versions).

Fix (1) is cleanest and matches how the tail-entry naive kernels already pass (they happen
to track cuDNN's error; a transform-based kernel does not).

Why this matters

The v2 boards are the community's practice ground. A gate that rejects correct-but-not-
cuDNN-matching kernels silently caps the achievable score at "however accurate cuDNN is,"
which quietly excludes exactly the transform-based approaches these large-kernel shapes
reward. The standings above the eager cluster may predate a reference change; today even
cudnn.benchmark=True in a different memory layout fails the gate the same way.

(Reported as part of independent kernel-optimization work. Repro is pure cuDNN-vs-fp64 and
reveals nothing about any specific submission.)

Full repro script

# Minimal repro: conv2d_v2 correctness gate rejects kernels MORE accurate than its
# fp32 cuDNN reference, on the ranked shape (256, k=32, C=128, batch=1).
# Zero dependencies beyond torch. Runs on any L4 (or the board's own runner).
import torch
torch.backends.cudnn.allow_tf32 = False          # match the board's DeterministicContext
torch.backends.cuda.matmul.allow_tf32 = False

dev = "cuda"
g = torch.Generator(device=dev); g.manual_seed(6252)   # the ranked benchmark's seed
x = torch.randn(1, 128, 256, 256, device=dev, generator=g)
w = torch.randn(128, 128, 32, 32, device=dev, generator=g)

# fp64 ground truth (what "correct" actually means)
truth = torch.nn.functional.conv2d(x.double(), w.double())

# the reference the gate compares against: fp32 cuDNN (reference.py ref_kernel)
ref_fp32 = torch.nn.functional.conv2d(x, w)

err = (ref_fp32.double() - truth).abs().max().item()
print(f"fp32 cuDNN reference vs fp64 truth: max abs err = {err:.3e}")
print(f"gate tolerance is atol=rtol=1e-3; near-zero output elements carry ~{err:.1e} of")
print(f"absolute error because cuDNN picks an FFT-class algo for k=32 (error scales with")
print(f"the global output magnitude ~sqrt(131072)~362, not each element's local value).")
print(f"=> ANY implementation closer to truth than cuDNN can still exceed the 1e-3 gate")
print(f"   at those near-zero elements, and is rejected as 'incorrect' - even though it")
print(f"   is MORE accurate than the reference it is graded against.")

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions