fix(linalg): support non-contiguous tensor⊗tensor for GPU Mul/Div (#1003, #988)#1096
fix(linalg): support non-contiguous tensor⊗tensor for GPU Mul/Div (#1003, #988)#1096yingjerkao wants to merge 2 commits into
Conversation
…1003, #988) Problem: the GPU out-of-place Mul and Div front ends rejected a non-contiguous operand with "two tensors must be contiguous", forcing the caller to contiguous-ize first -- even though Add/Sub accept it and the CPU path handles it. This was a leftover from the pre-#1013 era when cuMul/cuDiv had no working non-contiguous kernel (#988). Fix: #1013 unified all four arithmetic ops onto the shared cuArithmeticDispatchGPU kernel, which applies the layout mappers. Mul.cpp and Div.cpp now pass the layout (shape + inverse mappers) to cuMul_dispatch/cuDiv_dispatch in the non-contiguous branch, exactly as Add.cpp/Sub.cpp already do, instead of erroring. Div's output is already allocated with the true-division (floating) dtype before the branch. Behavior change: GPU Mul/Div now accept a non-contiguous operand and return the correct result (matching CPU and Add/Sub) rather than throwing. Testing: new non-contiguous tensor(op)tensor cases in Mul_test.cpp and Div_test.cpp -- GPU-vs-CPU across all dtypes AND independent hand-computed literals with distinct per-element results (so a wrong multi-index decomposition is caught, not just a wrong L/R pairing). Both fail on the pre-fix code (which threw "must be contiguous"). On an RTX 4070 Ti (CUDA 13, sm_89): gpu_test_main builds clean, the new tests pass, and the full Mul/Div GPU suites are green. clang-format-14 clean. Composes with (and is independent of) #1095: when both land, this newly-live non-contiguous path uses the by-value layout instead of per-call device allocation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Warning Gemini encountered an error creating the review. You can try again by commenting |
…Mul/Div (#1003) Review follow-up (#1096): pass out._impl->storage()._impl->size() -- the logical element count -- as the kernel launch length, not left._impl->storage()._impl->size(). This matches the CPU non-contiguous path (Add.cpp:120) and the contiguous GPU branch. For a strided view whose storage exceeds its logical size the old value would over-launch and write out of bounds on `out`; verified this is not currently reachable (Cytnx compacts slices, so a non-contiguous tensor's storage always equals its logical size), so the fix is for correctness/consistency, not a live crash. Add/Sub on master carry the same old pattern (pre-existing). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1096 +/- ##
=======================================
Coverage 72.71% 72.71%
=======================================
Files 226 226
Lines 28151 28151
Branches 71 71
=======================================
Hits 20471 20471
Misses 7659 7659
Partials 21 21
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
|
Addressed in the latest commit: the This is the same correctness fix as the critical one flagged on #1097 — see that thread for the verification that it isn't currently triggerable in Cytnx (slices compact to contiguous, so a non-contiguous tensor's storage always equals its logical size) but is applied for correctness and future-proofing. The rank-13+ shared-memory guard is handled centrally in #1095. |
ianmccul
left a comment
There was a problem hiding this comment.
OK, but noting that the current implementation is very slow; much slower than making it contiguous (assuming the algorithm for making it contiguous is effiicient -- I didn't check that)
Part of #1003; closes the GPU non-contiguous gap tracked in #988 for Mul/Div.
Problem
The GPU out-of-place Mul and Div front ends rejected a non-contiguous operand with
two tensors must be contiguous. Call Contiguous_() or Contiguous() first— forcing the caller to contiguous-ize first, even though Add/Sub accept it and the CPU path handles it. This was a leftover from the pre-#1013 era whencuMul/cuDivhad no working non-contiguous kernel (#988).Fix
#1013 unified all four arithmetic ops onto the shared
cuArithmeticDispatchGPUkernel, which already applies the layout mappers.Mul.cpp/Div.cppnow pass the layout (shape+ inverse mappers) tocuMul_dispatch/cuDiv_dispatchin the non-contiguous branch — exactly asAdd.cpp/Sub.cppalready do — instead of erroring. Div's output is already allocated with the true-division (floating) dtype before the branch.Mul/Divnow accept a non-contiguous operand and return the correct result (matching CPU and Add/Sub) rather than throwing. This removes a restriction; it does not change any result that previously succeeded.Testing
New non-contiguous
tensor(op)tensorcases inMul_test.cppandDiv_test.cpp:Both new tests fail on the pre-fix code (which threw "must be contiguous"). On an RTX 4070 Ti (CUDA 13, sm_89):
gpu_test_mainbuilds clean; the new tests pass; the full Mul/Div GPU suites (148 tests) are green. clang-format-14 clean.Composes with (and is independent of) #1095 — when both land, this newly-live non-contiguous path uses the by-value layout instead of per-call device allocation.
Note: GPU results are from the local RTX 4070 Ti — there is no GPU CI runner.
Cprstill rejects non-contiguous operands (separate Bool-output kernel); left as a follow-up.🤖 Generated with Claude Code