Prompt48 commited on
Commit
58588e4
·
verified ·
1 Parent(s): bb53c92

Upload edit\Qwen3-TTS-test\.venv\Lib\site-packages\torch\include\ATen\native\cuda\TensorModeKernel.cuh with huggingface_hub

Browse files
edit//Qwen3-TTS-test//.venv//Lib//site-packages//torch//include//ATen//native//cuda//TensorModeKernel.cuh ADDED
@@ -0,0 +1,433 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <ATen/cuda/detail/IndexUtils.cuh>
4
+ #include <ATen/native/cuda/Loops.cuh>
5
+ #include <ATen/native/cuda/SortingCommon.cuh>
6
+ #include <ATen/native/cuda/block_reduce.cuh>
7
+
8
+ namespace at::native {
9
+
10
+ // Used for a segmented reduction
11
+ struct ModeUnsignedBoolPair {
12
+ unsigned int val;
13
+ bool flag;
14
+ };
15
+
16
+ // In the kernel below, we have a common pattern of reducing (unsigned int,
17
+ // unsigned int) pairs of data
18
+ struct ModeUnsignedPair {
19
+ unsigned int val;
20
+ unsigned int index;
21
+ };
22
+
23
+ // Inclusive Scan via an upsweep/downsweep mechanism. Assumes:
24
+ //
25
+ // 1. Power2ScanSize is a power of 2. This code still works for collections that
26
+ // do not exactly contain a power of 2 number of elements, simply round up to
27
+ // the nearest power of 2 and then call.
28
+ //
29
+ // 2. That there are two-elements per thread, i.e. the size of the smem storage
30
+ // is 2 * blockDim.x * sizeof(T).
31
+ //
32
+ // Consider a (+)-Scan on the following elements:
33
+ //
34
+ // Upsweep:
35
+ //
36
+ // 0 1 2 3 4 5 6 7
37
+ // 1 5 9 13
38
+ // 6 22
39
+ // 28
40
+ //
41
+ // Downsweep:
42
+ // 15
43
+ // 3 10 21
44
+ template <int Power2ScanSize, typename T, class BinaryOp>
45
+ __device__ void inclusivePrefixScan(T* smem, BinaryOp binop) {
46
+ // Reduce step ("upsweep")
47
+ #pragma unroll
48
+ for (int stride = 1; stride < Power2ScanSize; stride <<= 1) {
49
+ int index = (threadIdx.x + 1) * stride * 2 - 1;
50
+ if (index < Power2ScanSize) {
51
+ smem[index] = binop(smem[index], smem[index - stride]);
52
+ }
53
+ __syncthreads();
54
+ }
55
+
56
+ // Post-reduce step ("downsweep")
57
+ #pragma unroll
58
+ for (int stride = Power2ScanSize / 4; stride > 0; stride >>= 1) {
59
+ int index = (threadIdx.x + 1) * stride * 2 - 1;
60
+ if ((index + stride) < Power2ScanSize) {
61
+ smem[index + stride] = binop(smem[index + stride], smem[index]);
62
+ }
63
+ __syncthreads();
64
+ }
65
+ }
66
+
67
+ // Block-wide reduction where each thread locally reduces N
68
+ // values before letting a single warp take over - assumes
69
+ // threadVals is in registers, not shared memory
70
+ //
71
+ // If smem is not used again, there is no need to __syncthreads before this
72
+ // call. However, if smem will be used, e.g., this function is called in a loop,
73
+ // then __syncthreads is needed either before or afterwards to prevent non-0
74
+ // threads overriding smem in the next loop before num-0 thread reads from it.
75
+ template <int N, typename T, typename ReduceOp>
76
+ __device__ T reduceBlockWithNThreadLocalReductions(
77
+ T* smem,
78
+ T threadVals[N],
79
+ const unsigned int numVals,
80
+ ReduceOp reduceOp,
81
+ T init) {
82
+ int offset = threadIdx.x * N;
83
+ T local = offset < numVals ? threadVals[0] : init;
84
+
85
+ #pragma unroll
86
+ for (int i = 1; i < N; ++i) {
87
+ ++offset;
88
+ T next = offset < numVals ? threadVals[i] : init;
89
+ local = reduceOp.combine(local, next);
90
+ }
91
+
92
+ return cuda_utils::BlockReduce(local, reduceOp, init, smem);
93
+ }
94
+
95
+ template <typename T>
96
+ __device__ inline void swapVars(T& t1, T& t2) {
97
+ T tmp = t1;
98
+ t1 = t2;
99
+ t2 = tmp;
100
+ }
101
+
102
+ template <typename Comparator, typename K, typename V>
103
+ __device__ inline void bitonicSwap(
104
+ K& kA,
105
+ V& vA,
106
+ bool& validA,
107
+ K& kB,
108
+ V& vB,
109
+ bool& validB,
110
+ bool dir,
111
+ const Comparator& comp) {
112
+ // Invalid entries always sort to the end
113
+ bool swap = (comp(kA, kB) && validA) || !validB;
114
+ if (swap == dir) {
115
+ swapVars(kA, kB);
116
+ swapVars(vA, vB);
117
+ swapVars(validA, validB);
118
+ }
119
+ };
120
+
121
+ template <typename Comparator, typename K>
122
+ __device__ inline void bitonicSwapKeys(
123
+ K& kA,
124
+ bool& validA,
125
+ K& kB,
126
+ bool& validB,
127
+ bool dir,
128
+ const Comparator& comp) {
129
+ bool swap = (comp(kA, kB) && validA) || !validB;
130
+ if (swap == dir) {
131
+ swapVars(kA, kB);
132
+ swapVars(validA, validB);
133
+ }
134
+ }
135
+
136
+ template <
137
+ typename K,
138
+ typename IndexType,
139
+ int Power2SortSize,
140
+ typename Comparator>
141
+ __device__ inline void bitonicSortKeys(
142
+ K keys[Power2SortSize],
143
+ bool valid[Power2SortSize],
144
+ const Comparator& comp) {
145
+ #if !defined(USE_ROCM)
146
+ #pragma unroll
147
+ #endif
148
+ for (unsigned int size = 2; size < Power2SortSize; size *= 2) {
149
+ bool flag = ((threadIdx.x & (size / 2)) != 0);
150
+
151
+ #if !defined(USE_ROCM)
152
+ #pragma unroll
153
+ #endif
154
+ for (unsigned int stride = size / 2; stride > 0; stride /= 2) {
155
+ __syncthreads();
156
+
157
+ unsigned int pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));
158
+ bitonicSwapKeys<Comparator, K>(
159
+ keys[pos],
160
+ valid[pos],
161
+ keys[pos + stride],
162
+ valid[pos + stride],
163
+ flag,
164
+ comp);
165
+ }
166
+ }
167
+
168
+ #if !defined(USE_ROCM)
169
+ #pragma unroll
170
+ #endif
171
+ for (unsigned int stride = Power2SortSize / 2; stride > 0; stride /= 2) {
172
+ __syncthreads();
173
+
174
+ unsigned int pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));
175
+ bitonicSwapKeys<Comparator, K>(
176
+ keys[pos],
177
+ valid[pos],
178
+ keys[pos + stride],
179
+ valid[pos + stride],
180
+ false,
181
+ comp);
182
+ }
183
+
184
+ __syncthreads();
185
+ }
186
+
187
+ // The mode kernel has the following characteristics: It uses internal shared
188
+ // memory buffers of Power2Size, which must be greater than the number of
189
+ // elements. Additionally, there is one block for every slice to calculate the
190
+ // mode for, and in each block there is one thread for every two elements.
191
+ //
192
+ // Both sorted and positions are assumed to be contiguous Tensors with the mode
193
+ // dimension as the innermost dim, such that we can get the particular slice for
194
+ // a Tensor via its linear block dimension * the slice size.
195
+ template <typename T, unsigned int Power2Size>
196
+ #if defined(CUDA_VERSION) && CUDA_VERSION >= 11070
197
+ __launch_bounds__(1024, 1)
198
+ #endif
199
+ __global__ void compute_mode(
200
+ const T* input,
201
+ at::cuda::detail::TensorInfo<T, unsigned int> values,
202
+ at::cuda::detail::TensorInfo<int64_t, unsigned int> indices,
203
+ int64_t sliceSize,
204
+ int64_t slices) {
205
+ int tidx = threadIdx.x;
206
+ int stidx = blockDim.x + threadIdx.x; // Second index this thread responsible for
207
+
208
+ // First, we need to calculate the offset into the sorted Tensor that
209
+ // represents the start of the slice for this block to calculate the mode for.
210
+ // This offset is a combination of the gridIndices, and the number of elements
211
+ // in the slice.
212
+ unsigned int blockId = getLinearBlockId<unsigned int>();
213
+ unsigned int linearOffset = blockId * sliceSize;
214
+
215
+ if (blockId >= slices) {
216
+ return;
217
+ }
218
+
219
+ // shmem is a dynamically sized buffer we will use throughout the kernel to
220
+ // handle computation efficiently. The size of this shmem must be
221
+ // sizeof(T) * Power2Size + (2 * sizeof(unsigned int) * Power2Size)
222
+ //
223
+ // Initially, the buffer will be organized as follows:
224
+ //
225
+ // [smem (slice elements) | bmem (valid indices) | <scratch space>]
226
+ extern __shared__ char shmem[];
227
+
228
+ // smem represents a proportion of the shared memory buffer that is used to
229
+ // store the elements from the slice:
230
+ T* smem = reinterpret_cast<T*>(shmem);
231
+
232
+ // Each thread loads up to two elements from the Tensor into shared memory
233
+ if (tidx < sliceSize) {
234
+ smem[tidx] = c10::load(&input[linearOffset + tidx]);
235
+ }
236
+ if (stidx < sliceSize) {
237
+ smem[stidx] = c10::load(&input[linearOffset + stidx]);
238
+ }
239
+
240
+ // Next, we initialize a boolean region of the buffer, offset by the loaded
241
+ // element smem region
242
+ bool* bmem = reinterpret_cast<bool*>(&smem[Power2Size]);
243
+
244
+ // The first use of this region stores bmem[i] = i < sliceSize to mark the
245
+ // valid components in the smem buffer
246
+ bmem[tidx] = tidx < sliceSize;
247
+ bmem[stidx] = stidx < sliceSize;
248
+ __syncthreads(); // barrier for smem, bmem initialization
249
+
250
+ // First, sort the input slice in ascending order. smem contains the input
251
+ // elements, and bmem marks the valid indices
252
+ bitonicSortKeys<T, unsigned int, Power2Size>(
253
+ smem, bmem, [&] GPU_LAMBDA(const auto& a, const auto& b) {
254
+ return a < b;
255
+ });
256
+ __syncthreads(); // make no assumptions that the sort syncs at end
257
+
258
+ // The next step of our algorithm is performing a block-wide comparison of
259
+ // neighboring elements. In particular, given an sorted input slice A, we
260
+ // produce an output slice B, such that B[i] = 1 if A[i-i] != A[i], otherwise
261
+ // 0.
262
+ //
263
+ // Given the input A = [0, 0, 1, 1, 2, 2, 2, 4, 5, 6, 6, 7, 8]
264
+ // B = [1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1]
265
+ //
266
+ // In particular, we can think of B[i] true indicating the start of a sequence
267
+ // of equal values in the sorted list. Similarly, we will also store the
268
+ // negation of B, which we'll call C. In particular, we can think of C[i] =
269
+ // true iff A[i-1] == A[i] in our original sorted slice.
270
+ //
271
+ // C = [0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0]
272
+
273
+ // We overwrite bmem, and treat the rest of shared memory as a buffer of
274
+ // (index, flag) pairs where the index represents values from C, and the flag
275
+ // represents values from B.
276
+ //
277
+ // [smem (sorted slice) | ubpmem (index, flag pairs)]
278
+
279
+ struct ModeUnsignedBoolPair* ubpmem =
280
+ reinterpret_cast<struct ModeUnsignedBoolPair*>(&smem[Power2Size]);
281
+
282
+ if (tidx == 0) {
283
+ ubpmem[0].flag = true;
284
+ ubpmem[0].val = 0;
285
+ }
286
+
287
+ // Compares elements (0, 1), (2, 3), ... and sets 1, 3, ...
288
+ ubpmem[tidx * 2 + 1].flag =
289
+ smem[tidx * 2] != smem[tidx * 2 + 1]; // (0, 1), (1, 2), etc.
290
+ ubpmem[tidx * 2 + 1].val = !ubpmem[tidx * 2 + 1].flag;
291
+
292
+ // Compares elements (1, 2), (3, 4), ... and sets 2, 4, ...
293
+ if (((tidx + 1) * 2) < Power2Size) {
294
+ ubpmem[(tidx + 1) * 2].flag =
295
+ smem[((tidx + 1) * 2) - 1] != smem[(tidx + 1) * 2];
296
+ ubpmem[(tidx + 1) * 2].val = !ubpmem[(tidx + 1) * 2].flag;
297
+ }
298
+ __syncthreads(); // barrier for ubpmem initialization
299
+
300
+ // Next, we perform a segmented prefix sum on the neighboring elements, where
301
+ // the presence of a one indicates the start of a segment. In this case B acts
302
+ // as the segment start flags, and C is the buffer to be summed:
303
+ //
304
+ // Input (C) = [0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0]
305
+ // Flag (B) = [1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1]
306
+ // Output (C) = [0, 1, 0, 1, 0, 1, 2, 0, 0, 0, 1, 0, 0]
307
+ //
308
+ // Afterwards, the (index) components of the ubpmem buffer contain the lengths
309
+ // of the segments (minus 1), i.e. the counts of each element in the original
310
+ // input.
311
+ inclusivePrefixScan<Power2Size>(
312
+ ubpmem, [=] GPU_LAMBDA(const auto& a, const auto& b) {
313
+ ModeUnsignedBoolPair c;
314
+ c.val = a.flag ? a.val : a.val + b.val;
315
+ c.flag = a.flag | b.flag;
316
+ return c;
317
+ });
318
+ // assumes scan syncs at the end
319
+
320
+ // Next, we reinterpret the ubpmem buffer as pairs of unsigned integers (i.e.
321
+ // we treat the boolean flag regions as integers). We initialize these to
322
+ // represent indices, and we'll call this buffer I
323
+ struct ModeUnsignedPair* uupmem =
324
+ reinterpret_cast<struct ModeUnsignedPair*>(ubpmem);
325
+
326
+ // At this point, we need to find the maximum element in lengths buffer C.
327
+ // This element will represent the count (-1) of the mode. Because of the
328
+ // way we have set up the problem, the index where this mode occurs will
329
+ // also be the location of the mode value in the sorted array, e.g.
330
+ //
331
+ // smem = [0, 0, 1, 1, 1, 2]
332
+ // C = [0, 1, 0, 1, 2, 0]
333
+ // I = [0, 1, 2, 3, 4, 5]
334
+ // ^
335
+ // maximum value, also aligned with mode = 1
336
+ //
337
+ // We perform a block wide max-reduction of the C buffer, but we also need the
338
+ // indices to come along with it, so we utilize the uupmem construction.
339
+ //
340
+ // At the end we need to return the ModeUnsignedPair containing index = 4, val
341
+ // = 2, which represents the max
342
+
343
+ // In practice, we will make each thread locally reduce 2 values in its
344
+ // registers prior to the global block-wide reduction. Note that instead of
345
+ // tidx/stidx, we utilize tidx * 2, tidx * 2 + 1, so each thread deals with
346
+ // adjacent elements. This is because the reduce code below relies on thread
347
+ // elements to be adjacent.
348
+ struct ModeUnsignedPair uup[2];
349
+ uup[0].index = tidx * 2;
350
+ uup[0].val = ubpmem[tidx * 2].val;
351
+ uup[1].index = tidx * 2 + 1;
352
+ uup[1].val = ubpmem[tidx * 2 + 1].val;
353
+ __syncthreads();
354
+
355
+ struct ModeUnsignedPair max = {0, 0};
356
+
357
+ struct MaxOp {
358
+ inline __device__ ModeUnsignedPair combine(ModeUnsignedPair a, ModeUnsignedPair b) const {
359
+ return b.val > a.val ? b : a;
360
+ }
361
+
362
+ inline __device__ ModeUnsignedPair warp_shfl_down(ModeUnsignedPair acc, int offset) const {
363
+ ModeUnsignedPair ret;
364
+ ret.index = WARP_SHFL_DOWN(acc.index, offset);
365
+ ret.val = WARP_SHFL_DOWN(acc.val, offset);
366
+ return ret;
367
+ }
368
+ } max_op;
369
+
370
+ max = reduceBlockWithNThreadLocalReductions<2>(
371
+ uupmem,
372
+ uup,
373
+ sliceSize,
374
+ max_op,
375
+ max);
376
+
377
+ // Store the mode in shared memory for use in finding the mode in the input
378
+ // slice
379
+ __shared__ T mode;
380
+
381
+ // Given the above constraints, the mode is the value at the reduced index in
382
+ // the original sorted element buffer
383
+ if (tidx == 0) {
384
+ mode = smem[max.index];
385
+ }
386
+ __syncthreads(); // broadcast mode
387
+
388
+ // Finally, we need to find "an" index of the mode in the input
389
+ // Tensor. The API does not constrain which index we pick, but here
390
+ // we always pick the largest index. We store the index if the value
391
+ // is the mode, or 0 otherwise. Then find the maximum value.
392
+ //
393
+ // Again we reduce 2 elements in the thread's registers prior to the
394
+ // block-wide reduction
395
+ unsigned mode_index[2] = {0u, 0u};
396
+ if (tidx * 2 < sliceSize) {
397
+ const unsigned idx = tidx * 2;
398
+ mode_index[0] = c10::load(&input[linearOffset + idx]) == mode ? idx : 0u;
399
+ }
400
+ if (tidx * 2 + 1 < sliceSize) {
401
+ const unsigned idx = tidx * 2 + 1;
402
+ mode_index[1] = c10::load(&input[linearOffset + idx]) == mode ? idx : 0u;
403
+ }
404
+
405
+ struct MaxIndexOp {
406
+ inline __device__ unsigned combine(unsigned a, unsigned b) const {
407
+ return b > a ? b : a;
408
+ }
409
+
410
+ inline __device__ unsigned warp_shfl_down(unsigned acc, int offset) const {
411
+ return WARP_SHFL_DOWN(acc, offset);
412
+ }
413
+ } max_index_op;
414
+
415
+ int64_t index = reduceBlockWithNThreadLocalReductions<2>(
416
+ reinterpret_cast<unsigned*>(&shmem[0]),
417
+ mode_index,
418
+ sliceSize,
419
+ max_index_op,
420
+ 0u);
421
+
422
+ // Finally, we have the mode, and an index where it occurs. We use a single
423
+ // thread to place this in the appropriate output position
424
+ if (tidx == 0) {
425
+ unsigned int outputOffset =
426
+ at::cuda::detail::IndexToOffset<T, unsigned int, -1>::get(
427
+ blockId, values);
428
+ values.data[outputOffset] = mode;
429
+ indices.data[outputOffset] = index;
430
+ }
431
+ }
432
+
433
+ } // namespace at::native