sparse-delta-memory 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sparse_delta_memory/__init__.py +13 -0
- sparse_delta_memory/backward.py +367 -0
- sparse_delta_memory/cache.py +70 -0
- sparse_delta_memory/cuda/__init__.py +3 -0
- sparse_delta_memory/cuda/_build.py +254 -0
- sparse_delta_memory/cuda/_device.py +158 -0
- sparse_delta_memory/cuda/_devmemo.py +64 -0
- sparse_delta_memory/cuda/_image.py +128 -0
- sparse_delta_memory/cuda/bwd_steps_cuda.py +111 -0
- sparse_delta_memory/cuda/gather_cuda.py +796 -0
- sparse_delta_memory/cuda/gather_ns_cuda.py +134 -0
- sparse_delta_memory/cuda/gx_gds_cuda.py +1297 -0
- sparse_delta_memory/cuda/gx_gemm_cuda.py +857 -0
- sparse_delta_memory/cuda/gx_pa2_cuda.py +1320 -0
- sparse_delta_memory/cuda/mover_da2_cuda.py +1802 -0
- sparse_delta_memory/cuda/mover_fx_cuda.py +2229 -0
- sparse_delta_memory/cuda/pa_dc_cuda.py +1500 -0
- sparse_delta_memory/cuda/phase1_bwd_cuda.py +852 -0
- sparse_delta_memory/cuda/phase1_common.cuh +181 -0
- sparse_delta_memory/cuda/phase1_cuda.py +3801 -0
- sparse_delta_memory/cuda/product_key_topk_anyf_cuda.py +668 -0
- sparse_delta_memory/cuda/product_key_topk_cuda.py +735 -0
- sparse_delta_memory/cuda/px_qrec_cuda.py +167 -0
- sparse_delta_memory/cuda/sdm_copy.cuh +90 -0
- sparse_delta_memory/cuda/sdm_plancap.cuh +150 -0
- sparse_delta_memory/cuda_graph.py +605 -0
- sparse_delta_memory/decode.py +140 -0
- sparse_delta_memory/forward.py +464 -0
- sparse_delta_memory/layer.py +663 -0
- sparse_delta_memory/memory_ops.py +1069 -0
- sparse_delta_memory/memory_stats.py +221 -0
- sparse_delta_memory/movers.py +198 -0
- sparse_delta_memory/phase2_kernels.py +267 -0
- sparse_delta_memory/product_key_topk.py +113 -0
- sparse_delta_memory/wy_solve.py +273 -0
- sparse_delta_memory-2.0.0.dist-info/METADATA +482 -0
- sparse_delta_memory-2.0.0.dist-info/RECORD +40 -0
- sparse_delta_memory-2.0.0.dist-info/WHEEL +5 -0
- sparse_delta_memory-2.0.0.dist-info/licenses/LICENSE +407 -0
- sparse_delta_memory-2.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1500 @@
|
|
|
1
|
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
"""pa_dc_cuda: the deterministic backward's Phase A (the backward of DW) and its GWR gather ("pa": Phase A).
|
|
3
|
+
|
|
4
|
+
Backward step ti (backward.py, deterministic path):
|
|
5
|
+
GWR (dc_gwr) d_delta_v[t] = sum_n write_w[t,n] * grad[slot[t,n]] (same order as token_gather)
|
|
6
|
+
d_k_pd[t,n] = <delta_v[t], grad[slot[t,n]]> (no grad_rows stash)
|
|
7
|
+
PA (dc_pa) per write group (slot s, sorted members m):
|
|
8
|
+
grad[s] = gdw * cpe_last + sum_m sc_m * d_dva[tok_m], sc = -beta*kv*exp(log_k)
|
|
9
|
+
bank[s] = snap (restore)
|
|
10
|
+
grad_g_decay[e] = cpe_e * <gdw, snap>, d_k_eff[e] = -beta_e * <d_dva[tok_e], snap>
|
|
11
|
+
gdw = grad[s] before this step's PA (read once per group), snap = the snapshot row of the group's FIRST
|
|
12
|
+
member (all members' snapshot rows are the same pre-write row).
|
|
13
|
+
|
|
14
|
+
PA geometry: block = (stream-chunk p, column slice, CTA range); warp = tile of sorted positions whose
|
|
15
|
+
bounds snap back to a group FIRST (no group crosses a tile; L from shapes only); lane = 4 columns of a
|
|
16
|
+
slice of SLW = 4*ceil(ceil(hd/4)/ceil(hd/128)) columns (hd 120 -> 1 x 120, hd 960 -> 8 x 120).
|
|
17
|
+
Per position: one 16-byte smem load of the token row, 4 FMAs into the group accumulator, 4 for the
|
|
18
|
+
d_k_eff partial. Per-entry dots: an incremental binary-counter transposed reduction over 32 positions
|
|
19
|
+
(level k pairs lanes l, l^(1<<k)); every entry is summed over the same lane tree, so its value does
|
|
20
|
+
not depend on the tiling, the warps per CTA or the ring depth. Per group the grad row and snapshot row
|
|
21
|
+
arrive through a per-warp cp.async ring D groups ahead; <gdw, snap> is one xor allreduce per group.
|
|
22
|
+
With several slices the per-entry dots are written as slice partial planes and summed in slice order
|
|
23
|
+
by one reduce launch per op call (dc_pa_reduce)."""
|
|
24
|
+
|
|
25
|
+
import functools
|
|
26
|
+
|
|
27
|
+
import torch
|
|
28
|
+
|
|
29
|
+
from ._build import load_cuda_extension
|
|
30
|
+
from ._device import headers
|
|
31
|
+
|
|
32
|
+
# dc_pa's WIDE walk (64-bit snapshot rows: chunk-local int32 rows from a 64-bit block base; the same bits) runs for
|
|
33
|
+
# snapshots of 2**31 rows or more, which dc_pa decides by itself, or with the host-only dbg bit DBG_WIDE, which the
|
|
34
|
+
# backward sets on the dc_pa route from _WIDE_SNAP_ROWS snapshot rows. Tests set _WIDE_SNAP_ROWS = 0 to run the wide walk
|
|
35
|
+
# at small sizes (as phase2_kernels._WIDE_ENTRIES).
|
|
36
|
+
_WIDE_SNAP_ROWS = 2 ** 31
|
|
37
|
+
DBG_WIDE = 1 << 18
|
|
38
|
+
|
|
39
|
+
_CPP = r"""
|
|
40
|
+
void dc_records_pa(at::Tensor kpay, at::Tensor k_idx, at::Tensor k_val, at::Tensor logk,
|
|
41
|
+
at::Tensor beta, at::Tensor cpe, at::Tensor rec);
|
|
42
|
+
void dc_copy_raw(at::Tensor dst, at::Tensor src);
|
|
43
|
+
bool dc_overlaps(at::Tensor t);
|
|
44
|
+
void dc_pa(at::Tensor grad, at::Tensor bank, at::Tensor snap, int64_t snap_step_rows,
|
|
45
|
+
at::Tensor dva, at::Tensor beta, at::Tensor kpay, at::Tensor rec, at::Tensor out_eff,
|
|
46
|
+
at::Tensor out_g, int64_t ti, int64_t P, int64_t C, int64_t W, int64_t L, int64_t nw,
|
|
47
|
+
int64_t depth, int64_t dbg, bool pipe, at::Tensor bpre, at::Tensor gdwc);
|
|
48
|
+
void dc_pa_reduce(at::Tensor part_eff, at::Tensor part_g, at::Tensor part_pd, at::Tensor beta,
|
|
49
|
+
at::Tensor cpe, at::Tensor d_k_eff, at::Tensor grad_g, at::Tensor d_k_pd,
|
|
50
|
+
int64_t P, int64_t C, int64_t W, int64_t T);
|
|
51
|
+
void dc_gwr(at::Tensor bank, at::Tensor idx, at::Tensor w, at::Tensor out, at::Tensor dv,
|
|
52
|
+
at::Tensor pd, int64_t row0, int64_t out_row0, int64_t tokens, int64_t chunk,
|
|
53
|
+
int64_t valid, int64_t vec_bytes, int64_t lanes, int64_t groups, int64_t tpc,
|
|
54
|
+
at::Tensor kpos, at::Tensor gdwc, int64_t pdl);
|
|
55
|
+
at::Tensor dc_gwr_plan(at::Tensor bank, at::Tensor idx, at::Tensor w, at::Tensor out, at::Tensor dv, at::Tensor pd,
|
|
56
|
+
int64_t row_step, int64_t out_row_step, int64_t tokens, int64_t chunk, int64_t valid_tokens,
|
|
57
|
+
int64_t ti_max, int64_t vec_bytes, int64_t lanes, int64_t groups, int64_t tpc,
|
|
58
|
+
at::Tensor kpos, at::Tensor gdwc);
|
|
59
|
+
int64_t dc_gwr_launch_addr();
|
|
60
|
+
at::Tensor dc_pa_plan(at::Tensor grad, at::Tensor bank, at::Tensor snap, int64_t snap_step_rows,
|
|
61
|
+
at::Tensor dva, at::Tensor beta, at::Tensor kpay, at::Tensor rec, at::Tensor out_eff,
|
|
62
|
+
at::Tensor out_g, int64_t ti_max, int64_t P, int64_t C, int64_t W, int64_t L, int64_t nw,
|
|
63
|
+
int64_t depth, int64_t dbg, bool pipe, at::Tensor bpre, at::Tensor gdwc);
|
|
64
|
+
int64_t dc_pa_launch_addr();
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
_CUDA = r"""
|
|
68
|
+
#include <torch/extension.h>
|
|
69
|
+
#include <ATen/MemoryOverlap.h>
|
|
70
|
+
#include <ATen/cuda/CUDAContext.h>
|
|
71
|
+
#include <c10/cuda/CUDAGuard.h>
|
|
72
|
+
#include <c10/cuda/CUDAException.h>
|
|
73
|
+
#include <cuda_bf16.h>
|
|
74
|
+
#include <algorithm>
|
|
75
|
+
#include <atomic>
|
|
76
|
+
#include <cstdint>
|
|
77
|
+
#include <numeric>
|
|
78
|
+
#include <vector>
|
|
79
|
+
#include <mutex>
|
|
80
|
+
#include <map>
|
|
81
|
+
#include <utility>
|
|
82
|
+
#include "sdm_copy.cuh"
|
|
83
|
+
#include "sdm_plancap.cuh"
|
|
84
|
+
|
|
85
|
+
namespace {
|
|
86
|
+
|
|
87
|
+
constexpr uint32_t kFirst = 0x80000000u;
|
|
88
|
+
constexpr uint32_t kLast = 0x40000000u;
|
|
89
|
+
constexpr uint32_t kInvalid = 0x20000u;
|
|
90
|
+
constexpr int kDistShift = 18;
|
|
91
|
+
constexpr uint32_t kDistMax = 4095u;
|
|
92
|
+
constexpr unsigned kFull = 0xFFFFFFFFu;
|
|
93
|
+
#define DC_THREADS 256
|
|
94
|
+
#define DC_MINB 2
|
|
95
|
+
|
|
96
|
+
__device__ __forceinline__ float f_of(float v) { return v; }
|
|
97
|
+
__device__ __forceinline__ float f_of(__nv_bfloat16 v) { return __bfloat162float(v); }
|
|
98
|
+
__device__ __forceinline__ float4 to_f4(uint2 v) {
|
|
99
|
+
return make_float4(__uint_as_float(v.x << 16), __uint_as_float(v.x & 0xFFFF0000u),
|
|
100
|
+
__uint_as_float(v.y << 16), __uint_as_float(v.y & 0xFFFF0000u));
|
|
101
|
+
}
|
|
102
|
+
__device__ __forceinline__ float4 to_f4(float4 v) { return v; }
|
|
103
|
+
__device__ __forceinline__ uint32_t pack_bf2(float a, float b) {
|
|
104
|
+
const __nv_bfloat162 h = __floats2bfloat162_rn(a, b);
|
|
105
|
+
return *reinterpret_cast<const uint32_t*>(&h);
|
|
106
|
+
}
|
|
107
|
+
template <typename E> struct RowOf;
|
|
108
|
+
template <> struct RowOf<__nv_bfloat16> { using T = uint2; }; // 4 bf16
|
|
109
|
+
template <> struct RowOf<float> { using T = float4; }; // 4 f32
|
|
110
|
+
__device__ __forceinline__ void store4(__nv_bfloat16* p, float4 v) {
|
|
111
|
+
*reinterpret_cast<uint2*>(p) = make_uint2(pack_bf2(v.x, v.y), pack_bf2(v.z, v.w));
|
|
112
|
+
}
|
|
113
|
+
__device__ __forceinline__ void store4(float* p, float4 v) { *reinterpret_cast<float4*>(p) = v; }
|
|
114
|
+
__device__ __forceinline__ void store_raw(__nv_bfloat16* p, uint2 v) { *reinterpret_cast<uint2*>(p) = v; }
|
|
115
|
+
__device__ __forceinline__ void store_raw(float* p, float4 v) { *reinterpret_cast<float4*>(p) = v; }
|
|
116
|
+
|
|
117
|
+
__device__ __forceinline__ uint32_t smem_u32(const void* p) {
|
|
118
|
+
return static_cast<uint32_t>(__cvta_generic_to_shared(p));
|
|
119
|
+
}
|
|
120
|
+
template <int BYTES>
|
|
121
|
+
__device__ __forceinline__ void cp_async(void* dst, const void* src, int src_bytes) {
|
|
122
|
+
if (BYTES == 16) {
|
|
123
|
+
asm volatile("cp.async.cg.shared.global [%0], [%1], 16, %2;\n" ::"r"(smem_u32(dst)),
|
|
124
|
+
"l"(src), "r"(src_bytes) : "memory");
|
|
125
|
+
} else {
|
|
126
|
+
asm volatile("cp.async.ca.shared.global [%0], [%1], %2, %3;\n" ::"r"(smem_u32(dst)),
|
|
127
|
+
"l"(src), "n"(BYTES), "r"(src_bytes) : "memory");
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Range prefetch into L2 (modern device passes): a performance hint only.
|
|
131
|
+
__device__ __forceinline__ void l2_prefetch(const void* p, int bytes) {
|
|
132
|
+
#if defined(__CUDA_ARCH__) && !defined(SDM_PORTABLE_ARCH)
|
|
133
|
+
asm volatile("cp.async.bulk.prefetch.L2.global [%0], %1;\n" ::"l"(p), "r"(bytes) : "memory");
|
|
134
|
+
#endif
|
|
135
|
+
}
|
|
136
|
+
__device__ __forceinline__ void cp_commit() { asm volatile("cp.async.commit_group;\n" ::: "memory"); }
|
|
137
|
+
template <int N>
|
|
138
|
+
__device__ __forceinline__ void cp_wait() { asm volatile("cp.async.wait_group %0;\n" ::"n"(N) : "memory"); }
|
|
139
|
+
// Waits until at most `pending` (0..7) of this thread's newest cp.async groups are outstanding; larger values
|
|
140
|
+
// wait for 7 (stricter, still correct). The ring depth is at most 8, so pending <= 7 here.
|
|
141
|
+
__device__ __forceinline__ void cp_wait_dyn(int pending) {
|
|
142
|
+
switch (pending) {
|
|
143
|
+
#define DC_W(k) case k: cp_wait<k>(); return;
|
|
144
|
+
DC_W(0) DC_W(1) DC_W(2) DC_W(3) DC_W(4) DC_W(5) DC_W(6)
|
|
145
|
+
#undef DC_W
|
|
146
|
+
default: break;
|
|
147
|
+
}
|
|
148
|
+
cp_wait<7>();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// FIRST position of the group holding position j. Warp-uniform; all lanes call it.
|
|
152
|
+
__device__ __forceinline__ int snap_back(const int32_t* __restrict__ kp, int j, int n, int lane) {
|
|
153
|
+
if (j <= 0) return 0;
|
|
154
|
+
if (j >= n) return n;
|
|
155
|
+
const uint32_t p = static_cast<uint32_t>(__ldg(kp + j));
|
|
156
|
+
if (p & kFirst) return j;
|
|
157
|
+
const uint32_t d = (p >> kDistShift) & kDistMax;
|
|
158
|
+
if (d < kDistMax) return j - static_cast<int>(d);
|
|
159
|
+
for (int base = j - static_cast<int>(kDistMax); ; base -= 32) {
|
|
160
|
+
const int q = base - lane;
|
|
161
|
+
const bool f = q <= 0 || (static_cast<uint32_t>(__ldg(kp + max(q, 0))) & kFirst);
|
|
162
|
+
const unsigned m = __ballot_sync(kFull, f);
|
|
163
|
+
if (m) return max(0, base - (__ffs(m) - 1));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Sum of one value over the 32 lanes in a fixed xor tree; every lane ends with the same bits.
|
|
168
|
+
__device__ __forceinline__ float4 add4(float4 a, float4 b) {
|
|
169
|
+
return make_float4(__fadd_rn(a.x, b.x), __fadd_rn(a.y, b.y), __fadd_rn(a.z, b.z), __fadd_rn(a.w, b.w));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
__device__ __forceinline__ float warp_allsum(float v) {
|
|
173
|
+
#pragma unroll
|
|
174
|
+
for (int k = 1; k < 32; k <<= 1) v = __fadd_rn(v, __shfl_xor_sync(kFull, v, k));
|
|
175
|
+
return v;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// One level of the transposed reduction: lanes with bit `off` clear keep entry a, the others entry b;
|
|
179
|
+
// each adds its partner's copy of the entry it keeps.
|
|
180
|
+
__device__ __forceinline__ float tr_comb(float a, float b, int off, int lane, int width) {
|
|
181
|
+
const bool up = (lane & off) != 0;
|
|
182
|
+
const float snd = up ? a : b;
|
|
183
|
+
const float kp = up ? b : a;
|
|
184
|
+
return __fadd_rn(kp, __shfl_xor_sync(kFull, snd, off, width));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Pushes the partial of entry k (0 <= k < 2^LEVELS, warp-uniform) into the counter c[]; after the
|
|
188
|
+
// last entry, returns the reduced dot of entry `lane % 2^LEVELS` in `out`.
|
|
189
|
+
template <int LEVELS>
|
|
190
|
+
__device__ __forceinline__ void tr_push(float v, int k, float (&c)[5], float& out, int lane) {
|
|
191
|
+
constexpr int WID = 1 << LEVELS;
|
|
192
|
+
if (!(k & 1)) { c[0] = v; return; }
|
|
193
|
+
v = tr_comb(c[0], v, 1, lane, WID);
|
|
194
|
+
if (LEVELS == 1) { out = v; return; }
|
|
195
|
+
if (!(k & 2)) { c[1] = v; return; }
|
|
196
|
+
v = tr_comb(c[1], v, 2, lane, WID);
|
|
197
|
+
if (LEVELS == 2) { out = v; return; }
|
|
198
|
+
if (!(k & 4)) { c[2] = v; return; }
|
|
199
|
+
v = tr_comb(c[2], v, 4, lane, WID);
|
|
200
|
+
if (LEVELS == 3) { out = v; return; }
|
|
201
|
+
if (!(k & 8)) { c[3] = v; return; }
|
|
202
|
+
v = tr_comb(c[3], v, 8, lane, WID);
|
|
203
|
+
if (LEVELS == 4) { out = v; return; }
|
|
204
|
+
if (!(k & 16)) { c[4] = v; return; }
|
|
205
|
+
out = tr_comb(c[4], v, 16, lane, WID);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Full transposed reduction of p[0..2^LEVELS) (entry k's partial in p[k], every lane): lane l ends with the
|
|
209
|
+
// sum over the 2^LEVELS-lane segment of entry l % 2^LEVELS, over the same lane tree as tr_push (level k pairs
|
|
210
|
+
// lanes l, l^(1<<k)), so the bits equal tr_push's. All shuffles of a level are independent (ILP).
|
|
211
|
+
template <int LEVELS>
|
|
212
|
+
__device__ __forceinline__ float tr_full(float (&p)[32], int lane) {
|
|
213
|
+
constexpr int WID = 1 << LEVELS;
|
|
214
|
+
#pragma unroll
|
|
215
|
+
for (int lev = 0; lev < LEVELS; ++lev) {
|
|
216
|
+
const int off = 1 << lev;
|
|
217
|
+
const int cnt = WID >> (lev + 1); // pairs at this level
|
|
218
|
+
#pragma unroll
|
|
219
|
+
for (int i = 0; i < cnt; ++i) p[i] = tr_comb(p[2 * i], p[2 * i + 1], off, lane, WID);
|
|
220
|
+
}
|
|
221
|
+
return p[0];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ------------------------------------------------------------------------------------------------
|
|
225
|
+
// Records: per sorted write position {pay, abs slot, f32 -beta*(kv*exp(log_k)), f32 cpe}.
|
|
226
|
+
template <typename IdxT, typename ValT>
|
|
227
|
+
__global__ void __launch_bounds__(256) dc_records_pa_kernel(
|
|
228
|
+
const int32_t* __restrict__ kpay, const IdxT* __restrict__ k_idx, const ValT* __restrict__ kv,
|
|
229
|
+
const float* __restrict__ logk, const float* __restrict__ beta, const ValT* __restrict__ cpe,
|
|
230
|
+
int4* __restrict__ rec, int64_t total, int n, int C, int W) {
|
|
231
|
+
const int64_t i = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
|
|
232
|
+
if (i >= total) return;
|
|
233
|
+
const int64_t chunk = i / n;
|
|
234
|
+
const uint32_t pay = static_cast<uint32_t>(kpay[i]);
|
|
235
|
+
const int tok = (pay >> 9) & 0xFF;
|
|
236
|
+
const int64_t o = chunk * n + static_cast<int64_t>(tok) * W + (pay & 0xFF);
|
|
237
|
+
int32_t slot = 0;
|
|
238
|
+
float sc = 0.0f, c = 1.0f;
|
|
239
|
+
if (!(pay & kInvalid)) {
|
|
240
|
+
slot = static_cast<int32_t>(k_idx[o]);
|
|
241
|
+
sc = -beta[chunk * C + tok] * (f_of(kv[o]) * expf(logk[o]));
|
|
242
|
+
c = f_of(cpe[o]);
|
|
243
|
+
}
|
|
244
|
+
rec[i] = make_int4(static_cast<int32_t>(pay), slot, __float_as_int(sc), __float_as_int(c));
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
__device__ __forceinline__ int4 load_rec(const int4* __restrict__ r, int j, int e) {
|
|
248
|
+
if (j < e) return __ldg(r + j);
|
|
249
|
+
return make_int4(static_cast<int32_t>(kFirst | kLast | kInvalid), 0, 0, 0);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ------------------------------------------------------------------------------------------------
|
|
253
|
+
// Phase A. Grid: P * NSL * ctas blocks (block b: cta = b % ctas, slice = b / ctas % NSL, p = rest).
|
|
254
|
+
// A 32-position window is walked as 4 sub-batches of 8 positions, each fully unrolled (static u): FIRST / LAST / valid
|
|
255
|
+
// come from warp-uniform ballot masks, the token offset and scale of position k from one shuffle each of the window
|
|
256
|
+
// registers, and the per-entry dot partials and the group <gdw, snap> partials go into static register arrays, reduced
|
|
257
|
+
// per sub-batch over lane levels 0-2 (xor 1, 2, 4) and across sub-batches over levels 3-4 (xor 8, 16): the lane tree of
|
|
258
|
+
// tr_push / warp_allsum.
|
|
259
|
+
// EG: grad-row dtype (the bank's, or f32 with the optional f32 grad bank); EB: bank / snapshot dtype. The launchers
|
|
260
|
+
// instantiate D = 8 and V4 = NS = TR = false. WIDE (dc_pa: snapshots of 2^31 rows or more, or the host-only dbg bit
|
|
261
|
+
// kDcWide): the block's 64-bit base snapshot row is folded into the snapshot pointer and the walk's snapshot rows are
|
|
262
|
+
// chunk-local int32 (gx_pa2's scheme); the same rows are read, so the outputs are the same bits. Without WIDE the
|
|
263
|
+
// snapshot rows are global int32, as before.
|
|
264
|
+
template <typename EG, typename EB, int D, bool V4, bool NS, bool TR, bool WIDE>
|
|
265
|
+
__global__ void __launch_bounds__(DC_THREADS, DC_MINB) dc_pa3_kernel(
|
|
266
|
+
EG* __restrict__ grad, EB* __restrict__ bank, const EB* __restrict__ snap, int64_t snap_step_rows,
|
|
267
|
+
const float* __restrict__ dva, const float* __restrict__ beta, const int32_t* __restrict__ kpay,
|
|
268
|
+
const int4* __restrict__ rec, float* __restrict__ out_eff, float* __restrict__ out_g,
|
|
269
|
+
int64_t plane, int ti, int P, int C, int W, int n, int hd, int NSL, int SLW, int ctas, int L, int flags,
|
|
270
|
+
const int32_t* __restrict__ bpre, const EG* __restrict__ gdwc) {
|
|
271
|
+
using RowG = typename RowOf<EG>::T;
|
|
272
|
+
using RowS = typename RowOf<EB>::T;
|
|
273
|
+
constexpr int kRowG = static_cast<int>(sizeof(RowG));
|
|
274
|
+
constexpr int kRowS = static_cast<int>(sizeof(RowS));
|
|
275
|
+
extern __shared__ float4 smem[];
|
|
276
|
+
const bool snapc = (flags & 256) != 0;
|
|
277
|
+
const bool dense = (flags & 1024) != 0; // compact rows by valid-group ordinal (needs snapc and bpre)
|
|
278
|
+
const bool nofetch = (flags & 8192) != 0; // dc_pa's dbg bit 8192 (never set by the package's backward): no slot-row fetch (wrong results; timing only)
|
|
279
|
+
const bool gdc = (flags & 32768) != 0; // gdw rows from the per-step dense gdwc (row p * n + ordinal); dbg bit 32768, never set by the package's backward
|
|
280
|
+
const bool l2d = (flags & 65536) != 0; // L2 bulk prefetch of the next window's dense rows (NSL == 1); dbg bit 65536, never set by the package's backward
|
|
281
|
+
const bool ordi = dense || gdc; // track the dense valid-group ordinal
|
|
282
|
+
const bool nostore = (flags & 16384) != 0; // dc_pa's dbg bit 16384 (never set by the package's backward): no grad / bank row stores (wrong results; timing only)
|
|
283
|
+
const int nw = blockDim.x >> 5;
|
|
284
|
+
const int lane = threadIdx.x & 31, warp = threadIdx.x >> 5;
|
|
285
|
+
const unsigned lt_mask = (1u << lane) - 1u;
|
|
286
|
+
float4* tok_s = smem; // [C][32] (not with NS)
|
|
287
|
+
float* beta_s = reinterpret_cast<float*>(tok_s + C * 32); // [C]
|
|
288
|
+
RowG* const gring_all = reinterpret_cast<RowG*>(beta_s + ((C + 3) & ~3)); // [nw][D][32] grad rows
|
|
289
|
+
RowG* const gring = gring_all + warp * D * 32;
|
|
290
|
+
RowS* const sring_all = reinterpret_cast<RowS*>(gring_all + nw * D * 32); // [nw][D][32] snap
|
|
291
|
+
RowS* const sring = sring_all + warp * D * 32;
|
|
292
|
+
int b = blockIdx.x;
|
|
293
|
+
const int cta = b % ctas; b /= ctas;
|
|
294
|
+
const int sl = b % NSL;
|
|
295
|
+
const int p = b / NSL;
|
|
296
|
+
const int64_t chunk = static_cast<int64_t>(ti) * P + p;
|
|
297
|
+
const int64_t tok0 = chunk * C;
|
|
298
|
+
const int col0 = sl * SLW + 4 * lane;
|
|
299
|
+
const bool on = 4 * lane < SLW && col0 < hd;
|
|
300
|
+
{
|
|
301
|
+
const float* rb = dva + tok0 * hd;
|
|
302
|
+
for (int t = warp; t < C; t += nw) {
|
|
303
|
+
cp_async<16>(&tok_s[t * 32 + lane], rb + static_cast<int64_t>(t) * hd + (on ? col0 : 0),
|
|
304
|
+
on ? 16 : 0);
|
|
305
|
+
}
|
|
306
|
+
cp_commit();
|
|
307
|
+
for (int i = threadIdx.x; i < C; i += blockDim.x) beta_s[i] = beta[tok0 + i];
|
|
308
|
+
cp_wait<0>();
|
|
309
|
+
__syncthreads();
|
|
310
|
+
}
|
|
311
|
+
const int32_t* kp = kpay + chunk * n;
|
|
312
|
+
const int4* rp = rec + chunk * n;
|
|
313
|
+
const int tile = cta * nw + warp;
|
|
314
|
+
const int s = snap_back(kp, tile * L, n, lane);
|
|
315
|
+
const int e = snap_back(kp, (tile + 1) * L, n, lane);
|
|
316
|
+
const EB* const snap_col = snap + (on ? col0 : 0);
|
|
317
|
+
EG* const grad_col = grad + (on ? col0 : 0);
|
|
318
|
+
const EG* const gsrc_col = gdc ? gdwc + (on ? col0 : 0) : grad_col; // where the pump reads gdw rows
|
|
319
|
+
const int64_t gbase = gdc ? static_cast<int64_t>(p) * n : 0;
|
|
320
|
+
const int64_t snap_base = snapc ? chunk * n : static_cast<int64_t>(ti) * snap_step_rows +
|
|
321
|
+
static_cast<int64_t>(p) * C * W;
|
|
322
|
+
// WIDE: the block's base row in the pointers (64-bit), the rows below chunk-local; else snap itself (global rows).
|
|
323
|
+
const EB* const snap_w = WIDE ? snap + snap_base * hd : snap;
|
|
324
|
+
const EB* const snap_colw = WIDE ? snap_col + snap_base * hd : snap_col;
|
|
325
|
+
float* const oeff = out_eff + (NSL == 1 ? 0 : static_cast<int64_t>(sl) * plane);
|
|
326
|
+
float* const og = out_g + (NSL == 1 ? 0 : static_cast<int64_t>(sl) * plane);
|
|
327
|
+
const int64_t obase = chunk * (static_cast<int64_t>(C) * W);
|
|
328
|
+
int4 w0 = load_rec(rp, s + lane, e);
|
|
329
|
+
int4 w1 = load_rec(rp, s + 32 + lane, e);
|
|
330
|
+
int4 w2 = load_rec(rp, s + 64 + lane, e);
|
|
331
|
+
int jb = s, issued = 0, seg = 0;
|
|
332
|
+
// Pump state, two record windows: bit u of pend0 (pend1) = position jb+u (jb+32+u) is a group FIRST whose
|
|
333
|
+
// rows are not issued yet; rdm = non-padding bits; gi/si = this lane's grad slot / snapshot row (32-bit; WIDE:
|
|
334
|
+
// relative to snap_w, sbase64 = 0: a chunk-local row below n + 64 for each of the three readers).
|
|
335
|
+
const int64_t sbase64 = WIDE ? int64_t(0) : snap_base;
|
|
336
|
+
#define DC3_FIRSTS(WR, J0) __ballot_sync(kFull, (static_cast<uint32_t>(WR.x) & kFirst) && (J0) + lane < e)
|
|
337
|
+
#define DC3_RDS(WR) __ballot_sync(kFull, !(static_cast<uint32_t>(WR.x) & kInvalid))
|
|
338
|
+
#define DC3_SROW(WR, J0) static_cast<int>(snapc ? sbase64 + (J0) + lane : \
|
|
339
|
+
sbase64 + static_cast<int64_t>((static_cast<uint32_t>(WR.x) >> 9) & 0xFF) * W + \
|
|
340
|
+
(static_cast<uint32_t>(WR.x) & 0xFF))
|
|
341
|
+
unsigned pend0 = DC3_FIRSTS(w0, s), pend1 = DC3_FIRSTS(w1, s + 32);
|
|
342
|
+
unsigned rdm0 = DC3_RDS(w0), rdm1 = DC3_RDS(w1);
|
|
343
|
+
int gi0 = w0.y, gi1 = w1.y;
|
|
344
|
+
// Dense rows: ordinal of the tile's first valid FIRST = block prefix + valid FIRSTs in [32 * (s / 32), s);
|
|
345
|
+
// then a running count per window (vf = the window's in-tile valid FIRSTs, captured before the pump eats it).
|
|
346
|
+
int gw0 = 0;
|
|
347
|
+
if (ordi) {
|
|
348
|
+
const int nb = (n + 31) >> 5;
|
|
349
|
+
const int bs = s >> 5;
|
|
350
|
+
const uint32_t pq = (32 * bs + lane < s) ? static_cast<uint32_t>(kp[32 * bs + lane]) : 0u;
|
|
351
|
+
gw0 = bpre[chunk * (nb + 1) + bs] + __popc(__ballot_sync(kFull, (pq & kFirst) && !(pq & kInvalid)));
|
|
352
|
+
}
|
|
353
|
+
unsigned vf1 = pend1 & rdm1;
|
|
354
|
+
int gw1 = gw0 + __popc(pend0 & rdm0);
|
|
355
|
+
#define DC3_SROWD(VF, G) static_cast<int>(sbase64 + (G) + __popc((VF) & lt_mask))
|
|
356
|
+
int si0 = dense ? DC3_SROWD(pend0 & rdm0, gw0) : DC3_SROW(w0, s);
|
|
357
|
+
int si1 = dense ? DC3_SROWD(vf1, gw1) : DC3_SROW(w1, s + 32);
|
|
358
|
+
#define DC3_GROWD(VF, G) static_cast<int>(gbase + (G) + __popc((VF) & lt_mask))
|
|
359
|
+
if (gdc) {
|
|
360
|
+
gi0 = DC3_GROWD(pend0 & rdm0, gw0);
|
|
361
|
+
gi1 = DC3_GROWD(vf1, gw1);
|
|
362
|
+
}
|
|
363
|
+
// Dense rows of a window's groups are contiguous: one L2 bulk prefetch per array, one window ahead.
|
|
364
|
+
const bool pf_rows = l2d && NSL == 1 && (static_cast<int64_t>(hd) * sizeof(EG)) % 16 == 0 &&
|
|
365
|
+
(static_cast<int64_t>(hd) * sizeof(EB)) % 16 == 0;
|
|
366
|
+
#define DC3_L2D(VF, G) \
|
|
367
|
+
if (pf_rows && lane == 0 && (VF) != 0u) { \
|
|
368
|
+
const int cnt_ = __popc(VF); \
|
|
369
|
+
if (gdc) l2_prefetch(gdwc + (gbase + (G)) * hd, cnt_ * hd * static_cast<int>(sizeof(EG))); \
|
|
370
|
+
if (dense) l2_prefetch(snap_w + (sbase64 + (G)) * hd, cnt_ * hd * static_cast<int>(sizeof(EB))); \
|
|
371
|
+
}
|
|
372
|
+
DC3_L2D(pend0 & rdm0, gw0)
|
|
373
|
+
DC3_L2D(vf1, gw1)
|
|
374
|
+
#define DC3_PUMP() \
|
|
375
|
+
while (!nofetch && issued < seg + D && (pend0 | pend1)) { \
|
|
376
|
+
const bool lo_ = pend0 != 0u; \
|
|
377
|
+
const int u_ = __ffs(lo_ ? pend0 : pend1) - 1; \
|
|
378
|
+
const int g_ = __shfl_sync(kFull, lo_ ? gi0 : gi1, u_); \
|
|
379
|
+
const int r_ = __shfl_sync(kFull, lo_ ? si0 : si1, u_); \
|
|
380
|
+
const bool rd_ = on && ((((lo_ ? rdm0 : rdm1) >> u_) & 1u) != 0u); \
|
|
381
|
+
const int rs_ = (issued & (D - 1)) * 32 + lane; \
|
|
382
|
+
cp_async<kRowG>(gring + rs_, gsrc_col + (rd_ ? static_cast<int64_t>(g_) * hd : 0), \
|
|
383
|
+
rd_ ? kRowG : 0); \
|
|
384
|
+
cp_async<kRowS>(sring + rs_, snap_colw + (rd_ ? static_cast<int64_t>(r_) * hd : 0), \
|
|
385
|
+
rd_ ? kRowS : 0); \
|
|
386
|
+
cp_commit(); \
|
|
387
|
+
++issued; \
|
|
388
|
+
if (lo_) pend0 &= pend0 - 1u; else pend1 &= pend1 - 1u; \
|
|
389
|
+
}
|
|
390
|
+
DC3_PUMP();
|
|
391
|
+
|
|
392
|
+
float4 acc = make_float4(0.f, 0.f, 0.f, 0.f);
|
|
393
|
+
float4 gdw = make_float4(0.f, 0.f, 0.f, 0.f), sn = make_float4(0.f, 0.f, 0.f, 0.f);
|
|
394
|
+
RowS sn_raw{};
|
|
395
|
+
float gp = 0.f;
|
|
396
|
+
while (jb < e) {
|
|
397
|
+
const unsigned inr = __ballot_sync(kFull, jb + lane < e);
|
|
398
|
+
const uint32_t pay_l = static_cast<uint32_t>(w0.x);
|
|
399
|
+
const unsigned fm = __ballot_sync(kFull, (pay_l & kFirst) != 0) & inr;
|
|
400
|
+
const unsigned lm = __ballot_sync(kFull, (pay_l & kLast) != 0) & inr;
|
|
401
|
+
const unsigned vm = __ballot_sync(kFull, !(pay_l & kInvalid)) & inr;
|
|
402
|
+
const int tko_l = static_cast<int>(((pay_l >> 9) & 0xFF) * 32);
|
|
403
|
+
float l3e = 0.f, l3g = 0.f, l4e = 0.f, l4g = 0.f, dot_mine = 0.f, g_mine = 0.f;
|
|
404
|
+
for (int sb = 0; sb < 4; ++sb) {
|
|
405
|
+
float pe[8], pg[8];
|
|
406
|
+
// The ring holds D >= 8 groups ahead, so after this pump every FIRST of the next 8 positions has
|
|
407
|
+
// been issued; one wait covers them all (no pump or wait inside the unrolled body).
|
|
408
|
+
const unsigned fsb = (fm >> (sb * 8)) & 0xFFu;
|
|
409
|
+
if (fsb && !nofetch) {
|
|
410
|
+
DC3_PUMP();
|
|
411
|
+
cp_wait_dyn(issued - seg - __popc(fsb));
|
|
412
|
+
}
|
|
413
|
+
#pragma unroll
|
|
414
|
+
for (int u = 0; u < 8; ++u) {
|
|
415
|
+
const int k = sb * 8 + u;
|
|
416
|
+
const int tk = __shfl_sync(kFull, tko_l, k);
|
|
417
|
+
const float sck = __int_as_float(__shfl_sync(kFull, w0.z, k));
|
|
418
|
+
const float4 x = tok_s[tk + lane];
|
|
419
|
+
if ((fsb >> u) & 1u) {
|
|
420
|
+
acc = make_float4(0.f, 0.f, 0.f, 0.f);
|
|
421
|
+
const int rs = (seg % D) * 32 + lane;
|
|
422
|
+
if (!nofetch) {
|
|
423
|
+
gdw = to_f4(gring[rs]);
|
|
424
|
+
sn_raw = sring[rs];
|
|
425
|
+
}
|
|
426
|
+
sn = to_f4(sn_raw);
|
|
427
|
+
++seg;
|
|
428
|
+
gp = __fmul_rn(gdw.x, sn.x);
|
|
429
|
+
gp = __fmaf_rn(gdw.y, sn.y, gp);
|
|
430
|
+
gp = __fmaf_rn(gdw.z, sn.z, gp);
|
|
431
|
+
gp = __fmaf_rn(gdw.w, sn.w, gp);
|
|
432
|
+
}
|
|
433
|
+
// No valid branch: a padding entry is a singleton group (FIRST|LAST|invalid) with scale 0 and
|
|
434
|
+
// zero-filled ring rows; its row is never stored and its outputs are forced to +0 below.
|
|
435
|
+
acc.x = __fmaf_rn(sck, x.x, acc.x);
|
|
436
|
+
acc.y = __fmaf_rn(sck, x.y, acc.y);
|
|
437
|
+
acc.z = __fmaf_rn(sck, x.z, acc.z);
|
|
438
|
+
acc.w = __fmaf_rn(sck, x.w, acc.w);
|
|
439
|
+
float part = __fmul_rn(x.x, sn.x);
|
|
440
|
+
part = __fmaf_rn(x.y, sn.y, part);
|
|
441
|
+
part = __fmaf_rn(x.z, sn.z, part);
|
|
442
|
+
part = __fmaf_rn(x.w, sn.w, part);
|
|
443
|
+
pe[u] = part;
|
|
444
|
+
pg[u] = gp;
|
|
445
|
+
if ((lm & vm) >> k & 1u) {
|
|
446
|
+
const int64_t slot = __shfl_sync(kFull, w0.y, k);
|
|
447
|
+
const float cpe_l = __int_as_float(__shfl_sync(kFull, w0.w, k));
|
|
448
|
+
if (on) {
|
|
449
|
+
float4 tot = acc;
|
|
450
|
+
float4 o;
|
|
451
|
+
o.x = __fmaf_rn(gdw.x, cpe_l, tot.x);
|
|
452
|
+
o.y = __fmaf_rn(gdw.y, cpe_l, tot.y);
|
|
453
|
+
o.z = __fmaf_rn(gdw.z, cpe_l, tot.z);
|
|
454
|
+
o.w = __fmaf_rn(gdw.w, cpe_l, tot.w);
|
|
455
|
+
if (!nostore) {
|
|
456
|
+
store4(grad + slot * hd + col0, o);
|
|
457
|
+
store_raw(bank + slot * hd + col0, sn_raw);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
// levels 0-2 (xor 1, 2, 4) over the sub-batch's 8 entries: lane l -> entry sb*8 + (l & 7)
|
|
463
|
+
#pragma unroll
|
|
464
|
+
for (int i = 0; i < 4; ++i) pe[i] = tr_comb(pe[2 * i], pe[2 * i + 1], 1, lane, 32);
|
|
465
|
+
#pragma unroll
|
|
466
|
+
for (int i = 0; i < 2; ++i) pe[i] = tr_comb(pe[2 * i], pe[2 * i + 1], 2, lane, 32);
|
|
467
|
+
const float ve = tr_comb(pe[0], pe[1], 4, lane, 32);
|
|
468
|
+
float vg = 0.f;
|
|
469
|
+
if (fsb) {
|
|
470
|
+
#pragma unroll
|
|
471
|
+
for (int i = 0; i < 4; ++i) pg[i] = tr_comb(pg[2 * i], pg[2 * i + 1], 1, lane, 32);
|
|
472
|
+
#pragma unroll
|
|
473
|
+
for (int i = 0; i < 2; ++i) pg[i] = tr_comb(pg[2 * i], pg[2 * i + 1], 2, lane, 32);
|
|
474
|
+
vg = tr_comb(pg[0], pg[1], 4, lane, 32);
|
|
475
|
+
} else {
|
|
476
|
+
// No FIRST in these 8 positions: every pg[u] is the same gp, so the transposed levels 0-2
|
|
477
|
+
// reduce to the octet butterfly of gp (the same lane tree, the same bits).
|
|
478
|
+
vg = gp;
|
|
479
|
+
vg = __fadd_rn(vg, __shfl_xor_sync(kFull, vg, 1));
|
|
480
|
+
vg = __fadd_rn(vg, __shfl_xor_sync(kFull, vg, 2));
|
|
481
|
+
vg = __fadd_rn(vg, __shfl_xor_sync(kFull, vg, 4));
|
|
482
|
+
}
|
|
483
|
+
// levels 3-4 (xor 8, 16) across sub-batches
|
|
484
|
+
if (!(sb & 1)) {
|
|
485
|
+
l3e = ve; l3g = vg;
|
|
486
|
+
} else {
|
|
487
|
+
const float te = tr_comb(l3e, ve, 8, lane, 32);
|
|
488
|
+
const float tg = tr_comb(l3g, vg, 8, lane, 32);
|
|
489
|
+
if (!(sb & 2)) {
|
|
490
|
+
l4e = te; l4g = tg;
|
|
491
|
+
} else {
|
|
492
|
+
dot_mine = tr_comb(l4e, te, 16, lane, 32);
|
|
493
|
+
g_mine = tr_comb(l4g, tg, 16, lane, 32);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
if ((inr >> lane) & 1u) { // lane l: position jb + l
|
|
498
|
+
const int tok = (pay_l >> 9) & 0xFF;
|
|
499
|
+
const int64_t o = obase + static_cast<int64_t>(tok) * W + (pay_l & 0xFF);
|
|
500
|
+
const bool valid = !(pay_l & kInvalid);
|
|
501
|
+
if (NSL == 1) {
|
|
502
|
+
oeff[o] = valid ? -beta_s[tok] * dot_mine : 0.f;
|
|
503
|
+
og[o] = valid ? __int_as_float(w0.w) * g_mine : 0.f;
|
|
504
|
+
} else {
|
|
505
|
+
oeff[o] = valid ? dot_mine : 0.f;
|
|
506
|
+
og[o] = valid ? g_mine : 0.f;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
jb += 32;
|
|
510
|
+
w0 = w1;
|
|
511
|
+
w1 = w2;
|
|
512
|
+
w2 = load_rec(rp, jb + 64 + lane, e);
|
|
513
|
+
pend0 = pend1; rdm0 = rdm1; gi0 = gi1; si0 = si1;
|
|
514
|
+
pend1 = DC3_FIRSTS(w1, jb + 32); rdm1 = DC3_RDS(w1); gi1 = w1.y;
|
|
515
|
+
gw1 += __popc(vf1);
|
|
516
|
+
vf1 = pend1 & rdm1;
|
|
517
|
+
si1 = dense ? DC3_SROWD(vf1, gw1) : DC3_SROW(w1, jb + 32);
|
|
518
|
+
if (gdc) gi1 = DC3_GROWD(vf1, gw1);
|
|
519
|
+
DC3_L2D(vf1, gw1)
|
|
520
|
+
DC3_PUMP();
|
|
521
|
+
}
|
|
522
|
+
cp_wait<0>();
|
|
523
|
+
#undef DC3_PUMP
|
|
524
|
+
#undef DC3_FIRSTS
|
|
525
|
+
#undef DC3_RDS
|
|
526
|
+
#undef DC3_SROW
|
|
527
|
+
#undef DC3_SROWD
|
|
528
|
+
#undef DC3_GROWD
|
|
529
|
+
#undef DC3_L2D
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Sums slice partial planes in slice order: d_k_eff = -beta * sum, grad_g = cpe * sum, d_k_pd = sum.
|
|
533
|
+
template <typename ValT>
|
|
534
|
+
__global__ void __launch_bounds__(256) dc_pa_reduce_kernel(
|
|
535
|
+
const float* __restrict__ pe, const float* __restrict__ pg, const float* __restrict__ pp,
|
|
536
|
+
const float* __restrict__ beta, const ValT* __restrict__ cpe, float* __restrict__ d_k_eff,
|
|
537
|
+
float* __restrict__ grad_g, float* __restrict__ d_k_pd, int64_t total, int ne, int ng, int np,
|
|
538
|
+
int P, int C, int W, int64_t T) {
|
|
539
|
+
const int64_t i = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
|
|
540
|
+
if (i >= total) return;
|
|
541
|
+
const int64_t row = i / W; // chunk * C + tok
|
|
542
|
+
const int64_t chunk = row / C;
|
|
543
|
+
const int tok = static_cast<int>(row - chunk * C);
|
|
544
|
+
const bool valid = (chunk / P) * C + tok < T;
|
|
545
|
+
if (ne > 0) {
|
|
546
|
+
float s = pe[i];
|
|
547
|
+
for (int k = 1; k < ne; ++k) s = __fadd_rn(s, pe[static_cast<int64_t>(k) * total + i]);
|
|
548
|
+
d_k_eff[i] = valid ? -beta[row] * s : 0.f;
|
|
549
|
+
float g = pg[i];
|
|
550
|
+
for (int k = 1; k < ng; ++k) g = __fadd_rn(g, pg[static_cast<int64_t>(k) * total + i]);
|
|
551
|
+
grad_g[i] = valid ? f_of(cpe[i]) * g : 0.f;
|
|
552
|
+
}
|
|
553
|
+
if (np > 0) {
|
|
554
|
+
float s = pp[i];
|
|
555
|
+
for (int k = 1; k < np; ++k) s = __fadd_rn(s, pp[static_cast<int64_t>(k) * total + i]);
|
|
556
|
+
d_k_pd[i] = valid ? s : 0.f;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// ------------------------------------------------------------------------------------------------
|
|
561
|
+
// GWR of the deterministic backward: token_gather with one operand and no stash, plus d_k_pd per entry. The
|
|
562
|
+
// d_delta_v accumulation is token_gather's (same row groups, same order, same partial sums).
|
|
563
|
+
template <int B> struct Bits;
|
|
564
|
+
template <> struct Bits<2> { using type = uint16_t; };
|
|
565
|
+
template <> struct Bits<4> { using type = uint32_t; };
|
|
566
|
+
template <> struct Bits<16> { using type = uint4; };
|
|
567
|
+
__device__ __forceinline__ float to_float(uint16_t b) { return __uint_as_float(uint32_t(b) << 16); }
|
|
568
|
+
__device__ __forceinline__ float to_float(uint32_t b) { return __uint_as_float(b); }
|
|
569
|
+
|
|
570
|
+
struct GArgs {
|
|
571
|
+
const void* bank; const void* idx; const void* w; float* out; const float* dv; float* pd;
|
|
572
|
+
int64_t row0, out_row0, plane;
|
|
573
|
+
int hd, vpr, tokens, chunk, valid, per_group, width;
|
|
574
|
+
bool idx64, w_bf16;
|
|
575
|
+
const int* kpos; // optional [rows, width]: dense valid-group ordinal of a FIRST entry, else -1
|
|
576
|
+
void* gdwc; // with kpos: per-step compact grad rows, row (chunk - row0 / chunk) * crow + ordinal
|
|
577
|
+
int crow;
|
|
578
|
+
};
|
|
579
|
+
__device__ __forceinline__ int load_slot(const void* p, int64_t i, bool idx64) {
|
|
580
|
+
return idx64 ? static_cast<int>(__ldg(static_cast<const long long*>(p) + i))
|
|
581
|
+
: __ldg(static_cast<const int*>(p) + i);
|
|
582
|
+
}
|
|
583
|
+
__device__ __forceinline__ float load_weight(const void* p, int64_t i, bool bf16) {
|
|
584
|
+
return bf16 ? to_float(__ldg(static_cast<const unsigned short*>(p) + i))
|
|
585
|
+
: __ldg(static_cast<const float*>(p) + i);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// Programmatic dependent launch: the deterministic backward launches step ti's GWR (ti < last) right after step ti +
|
|
589
|
+
// 1's GDS, which triggers its dependents once its own wait has returned. The delta_v / slot / weight / kpos loads
|
|
590
|
+
// (saved by the forward, written before the backward's step loop) run before griddepcontrol.wait; every grad-bank row
|
|
591
|
+
// is read after it through the coherent L2 path (__ldcg: the GDS has just written them), and no store precedes it.
|
|
592
|
+
// Right after its wait the grid lets its dependents launch (the cuBLAS baddbmm that follows may be a programmatic
|
|
593
|
+
// launch; a no-op otherwise). Arithmetic and order are unchanged.
|
|
594
|
+
#define FX_LDG_ROW(p) __ldcg(p)
|
|
595
|
+
#if defined(__CUDA_ARCH__) && !defined(SDM_PORTABLE_ARCH) // a modern device pass (sdm_copy.cuh); PDL: modern only
|
|
596
|
+
#define FX_PDL_WAIT_LAUNCH() asm volatile("griddepcontrol.wait;\n\tgriddepcontrol.launch_dependents;" ::: "memory")
|
|
597
|
+
#else
|
|
598
|
+
#define FX_PDL_WAIT_LAUNCH() ((void)0)
|
|
599
|
+
#endif
|
|
600
|
+
|
|
601
|
+
template <typename E, int VB, int LPR>
|
|
602
|
+
__global__ void __launch_bounds__(512) dc_gwr_kernel(const GArgs a) {
|
|
603
|
+
using V = typename Bits<VB>::type;
|
|
604
|
+
constexpr int VEC = VB / sizeof(E);
|
|
605
|
+
constexpr int STEP = (sizeof(E) == 4 ? 8 : 4);
|
|
606
|
+
constexpr int SLICE = LPR * VEC;
|
|
607
|
+
constexpr int PADW = LPR + 4; // dot-buffer row stride (floats): 16-byte rows, conflict-free reads
|
|
608
|
+
extern __shared__ float4 smem[];
|
|
609
|
+
float* partial = reinterpret_cast<float*>(smem);
|
|
610
|
+
const int lane = threadIdx.x, group = threadIdx.y, groups = blockDim.y;
|
|
611
|
+
// Per row group: [LPR entries][PADW] f32 dot partials (entry k's partial of lane l at k * PADW + l).
|
|
612
|
+
float* dbuf = partial + (groups > 1 ? blockDim.z * groups * SLICE : 0) +
|
|
613
|
+
(threadIdx.z * groups + group) * (LPR * PADW + LPR); // +LPR skews row-group banks
|
|
614
|
+
const int t = blockIdx.x * blockDim.z + threadIdx.z;
|
|
615
|
+
const bool in_range = t < a.tokens;
|
|
616
|
+
const bool live = in_range && (a.valid == a.chunk || t % a.chunk < a.valid);
|
|
617
|
+
const int v = blockIdx.y * LPR + lane;
|
|
618
|
+
const bool lane_on = v < a.vpr;
|
|
619
|
+
const int64_t col = int64_t(v) * VEC;
|
|
620
|
+
const int64_t row = a.row0 + t;
|
|
621
|
+
const E* bank = static_cast<const E*>(a.bank);
|
|
622
|
+
float* pd = a.pd + (gridDim.y == 1 ? 0 : static_cast<int64_t>(blockIdx.y) * a.plane);
|
|
623
|
+
float dvr[VEC];
|
|
624
|
+
#pragma unroll
|
|
625
|
+
for (int i = 0; i < VEC; ++i)
|
|
626
|
+
dvr[i] = (live && lane_on) ? __ldg(a.dv + row * a.hd + col + i) : 0.0f;
|
|
627
|
+
float acc[VEC];
|
|
628
|
+
#pragma unroll
|
|
629
|
+
for (int i = 0; i < VEC; ++i) acc[i] = 0.0f;
|
|
630
|
+
|
|
631
|
+
for (int jb = 0; jb < a.per_group; jb += LPR) {
|
|
632
|
+
const int nl = group + groups * (jb + lane);
|
|
633
|
+
const bool okl = live && jb + lane < a.per_group && nl < a.width;
|
|
634
|
+
const int64_t il = row * a.width + nl;
|
|
635
|
+
const int slot_l = okl ? load_slot(a.idx, il, a.idx64) : 0;
|
|
636
|
+
const float w_l = okl ? load_weight(a.w, il, a.w_bf16) : 0.0f;
|
|
637
|
+
const int kp_l = (a.kpos != nullptr && okl) ? __ldg(a.kpos + il) : -1;
|
|
638
|
+
if (jb == 0) FX_PDL_WAIT_LAUNCH(); // PDL: the previous grid (GDS) has completed; before any grad-bank read
|
|
639
|
+
const int batch = min(LPR, a.per_group - jb);
|
|
640
|
+
for (int s0 = 0; s0 < LPR; s0 += STEP) {
|
|
641
|
+
if (s0 >= batch) {
|
|
642
|
+
#pragma unroll
|
|
643
|
+
for (int s = 0; s < STEP; ++s) dbuf[(s0 + s) * PADW + lane] = 0.f;
|
|
644
|
+
} else {
|
|
645
|
+
const V* src[STEP];
|
|
646
|
+
bool load[STEP];
|
|
647
|
+
float w[STEP];
|
|
648
|
+
#pragma unroll
|
|
649
|
+
for (int s = 0; s < STEP; ++s) {
|
|
650
|
+
const int n = group + groups * (jb + s0 + s);
|
|
651
|
+
const int slot = __shfl_sync(kFull, slot_l, (s0 + s) & (LPR - 1), LPR);
|
|
652
|
+
w[s] = __shfl_sync(kFull, w_l, (s0 + s) & (LPR - 1), LPR);
|
|
653
|
+
src[s] = reinterpret_cast<const V*>(bank + int64_t(slot) * a.hd + col);
|
|
654
|
+
load[s] = live && lane_on && s0 + s < batch && n < a.width;
|
|
655
|
+
}
|
|
656
|
+
V r[STEP];
|
|
657
|
+
#pragma unroll
|
|
658
|
+
for (int s = 0; s < STEP; ++s) r[s] = load[s] ? FX_LDG_ROW(src[s]) : V{};
|
|
659
|
+
if (a.kpos != nullptr) { // gdwc: the raw grad row of each valid group FIRST, dense by ordinal
|
|
660
|
+
#pragma unroll
|
|
661
|
+
for (int s = 0; s < STEP; ++s) {
|
|
662
|
+
const int kj = __shfl_sync(kFull, kp_l, (s0 + s) & (LPR - 1), LPR);
|
|
663
|
+
if (kj >= 0 && load[s])
|
|
664
|
+
*reinterpret_cast<V*>(static_cast<E*>(a.gdwc) +
|
|
665
|
+
((row / a.chunk - a.row0 / a.chunk) * a.crow + kj) *
|
|
666
|
+
static_cast<int64_t>(a.hd) + col) = r[s];
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
#pragma unroll
|
|
670
|
+
for (int s = 0; s < STEP; ++s) {
|
|
671
|
+
const E* el = reinterpret_cast<const E*>(&r[s]);
|
|
672
|
+
#pragma unroll
|
|
673
|
+
for (int i = 0; i < VEC; ++i) acc[i] = fmaf(w[s], to_float(el[i]), acc[i]);
|
|
674
|
+
float pdp = __fmul_rn(dvr[0], to_float(el[0]));
|
|
675
|
+
#pragma unroll
|
|
676
|
+
for (int i = 1; i < VEC; ++i) pdp = __fmaf_rn(dvr[i], to_float(el[i]), pdp);
|
|
677
|
+
dbuf[(s0 + s) * PADW + lane] = pdp;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
__syncwarp();
|
|
682
|
+
// Lane l sums entry l's LPR partials pairwise in lane-index order: the xor tree of tr_push/tr_full.
|
|
683
|
+
float tv[LPR];
|
|
684
|
+
#pragma unroll
|
|
685
|
+
for (int j = 0; j < LPR; j += 4) {
|
|
686
|
+
const float4 q = *reinterpret_cast<const float4*>(dbuf + lane * PADW + j);
|
|
687
|
+
tv[j] = q.x; tv[j + 1] = q.y; tv[j + 2] = q.z; tv[j + 3] = q.w;
|
|
688
|
+
}
|
|
689
|
+
#pragma unroll
|
|
690
|
+
for (int wdt = LPR / 2; wdt >= 1; wdt >>= 1) {
|
|
691
|
+
#pragma unroll
|
|
692
|
+
for (int j = 0; j < wdt; ++j) tv[j] = __fadd_rn(tv[2 * j], tv[2 * j + 1]);
|
|
693
|
+
}
|
|
694
|
+
const float dot_mine = tv[0];
|
|
695
|
+
__syncwarp();
|
|
696
|
+
if (in_range && okl) pd[il] = dot_mine;
|
|
697
|
+
else if (in_range && jb + lane < a.per_group && nl < a.width) pd[il] = 0.0f;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
FX_PDL_WAIT_LAUNCH(); // PDL: also when no entry loop ran (no store, no exit before the wait)
|
|
701
|
+
if (groups == 1) {
|
|
702
|
+
if (in_range && lane_on) {
|
|
703
|
+
#pragma unroll
|
|
704
|
+
for (int i = 0; i < VEC; i += (VEC % 4 == 0 ? 4 : 1)) {
|
|
705
|
+
if constexpr (VEC % 4 == 0) {
|
|
706
|
+
*reinterpret_cast<float4*>(a.out + (a.out_row0 + t) * a.hd + col + i) =
|
|
707
|
+
make_float4(acc[i], acc[i + 1], acc[i + 2], acc[i + 3]);
|
|
708
|
+
} else {
|
|
709
|
+
a.out[(a.out_row0 + t) * a.hd + col + i] = acc[i];
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
{
|
|
716
|
+
float* dst = partial + (threadIdx.z * groups + group) * SLICE;
|
|
717
|
+
#pragma unroll
|
|
718
|
+
for (int i = 0; i < VEC; ++i) dst[lane * VEC + i] = acc[i];
|
|
719
|
+
}
|
|
720
|
+
__syncthreads();
|
|
721
|
+
const int tid = lane + LPR * (group + groups * threadIdx.z);
|
|
722
|
+
const int width = min(SLICE, a.hd - int(blockIdx.y) * SLICE);
|
|
723
|
+
for (int q = tid; q < blockDim.z * width; q += LPR * groups * blockDim.z) {
|
|
724
|
+
const int z = q / width, c = q - z * width;
|
|
725
|
+
const int tok = blockIdx.x * blockDim.z + z;
|
|
726
|
+
if (tok >= a.tokens) continue;
|
|
727
|
+
const float* src = partial + z * groups * SLICE + c;
|
|
728
|
+
float sum = 0.0f;
|
|
729
|
+
for (int g = 0; g < groups; ++g) sum += src[g * SLICE];
|
|
730
|
+
a.out[(a.out_row0 + tok) * a.hd + blockIdx.y * SLICE + c] = sum;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// A 64-register entry point (dc_gwr_r64_kernel) for the 256-thread launches of the bf16 / 16-byte / 32-lane instance:
|
|
735
|
+
// at 72 registers only 3 such CTAs fit an SM, at <= 64 registers 4 do (a grid of 512 CTAs then runs in one wave on
|
|
736
|
+
// GB300). dc_gwr_body_cap is dc_gwr_kernel's body copied verbatim except (1) the signature, (2) STEP = STEPX rows in
|
|
737
|
+
// flight (DC_GWR_CAP_STEP = 2: 63 registers and no spill; 4 would spill at 64), (3) the zero-fill bound zlim, which
|
|
738
|
+
// makes a smaller STEP process exactly STEP0's entries (padding included): every output element sees the same
|
|
739
|
+
// operations in the same order.
|
|
740
|
+
#ifndef DC_GWR_CAP_STEP
|
|
741
|
+
#define DC_GWR_CAP_STEP 2
|
|
742
|
+
#endif
|
|
743
|
+
template <typename E, int VB, int LPR, int STEPX>
|
|
744
|
+
__device__ __forceinline__ void dc_gwr_body_cap(const GArgs a) {
|
|
745
|
+
using V = typename Bits<VB>::type;
|
|
746
|
+
constexpr int VEC = VB / sizeof(E);
|
|
747
|
+
constexpr int STEP0 = (sizeof(E) == 4 ? 8 : 4);
|
|
748
|
+
constexpr int STEP = STEPX ? STEPX : STEP0; // rows in flight (STEPX divides STEP0)
|
|
749
|
+
static_assert(STEP >= 1 && (STEP0 % STEP) == 0, "STEPX must divide the default STEP");
|
|
750
|
+
constexpr int SLICE = LPR * VEC;
|
|
751
|
+
constexpr int PADW = LPR + 4; // dot-buffer row stride (floats): 16-byte rows, conflict-free reads
|
|
752
|
+
extern __shared__ float4 smem[];
|
|
753
|
+
float* partial = reinterpret_cast<float*>(smem);
|
|
754
|
+
const int lane = threadIdx.x, group = threadIdx.y, groups = blockDim.y;
|
|
755
|
+
// Per row group: [LPR entries][PADW] f32 dot partials (entry k's partial of lane l at k * PADW + l).
|
|
756
|
+
float* dbuf = partial + (groups > 1 ? blockDim.z * groups * SLICE : 0) +
|
|
757
|
+
(threadIdx.z * groups + group) * (LPR * PADW + LPR); // +LPR skews row-group banks
|
|
758
|
+
const int t = blockIdx.x * blockDim.z + threadIdx.z;
|
|
759
|
+
const bool in_range = t < a.tokens;
|
|
760
|
+
const bool live = in_range && (a.valid == a.chunk || t % a.chunk < a.valid);
|
|
761
|
+
const int v = blockIdx.y * LPR + lane;
|
|
762
|
+
const bool lane_on = v < a.vpr;
|
|
763
|
+
const int64_t col = int64_t(v) * VEC;
|
|
764
|
+
const int64_t row = a.row0 + t;
|
|
765
|
+
const E* bank = static_cast<const E*>(a.bank);
|
|
766
|
+
float* pd = a.pd + (gridDim.y == 1 ? 0 : static_cast<int64_t>(blockIdx.y) * a.plane);
|
|
767
|
+
float dvr[VEC];
|
|
768
|
+
#pragma unroll
|
|
769
|
+
for (int i = 0; i < VEC; ++i)
|
|
770
|
+
dvr[i] = (live && lane_on) ? __ldg(a.dv + row * a.hd + col + i) : 0.0f;
|
|
771
|
+
float acc[VEC];
|
|
772
|
+
#pragma unroll
|
|
773
|
+
for (int i = 0; i < VEC; ++i) acc[i] = 0.0f;
|
|
774
|
+
|
|
775
|
+
for (int jb = 0; jb < a.per_group; jb += LPR) {
|
|
776
|
+
const int nl = group + groups * (jb + lane);
|
|
777
|
+
const bool okl = live && jb + lane < a.per_group && nl < a.width;
|
|
778
|
+
const int64_t il = row * a.width + nl;
|
|
779
|
+
const int slot_l = okl ? load_slot(a.idx, il, a.idx64) : 0;
|
|
780
|
+
const float w_l = okl ? load_weight(a.w, il, a.w_bf16) : 0.0f;
|
|
781
|
+
const int kp_l = (a.kpos != nullptr && okl) ? __ldg(a.kpos + il) : -1;
|
|
782
|
+
if (jb == 0) FX_PDL_WAIT_LAUNCH(); // PDL: the previous grid (GDS) has completed; before any grad-bank read
|
|
783
|
+
const int batch = min(LPR, a.per_group - jb);
|
|
784
|
+
// STEP0 computes entries batch .. ceil(batch / STEP0) * STEP0 - 1 as padding (zero rows, zero weight)
|
|
785
|
+
// and zero-fills the rest; a smaller STEP pads to the same zlim, so acc and dbuf see STEP0's operations exactly.
|
|
786
|
+
const int zlim = (batch + STEP0 - 1) / STEP0 * STEP0;
|
|
787
|
+
for (int s0 = 0; s0 < LPR; s0 += STEP) {
|
|
788
|
+
if (s0 >= zlim) {
|
|
789
|
+
#pragma unroll
|
|
790
|
+
for (int s = 0; s < STEP; ++s) dbuf[(s0 + s) * PADW + lane] = 0.f;
|
|
791
|
+
} else {
|
|
792
|
+
const V* src[STEP];
|
|
793
|
+
bool load[STEP];
|
|
794
|
+
float w[STEP];
|
|
795
|
+
#pragma unroll
|
|
796
|
+
for (int s = 0; s < STEP; ++s) {
|
|
797
|
+
const int n = group + groups * (jb + s0 + s);
|
|
798
|
+
const int slot = __shfl_sync(kFull, slot_l, (s0 + s) & (LPR - 1), LPR);
|
|
799
|
+
w[s] = __shfl_sync(kFull, w_l, (s0 + s) & (LPR - 1), LPR);
|
|
800
|
+
src[s] = reinterpret_cast<const V*>(bank + int64_t(slot) * a.hd + col);
|
|
801
|
+
load[s] = live && lane_on && s0 + s < batch && n < a.width;
|
|
802
|
+
}
|
|
803
|
+
V r[STEP];
|
|
804
|
+
#pragma unroll
|
|
805
|
+
for (int s = 0; s < STEP; ++s) r[s] = load[s] ? FX_LDG_ROW(src[s]) : V{};
|
|
806
|
+
if (a.kpos != nullptr) { // gdwc: the raw grad row of each valid group FIRST, dense by ordinal
|
|
807
|
+
#pragma unroll
|
|
808
|
+
for (int s = 0; s < STEP; ++s) {
|
|
809
|
+
const int kj = __shfl_sync(kFull, kp_l, (s0 + s) & (LPR - 1), LPR);
|
|
810
|
+
if (kj >= 0 && load[s])
|
|
811
|
+
*reinterpret_cast<V*>(static_cast<E*>(a.gdwc) +
|
|
812
|
+
((row / a.chunk - a.row0 / a.chunk) * a.crow + kj) *
|
|
813
|
+
static_cast<int64_t>(a.hd) + col) = r[s];
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
#pragma unroll
|
|
817
|
+
for (int s = 0; s < STEP; ++s) {
|
|
818
|
+
const E* el = reinterpret_cast<const E*>(&r[s]);
|
|
819
|
+
#pragma unroll
|
|
820
|
+
for (int i = 0; i < VEC; ++i) acc[i] = fmaf(w[s], to_float(el[i]), acc[i]);
|
|
821
|
+
float pdp = __fmul_rn(dvr[0], to_float(el[0]));
|
|
822
|
+
#pragma unroll
|
|
823
|
+
for (int i = 1; i < VEC; ++i) pdp = __fmaf_rn(dvr[i], to_float(el[i]), pdp);
|
|
824
|
+
dbuf[(s0 + s) * PADW + lane] = pdp;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
__syncwarp();
|
|
829
|
+
// Lane l sums entry l's LPR partials pairwise in lane-index order: the xor tree of tr_push/tr_full.
|
|
830
|
+
float tv[LPR];
|
|
831
|
+
#pragma unroll
|
|
832
|
+
for (int j = 0; j < LPR; j += 4) {
|
|
833
|
+
const float4 q = *reinterpret_cast<const float4*>(dbuf + lane * PADW + j);
|
|
834
|
+
tv[j] = q.x; tv[j + 1] = q.y; tv[j + 2] = q.z; tv[j + 3] = q.w;
|
|
835
|
+
}
|
|
836
|
+
#pragma unroll
|
|
837
|
+
for (int wdt = LPR / 2; wdt >= 1; wdt >>= 1) {
|
|
838
|
+
#pragma unroll
|
|
839
|
+
for (int j = 0; j < wdt; ++j) tv[j] = __fadd_rn(tv[2 * j], tv[2 * j + 1]);
|
|
840
|
+
}
|
|
841
|
+
const float dot_mine = tv[0];
|
|
842
|
+
__syncwarp();
|
|
843
|
+
if (in_range && okl) pd[il] = dot_mine;
|
|
844
|
+
else if (in_range && jb + lane < a.per_group && nl < a.width) pd[il] = 0.0f;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
FX_PDL_WAIT_LAUNCH(); // PDL: also when no entry loop ran (no store, no exit before the wait)
|
|
848
|
+
if (groups == 1) {
|
|
849
|
+
if (in_range && lane_on) {
|
|
850
|
+
#pragma unroll
|
|
851
|
+
for (int i = 0; i < VEC; i += (VEC % 4 == 0 ? 4 : 1)) {
|
|
852
|
+
if constexpr (VEC % 4 == 0) {
|
|
853
|
+
*reinterpret_cast<float4*>(a.out + (a.out_row0 + t) * a.hd + col + i) =
|
|
854
|
+
make_float4(acc[i], acc[i + 1], acc[i + 2], acc[i + 3]);
|
|
855
|
+
} else {
|
|
856
|
+
a.out[(a.out_row0 + t) * a.hd + col + i] = acc[i];
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
{
|
|
863
|
+
float* dst = partial + (threadIdx.z * groups + group) * SLICE;
|
|
864
|
+
#pragma unroll
|
|
865
|
+
for (int i = 0; i < VEC; ++i) dst[lane * VEC + i] = acc[i];
|
|
866
|
+
}
|
|
867
|
+
__syncthreads();
|
|
868
|
+
const int tid = lane + LPR * (group + groups * threadIdx.z);
|
|
869
|
+
const int width = min(SLICE, a.hd - int(blockIdx.y) * SLICE);
|
|
870
|
+
for (int q = tid; q < blockDim.z * width; q += LPR * groups * blockDim.z) {
|
|
871
|
+
const int z = q / width, c = q - z * width;
|
|
872
|
+
const int tok = blockIdx.x * blockDim.z + z;
|
|
873
|
+
if (tok >= a.tokens) continue;
|
|
874
|
+
const float* src = partial + z * groups * SLICE + c;
|
|
875
|
+
float sum = 0.0f;
|
|
876
|
+
for (int g = 0; g < groups; ++g) sum += src[g * SLICE];
|
|
877
|
+
a.out[(a.out_row0 + tok) * a.hd + blockIdx.y * SLICE + c] = sum;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
template <typename E, int VB, int LPR>
|
|
882
|
+
__global__ void __launch_bounds__(256, 4) dc_gwr_r64_kernel(const GArgs a) {
|
|
883
|
+
dc_gwr_body_cap<E, VB, LPR, DC_GWR_CAP_STEP>(a);
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
template <typename K>
|
|
887
|
+
void set_smem(K kernel, size_t bytes) {
|
|
888
|
+
static std::mutex mu;
|
|
889
|
+
static std::map<std::pair<int, const void*>, size_t> done;
|
|
890
|
+
std::lock_guard<std::mutex> g(mu);
|
|
891
|
+
int dev = 0;
|
|
892
|
+
C10_CUDA_CHECK(cudaGetDevice(&dev));
|
|
893
|
+
auto key = std::make_pair(dev, reinterpret_cast<const void*>(kernel));
|
|
894
|
+
auto it = done.find(key);
|
|
895
|
+
if (it != done.end() && it->second >= bytes) return;
|
|
896
|
+
C10_CUDA_CHECK(cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize,
|
|
897
|
+
static_cast<int>(bytes)));
|
|
898
|
+
done[key] = bytes;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
} // namespace
|
|
902
|
+
|
|
903
|
+
void dc_records_pa(at::Tensor kpay, at::Tensor k_idx, at::Tensor k_val, at::Tensor logk,
|
|
904
|
+
at::Tensor beta, at::Tensor cpe, at::Tensor rec) {
|
|
905
|
+
const at::cuda::CUDAGuard guard(kpay.device());
|
|
906
|
+
const int64_t N = kpay.size(0), n = kpay.size(1);
|
|
907
|
+
const int C = static_cast<int>(k_idx.size(1)), W = static_cast<int>(k_idx.size(2));
|
|
908
|
+
TORCH_CHECK(k_idx.size(0) == N && static_cast<int64_t>(C) * W == n, "dc_records_pa: shapes");
|
|
909
|
+
TORCH_CHECK(rec.numel() == N * n * 4 && rec.scalar_type() == at::kInt && rec.is_contiguous() &&
|
|
910
|
+
(reinterpret_cast<uintptr_t>(rec.data_ptr()) & 15) == 0, "dc_records_pa: rec");
|
|
911
|
+
for (const at::Tensor* t : {&k_idx, &k_val, &logk, &beta, &cpe, &rec})
|
|
912
|
+
TORCH_CHECK(t->device() == kpay.device(), "dc_records_pa: all operands on one device");
|
|
913
|
+
TORCH_CHECK(cpe.scalar_type() == k_val.scalar_type() &&
|
|
914
|
+
(k_val.scalar_type() == at::kBFloat16 || k_val.scalar_type() == at::kFloat),
|
|
915
|
+
"dc_records_pa: k_val / cpe bf16 or f32 (the same)");
|
|
916
|
+
TORCH_CHECK(logk.scalar_type() == at::kFloat && beta.scalar_type() == at::kFloat &&
|
|
917
|
+
(k_idx.scalar_type() == at::kInt || k_idx.scalar_type() == at::kLong),
|
|
918
|
+
"dc_records_pa: log_k / beta f32, k_idx int32/int64");
|
|
919
|
+
TORCH_CHECK(k_idx.is_contiguous() && k_val.is_contiguous() && logk.is_contiguous() && beta.is_contiguous() &&
|
|
920
|
+
cpe.is_contiguous() && kpay.is_contiguous() && kpay.scalar_type() == at::kInt,
|
|
921
|
+
"dc_records_pa: contiguous operands");
|
|
922
|
+
TORCH_CHECK(k_val.numel() == N * n && logk.numel() == N * n && cpe.numel() == N * n &&
|
|
923
|
+
beta.numel() == N * C, "dc_records_pa: operand sizes");
|
|
924
|
+
const int64_t total = N * n;
|
|
925
|
+
if (total == 0) return;
|
|
926
|
+
auto stream = at::cuda::getCurrentCUDAStream();
|
|
927
|
+
const int blocks = static_cast<int>((total + 255) / 256);
|
|
928
|
+
#define DC_RECA(IDX, VAL) \
|
|
929
|
+
dc_records_pa_kernel<IDX, VAL><<<blocks, 256, 0, stream>>>( \
|
|
930
|
+
kpay.data_ptr<int32_t>(), k_idx.data_ptr<IDX>(), \
|
|
931
|
+
reinterpret_cast<const VAL*>(k_val.data_ptr()), logk.data_ptr<float>(), \
|
|
932
|
+
beta.data_ptr<float>(), reinterpret_cast<const VAL*>(cpe.data_ptr()), \
|
|
933
|
+
reinterpret_cast<int4*>(rec.data_ptr<int32_t>()), total, static_cast<int>(n), C, W)
|
|
934
|
+
const bool i64 = k_idx.scalar_type() == at::kLong;
|
|
935
|
+
const bool bf = k_val.scalar_type() == at::kBFloat16;
|
|
936
|
+
if (i64 && bf) { DC_RECA(int64_t, __nv_bfloat16); }
|
|
937
|
+
else if (i64) { DC_RECA(int64_t, float); }
|
|
938
|
+
else if (bf) { DC_RECA(int32_t, __nv_bfloat16); }
|
|
939
|
+
else { DC_RECA(int32_t, float); }
|
|
940
|
+
#undef DC_RECA
|
|
941
|
+
C10_CUDA_KERNEL_LAUNCH_CHECK();
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
// Raw device copy of a contiguous tensor into a tensor of the same shape and any strides (no autograd version bump
|
|
945
|
+
// on dst): the aligned-scratch bank restore of the backward writes the caller's saved bank (which a saved-tensor
|
|
946
|
+
// unpack hook may hand over as a strided view), like the other raw kernels.
|
|
947
|
+
struct StridedDst {
|
|
948
|
+
int64_t size[6];
|
|
949
|
+
int64_t stride[6];
|
|
950
|
+
int nd;
|
|
951
|
+
};
|
|
952
|
+
template <typename T>
|
|
953
|
+
__global__ void __launch_bounds__(256) dc_copy_strided_kernel(T* __restrict__ dst, const T* __restrict__ src,
|
|
954
|
+
int64_t n, StridedDst g) {
|
|
955
|
+
for (int64_t i = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x; i < n;
|
|
956
|
+
i += static_cast<int64_t>(gridDim.x) * blockDim.x) {
|
|
957
|
+
int64_t rem = i, off = 0;
|
|
958
|
+
for (int d = g.nd - 1; d >= 0; --d) {
|
|
959
|
+
const int64_t q = rem / g.size[d];
|
|
960
|
+
off += (rem - q * g.size[d]) * g.stride[d];
|
|
961
|
+
rem = q;
|
|
962
|
+
}
|
|
963
|
+
dst[off] = src[i];
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
// Self-overlap of a strided view (some two elements share an address). Exact for views with at most 2 dimensions of
|
|
968
|
+
// size > 1 (the bank is 2-D): with strides a <= b and sizes na, nb, elements collide iff b/g <= na-1 and a/g <= nb-1,
|
|
969
|
+
// g = gcd(a, b); any size > 1 dimension of stride 0 collides. Higher ranks: non-overlap is accepted only when proven
|
|
970
|
+
// by the sorted-extent test (each stride exceeds the reach of the smaller ones); otherwise reported as overlapping.
|
|
971
|
+
static bool view_overlaps(const at::Tensor& t) {
|
|
972
|
+
std::vector<std::pair<int64_t, int64_t>> d; // (stride, size) of the dimensions of size > 1
|
|
973
|
+
for (int64_t k = 0; k < t.dim(); ++k) {
|
|
974
|
+
if (t.size(k) > 1) {
|
|
975
|
+
if (t.stride(k) == 0) return true;
|
|
976
|
+
d.emplace_back(t.stride(k) < 0 ? -t.stride(k) : t.stride(k), t.size(k));
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
if (d.size() <= 1) return false;
|
|
980
|
+
std::sort(d.begin(), d.end());
|
|
981
|
+
if (d.size() == 2) {
|
|
982
|
+
const int64_t a = d[0].first, na = d[0].second, b = d[1].first, nb = d[1].second;
|
|
983
|
+
const int64_t g = std::gcd(a, b);
|
|
984
|
+
return (b / g) <= na - 1 && (a / g) <= nb - 1;
|
|
985
|
+
}
|
|
986
|
+
int64_t reach = 0;
|
|
987
|
+
for (const auto& sn : d) {
|
|
988
|
+
if (sn.first <= reach) return true;
|
|
989
|
+
reach += sn.first * (sn.second - 1);
|
|
990
|
+
}
|
|
991
|
+
return false;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
bool dc_overlaps(at::Tensor t) { return !t.is_contiguous() && view_overlaps(t); }
|
|
995
|
+
|
|
996
|
+
void dc_copy_raw(at::Tensor dst, at::Tensor src) {
|
|
997
|
+
const at::cuda::CUDAGuard guard(dst.device());
|
|
998
|
+
TORCH_CHECK(dst.is_cuda() && src.device() == dst.device() && dst.scalar_type() == src.scalar_type() &&
|
|
999
|
+
dst.sizes() == src.sizes() && src.is_contiguous(),
|
|
1000
|
+
"dc_copy_raw: same device, dtype and shape; a contiguous source");
|
|
1001
|
+
if (dst.numel() == 0) return;
|
|
1002
|
+
auto stream = at::cuda::getCurrentCUDAStream();
|
|
1003
|
+
if (dst.is_contiguous()) {
|
|
1004
|
+
C10_CUDA_CHECK(cudaMemcpyAsync(dst.data_ptr(), src.data_ptr(), dst.nbytes(), cudaMemcpyDeviceToDevice,
|
|
1005
|
+
stream));
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
TORCH_CHECK(!view_overlaps(dst),
|
|
1009
|
+
"dc_copy_raw: some elements of the destination refer to a single memory location (a self-overlapping "
|
|
1010
|
+
"view cannot hold a bank)");
|
|
1011
|
+
TORCH_CHECK(dst.dim() <= 6, "dc_copy_raw: at most 6 dimensions");
|
|
1012
|
+
StridedDst g{};
|
|
1013
|
+
g.nd = static_cast<int>(dst.dim());
|
|
1014
|
+
for (int d = 0; d < g.nd; ++d) {
|
|
1015
|
+
g.size[d] = dst.size(d);
|
|
1016
|
+
g.stride[d] = dst.stride(d);
|
|
1017
|
+
}
|
|
1018
|
+
const int64_t n = dst.numel();
|
|
1019
|
+
const unsigned blocks = static_cast<unsigned>(std::min<int64_t>((n + 255) / 256, int64_t(1) << 20));
|
|
1020
|
+
if (dst.element_size() == 2) {
|
|
1021
|
+
dc_copy_strided_kernel<uint16_t><<<blocks, 256, 0, stream>>>(
|
|
1022
|
+
static_cast<uint16_t*>(dst.data_ptr()), static_cast<const uint16_t*>(src.data_ptr()), n, g);
|
|
1023
|
+
} else if (dst.element_size() == 4) {
|
|
1024
|
+
dc_copy_strided_kernel<uint32_t><<<blocks, 256, 0, stream>>>(
|
|
1025
|
+
static_cast<uint32_t*>(dst.data_ptr()), static_cast<const uint32_t*>(src.data_ptr()), n, g);
|
|
1026
|
+
} else {
|
|
1027
|
+
TORCH_CHECK(false, "dc_copy_raw: 2- or 4-byte elements");
|
|
1028
|
+
}
|
|
1029
|
+
C10_CUDA_KERNEL_LAUNCH_CHECK();
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
// dbg bit 1 << 18 (host-only, never passed to the kernel's flags): the WIDE walk (64-bit snapshot rows) at any size.
|
|
1033
|
+
constexpr int64_t kDcWide = int64_t(1) << 18;
|
|
1034
|
+
|
|
1035
|
+
void dc_pa(at::Tensor grad, at::Tensor bank, at::Tensor snap, int64_t snap_step_rows,
|
|
1036
|
+
at::Tensor dva, at::Tensor beta, at::Tensor kpay, at::Tensor rec, at::Tensor out_eff,
|
|
1037
|
+
at::Tensor out_g, int64_t ti, int64_t P, int64_t C, int64_t W, int64_t L, int64_t nw,
|
|
1038
|
+
int64_t depth, int64_t dbg, bool pipe, at::Tensor bpre, at::Tensor gdwc) {
|
|
1039
|
+
const at::cuda::CUDAGuard guard(bank.device());
|
|
1040
|
+
const bool dense = (dbg & 1024) != 0;
|
|
1041
|
+
const bool gdc = (dbg & 32768) != 0;
|
|
1042
|
+
TORCH_CHECK(!gdc || ((dbg & 512) && depth == 8 && gdwc.scalar_type() == grad.scalar_type() &&
|
|
1043
|
+
gdwc.is_contiguous() && gdwc.device() == bank.device() &&
|
|
1044
|
+
gdwc.numel() >= P * kpay.size(1) * bank.size(1) &&
|
|
1045
|
+
bpre.scalar_type() == at::kInt && bpre.is_contiguous() &&
|
|
1046
|
+
bpre.device() == bank.device() &&
|
|
1047
|
+
bpre.numel() == kpay.size(0) * ((kpay.size(1) + 31) / 32 + 1) &&
|
|
1048
|
+
(reinterpret_cast<uintptr_t>(gdwc.data_ptr()) % 16) == 0),
|
|
1049
|
+
"dc_pa: gdwc needs the dense walk, depth 8, a [P * C * W, hd] grad-dtype buffer and bpre");
|
|
1050
|
+
(void)pipe;
|
|
1051
|
+
TORCH_CHECK((dbg & 512) && !(dbg & (2048 | 4096 | 131072)) && depth == 8,
|
|
1052
|
+
"dc_pa: only the dense walk is built (dbg bit 512, depth 8)");
|
|
1053
|
+
TORCH_CHECK(ti >= 0 && P >= 1 && C >= 1 && W >= 1, "dc_pa: ti >= 0, P, C, W >= 1");
|
|
1054
|
+
TORCH_CHECK(!dense || ((dbg & 256) && (dbg & 512) && depth == 8 && bpre.scalar_type() == at::kInt &&
|
|
1055
|
+
bpre.is_contiguous() &&
|
|
1056
|
+
bpre.numel() == kpay.size(0) * ((kpay.size(1) + 31) / 32 + 1) &&
|
|
1057
|
+
bpre.device() == bank.device()),
|
|
1058
|
+
"dc_pa: the dense compact snapshot needs the dense walk, the compact flag and bpre [N, nb + 1]");
|
|
1059
|
+
const int hd = static_cast<int>(bank.size(1));
|
|
1060
|
+
const int n = static_cast<int>(kpay.size(1));
|
|
1061
|
+
TORCH_CHECK(hd % 4 == 0, "dc_pa: head_dim must be a multiple of 4, got ", hd);
|
|
1062
|
+
TORCH_CHECK(C <= 256 && n == C * W, "dc_pa: C <= 256 and kpay [N, C * W]");
|
|
1063
|
+
TORCH_CHECK(dva.scalar_type() == at::kFloat && dva.is_contiguous(), "dc_pa: d_dva f32");
|
|
1064
|
+
TORCH_CHECK(snap.scalar_type() == bank.scalar_type(), "dc_pa: snapshot dtype must be the bank's");
|
|
1065
|
+
const bool g32 = grad.scalar_type() == at::kFloat && bank.scalar_type() == at::kBFloat16;
|
|
1066
|
+
TORCH_CHECK(grad.scalar_type() == bank.scalar_type() || (g32 && (dbg & 512)),
|
|
1067
|
+
"dc_pa: grad must have the bank dtype (or f32 with a bf16 bank)");
|
|
1068
|
+
TORCH_CHECK(rec.scalar_type() == at::kInt && rec.numel() == kpay.numel() * 4, "dc_pa: rec");
|
|
1069
|
+
TORCH_CHECK(L > 0 && nw > 0 && nw <= 32, "dc_pa: L > 0 and 0 < nw <= 32");
|
|
1070
|
+
// Snapshots of 2^31 rows or more take the WIDE instantiation (chunk-local int32 snapshot rows from a 64-bit block
|
|
1071
|
+
// base); so does any size with the host-only dbg bit kDcWide (tests: the same bits as the 32-bit walk). Grad / bank
|
|
1072
|
+
// rows (the records' slots) stay 31-bit.
|
|
1073
|
+
const bool wide = (dbg & kDcWide) != 0 || snap.numel() / hd >= (int64_t(1) << 31);
|
|
1074
|
+
TORCH_CHECK(grad.size(0) < (int64_t(1) << 31), "dc_pa: row indices must fit 31 bits");
|
|
1075
|
+
TORCH_CHECK(grad.is_contiguous() && bank.is_contiguous() && snap.is_contiguous() &&
|
|
1076
|
+
kpay.is_contiguous() && rec.is_contiguous() && beta.is_contiguous() &&
|
|
1077
|
+
out_eff.is_contiguous() && out_g.is_contiguous(), "dc_pa: contiguous operands");
|
|
1078
|
+
const int64_t ralign = bank.scalar_type() == at::kBFloat16 ? 8 : 16;
|
|
1079
|
+
TORCH_CHECK(reinterpret_cast<uintptr_t>(grad.data_ptr()) % (g32 ? 16 : ralign) == 0 &&
|
|
1080
|
+
reinterpret_cast<uintptr_t>(bank.data_ptr()) % ralign == 0 &&
|
|
1081
|
+
reinterpret_cast<uintptr_t>(snap.data_ptr()) % ralign == 0 &&
|
|
1082
|
+
reinterpret_cast<uintptr_t>(dva.data_ptr()) % 16 == 0,
|
|
1083
|
+
"dc_pa: row operands must be aligned to their vector size");
|
|
1084
|
+
const int nsl = (hd + 127) / 128;
|
|
1085
|
+
const int slw = 4 * (((hd / 4) + nsl - 1) / nsl);
|
|
1086
|
+
TORCH_CHECK(L >= 1 && L <= n && nw >= 1 && nw <= 32, "dc_pa: need 1 <= L <= C*W and 1 <= nw <= 32");
|
|
1087
|
+
const int64_t tiles64 = (static_cast<int64_t>(n) + L - 1) / L;
|
|
1088
|
+
const int64_t ctas64 = (tiles64 + nw - 1) / nw;
|
|
1089
|
+
TORCH_CHECK(ctas64 * nw * L < (int64_t(1) << 31) && P * nsl * ctas64 < (int64_t(1) << 31),
|
|
1090
|
+
"dc_pa: tile geometry overflows 32 bits");
|
|
1091
|
+
const int ctas = static_cast<int>(ctas64);
|
|
1092
|
+
const int blocks = static_cast<int>(P * nsl * ctas64);
|
|
1093
|
+
const int threads = static_cast<int>(nw) * 32;
|
|
1094
|
+
TORCH_CHECK(threads <= DC_THREADS, "dc_pa: at most ", DC_THREADS, " threads per CTA");
|
|
1095
|
+
TORCH_CHECK(kpay.size(0) >= (ti + 1) * P && dva.numel() >= (ti + 1) * P * C * hd &&
|
|
1096
|
+
beta.numel() >= (ti + 1) * P * C, "dc_pa: step ", ti, " out of range");
|
|
1097
|
+
TORCH_CHECK((reinterpret_cast<uintptr_t>(rec.data_ptr()) & 15) == 0, "dc_pa: records 16-byte aligned");
|
|
1098
|
+
TORCH_CHECK(grad.dim() == 2 && bank.dim() == 2 && grad.size(0) == bank.size(0) && grad.size(1) == hd,
|
|
1099
|
+
"dc_pa: grad and bank [slots, hd]");
|
|
1100
|
+
TORCH_CHECK(snap_step_rows >= 0 && snap.numel() % hd == 0, "dc_pa: snapshot rows");
|
|
1101
|
+
{
|
|
1102
|
+
const int64_t need_rows = (dbg & 256) ? (ti + 1) * P * n : ti * snap_step_rows + P * C * W;
|
|
1103
|
+
TORCH_CHECK(need_rows * hd <= snap.numel(), "dc_pa: the snapshot does not cover step ", ti);
|
|
1104
|
+
const int64_t planes = nsl == 1 ? 1 : nsl;
|
|
1105
|
+
TORCH_CHECK(out_eff.numel() == out_g.numel() && out_eff.numel() % planes == 0 &&
|
|
1106
|
+
out_eff.numel() / planes >= (ti + 1) * P * n && out_eff.scalar_type() == at::kFloat &&
|
|
1107
|
+
out_g.scalar_type() == at::kFloat, "dc_pa: output planes do not cover step ", ti);
|
|
1108
|
+
TORCH_CHECK(rec.numel() >= (ti + 1) * P * n * 4, "dc_pa: records do not cover step ", ti);
|
|
1109
|
+
}
|
|
1110
|
+
for (const at::Tensor* t : {&grad, &snap, &dva, &beta, &kpay, &rec, &out_eff, &out_g})
|
|
1111
|
+
TORCH_CHECK(t->device() == bank.device(), "dc_pa: all operands on the bank's device");
|
|
1112
|
+
const int64_t plane = out_eff.numel() / (nsl == 1 ? 1 : nsl);
|
|
1113
|
+
auto stream = at::cuda::getCurrentCUDAStream();
|
|
1114
|
+
const bool bf = bank.scalar_type() == at::kBFloat16;
|
|
1115
|
+
TORCH_CHECK(bf || bank.scalar_type() == at::kFloat, "dc_pa: bank must be bf16 or f32");
|
|
1116
|
+
const size_t head = static_cast<size_t>(C) * 32 * sizeof(float4) + ((C + 3) & ~3) * sizeof(float);
|
|
1117
|
+
int optin = 0;
|
|
1118
|
+
C10_CUDA_CHECK(cudaDeviceGetAttribute(&optin, cudaDevAttrMaxSharedMemoryPerBlockOptin,
|
|
1119
|
+
bank.device().index()));
|
|
1120
|
+
#define DC_PL3(EG, EB, DD) \
|
|
1121
|
+
{ \
|
|
1122
|
+
auto k3 = wide ? &dc_pa3_kernel<EG, EB, DD, false, false, false, true> \
|
|
1123
|
+
: &dc_pa3_kernel<EG, EB, DD, false, false, false, false>; \
|
|
1124
|
+
const size_t smem3 = head + static_cast<size_t>(nw) * (DD) * 32 * \
|
|
1125
|
+
(sizeof(RowOf<EG>::T) + sizeof(RowOf<EB>::T)); \
|
|
1126
|
+
TORCH_CHECK(smem3 <= static_cast<size_t>(optin), "dc_pa: ", smem3, \
|
|
1127
|
+
" bytes of shared memory exceed the device opt-in limit ", optin); \
|
|
1128
|
+
set_smem(k3, smem3); \
|
|
1129
|
+
if (sdm_plancap::take(k3, dim3(blocks), dim3(threads), smem3, /* plan capture */ \
|
|
1130
|
+
reinterpret_cast<EG*>(grad.data_ptr()), reinterpret_cast<EB*>(bank.data_ptr()), \
|
|
1131
|
+
reinterpret_cast<const EB*>(snap.data_ptr()), snap_step_rows, \
|
|
1132
|
+
dva.data_ptr<float>(), beta.data_ptr<float>(), kpay.data_ptr<int32_t>(), \
|
|
1133
|
+
reinterpret_cast<const int4*>(rec.data_ptr<int32_t>()), \
|
|
1134
|
+
out_eff.data_ptr<float>(), out_g.data_ptr<float>(), plane, \
|
|
1135
|
+
static_cast<int>(ti), static_cast<int>(P), static_cast<int>(C), \
|
|
1136
|
+
static_cast<int>(W), n, hd, nsl, slw, ctas, static_cast<int>(L), \
|
|
1137
|
+
static_cast<int>(dbg & (256 | 1024 | 8192 | 16384 | 32768 | 65536)), \
|
|
1138
|
+
(dense || gdc) ? bpre.data_ptr<int32_t>() : nullptr, \
|
|
1139
|
+
gdc ? reinterpret_cast<const EG*>(gdwc.data_ptr()) : nullptr)) { \
|
|
1140
|
+
return; \
|
|
1141
|
+
} \
|
|
1142
|
+
k3<<<blocks, threads, smem3, stream>>>( \
|
|
1143
|
+
reinterpret_cast<EG*>(grad.data_ptr()), reinterpret_cast<EB*>(bank.data_ptr()), \
|
|
1144
|
+
reinterpret_cast<const EB*>(snap.data_ptr()), snap_step_rows, \
|
|
1145
|
+
dva.data_ptr<float>(), beta.data_ptr<float>(), kpay.data_ptr<int32_t>(), \
|
|
1146
|
+
reinterpret_cast<const int4*>(rec.data_ptr<int32_t>()), \
|
|
1147
|
+
out_eff.data_ptr<float>(), out_g.data_ptr<float>(), plane, \
|
|
1148
|
+
static_cast<int>(ti), static_cast<int>(P), static_cast<int>(C), \
|
|
1149
|
+
static_cast<int>(W), n, hd, nsl, slw, ctas, static_cast<int>(L), \
|
|
1150
|
+
static_cast<int>(dbg & (256 | 1024 | 8192 | 16384 | 32768 | 65536)), \
|
|
1151
|
+
(dense || gdc) ? bpre.data_ptr<int32_t>() : nullptr, \
|
|
1152
|
+
gdc ? reinterpret_cast<const EG*>(gdwc.data_ptr()) : nullptr); \
|
|
1153
|
+
}
|
|
1154
|
+
if (g32) {
|
|
1155
|
+
DC_PL3(float, __nv_bfloat16, 8)
|
|
1156
|
+
} else if (bf) {
|
|
1157
|
+
DC_PL3(__nv_bfloat16, __nv_bfloat16, 8)
|
|
1158
|
+
} else {
|
|
1159
|
+
DC_PL3(float, float, 8)
|
|
1160
|
+
}
|
|
1161
|
+
#undef DC_PL3
|
|
1162
|
+
C10_CUDA_KERNEL_LAUNCH_CHECK();
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
// ---- the backward's C++ step loop (bwd_steps_cuda): dc_pa (Phase A, dc_pa3) as a per-backward plan --------------------
|
|
1166
|
+
// dc_pa_plan runs dc_pa ONCE under plan capture at step ti_max: every check runs (its step bounds grow with ti, so they hold
|
|
1167
|
+
// for every ti <= ti_max) and its one launch is recorded. dc_pa_plan_launch (a plain C++ entry point; address from
|
|
1168
|
+
// dc_pa_launch_addr) sets the step parameter ti and issues the recorded launch plain, as dc_pa launches (no PDL variant;
|
|
1169
|
+
// its pdl argument must be 0). The plan holds raw pointers: the caller keeps the operands alive while it is used.
|
|
1170
|
+
namespace {
|
|
1171
|
+
constexpr uint64_t kPaPlanMagic = 0x314e414c50334150ull;
|
|
1172
|
+
constexpr int kPaTiParam = 11; // (grad, bank, snap, snap_step_rows, dva, beta, kpay, rec, out_eff, out_g, plane, ti, ...)
|
|
1173
|
+
struct PaPlan {
|
|
1174
|
+
uint64_t magic;
|
|
1175
|
+
int32_t device;
|
|
1176
|
+
int32_t pad;
|
|
1177
|
+
int64_t ti_max;
|
|
1178
|
+
sdm_plancap::Launch cap;
|
|
1179
|
+
};
|
|
1180
|
+
|
|
1181
|
+
int64_t dc_pa_plan_launch(const void* plan_bytes, int64_t nbytes, int64_t ti, int64_t pdl, void* stream) {
|
|
1182
|
+
PaPlan pl;
|
|
1183
|
+
if (!sdm_plancap::from_bytes(pl, plan_bytes, nbytes, kPaPlanMagic)) return -1;
|
|
1184
|
+
if (pdl != 0) return -4;
|
|
1185
|
+
if (ti < 0 || ti > pl.ti_max) return -2;
|
|
1186
|
+
if (pl.cap.set == 0) return 0; // dc_pa launched nothing
|
|
1187
|
+
if (sdm_plancap::current_device() != pl.device) return -5;
|
|
1188
|
+
int* tip = sdm_plancap::param<int>(pl.cap, kPaTiParam);
|
|
1189
|
+
if (tip == nullptr) return -7;
|
|
1190
|
+
*tip = static_cast<int>(ti);
|
|
1191
|
+
return sdm_plancap::launch(pl.cap, false, static_cast<cudaStream_t>(stream));
|
|
1192
|
+
}
|
|
1193
|
+
} // namespace
|
|
1194
|
+
|
|
1195
|
+
at::Tensor dc_pa_plan(at::Tensor grad, at::Tensor bank, at::Tensor snap, int64_t snap_step_rows,
|
|
1196
|
+
at::Tensor dva, at::Tensor beta, at::Tensor kpay, at::Tensor rec, at::Tensor out_eff,
|
|
1197
|
+
at::Tensor out_g, int64_t ti_max, int64_t P, int64_t C, int64_t W, int64_t L, int64_t nw,
|
|
1198
|
+
int64_t depth, int64_t dbg, bool pipe, at::Tensor bpre, at::Tensor gdwc) {
|
|
1199
|
+
TORCH_CHECK(ti_max >= 0 && ti_max < (int64_t(1) << 31), "dc_pa_plan: ti_max");
|
|
1200
|
+
PaPlan pl;
|
|
1201
|
+
std::memset(&pl, 0, sizeof(pl));
|
|
1202
|
+
{
|
|
1203
|
+
sdm_plancap::Scope scope(&pl.cap);
|
|
1204
|
+
dc_pa(grad, bank, snap, snap_step_rows, dva, beta, kpay, rec, out_eff, out_g, ti_max, P, C, W, L, nw, depth,
|
|
1205
|
+
dbg, pipe, bpre, gdwc);
|
|
1206
|
+
}
|
|
1207
|
+
pl.magic = kPaPlanMagic;
|
|
1208
|
+
pl.device = static_cast<int32_t>(bank.get_device());
|
|
1209
|
+
pl.ti_max = ti_max;
|
|
1210
|
+
TORCH_CHECK(pl.cap.set == 0 || (pl.cap.nparams == 24 && sdm_plancap::param<int>(pl.cap, kPaTiParam) != nullptr),
|
|
1211
|
+
"dc_pa_plan: parameters");
|
|
1212
|
+
return sdm_plancap::to_tensor(pl);
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
int64_t dc_pa_launch_addr() { return reinterpret_cast<int64_t>(&dc_pa_plan_launch); }
|
|
1216
|
+
|
|
1217
|
+
void dc_pa_reduce(at::Tensor part_eff, at::Tensor part_g, at::Tensor part_pd, at::Tensor beta,
|
|
1218
|
+
at::Tensor cpe, at::Tensor d_k_eff, at::Tensor grad_g, at::Tensor d_k_pd,
|
|
1219
|
+
int64_t P, int64_t C, int64_t W, int64_t T) {
|
|
1220
|
+
const at::cuda::CUDAGuard guard(d_k_eff.device());
|
|
1221
|
+
const int64_t total = d_k_eff.numel();
|
|
1222
|
+
if (total == 0) return;
|
|
1223
|
+
const int ne = static_cast<int>(part_eff.numel() / total);
|
|
1224
|
+
const int np = static_cast<int>(part_pd.numel() / total);
|
|
1225
|
+
TORCH_CHECK(part_eff.numel() == ne * total && part_g.numel() == part_eff.numel() &&
|
|
1226
|
+
part_pd.numel() == np * total && grad_g.numel() == total && d_k_pd.numel() == total &&
|
|
1227
|
+
beta.numel() * W >= total && cpe.numel() >= total && P >= 1 && C >= 1 && W >= 1 &&
|
|
1228
|
+
total % (C * W) == 0, "dc_pa_reduce: operand sizes / geometry");
|
|
1229
|
+
for (const at::Tensor* t : {&part_eff, &part_g, &part_pd, &beta, &d_k_eff, &grad_g, &d_k_pd})
|
|
1230
|
+
TORCH_CHECK(t->scalar_type() == at::kFloat && t->is_contiguous() && t->device() == d_k_eff.device(),
|
|
1231
|
+
"dc_pa_reduce: f32 contiguous operands on one device");
|
|
1232
|
+
TORCH_CHECK((cpe.scalar_type() == at::kFloat || cpe.scalar_type() == at::kBFloat16) && cpe.is_contiguous() &&
|
|
1233
|
+
cpe.device() == d_k_eff.device(), "dc_pa_reduce: cpe");
|
|
1234
|
+
auto stream = at::cuda::getCurrentCUDAStream();
|
|
1235
|
+
const int blocks = static_cast<int>((total + 255) / 256);
|
|
1236
|
+
#define DC_RED(VAL) \
|
|
1237
|
+
dc_pa_reduce_kernel<VAL><<<blocks, 256, 0, stream>>>( \
|
|
1238
|
+
part_eff.data_ptr<float>(), part_g.data_ptr<float>(), part_pd.data_ptr<float>(), \
|
|
1239
|
+
beta.data_ptr<float>(), reinterpret_cast<const VAL*>(cpe.data_ptr()), \
|
|
1240
|
+
d_k_eff.data_ptr<float>(), grad_g.data_ptr<float>(), d_k_pd.data_ptr<float>(), total, \
|
|
1241
|
+
ne, ne, np, static_cast<int>(P), static_cast<int>(C), static_cast<int>(W), T)
|
|
1242
|
+
if (cpe.scalar_type() == at::kBFloat16) { DC_RED(__nv_bfloat16); } else { DC_RED(float); }
|
|
1243
|
+
#undef DC_RED
|
|
1244
|
+
C10_CUDA_KERNEL_LAUNCH_CHECK();
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
void dc_gwr(at::Tensor bank, at::Tensor idx, at::Tensor w, at::Tensor out, at::Tensor dv,
|
|
1248
|
+
at::Tensor pd, int64_t row0, int64_t out_row0, int64_t tokens, int64_t chunk,
|
|
1249
|
+
int64_t valid, int64_t vec_bytes, int64_t lanes, int64_t groups, int64_t tpc,
|
|
1250
|
+
at::Tensor kpos, at::Tensor gdwc, int64_t pdl) {
|
|
1251
|
+
const at::cuda::CUDAGuard guard(bank.device());
|
|
1252
|
+
TORCH_CHECK(pdl == 0 || pdl == 1, "dc_gwr: pdl must be 0 or 1");
|
|
1253
|
+
const int hd = static_cast<int>(bank.size(1));
|
|
1254
|
+
const int es = static_cast<int>(bank.element_size());
|
|
1255
|
+
TORCH_CHECK(out.scalar_type() == at::kFloat && dv.scalar_type() == at::kFloat &&
|
|
1256
|
+
pd.scalar_type() == at::kFloat, "dc_gwr: out, delta_v, d_k_pd f32");
|
|
1257
|
+
TORCH_CHECK(idx.dim() == 2 && w.sizes() == idx.sizes(), "dc_gwr: idx/w [rows, width]");
|
|
1258
|
+
TORCH_CHECK(idx.scalar_type() == at::kInt || idx.scalar_type() == at::kLong, "dc_gwr: idx int32/int64");
|
|
1259
|
+
TORCH_CHECK(w.scalar_type() == at::kFloat || w.scalar_type() == at::kBFloat16, "dc_gwr: weights f32/bf16");
|
|
1260
|
+
TORCH_CHECK(out.scalar_type() == at::kFloat && dv.scalar_type() == at::kFloat && pd.scalar_type() == at::kFloat,
|
|
1261
|
+
"dc_gwr: f32 outputs and delta_v");
|
|
1262
|
+
TORCH_CHECK(chunk >= 1 && tokens >= 0 && tokens % chunk == 0 && valid >= 0 && valid <= chunk,
|
|
1263
|
+
"dc_gwr: need tokens = k * chunk and 0 <= valid <= chunk");
|
|
1264
|
+
{
|
|
1265
|
+
const auto addr = [](const at::Tensor& x) { return reinterpret_cast<uintptr_t>(x.data_ptr()); };
|
|
1266
|
+
TORCH_CHECK(vec_bytes != 16 || (addr(bank) % 16 == 0 && addr(out) % 16 == 0 && addr(dv) % 4 == 0),
|
|
1267
|
+
"dc_gwr: 16-byte vectors need 16-byte aligned bank and output");
|
|
1268
|
+
TORCH_CHECK(!(addr(out) < addr(bank) + bank.nbytes() && addr(bank) < addr(out) + out.nbytes()) &&
|
|
1269
|
+
!(addr(pd) < addr(bank) + bank.nbytes() && addr(bank) < addr(pd) + pd.nbytes()),
|
|
1270
|
+
"dc_gwr: the outputs must not overlap the bank");
|
|
1271
|
+
}
|
|
1272
|
+
TORCH_CHECK(vec_bytes == 16 || vec_bytes == es, "dc_gwr: vec_bytes must be 16 or the element size");
|
|
1273
|
+
TORCH_CHECK(vec_bytes != 16 || (hd * es) % 16 == 0, "dc_gwr: 16-byte vectors need hd*es % 16 == 0");
|
|
1274
|
+
TORCH_CHECK(lanes == 8 || lanes == 16 || lanes == 32, "dc_gwr: lanes must be 8, 16 or 32");
|
|
1275
|
+
TORCH_CHECK(vec_bytes == 16 || lanes == 32, "dc_gwr: element vectors use 32 lanes");
|
|
1276
|
+
TORCH_CHECK(groups >= 1 && tpc >= 1 && lanes * groups * tpc <= 512, "dc_gwr: block too large");
|
|
1277
|
+
TORCH_CHECK(bank.is_contiguous() && idx.is_contiguous() && w.is_contiguous() && out.is_contiguous() &&
|
|
1278
|
+
dv.is_contiguous() && pd.is_contiguous(), "dc_gwr: contiguous operands");
|
|
1279
|
+
TORCH_CHECK(row0 >= 0 && row0 + tokens <= idx.size(0) && out_row0 >= 0 &&
|
|
1280
|
+
(out_row0 + tokens) * bank.size(1) <= out.numel() &&
|
|
1281
|
+
(row0 + tokens) * bank.size(1) <= dv.numel(), "dc_gwr: rows out of range");
|
|
1282
|
+
for (const at::Tensor* t : {&idx, &w, &out, &dv, &pd})
|
|
1283
|
+
TORCH_CHECK(t->device() == bank.device(), "dc_gwr: all operands on the bank's device");
|
|
1284
|
+
if (tokens == 0) return;
|
|
1285
|
+
GArgs a;
|
|
1286
|
+
a.bank = bank.data_ptr(); a.idx = idx.data_ptr(); a.w = w.data_ptr();
|
|
1287
|
+
a.out = out.data_ptr<float>(); a.dv = dv.data_ptr<float>(); a.pd = pd.data_ptr<float>();
|
|
1288
|
+
a.row0 = row0; a.out_row0 = out_row0;
|
|
1289
|
+
a.hd = hd; a.vpr = static_cast<int>(hd * es / vec_bytes);
|
|
1290
|
+
a.tokens = static_cast<int>(tokens); a.chunk = static_cast<int>(chunk);
|
|
1291
|
+
a.valid = static_cast<int>(valid);
|
|
1292
|
+
a.width = static_cast<int>(idx.size(1));
|
|
1293
|
+
a.per_group = static_cast<int>((a.width + groups - 1) / groups);
|
|
1294
|
+
a.idx64 = idx.scalar_type() == at::kLong;
|
|
1295
|
+
a.w_bf16 = w.scalar_type() == at::kBFloat16;
|
|
1296
|
+
const int slices = static_cast<int>((a.vpr + lanes - 1) / lanes);
|
|
1297
|
+
a.plane = idx.numel();
|
|
1298
|
+
TORCH_CHECK(pd.numel() == a.plane * (slices == 1 ? 1 : slices), "dc_gwr: d_k_pd planes");
|
|
1299
|
+
a.kpos = nullptr; a.gdwc = nullptr; a.crow = 0;
|
|
1300
|
+
if (kpos.numel() > 0) {
|
|
1301
|
+
const int64_t crow = chunk * a.width;
|
|
1302
|
+
TORCH_CHECK(kpos.scalar_type() == at::kInt && kpos.is_contiguous() && kpos.numel() == idx.numel() &&
|
|
1303
|
+
kpos.device() == bank.device(), "dc_gwr: kpos int32 like idx");
|
|
1304
|
+
TORCH_CHECK(gdwc.scalar_type() == bank.scalar_type() && gdwc.is_contiguous() &&
|
|
1305
|
+
gdwc.device() == bank.device() && row0 % chunk == 0 &&
|
|
1306
|
+
gdwc.numel() >= (tokens / chunk) * crow * hd &&
|
|
1307
|
+
(reinterpret_cast<uintptr_t>(gdwc.data_ptr()) % 16) == 0,
|
|
1308
|
+
"dc_gwr: gdwc [tokens / chunk * chunk * width, hd] in the grad dtype, 16-byte aligned");
|
|
1309
|
+
{
|
|
1310
|
+
const auto addr = [](const at::Tensor& x) { return reinterpret_cast<uintptr_t>(x.data_ptr()); };
|
|
1311
|
+
TORCH_CHECK(!(addr(gdwc) < addr(bank) + bank.nbytes() && addr(bank) < addr(gdwc) + gdwc.nbytes()),
|
|
1312
|
+
"dc_gwr: gdwc must not overlap the gathered rows");
|
|
1313
|
+
}
|
|
1314
|
+
a.kpos = kpos.data_ptr<int>(); a.gdwc = gdwc.data_ptr(); a.crow = static_cast<int>(crow);
|
|
1315
|
+
}
|
|
1316
|
+
const dim3 grid(static_cast<unsigned>((tokens + tpc - 1) / tpc), static_cast<unsigned>(slices));
|
|
1317
|
+
const dim3 block(static_cast<unsigned>(lanes), static_cast<unsigned>(groups),
|
|
1318
|
+
static_cast<unsigned>(tpc));
|
|
1319
|
+
const size_t smem = (groups > 1 ? static_cast<size_t>(tpc) * groups * lanes *
|
|
1320
|
+
(vec_bytes / es) * sizeof(float) : 0) +
|
|
1321
|
+
static_cast<size_t>(tpc) * groups * (lanes * (lanes + 4) + lanes) * sizeof(float);
|
|
1322
|
+
auto stream = at::cuda::getCurrentCUDAStream();
|
|
1323
|
+
const bool bf = bank.scalar_type() == at::kBFloat16;
|
|
1324
|
+
TORCH_CHECK(bf || bank.scalar_type() == at::kFloat, "dc_gwr: bank bf16 or f32");
|
|
1325
|
+
// pdl = 1: programmatic stream serialization (the caller asserts the launch-order rule: the launch right before is
|
|
1326
|
+
// the next step's GDS, which wrote none of the pre-wait inputs).
|
|
1327
|
+
cudaLaunchAttribute attr[1];
|
|
1328
|
+
attr[0].id = cudaLaunchAttributeProgrammaticStreamSerialization;
|
|
1329
|
+
attr[0].val.programmaticStreamSerializationAllowed = 1;
|
|
1330
|
+
cudaLaunchConfig_t cfg = {};
|
|
1331
|
+
cfg.gridDim = grid;
|
|
1332
|
+
cfg.blockDim = block;
|
|
1333
|
+
cfg.dynamicSmemBytes = smem;
|
|
1334
|
+
cfg.stream = stream.stream();
|
|
1335
|
+
cfg.attrs = attr;
|
|
1336
|
+
cfg.numAttrs = 1;
|
|
1337
|
+
#define DC_G(E, VB, LPR) \
|
|
1338
|
+
{ auto kf = dc_gwr_kernel<E, VB, LPR>; set_smem(kf, smem); \
|
|
1339
|
+
if (sdm_plancap::take(kf, grid, block, smem, a)) return; /* plan capture */ \
|
|
1340
|
+
if (pdl == 1) { C10_CUDA_CHECK(cudaLaunchKernelEx(&cfg, kf, a)); } \
|
|
1341
|
+
else { kf<<<grid, block, smem, stream>>>(a); } }
|
|
1342
|
+
// Elements are raw bits, as in token_gather: uint16_t = bfloat16, uint32_t = float.
|
|
1343
|
+
// 256-thread CTAs of the bf16 / 16-byte / 32-lane instance launch the 64-register entry point (same body).
|
|
1344
|
+
if (bf && vec_bytes == 16 && lanes == 32 && lanes * groups * tpc == 256) {
|
|
1345
|
+
{ auto kf = dc_gwr_r64_kernel<uint16_t, 16, 32>; set_smem(kf, smem);
|
|
1346
|
+
if (sdm_plancap::take(kf, grid, block, smem, a)) return; // plan capture
|
|
1347
|
+
if (pdl == 1) { C10_CUDA_CHECK(cudaLaunchKernelEx(&cfg, kf, a)); }
|
|
1348
|
+
else { kf<<<grid, block, smem, stream>>>(a); } }
|
|
1349
|
+
C10_CUDA_KERNEL_LAUNCH_CHECK();
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
if (vec_bytes != 16) {
|
|
1353
|
+
TORCH_CHECK(lanes == 32, "dc_gwr: element vectors use 32 lanes");
|
|
1354
|
+
if (bf) DC_G(uint16_t, 2, 32) else DC_G(uint32_t, 4, 32)
|
|
1355
|
+
} else if (bf) {
|
|
1356
|
+
if (lanes == 8) DC_G(uint16_t, 16, 8)
|
|
1357
|
+
else if (lanes == 16) DC_G(uint16_t, 16, 16)
|
|
1358
|
+
else DC_G(uint16_t, 16, 32)
|
|
1359
|
+
} else {
|
|
1360
|
+
if (lanes == 8) DC_G(uint32_t, 16, 8)
|
|
1361
|
+
else if (lanes == 16) DC_G(uint32_t, 16, 16)
|
|
1362
|
+
else DC_G(uint32_t, 16, 32)
|
|
1363
|
+
}
|
|
1364
|
+
#undef DC_G
|
|
1365
|
+
C10_CUDA_KERNEL_LAUNCH_CHECK();
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
// ---- the backward's C++ step loop (bwd_steps_cuda): dc_gwr as a per-backward plan -------------------------------------
|
|
1369
|
+
// dc_gwr_plan runs dc_gwr ONCE under plan capture at the loop's largest step ti_max (row0 = ti_max * row_step, out_row0 =
|
|
1370
|
+
// ti_max * out_row_step, valid = min(chunk, valid_tokens - ti_max * chunk)): every check of dc_gwr runs (its row bounds
|
|
1371
|
+
// grow with the step, so they hold for every ti <= ti_max), and its one launch is recorded. dc_gwr_plan_launch (a plain
|
|
1372
|
+
// C++ entry point; address from dc_gwr_launch_addr) sets row0 / out_row0 / valid of step ti in the recorded GArgs and
|
|
1373
|
+
// issues the recorded launch: plain (pdl 0) or with programmatic stream serialization (pdl 1), as dc_gwr does for the same
|
|
1374
|
+
// pdl. The plan holds raw pointers: the caller keeps the operands alive and unresized while it is used.
|
|
1375
|
+
namespace {
|
|
1376
|
+
constexpr uint64_t kGwrPlanMagic = 0x314e414c50525747ull;
|
|
1377
|
+
struct GwrPlan {
|
|
1378
|
+
uint64_t magic;
|
|
1379
|
+
int32_t device;
|
|
1380
|
+
int32_t pad;
|
|
1381
|
+
int64_t ti_max, row_step, out_row_step, chunk, valid_tokens;
|
|
1382
|
+
sdm_plancap::Launch cap;
|
|
1383
|
+
};
|
|
1384
|
+
|
|
1385
|
+
int64_t dc_gwr_plan_launch(const void* plan_bytes, int64_t nbytes, int64_t ti, int64_t pdl, void* stream) {
|
|
1386
|
+
GwrPlan pl;
|
|
1387
|
+
if (!sdm_plancap::from_bytes(pl, plan_bytes, nbytes, kGwrPlanMagic)) return -1;
|
|
1388
|
+
if (pdl != 0 && pdl != 1) return -4;
|
|
1389
|
+
if (ti < 0 || ti > pl.ti_max) return -2;
|
|
1390
|
+
if (pl.cap.set == 0) return 0; // dc_gwr launched nothing (tokens == 0)
|
|
1391
|
+
if (sdm_plancap::current_device() != pl.device) return -5;
|
|
1392
|
+
GArgs* a = sdm_plancap::param<GArgs>(pl.cap, 0);
|
|
1393
|
+
if (a == nullptr) return -7;
|
|
1394
|
+
a->row0 = ti * pl.row_step;
|
|
1395
|
+
a->out_row0 = ti * pl.out_row_step;
|
|
1396
|
+
a->valid = static_cast<int>(std::min(pl.chunk, pl.valid_tokens - ti * pl.chunk));
|
|
1397
|
+
return sdm_plancap::launch(pl.cap, pdl == 1, static_cast<cudaStream_t>(stream));
|
|
1398
|
+
}
|
|
1399
|
+
} // namespace
|
|
1400
|
+
|
|
1401
|
+
at::Tensor dc_gwr_plan(at::Tensor bank, at::Tensor idx, at::Tensor w, at::Tensor out, at::Tensor dv, at::Tensor pd,
|
|
1402
|
+
int64_t row_step, int64_t out_row_step, int64_t tokens, int64_t chunk, int64_t valid_tokens,
|
|
1403
|
+
int64_t ti_max, int64_t vec_bytes, int64_t lanes, int64_t groups, int64_t tpc,
|
|
1404
|
+
at::Tensor kpos, at::Tensor gdwc) {
|
|
1405
|
+
TORCH_CHECK(ti_max >= 0 && ti_max < (int64_t(1) << 31) && row_step >= 0 && out_row_step >= 0 && chunk >= 1 &&
|
|
1406
|
+
valid_tokens > ti_max * chunk && valid_tokens <= (ti_max + 1) * chunk,
|
|
1407
|
+
"dc_gwr_plan: steps 0..ti_max must each hold 1..chunk valid tokens");
|
|
1408
|
+
TORCH_CHECK(kpos.numel() == 0 || row_step % chunk == 0, "dc_gwr_plan: kpos needs chunk-aligned row steps");
|
|
1409
|
+
GwrPlan pl;
|
|
1410
|
+
std::memset(&pl, 0, sizeof(pl));
|
|
1411
|
+
{
|
|
1412
|
+
sdm_plancap::Scope scope(&pl.cap);
|
|
1413
|
+
dc_gwr(bank, idx, w, out, dv, pd, ti_max * row_step, ti_max * out_row_step, tokens, chunk,
|
|
1414
|
+
std::min(chunk, valid_tokens - ti_max * chunk), vec_bytes, lanes, groups, tpc, kpos, gdwc, 0);
|
|
1415
|
+
}
|
|
1416
|
+
pl.magic = kGwrPlanMagic;
|
|
1417
|
+
pl.device = static_cast<int32_t>(bank.get_device());
|
|
1418
|
+
pl.ti_max = ti_max;
|
|
1419
|
+
pl.row_step = row_step;
|
|
1420
|
+
pl.out_row_step = out_row_step;
|
|
1421
|
+
pl.chunk = chunk;
|
|
1422
|
+
pl.valid_tokens = valid_tokens;
|
|
1423
|
+
TORCH_CHECK(pl.cap.set == 0 || (pl.cap.nparams == 1 && sdm_plancap::param<GArgs>(pl.cap, 0) != nullptr),
|
|
1424
|
+
"dc_gwr_plan: GArgs");
|
|
1425
|
+
return sdm_plancap::to_tensor(pl);
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
int64_t dc_gwr_launch_addr() { return reinterpret_cast<int64_t>(&dc_gwr_plan_launch); }
|
|
1429
|
+
"""
|
|
1430
|
+
|
|
1431
|
+
|
|
1432
|
+
def gwr_tail_supported(bank, chunk_size, quant="none") -> bool:
|
|
1433
|
+
"""Tail hook (movers.tail_admits): the backward's GWR (dc_gwr) runs the call's tail class on every backend (rtx: chunks
|
|
1434
|
+
of <= 128 tokens): it reads grad rows with memory_ops.pinned_gather_geometry (any head width), never snapshot rows."""
|
|
1435
|
+
from ._device import backend
|
|
1436
|
+
if not bank.is_cuda or bank.dim() != 2 or bank.dtype not in (torch.bfloat16, torch.float32):
|
|
1437
|
+
return False
|
|
1438
|
+
try:
|
|
1439
|
+
b = backend(bank.device)
|
|
1440
|
+
except RuntimeError:
|
|
1441
|
+
return False
|
|
1442
|
+
return ((b in ("modern", "a100") or (b == "rtx" and chunk_size <= 128)) and 1 <= chunk_size <= 256
|
|
1443
|
+
and 1 <= bank.shape[-1] <= 2 ** 20 and bank.shape[0] < 2 ** 31)
|
|
1444
|
+
|
|
1445
|
+
|
|
1446
|
+
@functools.cache
|
|
1447
|
+
def _module():
|
|
1448
|
+
return load_cuda_extension(
|
|
1449
|
+
"sdm_pa_dc",
|
|
1450
|
+
cpp_sources=_CPP,
|
|
1451
|
+
cuda_sources=_CUDA,
|
|
1452
|
+
functions=("dc_records_pa", "dc_copy_raw", "dc_overlaps", "dc_pa", "dc_pa_reduce", "dc_gwr", "dc_gwr_plan",
|
|
1453
|
+
"dc_gwr_launch_addr", "dc_pa_plan", "dc_pa_launch_addr"),
|
|
1454
|
+
headers=headers(),
|
|
1455
|
+
extra_cuda_cflags=("-O3", "-lineinfo"),
|
|
1456
|
+
)
|
|
1457
|
+
|
|
1458
|
+
|
|
1459
|
+
def gwr_plan(bank, idx, w, out, dv, pd, *, row_step, out_row_step, tokens, chunk, valid_tokens, ti_max, vec_bytes,
|
|
1460
|
+
lanes, groups, tpc, kpos, gdwc):
|
|
1461
|
+
"""(plan, launch address) of dc_gwr for steps 0 .. ti_max of the backward's C++ step loop (bwd_steps_cuda)."""
|
|
1462
|
+
m = _module()
|
|
1463
|
+
plan = m.dc_gwr_plan(bank, idx, w, out, dv, pd, int(row_step), int(out_row_step), int(tokens), int(chunk),
|
|
1464
|
+
int(valid_tokens), int(ti_max), int(vec_bytes), int(lanes), int(groups), int(tpc), kpos, gdwc)
|
|
1465
|
+
return plan, int(m.dc_gwr_launch_addr())
|
|
1466
|
+
|
|
1467
|
+
|
|
1468
|
+
def pa_step_plan(grad, bank, snap, snap_step_rows, dva, beta, kpay, rec, out_eff, out_g, ti_max, P, C, W, L, nw, depth,
|
|
1469
|
+
dbg, pipe, bpre, gdwc):
|
|
1470
|
+
"""(plan, launch address) of dc_pa for steps 0 .. ti_max of the backward's C++ step loop (bwd_steps_cuda)."""
|
|
1471
|
+
m = _module()
|
|
1472
|
+
plan = m.dc_pa_plan(grad, bank, snap, int(snap_step_rows), dva, beta, kpay, rec, out_eff, out_g, int(ti_max), int(P),
|
|
1473
|
+
int(C), int(W), int(L), int(nw), int(depth), int(dbg), bool(pipe), bpre, gdwc)
|
|
1474
|
+
return plan, int(m.dc_pa_launch_addr())
|
|
1475
|
+
|
|
1476
|
+
|
|
1477
|
+
def slices(head_dim: int) -> int:
|
|
1478
|
+
return -(-head_dim // 128)
|
|
1479
|
+
|
|
1480
|
+
|
|
1481
|
+
def tile_len(chunk_size: int, width: int, head_dim: int, num_parallel: int) -> int:
|
|
1482
|
+
"""Sorted positions per warp tile from SHAPES only: 256, halved while the step would hold fewer
|
|
1483
|
+
than 4096 tiles (2048 when the head is split into column slices), floor 32. Results do not depend on it."""
|
|
1484
|
+
nsl = slices(head_dim)
|
|
1485
|
+
work = num_parallel * nsl * chunk_size * width
|
|
1486
|
+
target = 4096 if nsl == 1 else 2048
|
|
1487
|
+
tl = 256
|
|
1488
|
+
while tl > 32 and work // tl < target:
|
|
1489
|
+
tl //= 2
|
|
1490
|
+
return max(1, min(tl, chunk_size * width))
|
|
1491
|
+
|
|
1492
|
+
|
|
1493
|
+
def build_records_pa(spay_k, k_idx, k_val, log_k, beta, cpe):
|
|
1494
|
+
"""int32 [N, C * W, 4] records per sorted write position: pay, slot, sc bits, cpe bits."""
|
|
1495
|
+
n = spay_k.shape[-1]
|
|
1496
|
+
spay_k = spay_k.reshape(-1, n)
|
|
1497
|
+
rec = torch.empty(spay_k.shape[0], n, 4, device=spay_k.device, dtype=torch.int32)
|
|
1498
|
+
_module().dc_records_pa(spay_k, k_idx.contiguous(), k_val.contiguous(), log_k.contiguous(),
|
|
1499
|
+
beta.contiguous().view(-1), cpe.contiguous(), rec)
|
|
1500
|
+
return rec
|