feat(cuda): selectable compilation target via -K cuda_arch, traced by Lake - #27
NicolasRouquette wants to merge 2 commits into
Conversation
69d5011 to
e4aa79f
Compare
e4aa79f to
19c40bc
Compare
|
Thanks, Nicolas. We still want this. We just landed a large cleanup on Could you rebase onto the current After the rebase, please rerun |
… Lake Device code is compiled *for* a GPU architecture, but the native kernels were compiled with no -arch/-gencode at all, leaving nvcc on its built-in default. That default is a property of the toolkit, not of the machine: CUDA 13.0 emits sm_75 SASS plus compute_75 PTX. On any other GPU the kernels then run through forward PTX JIT rather than native SASS, or on an older architecture do not load at all, and nothing about the build says so. Add -K cuda_arch=<spec>, with TORCHLEAN_CUDA_ARCH as a fallback so a container image or CI job can select a target without rewriting its build command. A bare spec becomes -arch=<spec>, which covers sm_86, compute_86, native, all, and all-major; a spec starting with `-` is passed to nvcc verbatim, which is how a multi-architecture binary is requested and which keeps this package free of any policy about which architecture carries the PTX. Selecting a target is only meaningful if changing it rebuilds, so also split buildNativeBackendLib's compiler arguments the way buildO intends: include paths stay in weakArgs, where a moved checkout does not invalidate every object, and the flags that change what the compiler emits move to traceArgs, which buildO hashes. Previously every argument sat in weakArgs, so a changed optimization level or compilation target left the existing objects looking current. nvcc's own NVCC_APPEND_FLAGS has the same hole and Lake cannot close it — the option and the environment variable added here both participate in the trace, and the documentation says which to prefer. scripts/checks/cuda_arch_target.sh asserts the whole surface — flags reaching nvcc, recompilation on a changed target, no recompilation on an unchanged one, the environment fallback, its precedence, and verbatim pass-through — against a recording stand-in for nvcc, so it needs neither a CUDA toolkit nor a GPU and runs in CI. One consequence worth expecting: moving the flags into the trace changes the trace of every object built by buildNativeBackendLib, so the first build after this lands recompiles those four objects once — the four .cu kernels under CUDA, or the four C stubs on a CPU-only machine.
The seven existing checks all pass `-R`, and the cleanup restores the stored configuration, so the hazard was already understood here — but nothing asserted it. `-K` is read when the package configuration is elaborated, not when the build runs: a `-K cuda=true` that omits `-R` after a stub build is accepted, ignored, and links the CPU parity stubs. That failure is invisible in every signal a reader normally consults. The build reports success, the job count is the same, and the test suite it produces passes every test having executed no kernel; only `ldd` on the executable, or the suite's own "CUDA kernels: skipped (CPU build)" line, distinguishes it. The two rows are checked as a pair because either alone proves nothing: zero compilations is also what an up-to-date target reports. Running the same command twice, differing only in `-R`, is what makes the result evidence — the recording nvcc sees no invocation in the first and one `-arch=sm_86` invocation in the second. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
19c40bc to
43e8f9c
Compare
|
Thanks for working on this! We added CUDA build improvements during the recent TorchLean upgrade, including cuda_arch selection, an all-major default, and tracking compiler settings so changes invalidate cached objects. That covers the main need behind this PR, though our configuration interface differs from the one proposed here. Closing this for now to avoid maintaining two approaches. If there is a use case the updated main still misses, please let us know! |
Problem
The native kernels are compiled with no
-arch/-gencode, sonvccapplies its built-indefault. That default belongs to the toolkit rather than to the machine — CUDA 13.0 emits
sm_75SASS pluscompute_75PTX. A binary built that way runs at full speed on that onearchitecture, reaches a newer GPU only through forward PTX just-in-time compilation, and does
not load on an older one at all. Nothing in the build reports which of those happened, so the
cost surfaces only as unexplained throughput.
nvccdoes readNVCC_APPEND_FLAGS, which is how a target can be forced today. But Lake neversees that variable, so in a warm tree it judges the existing objects current and links kernels
compiled for the previous target. Verified, not assumed — with
NVCC_APPEND_FLAGS=-arch=sm_86set on an up-to-date build,
nvccis not invoked at all.Change
-K cuda_arch=<spec>, withTORCHLEAN_CUDA_ARCHas a fallback so a container image orCI job can select a target without rewriting its build command.
A bare spec becomes
-arch=<spec>(coveringsm_86,compute_86,native,all,all-major); a spec starting with-is passed tonvccverbatim. The verbatim form is how amulti-architecture binary is requested, and it deliberately keeps this package free of any policy
about which architecture carries the PTX.
The trace fix that makes the option real.
buildNativeBackendLibpassed every compilerargument as
buildO'sweakArgs, whichbuildOexcludes from the trace by design;traceArgswas
#[]. A changed optimization level or compilation target therefore left the existing objectslooking up to date. Arguments are now split the way
buildOintends: include paths stay weak,where a moved checkout does not invalidate every object, and the flags that change what the
compiler emits are traced.
Evidence
scripts/checks/cuda_arch_target.shputs a recording stand-in fornvccfirst onPATH, buildsone extern library repeatedly, and asserts the argument vector and the recompilation count:
-arch, argv unchanged from today-K cuda_arch=sm_86-arch=sm_86-K cuda_arch=sm_89TORCHLEAN_CUDA_ARCH=sm_90-arch=sm_90-gencodelistIt needs neither a CUDA toolkit nor a GPU, so it is wired into CI. It removes the stand-in's
objects on exit and restores the stored build configuration, since every build in it enables CUDA.
Checks
lake build NN(4166 jobs, no errors, warnings, orsorry) ·lake test·lake -R lint·scripts/checks/cuda_arch_target.sh(7/7). No real-CUDA build was run: the machine used here hasno toolkit, which is precisely why the check is written against a stand-in.
Expected consequence
Moving the flags into the trace changes the trace of every object built by
buildNativeBackendLib, so the first build after this lands recompiles those four objects once —the four
.cukernels under CUDA, or the four C stubs on a CPU-only machine.