Treat zero compute-dispatch-group limit as unbounded (fixes #1136) - #1137
Draft
nv-slang-bot[bot] wants to merge 1 commit into
Draft
Treat zero compute-dispatch-group limit as unbounded (fixes #1136)#1137nv-slang-bot[bot] wants to merge 1 commit into
nv-slang-bot[bot] wants to merge 1 commit into
Conversation
The CPU backend never populates DeviceLimits.maxComputeDispatchThreadGroups,
so it stays {0,0,0}. Since 0.43.0's large-dispatch clamp (#995), every CPU
compute dispatch computed zero X dispatch groups and raised "Device reports
zero compute dispatch groups in X" (a small dispatch also fails the Y-limit
check for the same reason). CPU is the only backend that leaves this limit
unset.
Treat a zero limit as unbounded in both consumers -- the native dispatch-count
path (X and Y) and the generated group-stride constants -- so CPU dispatch
works and the emitted dispatch_group_x_stride / dispatch_thread_x_stride stay
nonzero. This is a no-op for any backend that reports a real (nonzero) limit:
min(limit, ceiling) is unchanged.
Add a CPU dispatch regression test. DeviceType.cpu is absent from the default
test device sets on every platform, so no test exercised this path.
Contributor
|
Automated notice (PR board sync) — do not reply to this comment. Auto-assigned @kaizhangNV as shepherd for this Bot PR. FYI for maintainers: committer signal on the changed files is highest for ccummingsNV among collaborators other than the assignee. They were not auto-requested; a human may optionally add them as a reviewer. |
1 similar comment
Contributor
|
Automated notice (PR board sync) — do not reply to this comment. Auto-assigned @kaizhangNV as shepherd for this Bot PR. FYI for maintainers: committer signal on the changed files is highest for ccummingsNV among collaborators other than the assignee. They were not auto-requested; a human may optionally add them as a reviewer. |
This was referenced Sep 4, 2026
CPU device: passing a Python list to a float[N] param segfaults in array-parameter marshalling
#1138
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Since 0.43.0, every compute dispatch on
DeviceType.cpufails withRuntimeError: Device reports zero compute dispatch groups in X, making the CPU backend unusable for compute. GPU backends are unaffected.Root cause. The slang-rhi CPU backend (
external/slang-rhi/src/cpu/cpu-device.cpp) never populatesDeviceLimits.maxComputeDispatchThreadGroups, so it stays{0,0,0}— CPU is the only backend that omits this, andDeviceLimitshas no default member initializers. PR #995's large-dispatch clamp then computesmin(0, ceiling) = 0dispatch groups and throws. Two further consequences of the same zero limit: a small dispatch also fails the Y-limit check (dispatch_y <= limits.ywithlimits.y == 0), and the code generator emitsdispatch_group_x_stride = 0/dispatch_thread_x_stride = 0, which would corrupt the physical→logical group flattening for large dispatches.What this PR does (slangpy-side defensive fix)
Treats a zero dispatch-group limit as unbounded in both consumers, since zero is not a meaningful hardware limit — any compute-capable device can launch at least one group:
src/slangpy_ext/utils/slangpy.cpp— the native dispatch-count path falls back to the representable-stride ceiling for X and toUINT32_MAXfor Y when the reported limit is 0.slangpy/core/generator.py— extractsresolve_max_dispatch_groups_x(), which maps a zero limit to the ceiling so the emitted strides stay nonzero.For any backend that reports a real (nonzero) X/Y limit the fallback branch is not taken and
min(limit, ceiling)is unchanged, so the computed dispatch dimensions and emitted constants are identical to before (the native binary and the code path do change; the observable behavior does not).Why at the consumption sites, not at population?
src/sgl/device/device.cpp:314copies the rhi limit verbatim, so normalizing0 → ceilingthere would fix every consumer at once. This PR deliberately patches the two consumers instead: it is more conservative and does not change whatdevice.info.limitsreports (e.g.test_large_dispatches.pyreadslimits.x/limits.ydirectly). Makingdevice.info.limitsitself report a sane value is the job of the canonical slang-rhi fix (Approach A), which populates the real limit at the source.Relationship to the canonical fix
The durable root-cause fix belongs in slang-rhi (populate the CPU limit, e.g.
0xFFFFFFFFmirroring the Metal backend) and is being handled separately in that repo. Once it lands and the submodule is bumped here, this fallback becomes harmless (min(0xFFFFFFFF, ceiling) == ceiling, identical to the fallback value). The slangpy-side fallback unblocks CPU immediately without waiting for the cross-repo submodule bump, and hardens slangpy against any future backend that leaves the limit unset.Tests
slangpy/tests/slangpy_tests/test_cpu_dispatch.py:test_zero_dispatch_group_limit_is_unbounded— device-free unit test of the stride fallback; keeps guarding the logic even after slang-rhi starts reporting a real CPU limit.test_cpu_dispatch_grid— end-to-end CPU dispatch overgrid((1000,)), asserting correct output.DeviceType.cpuwas absent from the default test device sets on every platform, so no prior test exercised this path.CI coverage:
unit_test_python(tools/ci.py) runspytest slangpy/testswith no--device-types, soSELECTED_DEVICE_TYPESisNoneand the pytest plugin does not skip device tests. Both new tests therefore run in the standardunit-testlane on every OS runner (verified locally: unscopedpytest test_cpu_dispatch.py→2 passed, 0 skipped), exercising the C++limit_x/limit_ypath and the end-to-end CPU dispatch.test_cpu_dispatch_gridis skipped only under an explicit GPU-scoped--device-typesselection (correct — it should not run in a Vulkan/CUDA-only lane). Note: the CPU backend has known issues on Linux in slang-rhi's own test harness; this simple scalar/grid dispatch is in the working subset, but if it proves flaky on a CI runner a maintainer may gate it.Known limitation / follow-up (separate issue)
Observed while validating on the CPU backend (linux-gcc debug):
test_cpu_dispatch_gridpasses end-to-end.test_simple_function_call.py::test_pass_float_array(Python list →float x[3]) against the CPU device segfaults during execution. This path was previously unreachable because the zero-groups throw fired first, so it is a distinct failure that needs its own root-cause investigation — this PR does not address it and does not claim CPU support is complete.For context, the pinned slang-rhi disables the CPU backend in its own test harness on Linux (
external/slang-rhi/tests/testing.cpp: "Known issues with CPU backend on linux"), which is consistent with the segfault above. The simple grid/scalar dispatch this PR exercises does work on Linux (verified), but broader CPU coverage should be gated on those backend issues being resolved.main(rebased; slang-rhi @ 22239042). New tests pass; pre-commit clean.Fixes #1136