Repository navigation
[WS1][CUDA][MiniMax-H3] h3_rmsnorm: RMSNorm with fused AdaLN modulation (RFC #420 stack 5/10) #487
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
maxiaosong1124
merged 103 commits into
RL-Align:test-h3
from
fusheng-ji:feat/h3-rmsnorm
Oct 28, 2026
Merged
Changes from all commits
Commits
Show all changes
103 commits
Select commit
Hold shift + click to select a range
d275a06
[WS1][CUDA][MiniMax-H3] adaln_row_gather: fused six-way AdaLN row gather
fusheng-ji 1f7253c
[WS1][MiniMax-H3] adaln_row_gather: B200 evidence report, chain repla…
fusheng-ji 024ee08
[WS1][CUDA][MiniMax-H3] h3_rmsnorm: RMSNorm with fused AdaLN modulation
fusheng-ji 7299784
[WS1][MiniMax-H3] h3_rmsnorm: B200 evidence report and figure
fusheng-ji 010c240
Merge inherited RFC 420 review fixes into PR #486
fusheng-ji 82c9910
Merge inherited RFC 420 review fixes into PR #487
fusheng-ji 2bbef22
fix(h3): validate native gather indices and replay stage dependencies
fusheng-ji d113891
ci: enable CodeRabbit automatic reviews for test-h3
fusheng-ji 5c79216
ci: inherit existing CodeRabbit review settings
fusheng-ji d2a6546
fix(h3): validate RMSNorm inputs and isolate backward benchmarks
fusheng-ji df519d8
docs(h3): refresh B200 evidence with backward-only alternating samples
fusheng-ji e880a10
test(h3): assert adaln_row_gather batch invariance per logical row
fusheng-ji 93c692f
fix(h3): store norm(x) and 1 + scale in BF16 in the modulated RMSNorm…
fusheng-ji 1438c4c
Merge inherited RFC 420 review fixes into PR #486
fusheng-ji df9b836
Merge inherited RFC 420 review fixes into PR #487
fusheng-ji 604c134
Merge the H3 prior-art runner into PR #486
fusheng-ji 17b2342
Merge the H3 prior-art runner into PR #487
fusheng-ji 7083548
Merge the H3 prior-art runner fix into PR #486
fusheng-ji f6ad7a2
Merge the H3 prior-art runner fix into PR #487
fusheng-ji c06b1e0
Merge the H3 prior-art figure fix into PR #486
fusheng-ji 0460cc7
Merge the H3 prior-art figure fix into PR #487
fusheng-ji 213d6ef
Merge the H3 prior-art figure fix into PR #486
fusheng-ji 3098f4a
Merge the H3 prior-art figure fix into PR #487
fusheng-ji 9bf0605
docs(h3): adaln_row_gather vs existing implementations (B200, 6c900ae)
fusheng-ji 656b64b
docs(h3): norm_modulate vs existing implementations (B200, ee83dec)
fusheng-ji 3908e8f
Merge the H3 prior-art evidence into PR #486
fusheng-ji f9d7768
Merge the H3 prior-art evidence into PR #487
fusheng-ji ef45a37
Merge the H3 prior-art figure layout fix into PR #486
fusheng-ji 0329d03
Merge the H3 prior-art figure layout fix into PR #487
fusheng-ji 96b0475
Merge test-h3 refactor and migrate H3 kernels, tools, and tests
fusheng-ji 4f9da3d
Merge migrated H3 dependency PR #485
fusheng-ji d8039fd
Merge test-h3 refactor and migrate H3 kernels, tools, and tests
fusheng-ji a427494
Merge migrated H3 dependency PR #486
fusheng-ji 78b8942
docs(h3): point row-gather validation to migrated tests
fusheng-ji 1481baa
Merge migrated validation documentation from PR #486
fusheng-ji 9243718
Merge feat/h3-adaln-projection-3mod into feat/h3-adaln-row-gather
fusheng-ji b52916a
test(h3): skip the CUDA chain backward e2e when the H3 kernels are no…
fusheng-ji db55298
Merge feat/h3-adaln-row-gather into feat/h3-rmsnorm
fusheng-ji 5fef115
Merge feat/h3-adaln-projection-3mod into feat/h3-adaln-row-gather
fusheng-ji 62f8009
test(h3): fix the flaky FP32 table-gradient check in adaln_modulation
fusheng-ji 7aeb6ba
evidence(h3): adaln_row_gather direct bitwise comparison on B200
fusheng-ji 8379783
Merge feat/h3-adaln-row-gather into feat/h3-rmsnorm
fusheng-ji cad31b9
evidence(h3): norm_modulate direct bitwise comparison on B200
fusheng-ji 5783276
Merge feat/h3-adaln-projection-3mod into feat/h3-adaln-row-gather
fusheng-ji ff3e3f3
Merge feat/h3-adaln-row-gather into feat/h3-rmsnorm
fusheng-ji a07f1ed
Merge feat/h3-adaln-projection-3mod into feat/h3-adaln-row-gather
fusheng-ji 2dc21b8
Merge feat/h3-adaln-row-gather into feat/h3-rmsnorm
fusheng-ji 60aa286
Merge feat/h3-adaln-projection-3mod into feat/h3-adaln-row-gather
fusheng-ji c8659cf
Merge feat/h3-adaln-row-gather into feat/h3-rmsnorm
fusheng-ji 61d23ad
Merge feat/h3-adaln-projection-3mod into feat/h3-adaln-row-gather
fusheng-ji 0032d07
Merge feat/h3-adaln-row-gather into feat/h3-rmsnorm
fusheng-ji a00939e
Merge feat/h3-adaln-projection-3mod into feat/h3-adaln-row-gather
fusheng-ji ecb30e2
evidence(h3): regenerate adaln_row_gather report and chain replay aft…
fusheng-ji 4315cee
Merge feat/h3-adaln-row-gather into feat/h3-rmsnorm
fusheng-ji a1e37ef
evidence(h3): regenerate the row-gather report on the newer runner af…
fusheng-ji a6c8545
fix(h3): seed the row-gather op-backward check in the evidence report
fusheng-ji 29b07c5
Merge feat/h3-adaln-row-gather into feat/h3-rmsnorm
fusheng-ji cbbfaed
evidence(h3): regenerate the row-gather report with the seeded op-bac…
fusheng-ji 6136f31
Merge feat/h3-adaln-row-gather into feat/h3-rmsnorm
fusheng-ji fe0350c
evidence(h3): regenerate this branch's row-gather report with the see…
fusheng-ji 169af74
Merge feat/h3-adaln-projection-3mod into feat/h3-adaln-row-gather
fusheng-ji 2553881
fix(h3): seed the row-gather timing inputs in the evidence report
fusheng-ji 9778e66
Merge feat/h3-adaln-row-gather into feat/h3-rmsnorm
fusheng-ji 95186cb
fix(h3): seed the plain-RMSNorm inputs of the norm evidence check
fusheng-ji 8725b3d
test(h3): let the CPU timing shim accept seeded CUDA generators
fusheng-ji e26e369
docs: move the measurement outputs out of the pull request
fusheng-ji 2985fd2
docs: move the measurement outputs out of the pull request
fusheng-ji 1a8e7b2
docs: point commit hashes at the rewritten history
fusheng-ji 26a7cab
docs: point commit hashes at the rewritten history
fusheng-ji 372544c
Merge test-h3 into feat/h3-adaln-row-gather
fusheng-ji a548fb0
Merge test-h3 into feat/h3-rmsnorm
fusheng-ji 343fa40
fix(h3): use one timing harness across the stacked PRs
fusheng-ji 5160b8b
Merge feat/h3-adaln-projection-3mod into feat/h3-adaln-row-gather
fusheng-ji 7ec2964
Merge feat/h3-adaln-row-gather into feat/h3-rmsnorm
fusheng-ji 10d2671
Merge feat/h3-adaln-projection-3mod into feat/h3-adaln-row-gather
fusheng-ji edfa5ab
Merge feat/h3-adaln-row-gather into feat/h3-rmsnorm
fusheng-ji e08e5e8
Merge feat/h3-adaln-projection-3mod into feat/h3-adaln-row-gather
fusheng-ji 9ed5d9c
fix(h3): reject indices on another device than temb, pre-SM80 devices…
fusheng-ji 6bb1849
Merge feat/h3-adaln-row-gather into feat/h3-rmsnorm
fusheng-ji 0f67234
fix(h3): bound-check the RMSNorm modulation row and the row-gather ba…
fusheng-ji 223a706
Merge feat/h3 parent (#485) into #486
fusheng-ji c8e3eb4
Merge feat/h3 parent (#486) into #487
fusheng-ji 4a1cee4
Merge parent (#485) into #486
fusheng-ji 02531af
Merge parent (#486) into #487
fusheng-ji 42455bc
Merge feat/h3-adaln-projection-3mod into feat/h3-adaln-row-gather
fusheng-ji f2f51f3
fix(h3): state the position contract of the gather backward and the n…
fusheng-ji bb4b784
Merge feat/h3-adaln-row-gather into feat/h3-rmsnorm
fusheng-ji fb72001
fix(h3): check the position repeat count in the modulated RMSNorm bac…
fusheng-ji 2ef5b80
Merge feat/h3-adaln-projection-3mod into feat/h3-adaln-row-gather
fusheng-ji c416a10
fix(h3): check the row-gather tile metadata in one pre-pass, time the…
fusheng-ji 7a5181e
Merge feat/h3-adaln-row-gather into feat/h3-rmsnorm
fusheng-ji 3e4c849
fix(h3): check the rmsnorm table layout and tile metadata, scan rows …
fusheng-ji 86d6f11
docs(h3): row-gather backward timings measured without the forward pass
fusheng-ji 8dc79df
Merge feat/h3-adaln-row-gather into feat/h3-rmsnorm
fusheng-ji 3493f03
Merge feat/h3-adaln-projection-3mod into feat/h3-adaln-row-gather
fusheng-ji 65e8620
fix(h3): check that the gather segments cut the tile list exactly, an…
fusheng-ji 951bdc3
Merge feat/h3-adaln-row-gather into feat/h3-rmsnorm
fusheng-ji 5601118
fix(h3): bound the sizes the rmsnorm kernels take as int, check the m…
fusheng-ji b5113d1
Merge #485 round-5 fixes into #486
fusheng-ji e366ece
fix(h3): require the segment tiles to partition the positions in order
fusheng-ji c75a8d3
Merge #486 round-5 fixes into #487
fusheng-ji 2164211
fix(h3): reject a missing shift, scale or index in the modulated RMSNorm
fusheng-ji 6baf77f
Merge branch 'test-h3' into feat/h3-rmsnorm
maxiaosong1124 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,264 @@ | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| // Copyright (c) 2026 RL-Kernel Contributors | ||
| // | ||
| // MiniMax-H3 AdaLN row gather (RFC #420 `adaln_row_gather`). | ||
| // | ||
| // rows: (3T, C * H) modulation rows, row r = timestep * 3 + modality and | ||
| // column block c = shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, | ||
| // gate_mlp. For every packed-sequence position s: | ||
| // | ||
| // r[s] = timestep_indices[s] * modality_num + token_tags[s] | ||
| // out[c, s, :] = rows[r[s], c * H : (c + 1) * H] | ||
| // | ||
| // Forward is a pure copy: bitwise equal to C index_select calls, one launch, | ||
| // independent of S, packing order and repeats. | ||
| // | ||
| // Backward d_rows[r, j] = sum_{s : r[s] = r} grad[c(j), s, h(j)] is the one | ||
| // cross-row reduction. Positions are visited in a fixed order: the caller | ||
| // sorts them by (r, s) with a stable sort and cuts every segment into tiles of | ||
| // at most kTile positions. Each tile is an ascending FP32 chain from 0; the | ||
| // tiles of a segment are then left-folded in ascending order and cast once. | ||
| // No atomics: the result is a function of (r[s], grad) only. | ||
|
|
||
| #include <torch/extension.h> | ||
| #include <ATen/cuda/CUDAContext.h> | ||
| #include <c10/cuda/CUDAGuard.h> | ||
| #include <c10/cuda/CUDAException.h> | ||
| #include <cuda_bf16.h> | ||
|
|
||
| #include <cstdint> | ||
|
|
||
| #include "tile_metadata.cuh" | ||
|
|
||
| namespace { | ||
|
|
||
| __device__ __forceinline__ float to_float(float v) { return v; } | ||
| __device__ __forceinline__ float to_float(__nv_bfloat16 v) { return __bfloat162float(v); } | ||
| __device__ __forceinline__ float to_float(__half v) { return __half2float(v); } | ||
| __device__ __forceinline__ float to_float(double v) { return static_cast<float>(v); } | ||
|
|
||
| template <typename T> | ||
| __device__ __forceinline__ T from_float(float v); | ||
| template <> | ||
| __device__ __forceinline__ float from_float<float>(float v) { return v; } | ||
| template <> | ||
| __device__ __forceinline__ __nv_bfloat16 from_float<__nv_bfloat16>(float v) { | ||
| return __float2bfloat16(v); | ||
| } | ||
| template <> | ||
| __device__ __forceinline__ __half from_float<__half>(float v) { return __float2half(v); } | ||
| template <> | ||
| __device__ __forceinline__ double from_float<double>(float v) { return v; } | ||
|
|
||
| template <typename T> | ||
| struct CudaType { | ||
| using type = T; | ||
| }; | ||
| template <> | ||
| struct CudaType<at::BFloat16> { | ||
| using type = __nv_bfloat16; | ||
| }; | ||
| template <> | ||
| struct CudaType<at::Half> { | ||
| using type = __half; | ||
| }; | ||
|
|
||
| template <typename idx_t> | ||
| __device__ __forceinline__ int64_t adaln_row(const idx_t* ti, const idx_t* tags, int64_t s, | ||
| int64_t modality_num, int64_t num_timesteps) { | ||
| const int64_t timestep = static_cast<int64_t>(ti[s]); | ||
| const int64_t tag = static_cast<int64_t>(tags[s]); | ||
| // Check each semantic index before multiplication, including values that overflow int64. | ||
| CUDA_KERNEL_ASSERT(timestep >= 0 && timestep < num_timesteps); | ||
| CUDA_KERNEL_ASSERT(tag >= 0 && tag < modality_num); | ||
| return timestep * modality_num + tag; | ||
| } | ||
|
|
||
| // One block per (s, c); 16-byte copies when every row start is 16-byte aligned. | ||
| template <typename T, typename idx_t, bool kVector> | ||
| __global__ void adaln_row_gather_kernel(const T* __restrict__ rows, int64_t row_stride, | ||
| const idx_t* __restrict__ ti, | ||
| const idx_t* __restrict__ tags, T* __restrict__ out, | ||
| int64_t seq, int64_t hidden, int64_t chunks, | ||
| int64_t modality_num, int64_t num_timesteps) { | ||
| for (int64_t s = blockIdx.x; s < seq; s += gridDim.x) { | ||
| const int64_t c = blockIdx.y; | ||
| const int64_t r = adaln_row(ti, tags, s, modality_num, num_timesteps); | ||
| const T* src = rows + r * row_stride + c * hidden; | ||
| T* dst = out + (c * seq + s) * hidden; | ||
| if constexpr (kVector) { | ||
| constexpr int kVec = 16 / sizeof(T); | ||
| const uint4* src4 = reinterpret_cast<const uint4*>(src); | ||
| uint4* dst4 = reinterpret_cast<uint4*>(dst); | ||
| for (int64_t i = threadIdx.x; i < hidden / kVec; i += blockDim.x) dst4[i] = src4[i]; | ||
| } else { | ||
| for (int64_t i = threadIdx.x; i < hidden; i += blockDim.x) dst[i] = src[i]; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // partial[tile, j] = sum over sorted positions p in tile (ascending) of grad[c, s_p, h] | ||
| template <typename g_t> | ||
| __global__ void adaln_row_gather_partial_kernel(const g_t* __restrict__ grad, | ||
| const int64_t* __restrict__ sorted_pos, | ||
| const int64_t* __restrict__ tile_begin, | ||
| const int64_t* __restrict__ tile_end, | ||
| float* __restrict__ partial, int64_t seq, | ||
| int64_t hidden, int64_t width) { | ||
| const int64_t tile = blockIdx.x; | ||
| const int64_t j = static_cast<int64_t>(blockIdx.y) * blockDim.x + threadIdx.x; | ||
| if (j >= width) return; | ||
| const int64_t c = j / hidden; | ||
| const int64_t h = j - c * hidden; | ||
| const g_t* g = grad + c * seq * hidden + h; | ||
| const int64_t begin = tile_begin[tile]; | ||
| const int64_t end = tile_end[tile]; | ||
| float acc = 0.0f; | ||
| for (int64_t p = begin; p < end; ++p) { | ||
| acc += to_float(g[sorted_pos[p] * hidden]); | ||
| } | ||
| partial[tile * width + j] = acc; | ||
| } | ||
|
|
||
| template <typename out_t> | ||
| __global__ void adaln_row_gather_fold_kernel(const float* __restrict__ partial, | ||
| const int64_t* __restrict__ seg_first_tile, | ||
| out_t* __restrict__ out, int64_t width) { | ||
| const int64_t r = blockIdx.x; | ||
| const int64_t j = static_cast<int64_t>(blockIdx.y) * blockDim.x + threadIdx.x; | ||
| if (j >= width) return; | ||
| float acc = 0.0f; | ||
| for (int64_t tile = seg_first_tile[r]; tile < seg_first_tile[r + 1]; ++tile) { | ||
| acc += partial[tile * width + j]; | ||
| } | ||
| out[r * width + j] = from_float<out_t>(acc); | ||
| } | ||
|
|
||
| void check_index(const torch::Tensor& t, const char* name, int64_t seq, | ||
| const torch::Tensor& like) { | ||
| TORCH_CHECK(t.is_cuda() && t.device() == like.device(), name, " must be on ", like.device()); | ||
| TORCH_CHECK(t.dim() == 1 && t.size(0) == seq, name, " must be (", seq, ",)"); | ||
| TORCH_CHECK(t.scalar_type() == at::kLong || t.scalar_type() == at::kInt, name, | ||
| " must be int64 or int32"); | ||
| TORCH_CHECK(t.is_contiguous(), name, " must be contiguous"); | ||
| } | ||
|
|
||
| } // namespace | ||
|
|
||
| // rows (R, C * H) with unit column stride -> out (C, S, H) contiguous. | ||
| torch::Tensor h3_adaln_row_gather_forward(torch::Tensor rows, torch::Tensor timestep_indices, | ||
| torch::Tensor token_tags, int64_t chunks, | ||
| int64_t modality_num) { | ||
| TORCH_CHECK(rows.is_cuda() && rows.dim() == 2 && rows.stride(1) == 1, | ||
| "rows must be a 2-D CUDA tensor with unit column stride"); | ||
| TORCH_CHECK(chunks > 0 && rows.size(1) % chunks == 0, "rows width ", rows.size(1), | ||
| " is not a multiple of ", chunks); | ||
| TORCH_CHECK(modality_num > 0 && rows.size(0) % modality_num == 0, "rows count ", | ||
| rows.size(0), " is not a multiple of ", modality_num); | ||
| TORCH_CHECK(chunks <= 65535, "chunks=", chunks, " exceeds the grid.y limit of 65535"); | ||
| const int64_t num_timesteps = rows.size(0) / modality_num; | ||
| const int64_t seq = timestep_indices.numel(); | ||
| TORCH_CHECK(seq > 0, "the packed sequence must not be empty"); | ||
| check_index(timestep_indices, "timestep_indices", seq, rows); | ||
| check_index(token_tags, "token_tags", seq, rows); | ||
| TORCH_CHECK(timestep_indices.scalar_type() == token_tags.scalar_type(), | ||
| "timestep_indices and token_tags must share an integer dtype"); | ||
| const int64_t hidden = rows.size(1) / chunks; | ||
| const c10::cuda::CUDAGuard device_guard(rows.device()); | ||
| auto out = torch::empty({chunks, seq, hidden}, rows.options()); | ||
| auto stream = at::cuda::getCurrentCUDAStream(); | ||
| const dim3 grid(static_cast<unsigned>(std::min<int64_t>(seq, 1 << 20)), | ||
| static_cast<unsigned>(chunks)); | ||
| const int threads = 128; | ||
| const int64_t elem = rows.element_size(); | ||
| const bool vector = (hidden * elem) % 16 == 0 && (rows.stride(0) * elem) % 16 == 0 && | ||
| reinterpret_cast<uintptr_t>(rows.data_ptr()) % 16 == 0; | ||
|
|
||
| AT_DISPATCH_FLOATING_TYPES_AND2( | ||
| at::kHalf, at::kBFloat16, rows.scalar_type(), "h3_adaln_row_gather_forward", [&] { | ||
| using T = typename CudaType<scalar_t>::type; | ||
| const T* src = reinterpret_cast<const T*>(rows.data_ptr<scalar_t>()); | ||
| T* dst = reinterpret_cast<T*>(out.data_ptr<scalar_t>()); | ||
| auto launch = [&](auto idx_tag) { | ||
| using idx_t = decltype(idx_tag); | ||
| const idx_t* ti = timestep_indices.data_ptr<idx_t>(); | ||
| const idx_t* tags = token_tags.data_ptr<idx_t>(); | ||
| if (vector) { | ||
| adaln_row_gather_kernel<T, idx_t, true><<<grid, threads, 0, stream>>>( | ||
| src, rows.stride(0), ti, tags, dst, seq, hidden, chunks, modality_num, | ||
| num_timesteps); | ||
| } else { | ||
| adaln_row_gather_kernel<T, idx_t, false><<<grid, threads, 0, stream>>>( | ||
| src, rows.stride(0), ti, tags, dst, seq, hidden, chunks, modality_num, | ||
| num_timesteps); | ||
| } | ||
| }; | ||
| if (timestep_indices.scalar_type() == at::kLong) { | ||
| launch(int64_t{0}); | ||
| } else { | ||
| launch(int32_t{0}); | ||
| } | ||
| }); | ||
| C10_CUDA_KERNEL_LAUNCH_CHECK(); | ||
| return out; | ||
| } | ||
|
|
||
| // grad (C, S, H) contiguous; sorted_pos (S,) positions sorted stably by row; | ||
| // tiles [tile_begin, tile_end) in sorted order; seg_first_tile (R + 1,). | ||
| torch::Tensor h3_adaln_row_gather_backward(torch::Tensor grad, torch::Tensor sorted_pos, | ||
| torch::Tensor tile_begin, torch::Tensor tile_end, | ||
| torch::Tensor seg_first_tile, | ||
| c10::ScalarType out_dtype) { | ||
| TORCH_CHECK(grad.is_cuda() && grad.dim() == 3 && grad.is_contiguous(), | ||
| "grad must be a contiguous (C, S, H) CUDA tensor"); | ||
| for (const auto* t : {&sorted_pos, &tile_begin, &tile_end, &seg_first_tile}) { | ||
| TORCH_CHECK(t->is_cuda() && t->device() == grad.device() && t->dim() == 1 && | ||
| t->is_contiguous() && t->scalar_type() == at::kLong, | ||
| "tile metadata must be contiguous 1-D int64 tensors on grad's device"); | ||
| } | ||
| TORCH_CHECK(seg_first_tile.numel() >= 1, "seg_first_tile must hold R + 1 entries"); | ||
| TORCH_CHECK(tile_begin.numel() == tile_end.numel(), "tile_begin/tile_end length mismatch"); | ||
| const int64_t chunks = grad.size(0); | ||
| const int64_t seq = grad.size(1); | ||
| const int64_t hidden = grad.size(2); | ||
| const int64_t width = chunks * hidden; | ||
| const int64_t num_rows = seg_first_tile.numel() - 1; | ||
| const int64_t tiles = tile_begin.numel(); | ||
| TORCH_CHECK(sorted_pos.numel() == seq, "sorted_pos must have S entries"); | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| const c10::cuda::CUDAGuard device_guard(grad.device()); | ||
| auto out = torch::empty({num_rows, width}, grad.options().dtype(out_dtype)); | ||
| if (num_rows == 0) return out; // a zero-sized grid is not a valid launch | ||
| auto partial = torch::empty({std::max<int64_t>(tiles, 1), width}, | ||
| grad.options().dtype(at::kFloat)); | ||
| auto stream = at::cuda::getCurrentCUDAStream(); | ||
| const int threads = 256; | ||
| const int64_t col_blocks64 = (width + threads - 1) / threads; | ||
| TORCH_CHECK(col_blocks64 <= 65535, "width ", width, " needs ", col_blocks64, | ||
| " column blocks, more than the grid.y limit of 65535"); | ||
| const unsigned col_blocks = static_cast<unsigned>(col_blocks64); | ||
| h3_tiles::check_tile_metadata(stream, sorted_pos.data_ptr<int64_t>(), seq, | ||
| tile_begin.data_ptr<int64_t>(), tile_end.data_ptr<int64_t>(), tiles, | ||
| seg_first_tile.data_ptr<int64_t>(), num_rows); | ||
| if (tiles > 0) { | ||
| AT_DISPATCH_FLOATING_TYPES_AND2( | ||
| at::kHalf, at::kBFloat16, grad.scalar_type(), "h3_adaln_row_gather_partial", [&] { | ||
| using G = typename CudaType<scalar_t>::type; | ||
| adaln_row_gather_partial_kernel<G> | ||
| <<<dim3(static_cast<unsigned>(tiles), col_blocks), threads, 0, stream>>>( | ||
| reinterpret_cast<const G*>(grad.data_ptr<scalar_t>()), | ||
| sorted_pos.data_ptr<int64_t>(), tile_begin.data_ptr<int64_t>(), | ||
| tile_end.data_ptr<int64_t>(), partial.data_ptr<float>(), seq, hidden, width); | ||
| }); | ||
| C10_CUDA_KERNEL_LAUNCH_CHECK(); | ||
| } | ||
| AT_DISPATCH_FLOATING_TYPES_AND2( | ||
| at::kHalf, at::kBFloat16, out_dtype, "h3_adaln_row_gather_fold", [&] { | ||
| using O = typename CudaType<scalar_t>::type; | ||
| adaln_row_gather_fold_kernel<O> | ||
| <<<dim3(static_cast<unsigned>(num_rows), col_blocks), threads, 0, stream>>>( | ||
| partial.data_ptr<float>(), seg_first_tile.data_ptr<int64_t>(), | ||
| reinterpret_cast<O*>(out.data_ptr<scalar_t>()), width); | ||
| }); | ||
| C10_CUDA_KERNEL_LAUNCH_CHECK(); | ||
| return out; | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
h3_adaln_row_gather_backwardderiveswidth = chunks * hiddenfrom the gradient's shape (grad.size(0)andgrad.size(2)) and the output table{num_rows, width}is returned without any cross-check against the forward'srows.size(1). The Python caller passesgradof shape(chunks, S, H)— if a caller builds a(C', S, H')gradient whose width differs from the forward table's row width, the result silently has a different width and broadcasts/mismatches only later. The forward validatesrows.size(1) % chunks == 0and requiresR % modality_num == 0, but the backward has no equivalent shape contract. Add a check thatgrad.size(0) > 0 && grad.size(2) > 0and document/enforce that the output width matches the forward's table width, or accept an expected width argument to validate against.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not changed. The entry point has no forward state to compare with:
chunksandhiddenare properties of the gradient it is given, which for the module is the forward's output(6, S, H), and autograd checks both sides of that. A cotangent of another shape never reachesbackward(torch.autograd.grad(out, rows, wrong_shape)raisesMismatch in shape: grad_output[0] has a shape of ...), and the table gradient the backward returns is validated against the shape ofrows. A zero-sizedchunksorhiddenis refused by the native function (gradient dimensions and table row count must be positive, checked by running it), and the Python validators requireH > 0.