feat: detect when the resolved torch/CUDA ships no kernels for the local GPU#3104
feat: detect when the resolved torch/CUDA ships no kernels for the local GPU#3104golithe wants to merge 2 commits into
Conversation
anish-sahoo
left a comment
There was a problem hiding this comment.
Thanks for putting this together. This is a useful diagnostic and the overall approach looks promising. I found several compatibility edge cases, along with some test and documentation gaps, that need additional fixes before we can work toward merging this. Details are inline.
| } | ||
| return caps[i][1] < caps[j][1] | ||
| }) | ||
| return caps[0], true |
There was a problem hiding this comment.
Could we evaluate every detected capability, or select the capability with the strictest applicable floor? Sorting ascending and returning only caps[0] checks the numerically lowest GPU, which is not necessarily the least compatible one. On a host with sm_90 and sm_120, torch 2.4.1/CUDA 12.4 passes the sm_90 floor and produces no finding even though operations on sm_120 will fail. A mixed-GPU regression test would help cover this.
There was a problem hiding this comment.
the check now evaluates every distinct capability rather than only the lowest. your described scenario (sm_90 + sm_120 with torch 2.4.1 is the regression test TestGPUCompatibilityCheck_MixedGPUsFireForStrictestFloor
wdyt?
| return nil, nil | ||
| } | ||
|
|
||
| torchVersion, hasTorch := ctx.Config.TorchVersion() |
There was a problem hiding this comment.
TorchVersion() does not establish that this is an exact pin: the current requirement parsing discards the comparator and returns the first version token. As a result, torch<2.7.0 is treated as 2.7.0, so this check can pass even though pip installs an older, incompatible release. Could we evaluate only normalized exact == pins and skip ranges, direct URLs, and unresolved requirements? This should also have regression coverage.
There was a problem hiding this comment.
- added
requirements.ExactVersionandConfig.TorchExactVersionwhere only a single==specifier counts - ranges/wildcards/direct URLs/unpinned all produce no finding rather than a guess
- tested in
TestExactVersionandTestGPUCompatibilityCheck_SkipsNonExactPin
| return nil | ||
| } | ||
|
|
||
| torchOK := version.GreaterOrEqual(torchVersion, floor.MinTorch) |
There was a problem hiding this comment.
version.GreaterOrEqual includes local metadata in equality, so 2.7.0+cu128 is not considered greater than or equal to 2.7.0: the release is not greater, and equality fails because the metadata differs. That produces a false warning at the exact supported floor. Could we strip the local modifier for the torch release comparison, retain it separately for CUDA resolution, and add an exact-boundary test?
There was a problem hiding this comment.
- the torch comparison now strips the local tag before
version.GreaterOrEqual, so 2.7.0+cu128 passes at the exact floor - CUDA bound is still checked separtely from Build.CUDA
- boundari case in
TestEvaluateGPUCompat("sm_120 exact floor with local tag passes)
| func TestGPUCompatibilityCheck_RunsWithoutError(t *testing.T) { | ||
| // A GPU may or may not be present in the test environment; just ensure no panic or error. | ||
| ctx := gpuContext(t, true, "2.4.1", "12.4") | ||
| _, err := (&GPUCompatibilityCheck{}).Check(ctx) |
There was a problem hiding this comment.
This test only verifies that the check returns no error and ignores its findings, so the production detection path is not exercised deterministically. Could we test it with a fake nvidia-smi and cover multiple GPUs, malformed output, command failure, and registration through the doctor runner? That would also catch the mixed-GPU issue noted above.
There was a problem hiding this comment.
- replaced the
RunsWithoutErrortest entirely - the nvidia-smi probe is now a package-level functin variable that tests stub
- went with that over fake bin on PATH to keep it hermetic, same intent
- now covers multiple GPUs, command failure, malformed output, non-exact pins, and runner registration
| func (c *GPUCompatibilityCheck) Description() string { return "GPU compatibility" } | ||
|
|
||
| func (c *GPUCompatibilityCheck) Check(ctx *CheckContext) ([]Finding, error) { | ||
| if os.Getenv("COG_SKIP_GPU_CHECK") != "" { |
There was a problem hiding this comment.
Could we document COG_SKIP_GPU_CHECK in docs/environment.md alongside COG_SKIP_DOCKER_CHECK, then regenerate docs/llms.txt? This is a new user-facing environment variable and its intended use when building for different hardware should be discoverable outside the source.
| @@ -0,0 +1,199 @@ | |||
| package doctor | |||
There was a problem hiding this comment.
Could we rename these files to gpu_compatibility.go and gpu_compatibility_test.go? We prefer subject-based filenames here and spell out compatibility rather than shortening it to compat.
|
@anish-sahoo ty for your thorough review! i addressed your comments, PTAL |
torch==2.4.1builds green, and every kernel launch fails withCUDA error: no kernel image is available for execution on the devicetorch.cuda.is_available()returnsTrue, and PyTorch's own diagnostic is aUserWarningcog rundoes not work with A100 #389 is the same failure class on an A100 in 2022 (closed when the reporter worked around it); Blackwell makes it current againThis PR adds:
GPUCompatibilityChecktocog doctor, modelled onDockerChecknvidia-smi --query-gpu=compute_cap(lowest across GPUs, since the image must run on the weakest) and compares the resolved torch/CUDA against the oldest release known to ship kernels for it.cog doctoroutput when it fires:Notes for review:
torch._C._cuda_getArchFlags(), which works without a GPU2.7.0+cu118is a genuine 2.7 build with no Blackwell kernelsSeverityWarning, matchingPythonVersionCheck: the image is valid and runs fine on other hardware; it only fails when executed on this machine's GPUgpu: false;COG_SKIP_GPU_CHECK=1skips it when building for different hardware than the local card. Doctor-only, no build-path changesTesting:
evaluateGPUCompat), table-tested without a GPU, including+cu128local-tag pins and the new-torch/old-CUDA casetorch==2.4.1, silent on2.7.1and ongpu: falseOut of scope (deliberately):
MinimumTorchVersionand friends inpkg/dockerfile/base.go)tools/to regenerate them on demand could be a follow-up; fullcompatgenintegration seems like a poor fit, since new capability majors ship every couple of years and the probe needs multi-GB wheel installs. Happy to add the script here or in a follow-up PR if useful