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,735 @@
|
|
|
1
|
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
"""product_key_topk_cuda: CUDA kernels of the product-key top-k (``product_key_topk.product_key_topk``).
|
|
3
|
+
|
|
4
|
+
One warp handles one row. The F = 2..4 factors are combined in order: stage f merges the
|
|
5
|
+
running list (factor 0's ranking at first) with factor f. Two-factor kernels run one stage;
|
|
6
|
+
the three- and four-factor kernels loop over the stages. Forward:
|
|
7
|
+
|
|
8
|
+
1. Factors. A half-warp keeps the top ``R = min(KMAX, 16 * M)`` keys of a factor, where
|
|
9
|
+
``KMAX = pow2ceil(kk)`` (at least 16), with a bitonic network over registers (M keys per
|
|
10
|
+
lane): groups of ``R / M`` lanes sort, then merge pairwise until every group holds the top
|
|
11
|
+
``R``. Factors wider than 256 columns stream 256-column tiles through the same merge.
|
|
12
|
+
Stage 1 sorts factors 0 and 1 in the two half-warps, later stages one factor each.
|
|
13
|
+
2. Stage. The candidates are the pairs of a running rank i < n and a factor rank j < m with
|
|
14
|
+
``(i + 1) * (j + 1) <= c = min(k, n * m)``; any other pair has at least c pairs of lower
|
|
15
|
+
or equal ranks, none of them scoring less. A warp-wide bisection over the candidates'
|
|
16
|
+
score keys finds the c-th best score, then resolves an exactly tied boundary by the rank
|
|
17
|
+
payload ``(i + j) << 8 | i``, larger first.
|
|
18
|
+
3. Output. An intermediate stage sorts its selection by (score, payload) into the next
|
|
19
|
+
running list, rounding each score to bfloat16 for bfloat16 input. The last stage sorts its
|
|
20
|
+
selection by slot index (bit-packed columns) and recomputes each value from the input row.
|
|
21
|
+
|
|
22
|
+
Backward: one warp per row zero-fills its gradient row, then writes each touched column once
|
|
23
|
+
with a segmented sum in a fixed order, so it is deterministic and needs no atomics."""
|
|
24
|
+
import functools
|
|
25
|
+
|
|
26
|
+
from ._build import load_cuda_extension
|
|
27
|
+
|
|
28
|
+
# Device helpers shared with product_key_topk_anyf_cuda.py (spliced into both CUDA sources, inside their anonymous
|
|
29
|
+
# namespace): storage <-> float conversions, the order-preserving key map, the bitonic compare-exchange networks and
|
|
30
|
+
# the backward's run sums.
|
|
31
|
+
TOPK_COMMON = r"""
|
|
32
|
+
__device__ __forceinline__ float to_float(float x) { return x; }
|
|
33
|
+
__device__ __forceinline__ float to_float(__nv_bfloat16 x) { return __bfloat162float(x); }
|
|
34
|
+
|
|
35
|
+
template <typename T> __device__ __forceinline__ T from_float(float x);
|
|
36
|
+
template <> __device__ __forceinline__ float from_float<float>(float x) { return x; }
|
|
37
|
+
template <> __device__ __forceinline__ __nv_bfloat16 from_float<__nv_bfloat16>(float x) {
|
|
38
|
+
return __float2bfloat16_rn(x);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
template <typename T> __device__ __forceinline__ float round_to(float x) {
|
|
42
|
+
return to_float(from_float<T>(x));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Order-preserving map of a float to an unsigned key (-0 below +0), with NaN ranked as -inf.
|
|
46
|
+
__device__ __forceinline__ uint32_t ordered(float x) {
|
|
47
|
+
const uint32_t b = x != x ? 0xff800000u : __float_as_uint(x);
|
|
48
|
+
return (b & 0x80000000u) ? ~b : (b | 0x80000000u);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// One compare-exchange stage of a bitonic network over the G * M keys that G consecutive
|
|
52
|
+
// lanes hold in blocked order: element (lane % G) * M + r is register r.
|
|
53
|
+
template <bool kDescending, int G, int M, typename K>
|
|
54
|
+
__device__ __forceinline__ void bitonic_stage(K (&x)[M], int lane, int size, int stride) {
|
|
55
|
+
const int g = lane % G;
|
|
56
|
+
if (stride >= M) {
|
|
57
|
+
const bool upper = g & (stride / M);
|
|
58
|
+
const bool desc = ((g * M & size) == 0) == kDescending;
|
|
59
|
+
#pragma unroll
|
|
60
|
+
for (int r = 0; r < M; ++r) {
|
|
61
|
+
const K y = __shfl_xor_sync(kFull, x[r], stride / M);
|
|
62
|
+
x[r] = ((desc != upper) == (y > x[r])) ? y : x[r];
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
#pragma unroll
|
|
66
|
+
for (int r = 0; r < M; ++r) {
|
|
67
|
+
if (r & stride) continue;
|
|
68
|
+
const bool desc = (((g * M + r) & size) == 0) == kDescending;
|
|
69
|
+
const K a = x[r], b = x[r + stride];
|
|
70
|
+
const bool swap = desc == (b > a);
|
|
71
|
+
x[r] = swap ? b : a;
|
|
72
|
+
x[r + stride] = swap ? a : b;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
template <bool kDescending, int G, int M, typename K>
|
|
78
|
+
__device__ __forceinline__ void bitonic_sort(K (&x)[M], int lane) {
|
|
79
|
+
#pragma unroll
|
|
80
|
+
for (int size = 2; size <= G * M; size *= 2)
|
|
81
|
+
#pragma unroll
|
|
82
|
+
for (int stride = size / 2; stride > 0; stride /= 2)
|
|
83
|
+
bitonic_stage<kDescending, G, M>(x, lane, size, stride);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// q <- the G * M largest of q and of t as held by lane ^ mask, where the mask maps a G-lane
|
|
87
|
+
// group onto its mirror image in the partner group. Both inputs are sorted descending.
|
|
88
|
+
template <int G, int M, typename K>
|
|
89
|
+
__device__ __forceinline__ void merge_top(K (&q)[M], const K (&t)[M], int lane, int mask) {
|
|
90
|
+
K y[M];
|
|
91
|
+
#pragma unroll
|
|
92
|
+
for (int r = 0; r < M; ++r) y[r] = __shfl_xor_sync(kFull, t[M - 1 - r], mask);
|
|
93
|
+
#pragma unroll
|
|
94
|
+
for (int r = 0; r < M; ++r) q[r] = y[r] > q[r] ? y[r] : q[r];
|
|
95
|
+
#pragma unroll
|
|
96
|
+
for (int stride = G * M / 2; stride > 0; stride /= 2)
|
|
97
|
+
bitonic_stage<true, G, M>(q, lane, G * M, stride);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Every group of G lanes of a half-warp ends with the G * M largest of the half-warp's
|
|
101
|
+
// 16 * M keys, sorted descending.
|
|
102
|
+
template <int G, int M, typename K>
|
|
103
|
+
__device__ __forceinline__ void top_sorted(K (&x)[M], int lane) {
|
|
104
|
+
bitonic_sort<true, G, M>(x, lane);
|
|
105
|
+
#pragma unroll
|
|
106
|
+
for (int d = G; d < 16; d *= 2) merge_top<G, M>(x, x, lane, d | (G - 1));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
template <int M, typename K> __device__ __forceinline__ K register_at(const K (&x)[M], int r) {
|
|
110
|
+
K v = x[0];
|
|
111
|
+
#pragma unroll
|
|
112
|
+
for (int i = 1; i < M; ++i) v = i == r ? x[i] : v;
|
|
113
|
+
return v;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Loads 16 * M columns of a factor starting at `base`; columns past `width` get key 0, below
|
|
117
|
+
// every real key. The load order is irrelevant because the keys are sorted next.
|
|
118
|
+
|
|
119
|
+
// For keys ascending in blocked order, writes the sum of v over each run of equal keys to
|
|
120
|
+
// out[key] in a fixed summation order. Keys >= limit are padding.
|
|
121
|
+
template <int MQ, typename T>
|
|
122
|
+
__device__ __forceinline__ void store_run_sums(const uint32_t (&key)[MQ], const float (&v)[MQ],
|
|
123
|
+
int lane, uint32_t limit, T* out) {
|
|
124
|
+
float s[MQ];
|
|
125
|
+
#pragma unroll
|
|
126
|
+
for (int r = 0; r < MQ; ++r) s[r] = (r > 0 && key[r] == key[r - 1]) ? s[r - 1] + v[r] : v[r];
|
|
127
|
+
// Sum of the run holding each lane's last key over lanes <= lane (segmented scan).
|
|
128
|
+
const bool joins = __shfl_up_sync(kFull, key[MQ - 1], 1) == key[0] && lane > 0;
|
|
129
|
+
bool head = !(joins && key[0] == key[MQ - 1]);
|
|
130
|
+
float tail = s[MQ - 1];
|
|
131
|
+
#pragma unroll
|
|
132
|
+
for (int d = 1; d < 32; d *= 2) {
|
|
133
|
+
const float t = __shfl_up_sync(kFull, tail, d);
|
|
134
|
+
const bool h = __shfl_up_sync(kFull, head, d);
|
|
135
|
+
if (lane >= d) {
|
|
136
|
+
if (!head) tail = t + tail;
|
|
137
|
+
head = head || h;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const float carry = __shfl_up_sync(kFull, tail, 1);
|
|
141
|
+
const uint32_t next = __shfl_down_sync(kFull, key[0], 1);
|
|
142
|
+
#pragma unroll
|
|
143
|
+
for (int r = 0; r < MQ; ++r) {
|
|
144
|
+
const bool last = r + 1 < MQ ? key[r + 1] != key[r] : (lane == 31 || next != key[r]);
|
|
145
|
+
if (last && key[r] < limit)
|
|
146
|
+
out[key[r]] = from_float<T>(joins && key[r] == key[0] ? carry + s[r] : s[r]);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
_CUDA_SOURCE = r"""
|
|
152
|
+
#include <torch/extension.h>
|
|
153
|
+
#include <ATen/cuda/CUDAContext.h>
|
|
154
|
+
#include <c10/cuda/CUDAGuard.h>
|
|
155
|
+
#include <cuda_bf16.h>
|
|
156
|
+
#include <cstdint>
|
|
157
|
+
|
|
158
|
+
namespace {
|
|
159
|
+
|
|
160
|
+
constexpr int kWarps = 8; // rows per block
|
|
161
|
+
constexpr int kMaxK = 256;
|
|
162
|
+
constexpr int kMaxFactors = 4;
|
|
163
|
+
constexpr int kMaxWidth = 65535;
|
|
164
|
+
constexpr unsigned kFull = 0xffffffffu;
|
|
165
|
+
|
|
166
|
+
constexpr int num_pairs(int k) {
|
|
167
|
+
int n = 0;
|
|
168
|
+
for (int i = 1; i <= k; ++i) n += k / i;
|
|
169
|
+
return n;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Rank pairs (i, j) with (i + 1) * (j + 1) <= kMaxK, encoded i << 8 | j and ordered by
|
|
173
|
+
// (i + 1) * (j + 1): the pairs with (i + 1) * (j + 1) <= k are the first num_pairs(k).
|
|
174
|
+
struct PairTable {
|
|
175
|
+
uint16_t code[num_pairs(kMaxK)];
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
constexpr PairTable make_pair_table() {
|
|
179
|
+
PairTable t{};
|
|
180
|
+
int n = 0;
|
|
181
|
+
for (int p = 1; p <= kMaxK; ++p)
|
|
182
|
+
for (int d = 1; d <= p; ++d)
|
|
183
|
+
if (p % d == 0) t.code[n++] = uint16_t((d - 1) << 8 | (p / d - 1));
|
|
184
|
+
return t;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
__device__ const PairTable kPairs = make_pair_table();
|
|
188
|
+
|
|
189
|
+
struct FwdProblem {
|
|
190
|
+
int64_t rows, row_stride;
|
|
191
|
+
int nf, kk;
|
|
192
|
+
int width[kMaxFactors], offset[kMaxFactors];
|
|
193
|
+
int kf[kMaxFactors]; // min(k, width)
|
|
194
|
+
int count[kMaxFactors]; // running list length after stage f: min(k, prod(width[:f + 1]))
|
|
195
|
+
int lo_i[kMaxFactors], lo_j[kMaxFactors]; // stage f: lo_i * lo_j == count[f], or 0
|
|
196
|
+
int bits[kMaxFactors], pos[kMaxFactors]; // column bit field of the packed slot index
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
struct BwdProblem {
|
|
200
|
+
int64_t rows, gv_row_stride, gv_col_stride;
|
|
201
|
+
int nf, kk, row_width;
|
|
202
|
+
int width[kMaxFactors], offset[kMaxFactors];
|
|
203
|
+
int bits[kMaxFactors], pos[kMaxFactors]; // column bit field of the packed slot index
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
""" + TOPK_COMMON + r"""
|
|
207
|
+
|
|
208
|
+
__device__ __forceinline__ uint32_t unordered_bits(uint32_t u) {
|
|
209
|
+
return (u & 0x80000000u) ? (u & 0x7fffffffu) : ~u;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Bit field [pos, pos + bits) of a packed slot index; an empty field may sit at pos 32.
|
|
213
|
+
__device__ __forceinline__ uint32_t field(uint32_t packed, int pos, int bits) {
|
|
214
|
+
return uint32_t(uint64_t(packed) >> pos) & ((1u << bits) - 1);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Factor sort key: value, then larger column first; the low 16 bits hold the column. value()
|
|
218
|
+
// decodes the value with NaN as -inf, whose sums rank exactly like those of the raw values.
|
|
219
|
+
template <typename T> struct FactorKey;
|
|
220
|
+
template <> struct FactorKey<float> {
|
|
221
|
+
using type = uint64_t;
|
|
222
|
+
__device__ static type make(float v, int col) {
|
|
223
|
+
return uint64_t(ordered(v)) << 32 | uint32_t(col);
|
|
224
|
+
}
|
|
225
|
+
__device__ static float value(type k) { return __uint_as_float(unordered_bits(k >> 32)); }
|
|
226
|
+
};
|
|
227
|
+
template <> struct FactorKey<__nv_bfloat16> {
|
|
228
|
+
using type = uint32_t;
|
|
229
|
+
__device__ static type make(__nv_bfloat16 v, int col) {
|
|
230
|
+
return (ordered(__bfloat162float(v)) & 0xffff0000u) | uint32_t(col);
|
|
231
|
+
}
|
|
232
|
+
__device__ static float value(type k) {
|
|
233
|
+
return __uint_as_float(unordered_bits(k & 0xffff0000u) & 0xffff0000u);
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
template <typename T, int M>
|
|
238
|
+
__device__ __forceinline__ void load_tile(const T* f, int width, int base, int sub,
|
|
239
|
+
typename FactorKey<T>::type (&t)[M]) {
|
|
240
|
+
#pragma unroll
|
|
241
|
+
for (int r = 0; r < M; ++r) {
|
|
242
|
+
const int c = base + r * 16 + sub;
|
|
243
|
+
t[r] = c < width ? FactorKey<T>::make(f[c], c) : 0;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Top kf keys of a factor, per half-warp (each half-warp may read its own factor); tiles
|
|
248
|
+
// are streamed up to the warp-uniform bound.
|
|
249
|
+
template <typename T, int G, int M, bool kStream>
|
|
250
|
+
__device__ __forceinline__ void factor_top(const T* f, int width, int kf, int bound, int lane,
|
|
251
|
+
typename FactorKey<T>::type (&q)[M]) {
|
|
252
|
+
using K = typename FactorKey<T>::type;
|
|
253
|
+
const int sub = lane & 15;
|
|
254
|
+
load_tile<T, M>(f, width, 0, sub, q);
|
|
255
|
+
top_sorted<G, M>(q, lane);
|
|
256
|
+
if constexpr (kStream) {
|
|
257
|
+
const int src = (lane & ~(G - 1)) | (kf - 1) / M;
|
|
258
|
+
for (int base = 16 * M; base < bound; base += 16 * M) {
|
|
259
|
+
K t[M];
|
|
260
|
+
load_tile<T, M>(f, width, base, sub, t);
|
|
261
|
+
const K thr = __shfl_sync(kFull, register_at(q, (kf - 1) % M), src);
|
|
262
|
+
bool hit = false;
|
|
263
|
+
#pragma unroll
|
|
264
|
+
for (int r = 0; r < M; ++r) hit |= t[r] > thr;
|
|
265
|
+
if (!__any_sync(kFull, hit)) continue;
|
|
266
|
+
top_sorted<G, M>(t, lane);
|
|
267
|
+
merge_top<G, M>(q, t, lane, G - 1);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
template <int N, typename Pred> __device__ __forceinline__ int warp_count(Pred pred) {
|
|
273
|
+
int n = 0;
|
|
274
|
+
#pragma unroll
|
|
275
|
+
for (int t = 0; t < N; ++t) n += pred(t);
|
|
276
|
+
return __reduce_add_sync(kFull, n);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
__device__ __forceinline__ int warp_exclusive_sum(int v, int lane) {
|
|
280
|
+
int s = v;
|
|
281
|
+
#pragma unroll
|
|
282
|
+
for (int d = 1; d < 32; d *= 2) {
|
|
283
|
+
const int y = __shfl_up_sync(kFull, s, d);
|
|
284
|
+
if (lane >= d) s += y;
|
|
285
|
+
}
|
|
286
|
+
return s - v;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
template <int KMAX> struct Scratch {
|
|
290
|
+
float val[2][KMAX]; // running scores by rank, factor values by rank
|
|
291
|
+
uint32_t lin[KMAX]; // running packed slot indices by rank
|
|
292
|
+
uint16_t col[KMAX]; // factor columns by rank
|
|
293
|
+
union {
|
|
294
|
+
uint32_t sel[KMAX]; // last stage: selected packed slot indices
|
|
295
|
+
uint64_t order[KMAX]; // other stages: selected (score, payload) keys
|
|
296
|
+
};
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
// kMulti: three or four factors; otherwise two, in one stage.
|
|
300
|
+
template <typename T, int M, bool kStream, int KMAX, bool kMulti>
|
|
301
|
+
__global__ __launch_bounds__(kWarps * 32) void product_key_topk_fwd_kernel(
|
|
302
|
+
const T* __restrict__ raw, T* __restrict__ out_val, int64_t* __restrict__ out_lin,
|
|
303
|
+
FwdProblem p) {
|
|
304
|
+
using FK = FactorKey<T>;
|
|
305
|
+
using K = typename FK::type;
|
|
306
|
+
constexpr int MQ = KMAX < 32 ? 1 : KMAX / 32; // selected slots per lane
|
|
307
|
+
constexpr int G = KMAX / M < 16 ? KMAX / M : 16; // lanes per group holding the top R
|
|
308
|
+
constexpr int NC = (num_pairs(KMAX) + 31) / 32; // candidates per lane
|
|
309
|
+
constexpr int NP = num_pairs(kMaxK);
|
|
310
|
+
constexpr int NF = kMulti ? kMaxFactors : 2; // bound of the unrolled factor loops
|
|
311
|
+
__shared__ Scratch<KMAX> scratch[kWarps];
|
|
312
|
+
|
|
313
|
+
const int warp = threadIdx.x >> 5, lane = threadIdx.x & 31;
|
|
314
|
+
const int64_t row = int64_t(blockIdx.x) * kWarps + warp;
|
|
315
|
+
if (row >= p.rows) return;
|
|
316
|
+
Scratch<KMAX>& s = scratch[warp];
|
|
317
|
+
const T* x = raw + row * p.row_stride;
|
|
318
|
+
const int half = lane >> 4, sub = lane & 15;
|
|
319
|
+
|
|
320
|
+
for (int f = 1; f < p.nf; ++f) {
|
|
321
|
+
// 1. Top kf of factor f (and of factor 0 in stage 1), by (value desc, column desc).
|
|
322
|
+
{
|
|
323
|
+
const int fi = half || f > 1 ? f : 0;
|
|
324
|
+
const int bound = f > 1 ? p.width[f] : max(p.width[0], p.width[1]);
|
|
325
|
+
K q[M];
|
|
326
|
+
factor_top<T, G, M, kStream>(x + p.offset[fi], p.width[fi], p.kf[fi], bound, lane, q);
|
|
327
|
+
if (sub < G && (half || f == 1)) {
|
|
328
|
+
#pragma unroll
|
|
329
|
+
for (int r = 0; r < M; ++r) {
|
|
330
|
+
const int e = sub * M + r;
|
|
331
|
+
if (e < p.kf[fi]) {
|
|
332
|
+
s.val[half][e] = FK::value(q[r]);
|
|
333
|
+
if (half) s.col[e] = uint16_t(q[r] & 0xffffu);
|
|
334
|
+
else s.lin[e] = uint32_t(q[r] & 0xffffu);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
__syncwarp();
|
|
340
|
+
const int n = p.count[f - 1], m = p.kf[f], kk = p.count[f];
|
|
341
|
+
|
|
342
|
+
// 2. Score keys of the candidate rank pairs; lane l holds pairs l, l + 32, ... and key 0
|
|
343
|
+
// marks a non-candidate (every real key is above it).
|
|
344
|
+
uint32_t key[NC];
|
|
345
|
+
#pragma unroll
|
|
346
|
+
for (int t = 0; t < NC; ++t) {
|
|
347
|
+
const int c = t * 32 + lane;
|
|
348
|
+
key[t] = 0;
|
|
349
|
+
if (c < NP) {
|
|
350
|
+
const int i = kPairs.code[c] >> 8, j = kPairs.code[c] & 0xff;
|
|
351
|
+
if ((i + 1) * (j + 1) <= kk && i < n && j < m)
|
|
352
|
+
key[t] = ordered(__fadd_rn(s.val[0][i], s.val[1][j]));
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Bisect for the kk-th best key, keeping count(key >= lo) >= kk > count(key > hi).
|
|
357
|
+
uint32_t lo = p.lo_i[f] ? ordered(__fadd_rn(s.val[0][p.lo_i[f] - 1],
|
|
358
|
+
s.val[1][p.lo_j[f] - 1])) : 0;
|
|
359
|
+
uint32_t hi = ordered(__fadd_rn(s.val[0][0], s.val[1][0]));
|
|
360
|
+
int n_lo = 0;
|
|
361
|
+
while (lo < hi) {
|
|
362
|
+
const uint32_t mid = hi - (hi - lo) / 2;
|
|
363
|
+
const int cnt = warp_count<NC>([&](int t) { return key[t] >= mid; });
|
|
364
|
+
if (cnt < kk) {
|
|
365
|
+
hi = mid - 1;
|
|
366
|
+
} else {
|
|
367
|
+
lo = mid;
|
|
368
|
+
n_lo = cnt;
|
|
369
|
+
if (cnt == kk) break;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (n_lo == 0) n_lo = warp_count<NC>([&](int t) { return key[t] >= lo; });
|
|
373
|
+
|
|
374
|
+
// Re-key by final rank class: 1 above the boundary, 2 + payload when tied with it
|
|
375
|
+
// (payload 0xffff - ((i + j) << 8 | i): larger i + j, then larger i, first), ~0 below.
|
|
376
|
+
// Select key <= rho.
|
|
377
|
+
#pragma unroll
|
|
378
|
+
for (int t = 0; t < NC; ++t) {
|
|
379
|
+
const uint32_t code = key[t] == lo ? kPairs.code[t * 32 + lane] : 0;
|
|
380
|
+
const uint32_t payload = 0xffffu - (((code >> 8) + (code & 0xff)) << 8 | code >> 8);
|
|
381
|
+
key[t] = key[t] > lo ? 1u : key[t] == lo ? 2u + payload : ~0u;
|
|
382
|
+
}
|
|
383
|
+
uint32_t rho = 0x10001u;
|
|
384
|
+
if (n_lo > kk) {
|
|
385
|
+
uint32_t plo = 2;
|
|
386
|
+
while (plo < rho) { // smallest rho with count(key <= rho) >= kk
|
|
387
|
+
const uint32_t mid = plo + (rho - plo) / 2;
|
|
388
|
+
const int cnt = warp_count<NC>([&](int t) { return key[t] <= mid; });
|
|
389
|
+
if (cnt < kk) {
|
|
390
|
+
plo = mid + 1;
|
|
391
|
+
} else {
|
|
392
|
+
rho = mid;
|
|
393
|
+
if (cnt == kk) break;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
int n_sel = 0;
|
|
398
|
+
#pragma unroll
|
|
399
|
+
for (int t = 0; t < NC; ++t) n_sel += key[t] <= rho;
|
|
400
|
+
int pos = warp_exclusive_sum(n_sel, lane);
|
|
401
|
+
const bool last = !kMulti || f + 1 == p.nf;
|
|
402
|
+
#pragma unroll
|
|
403
|
+
for (int t = 0; t < NC; ++t) {
|
|
404
|
+
if (key[t] <= rho) {
|
|
405
|
+
const int code = kPairs.code[t * 32 + lane], i = code >> 8, j = code & 0xff;
|
|
406
|
+
if (last) {
|
|
407
|
+
s.sel[pos++] = s.lin[i] << p.bits[f] | s.col[j];
|
|
408
|
+
} else {
|
|
409
|
+
const uint32_t score = ordered(__fadd_rn(s.val[0][i], s.val[1][j]));
|
|
410
|
+
s.order[pos++] = uint64_t(score) << 32 | uint32_t((i + j) << 8 | i);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
__syncwarp();
|
|
415
|
+
if (last) break;
|
|
416
|
+
|
|
417
|
+
// 3a. Sort the selection by (score, payload) into the running list of stage f + 1.
|
|
418
|
+
uint64_t o[MQ];
|
|
419
|
+
#pragma unroll
|
|
420
|
+
for (int r = 0; r < MQ; ++r) {
|
|
421
|
+
const int e = lane * MQ + r;
|
|
422
|
+
o[r] = e < kk ? s.order[e] : 0;
|
|
423
|
+
}
|
|
424
|
+
bitonic_sort<true, 32, MQ>(o, lane);
|
|
425
|
+
float nv[MQ];
|
|
426
|
+
uint32_t nl[MQ];
|
|
427
|
+
#pragma unroll
|
|
428
|
+
for (int r = 0; r < MQ; ++r) {
|
|
429
|
+
const int ij = int(o[r] >> 8) & 0xff, i = int(o[r]) & 0xff, j = ij - i;
|
|
430
|
+
nv[r] = round_to<T>(__fadd_rn(s.val[0][i], s.val[1][j]));
|
|
431
|
+
nl[r] = s.lin[i] << p.bits[f] | s.col[j];
|
|
432
|
+
}
|
|
433
|
+
__syncwarp();
|
|
434
|
+
#pragma unroll
|
|
435
|
+
for (int r = 0; r < MQ; ++r) {
|
|
436
|
+
const int e = lane * MQ + r;
|
|
437
|
+
if (e < kk) {
|
|
438
|
+
s.val[0][e] = nv[r];
|
|
439
|
+
s.lin[e] = nl[r];
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
__syncwarp();
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// 3b. Sort by slot index; recompute each value from the input row, factor by factor.
|
|
446
|
+
const int kk = p.kk;
|
|
447
|
+
uint32_t out[MQ];
|
|
448
|
+
#pragma unroll
|
|
449
|
+
for (int r = 0; r < MQ; ++r) {
|
|
450
|
+
const int e = lane * MQ + r;
|
|
451
|
+
out[r] = e < kk ? s.sel[e] : 0xffffffffu;
|
|
452
|
+
}
|
|
453
|
+
bitonic_sort<false, 32, MQ>(out, lane);
|
|
454
|
+
#pragma unroll
|
|
455
|
+
for (int r = 0; r < MQ; ++r) {
|
|
456
|
+
const int e = lane * MQ + r;
|
|
457
|
+
if (e < kk) {
|
|
458
|
+
int64_t lin = uint64_t(out[r]) >> p.pos[0];
|
|
459
|
+
float v = to_float(x[lin]);
|
|
460
|
+
#pragma unroll
|
|
461
|
+
for (int f = 1; f < NF; ++f) {
|
|
462
|
+
if (kMulti && f >= p.nf) break;
|
|
463
|
+
const int c = field(out[r], p.pos[f], p.bits[f]);
|
|
464
|
+
const float y = to_float(x[p.offset[f] + c]);
|
|
465
|
+
lin = lin * p.width[f] + c;
|
|
466
|
+
v = __fadd_rn(f > 1 ? round_to<T>(v) : v, y);
|
|
467
|
+
}
|
|
468
|
+
out_lin[row * kk + e] = lin;
|
|
469
|
+
out_val[row * kk + e] = from_float<T>(v);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
template <typename T, int MQ, bool kMulti>
|
|
476
|
+
__global__ __launch_bounds__(kWarps * 32) void product_key_topk_bwd_kernel(
|
|
477
|
+
const int64_t* __restrict__ lin, const T* __restrict__ grad_val, T* __restrict__ grad_raw,
|
|
478
|
+
BwdProblem p) {
|
|
479
|
+
constexpr int NF = kMulti ? kMaxFactors : 2;
|
|
480
|
+
__shared__ float stage[kWarps][32 * MQ];
|
|
481
|
+
const int warp = threadIdx.x >> 5, lane = threadIdx.x & 31;
|
|
482
|
+
const int64_t row = int64_t(blockIdx.x) * kWarps + warp;
|
|
483
|
+
if (row >= p.rows) return;
|
|
484
|
+
const int kk = p.kk;
|
|
485
|
+
T* out = grad_raw + row * p.row_width;
|
|
486
|
+
for (int c = lane; c < p.row_width; c += 32) out[c] = from_float<T>(0.f);
|
|
487
|
+
|
|
488
|
+
uint32_t packed[MQ]; // bit-packed columns
|
|
489
|
+
float g[MQ];
|
|
490
|
+
#pragma unroll
|
|
491
|
+
for (int r = 0; r < MQ; ++r) {
|
|
492
|
+
const int e = lane * MQ + r;
|
|
493
|
+
packed[r] = 0;
|
|
494
|
+
g[r] = 0.f;
|
|
495
|
+
if (e < kk) {
|
|
496
|
+
uint32_t rest = uint32_t(lin[row * kk + e]);
|
|
497
|
+
#pragma unroll
|
|
498
|
+
for (int f = NF - 1; f > 0; --f) {
|
|
499
|
+
if (kMulti && f >= p.nf) continue;
|
|
500
|
+
const uint32_t q = rest / p.width[f];
|
|
501
|
+
packed[r] |= (rest - q * p.width[f]) << p.pos[f];
|
|
502
|
+
rest = q;
|
|
503
|
+
}
|
|
504
|
+
packed[r] |= uint32_t(uint64_t(rest) << p.pos[0]);
|
|
505
|
+
g[r] = to_float(grad_val[row * p.gv_row_stride + e * p.gv_col_stride]);
|
|
506
|
+
stage[warp][e] = g[r];
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
__syncwarp();
|
|
510
|
+
// lin is ascending, so each factor-0 column is a contiguous run; the other factors sort
|
|
511
|
+
// (column, position) first.
|
|
512
|
+
#pragma unroll
|
|
513
|
+
for (int f = 0; f < NF; ++f) {
|
|
514
|
+
if (kMulti && f >= p.nf) break;
|
|
515
|
+
uint32_t c[MQ];
|
|
516
|
+
float v[MQ];
|
|
517
|
+
#pragma unroll
|
|
518
|
+
for (int r = 0; r < MQ; ++r) {
|
|
519
|
+
const int e = lane * MQ + r;
|
|
520
|
+
const uint32_t col = field(packed[r], p.pos[f], p.bits[f]);
|
|
521
|
+
c[r] = e >= kk ? 0xffffffffu : f ? col << 8 | e : col;
|
|
522
|
+
v[r] = g[r];
|
|
523
|
+
}
|
|
524
|
+
if (f) {
|
|
525
|
+
bitonic_sort<false, 32, MQ>(c, lane);
|
|
526
|
+
#pragma unroll
|
|
527
|
+
for (int r = 0; r < MQ; ++r) {
|
|
528
|
+
v[r] = c[r] == 0xffffffffu ? 0.f : stage[warp][c[r] & 0xff];
|
|
529
|
+
c[r] >>= 8;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
store_run_sums<MQ>(c, v, lane, p.width[f], out + p.offset[f]);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
int pow2_ceil(int64_t x) {
|
|
537
|
+
int p = 1;
|
|
538
|
+
while (p < x) p *= 2;
|
|
539
|
+
return p;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
int bit_width(int64_t x) {
|
|
543
|
+
int b = 0;
|
|
544
|
+
while ((int64_t(1) << b) <= x) ++b;
|
|
545
|
+
return b;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
template <typename T, int M, bool kStream, int KMAX, bool kMulti>
|
|
549
|
+
void launch_fwd(const at::Tensor& raw, at::Tensor& val, at::Tensor& lin, const FwdProblem& p) {
|
|
550
|
+
product_key_topk_fwd_kernel<T, M, kStream, KMAX, kMulti>
|
|
551
|
+
<<<(p.rows + kWarps - 1) / kWarps, kWarps * 32, 0, at::cuda::getCurrentCUDAStream()>>>(
|
|
552
|
+
reinterpret_cast<const T*>(raw.data_ptr()), reinterpret_cast<T*>(val.data_ptr()),
|
|
553
|
+
lin.data_ptr<int64_t>(), p);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
template <typename T, int M, bool kStream, bool kMulti>
|
|
557
|
+
void dispatch_fwd(int kmax, const at::Tensor& raw, at::Tensor& val, at::Tensor& lin,
|
|
558
|
+
const FwdProblem& p) {
|
|
559
|
+
switch (kmax) {
|
|
560
|
+
case 16: return launch_fwd<T, M, kStream, 16, kMulti>(raw, val, lin, p);
|
|
561
|
+
case 32: return launch_fwd<T, M, kStream, 32, kMulti>(raw, val, lin, p);
|
|
562
|
+
case 64: return launch_fwd<T, M, kStream, 64, kMulti>(raw, val, lin, p);
|
|
563
|
+
case 128: return launch_fwd<T, M, kStream, 128, kMulti>(raw, val, lin, p);
|
|
564
|
+
default: return launch_fwd<T, M, kStream, 256, kMulti>(raw, val, lin, p);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Two factors pick M = pow2ceil(ceil(width / 16)) keys per lane (streamed past 256 columns);
|
|
569
|
+
// more factors use M = 1 or 2 up to 32 columns and streamed M = 16 beyond.
|
|
570
|
+
template <typename T>
|
|
571
|
+
void dispatch_fwd(int max_width, int kmax, const at::Tensor& raw, at::Tensor& val,
|
|
572
|
+
at::Tensor& lin, const FwdProblem& p) {
|
|
573
|
+
if (p.nf > 2) {
|
|
574
|
+
if (max_width <= 16) return dispatch_fwd<T, 1, false, true>(kmax, raw, val, lin, p);
|
|
575
|
+
if (max_width <= 32) return dispatch_fwd<T, 2, false, true>(kmax, raw, val, lin, p);
|
|
576
|
+
return dispatch_fwd<T, 16, true, true>(kmax, raw, val, lin, p);
|
|
577
|
+
}
|
|
578
|
+
switch (std::min(16, pow2_ceil((max_width + 15) / 16))) {
|
|
579
|
+
case 1: return dispatch_fwd<T, 1, false, false>(kmax, raw, val, lin, p);
|
|
580
|
+
case 2: return dispatch_fwd<T, 2, false, false>(kmax, raw, val, lin, p);
|
|
581
|
+
case 4: return dispatch_fwd<T, 4, false, false>(kmax, raw, val, lin, p);
|
|
582
|
+
case 8: return dispatch_fwd<T, 8, false, false>(kmax, raw, val, lin, p);
|
|
583
|
+
default:
|
|
584
|
+
if (max_width > 256) return dispatch_fwd<T, 16, true, false>(kmax, raw, val, lin, p);
|
|
585
|
+
return dispatch_fwd<T, 16, false, false>(kmax, raw, val, lin, p);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
template <typename T, int MQ, bool kMulti>
|
|
590
|
+
void launch_bwd(const at::Tensor& lin, const at::Tensor& grad_val, at::Tensor& grad,
|
|
591
|
+
const BwdProblem& p) {
|
|
592
|
+
product_key_topk_bwd_kernel<T, MQ, kMulti>
|
|
593
|
+
<<<(p.rows + kWarps - 1) / kWarps, kWarps * 32, 0, at::cuda::getCurrentCUDAStream()>>>(
|
|
594
|
+
lin.data_ptr<int64_t>(), reinterpret_cast<const T*>(grad_val.data_ptr()),
|
|
595
|
+
reinterpret_cast<T*>(grad.data_ptr()), p);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
template <typename T, bool kMulti>
|
|
599
|
+
void dispatch_bwd(int mq, const at::Tensor& lin, const at::Tensor& grad_val, at::Tensor& grad,
|
|
600
|
+
const BwdProblem& p) {
|
|
601
|
+
switch (mq) {
|
|
602
|
+
case 1: return launch_bwd<T, 1, kMulti>(lin, grad_val, grad, p);
|
|
603
|
+
case 2: return launch_bwd<T, 2, kMulti>(lin, grad_val, grad, p);
|
|
604
|
+
case 4: return launch_bwd<T, 4, kMulti>(lin, grad_val, grad, p);
|
|
605
|
+
default: return launch_bwd<T, 8, kMulti>(lin, grad_val, grad, p);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
template <typename T>
|
|
610
|
+
void dispatch_bwd(int mq, const at::Tensor& lin, const at::Tensor& grad_val, at::Tensor& grad,
|
|
611
|
+
const BwdProblem& p) {
|
|
612
|
+
if (p.nf > 2) return dispatch_bwd<T, true>(mq, lin, grad_val, grad, p);
|
|
613
|
+
return dispatch_bwd<T, false>(mq, lin, grad_val, grad, p);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
void check_dtype(const at::Tensor& t, const char* name) {
|
|
617
|
+
TORCH_CHECK(t.scalar_type() == at::kBFloat16 || t.scalar_type() == at::kFloat, name,
|
|
618
|
+
" must be bfloat16 or float32, got ", t.scalar_type());
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// Validates the factor widths and fills the factor fields of a problem; returns the number
|
|
622
|
+
// of slots (< 2^32).
|
|
623
|
+
template <typename P>
|
|
624
|
+
int64_t set_factors(P& p, const std::vector<int64_t>& factors, int64_t row_width) {
|
|
625
|
+
p.nf = int(factors.size());
|
|
626
|
+
TORCH_CHECK(p.nf >= 2 && p.nf <= kMaxFactors, "need 2 to ", kMaxFactors, " factors, got ",
|
|
627
|
+
p.nf);
|
|
628
|
+
int64_t sum = 0, slots = 1;
|
|
629
|
+
int bits = 0;
|
|
630
|
+
for (int f = p.nf - 1; f >= 0; --f) {
|
|
631
|
+
const int64_t w = factors[f];
|
|
632
|
+
TORCH_CHECK(w >= 1 && w <= kMaxWidth, "factor widths must be in [1, ", kMaxWidth,
|
|
633
|
+
"], got ", w);
|
|
634
|
+
p.width[f] = int(w);
|
|
635
|
+
p.bits[f] = bit_width(w - 1);
|
|
636
|
+
p.pos[f] = bits;
|
|
637
|
+
bits += p.bits[f];
|
|
638
|
+
sum += w;
|
|
639
|
+
slots *= w;
|
|
640
|
+
}
|
|
641
|
+
TORCH_CHECK(sum == row_width, "the factor widths must sum to the row width ", row_width,
|
|
642
|
+
", got ", sum);
|
|
643
|
+
TORCH_CHECK(bits <= 32, "the factors need ", bits, " bits of slot index (at most 32)");
|
|
644
|
+
for (int f = 1; f < p.nf; ++f) p.offset[f] = p.offset[f - 1] + p.width[f - 1];
|
|
645
|
+
return slots;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
} // namespace
|
|
649
|
+
|
|
650
|
+
std::vector<at::Tensor> topk_fwd(const at::Tensor& raw, std::vector<int64_t> factors,
|
|
651
|
+
int64_t k) {
|
|
652
|
+
TORCH_CHECK(raw.is_cuda() && raw.dim() == 2 && raw.stride(1) == 1,
|
|
653
|
+
"raw must be a CUDA [rows, sum(factors)] tensor with unit column stride");
|
|
654
|
+
check_dtype(raw, "raw");
|
|
655
|
+
FwdProblem p{};
|
|
656
|
+
const int64_t slots = set_factors(p, factors, raw.size(1));
|
|
657
|
+
TORCH_CHECK(k >= 1 && k <= kMaxK, "k must be in [1, ", kMaxK, "], got ", k);
|
|
658
|
+
p.rows = raw.size(0);
|
|
659
|
+
p.row_stride = raw.stride(0);
|
|
660
|
+
p.kk = int(std::min<int64_t>(k, slots));
|
|
661
|
+
int max_width = 0;
|
|
662
|
+
for (int f = 0; f < p.nf; ++f) {
|
|
663
|
+
max_width = std::max(max_width, p.width[f]);
|
|
664
|
+
p.kf[f] = int(std::min<int64_t>(k, p.width[f]));
|
|
665
|
+
p.count[f] = f ? int(std::min<int64_t>(k, int64_t(p.count[f - 1]) * p.kf[f])) : p.kf[0];
|
|
666
|
+
if (f == 0) continue;
|
|
667
|
+
for (int a = 1; a * a <= p.count[f]; ++a) {
|
|
668
|
+
if (p.count[f] % a == 0 && a <= p.count[f - 1] && p.count[f] / a <= p.kf[f]) {
|
|
669
|
+
p.lo_i[f] = a;
|
|
670
|
+
p.lo_j[f] = p.count[f] / a;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
auto val = at::empty({p.rows, p.kk}, raw.options());
|
|
675
|
+
auto lin = at::empty({p.rows, p.kk}, raw.options().dtype(at::kLong));
|
|
676
|
+
if (p.rows == 0) return {val, lin};
|
|
677
|
+
const c10::cuda::CUDAGuard guard(raw.device());
|
|
678
|
+
const int kmax = std::max(16, pow2_ceil(p.kk));
|
|
679
|
+
if (raw.scalar_type() == at::kFloat)
|
|
680
|
+
dispatch_fwd<float>(max_width, kmax, raw, val, lin, p);
|
|
681
|
+
else
|
|
682
|
+
dispatch_fwd<__nv_bfloat16>(max_width, kmax, raw, val, lin, p);
|
|
683
|
+
C10_CUDA_KERNEL_LAUNCH_CHECK();
|
|
684
|
+
return {val, lin};
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
at::Tensor topk_bwd(const at::Tensor& lin, const at::Tensor& grad_val,
|
|
688
|
+
std::vector<int64_t> factors) {
|
|
689
|
+
TORCH_CHECK(lin.is_cuda() && lin.dim() == 2 && lin.is_contiguous() &&
|
|
690
|
+
lin.scalar_type() == at::kLong, "lin must be a contiguous CUDA int64 matrix");
|
|
691
|
+
TORCH_CHECK(grad_val.device() == lin.device() && grad_val.sizes() == lin.sizes(),
|
|
692
|
+
"grad_val must match lin's shape and device");
|
|
693
|
+
check_dtype(grad_val, "grad_val");
|
|
694
|
+
BwdProblem p{};
|
|
695
|
+
int64_t row_width = 0;
|
|
696
|
+
for (const int64_t w : factors) row_width += w;
|
|
697
|
+
const int64_t slots = set_factors(p, factors, row_width);
|
|
698
|
+
TORCH_CHECK(lin.size(1) >= 1 && lin.size(1) <= std::min<int64_t>(kMaxK, slots),
|
|
699
|
+
"inconsistent factors or selection width");
|
|
700
|
+
p.rows = lin.size(0);
|
|
701
|
+
p.gv_row_stride = grad_val.stride(0);
|
|
702
|
+
p.gv_col_stride = grad_val.stride(1);
|
|
703
|
+
p.kk = int(lin.size(1));
|
|
704
|
+
p.row_width = int(row_width);
|
|
705
|
+
auto grad = at::empty({p.rows, row_width}, grad_val.options());
|
|
706
|
+
if (p.rows == 0) return grad;
|
|
707
|
+
const c10::cuda::CUDAGuard guard(lin.device());
|
|
708
|
+
const int mq = pow2_ceil((p.kk + 31) / 32);
|
|
709
|
+
if (grad_val.scalar_type() == at::kFloat)
|
|
710
|
+
dispatch_bwd<float>(mq, lin, grad_val, grad, p);
|
|
711
|
+
else
|
|
712
|
+
dispatch_bwd<__nv_bfloat16>(mq, lin, grad_val, grad, p);
|
|
713
|
+
C10_CUDA_KERNEL_LAUNCH_CHECK();
|
|
714
|
+
return grad;
|
|
715
|
+
}
|
|
716
|
+
"""
|
|
717
|
+
|
|
718
|
+
_CPP_SOURCE = (
|
|
719
|
+
"std::vector<at::Tensor> topk_fwd(const at::Tensor& raw, std::vector<int64_t> factors, "
|
|
720
|
+
"int64_t k);\n"
|
|
721
|
+
"at::Tensor topk_bwd(const at::Tensor& lin, const at::Tensor& grad_val, "
|
|
722
|
+
"std::vector<int64_t> factors);\n"
|
|
723
|
+
)
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
@functools.cache
|
|
727
|
+
def _module():
|
|
728
|
+
"""The compiled extension (built on first use)."""
|
|
729
|
+
return load_cuda_extension(
|
|
730
|
+
"sdm_product_key_topk",
|
|
731
|
+
cpp_sources=_CPP_SOURCE,
|
|
732
|
+
cuda_sources=_CUDA_SOURCE,
|
|
733
|
+
functions=("topk_fwd", "topk_bwd"),
|
|
734
|
+
extra_cuda_cflags=("-O3",),
|
|
735
|
+
)
|