Upload edit\Qwen3-TTS-test\.venv\Lib\site-packages\torch\include\ATen\native\cuda\ScanUtils.cuh with huggingface_hub
Browse files
edit//Qwen3-TTS-test//.venv//Lib//site-packages//torch//include//ATen//native//cuda//ScanUtils.cuh
ADDED
|
@@ -0,0 +1,471 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#pragma once
|
| 2 |
+
#include <ATen/NumericUtils.h>
|
| 3 |
+
#include <ATen/core/TensorBase.h>
|
| 4 |
+
#include <ATen/cuda/cub.cuh>
|
| 5 |
+
#include <ATen/cuda/CUDAContext.h>
|
| 6 |
+
|
| 7 |
+
#include <c10/util/Load.h>
|
| 8 |
+
#include <limits>
|
| 9 |
+
#include <cmath>
|
| 10 |
+
|
| 11 |
+
namespace at::native {
|
| 12 |
+
|
| 13 |
+
template <typename integer>
|
| 14 |
+
constexpr inline integer ceil_div(integer n, integer m) {
|
| 15 |
+
return (n + m - 1) / m;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
template <typename integer>
|
| 19 |
+
constexpr inline integer get_log_num_threads_x_inner_scan(integer num_rows, integer row_size) {
|
| 20 |
+
integer log_num_threads_x = 0;
|
| 21 |
+
integer log_num_threads_y = 0;
|
| 22 |
+
while (((integer)1 << log_num_threads_x) < row_size) {
|
| 23 |
+
++log_num_threads_x;
|
| 24 |
+
}
|
| 25 |
+
while (((integer)1 << log_num_threads_y) < num_rows) {
|
| 26 |
+
++log_num_threads_y;
|
| 27 |
+
}
|
| 28 |
+
// we want to keep the ratio between the x-threads and y-threads about the same as
|
| 29 |
+
// the ratio between the row_size and num_rows, but the total number of threads in
|
| 30 |
+
// a block should be about 512
|
| 31 |
+
integer diff = log_num_threads_x - log_num_threads_y;
|
| 32 |
+
// 9 is from log2(512)
|
| 33 |
+
log_num_threads_x = ((integer)9 + diff) / (integer)2;
|
| 34 |
+
// I found that in having larger log_num_threads_x can give significant speed up in some cases,
|
| 35 |
+
// but detrimental in another case, so just keep the lower bound to be log2(16) == 4 to make it
|
| 36 |
+
// similar to the previous implementation
|
| 37 |
+
// Keeping the upper bound to be log2(512) == 9 as the maximum number of threads in a block.
|
| 38 |
+
log_num_threads_x = std::min(std::max((integer)4, log_num_threads_x), (integer)9);
|
| 39 |
+
return log_num_threads_x;
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
template<typename scalar_t, typename idx_t, typename BinaryOperation>
|
| 43 |
+
__device__ void binary_op_update(const scalar_t lhs, scalar_t& rhs, const idx_t lhs_idx, idx_t& rhs_idx, BinaryOperation binary_op) {
|
| 44 |
+
if(!at::_isnan(rhs) && (at::_isnan(lhs) || !binary_op(rhs, lhs))) {
|
| 45 |
+
rhs = lhs;
|
| 46 |
+
rhs_idx = lhs_idx;
|
| 47 |
+
}
|
| 48 |
+
}
|
| 49 |
+
/* Perform an inclusive scan along the innermost dimension of a tensor.
|
| 50 |
+
*
|
| 51 |
+
* - num_rows is the size of the flattened outer dimensions;
|
| 52 |
+
* - row_size is the size of the innermost dimension;
|
| 53 |
+
*
|
| 54 |
+
* The outer dimensions of the tensor are considered as a single dimension, i.e. the tensor is
|
| 55 |
+
* considered as having 'num_rows' rows of size 'row_size'.
|
| 56 |
+
* Each thread block processes one or more sets of contiguous rows (processing multiple rows
|
| 57 |
+
* per thread block is quicker than processing a single row, especially for short rows).
|
| 58 |
+
*/
|
| 59 |
+
template<typename scalar_t, class BinaryFunction>
|
| 60 |
+
__global__ void tensor_kernel_scan_innermost_dim_with_indices(const scalar_t *self_, scalar_t *values_, int64_t *indices_,
|
| 61 |
+
int num_rows, int row_size,
|
| 62 |
+
const uint32_t num_threads, const uint32_t log_num_threads_x,
|
| 63 |
+
scalar_t init, BinaryFunction binary_op) {
|
| 64 |
+
// dynamic memory allocation for vbuf and ibuf
|
| 65 |
+
alignas(sizeof(double)) extern __shared__ char buf[];
|
| 66 |
+
scalar_t* vbuf = reinterpret_cast<scalar_t*>(buf); // the size is num_threads * 2
|
| 67 |
+
int64_t* ibuf = reinterpret_cast<int64_t*>(vbuf + num_threads * 2);
|
| 68 |
+
const uint32_t num_threads_x = 1 << log_num_threads_x;
|
| 69 |
+
scalar_t* row_buf = vbuf + 2 * num_threads_x * threadIdx.y;
|
| 70 |
+
int64_t* row_idx_buf = ibuf + 2 * num_threads_x * threadIdx.y;
|
| 71 |
+
|
| 72 |
+
for (int block_row = blockIdx.x * blockDim.y;
|
| 73 |
+
block_row < num_rows;
|
| 74 |
+
block_row += blockDim.y * gridDim.x) {
|
| 75 |
+
int row = block_row + threadIdx.y;
|
| 76 |
+
const scalar_t *row_self = self_ + row * row_size;
|
| 77 |
+
scalar_t *row_values = values_ + row * row_size;
|
| 78 |
+
int64_t *row_indices = indices_ + row * row_size;
|
| 79 |
+
scalar_t block_total = init;
|
| 80 |
+
int64_t block_idx_final = 0;
|
| 81 |
+
const bool row_exists = row < num_rows;
|
| 82 |
+
// Perform scan on one block at a time, keeping track of the total value of
|
| 83 |
+
// all blocks processed so far.
|
| 84 |
+
for (int block_col = 0; block_col < row_size; block_col += 2 * num_threads_x) {
|
| 85 |
+
// Load data into shared memory (two values per thread).
|
| 86 |
+
int col1 = block_col + threadIdx.x;
|
| 87 |
+
int col2 = block_col + num_threads_x + threadIdx.x;
|
| 88 |
+
if (row_exists) {
|
| 89 |
+
if (col1 < row_size) {
|
| 90 |
+
row_buf[threadIdx.x] = c10::load(&row_self[col1]);
|
| 91 |
+
row_idx_buf[threadIdx.x] = col1;
|
| 92 |
+
} else {
|
| 93 |
+
row_buf[threadIdx.x] = init;
|
| 94 |
+
// No need to set the index here as the value in init will never be selected
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
if (col2 < row_size) {
|
| 98 |
+
row_buf[num_threads_x + threadIdx.x] = c10::load(&row_self[col2]);
|
| 99 |
+
row_idx_buf[num_threads_x + threadIdx.x] = col2;
|
| 100 |
+
} else {
|
| 101 |
+
row_buf[num_threads_x + threadIdx.x] = init;
|
| 102 |
+
// No need to set the index here as the value in init will never be selected
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
// Add the total value of all previous blocks to the first value of this block.
|
| 106 |
+
if (threadIdx.x == 0) {
|
| 107 |
+
binary_op_update(block_total, row_buf[0], block_idx_final, row_idx_buf[0], binary_op);
|
| 108 |
+
}
|
| 109 |
+
}
|
| 110 |
+
__syncthreads();
|
| 111 |
+
|
| 112 |
+
// Parallel reduction with Sklansky method. The diagram can be seen on this paper:
|
| 113 |
+
// https://research.nvidia.com/publication/single-pass-parallel-prefix-scan-decoupled-look-back
|
| 114 |
+
for (uint32_t s = 1; s <= num_threads_x; s <<= 1) {
|
| 115 |
+
if (row_exists) {
|
| 116 |
+
uint32_t a = (threadIdx.x / s) * (2 * s) + s;
|
| 117 |
+
uint32_t ti = a + (threadIdx.x % s);
|
| 118 |
+
uint32_t si = a - 1;
|
| 119 |
+
binary_op_update(row_buf[si], row_buf[ti], row_idx_buf[si], row_idx_buf[ti], binary_op);
|
| 120 |
+
}
|
| 121 |
+
__syncthreads();
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
// Write back to output.
|
| 125 |
+
if (row_exists) {
|
| 126 |
+
if (col1 < row_size){
|
| 127 |
+
row_values[col1] = row_buf[threadIdx.x];
|
| 128 |
+
row_indices[col1] = row_idx_buf[threadIdx.x];
|
| 129 |
+
}
|
| 130 |
+
if (col2 < row_size) {
|
| 131 |
+
row_values[col2] = row_buf[num_threads_x + threadIdx.x];
|
| 132 |
+
row_indices[col2] = row_idx_buf[num_threads_x + threadIdx.x];
|
| 133 |
+
}
|
| 134 |
+
}
|
| 135 |
+
block_total = row_buf[2 * num_threads_x - 1];
|
| 136 |
+
block_idx_final = row_idx_buf[2 * num_threads_x - 1];
|
| 137 |
+
__syncthreads();
|
| 138 |
+
}
|
| 139 |
+
}
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
/* Perform an inclusive scan along an outer dimension of a tensor.
|
| 143 |
+
*
|
| 144 |
+
* - num_orows is the size of the flattened outer dimensions;
|
| 145 |
+
* - num_irows is the size of the flattened inner dimensions;
|
| 146 |
+
* - row_size is the size of the dimension along which to compute the variance;
|
| 147 |
+
*
|
| 148 |
+
* The dimensions to the outside and inside of the specified dimension are considered as flattened.
|
| 149 |
+
* Thread blocks with the same blockIdx.y process an "outer row" (i.e. an element of the flattened
|
| 150 |
+
* outer dimensions, which contains several "inner rows").
|
| 151 |
+
* Each thread processes a single inner row at a time.
|
| 152 |
+
*/
|
| 153 |
+
template<typename scalar_t, class BinaryFunction>
|
| 154 |
+
__global__ void tensor_kernel_scan_outer_dim_with_indices(const scalar_t *self_, scalar_t *values_, int64_t *indices_,
|
| 155 |
+
const uint32_t num_orows, const uint32_t num_irows, const uint32_t row_size, scalar_t init, BinaryFunction binary_op) {
|
| 156 |
+
for (uint32_t orow = blockIdx.x; orow < num_orows; orow += gridDim.x) {
|
| 157 |
+
for (uint32_t irow = blockIdx.y * blockDim.x + threadIdx.x; irow < num_irows; irow += gridDim.y * blockDim.x) {
|
| 158 |
+
const scalar_t *self = self_ + orow * row_size * num_irows + irow;
|
| 159 |
+
scalar_t *values = values_ + orow * row_size * num_irows + irow;
|
| 160 |
+
int64_t *indices = indices_ + orow * row_size * num_irows + irow;
|
| 161 |
+
scalar_t out = init;
|
| 162 |
+
int64_t out_idx = 0;
|
| 163 |
+
|
| 164 |
+
for (auto col = decltype(row_size){0}; col < row_size; ++col) {
|
| 165 |
+
const auto val = c10::load(self);
|
| 166 |
+
if(at::_isnan(val) || (!at::_isnan(out) && binary_op(val, out))) {
|
| 167 |
+
out = val;
|
| 168 |
+
out_idx = col;
|
| 169 |
+
}
|
| 170 |
+
*values = out;
|
| 171 |
+
*indices = out_idx;
|
| 172 |
+
self += num_irows;
|
| 173 |
+
values += num_irows;
|
| 174 |
+
indices += num_irows;
|
| 175 |
+
}
|
| 176 |
+
}
|
| 177 |
+
}
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
inline void check_fits_in_unsigned(int64_t val, const char* name) {
|
| 181 |
+
constexpr auto umax = std::numeric_limits<uint32_t>::max();
|
| 182 |
+
TORCH_CHECK(
|
| 183 |
+
val >= 0 && val <= umax, name, " must fit in a 32-bit uint32_t value");
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
template<typename scalar_t, class BinaryFunction>
|
| 188 |
+
__host__ void scan_outer_dim_with_indices(
|
| 189 |
+
const TensorBase& self, const TensorBase& values, const TensorBase& indices,
|
| 190 |
+
int dim, scalar_t init, BinaryFunction binary_op) {
|
| 191 |
+
int64_t row_size = self.size(dim);
|
| 192 |
+
auto sizes = self.sizes();
|
| 193 |
+
|
| 194 |
+
// Treat all outer dimensions (i.e. dim_ < dim) as one.
|
| 195 |
+
const int64_t num_orows = c10::multiply_integers(sizes.begin(), sizes.begin() + dim);
|
| 196 |
+
|
| 197 |
+
// Treat all inner dimensions (i.e. dim > dimension) as one.
|
| 198 |
+
const int64_t num_irows = c10::multiply_integers(sizes.begin() + dim + 1, sizes.end());
|
| 199 |
+
//for performance reasons, cuda kernels use uint32_t for loops over irows, orows and row,
|
| 200 |
+
//make sure that input is not bigger than supported by uint32_t
|
| 201 |
+
check_fits_in_unsigned(num_irows, "num_irows");
|
| 202 |
+
check_fits_in_unsigned(num_orows, "num_orows");
|
| 203 |
+
check_fits_in_unsigned(row_size, "row_size");
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
dim3 threads(std::min(512, int(num_irows)));
|
| 207 |
+
int64_t maxGridDim = at::cuda::getCurrentDeviceProperties()->maxGridSize[1];
|
| 208 |
+
dim3 grid(std::min(maxGridDim, num_orows), std::min(maxGridDim, ceil_div(num_irows, int64_t{threads.x})));
|
| 209 |
+
tensor_kernel_scan_outer_dim_with_indices<scalar_t><<<grid, threads, 0, at::cuda::getCurrentCUDAStream()>>>(
|
| 210 |
+
self.const_data_ptr<scalar_t>(), values.mutable_data_ptr<scalar_t>(), indices.mutable_data_ptr<int64_t>(),
|
| 211 |
+
num_orows, num_irows, row_size, init, binary_op);
|
| 212 |
+
C10_CUDA_KERNEL_LAUNCH_CHECK();
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
template <typename scalar_t, class BinaryFunction>
|
| 216 |
+
__host__ void scan_innermost_dim_with_indices(
|
| 217 |
+
const TensorBase& self, const TensorBase& values, const TensorBase& indices,
|
| 218 |
+
scalar_t init, BinaryFunction binary_op) {
|
| 219 |
+
int ndim = self.dim();
|
| 220 |
+
// Treat all outer dimensions as a single dimension.
|
| 221 |
+
int row_size = self.size(ndim - 1);
|
| 222 |
+
int num_rows = self.numel() / row_size;
|
| 223 |
+
|
| 224 |
+
// assuming max_num_threads per block is 512
|
| 225 |
+
const uint32_t num_threads = 512;
|
| 226 |
+
const uint32_t log_num_threads_x = get_log_num_threads_x_inner_scan<uint32_t>(num_rows, row_size);
|
| 227 |
+
const uint32_t num_threads_x = (1 << log_num_threads_x);
|
| 228 |
+
const uint32_t num_threads_y = num_threads / num_threads_x;
|
| 229 |
+
dim3 threads(num_threads_x, num_threads_y);
|
| 230 |
+
dim3 grid(std::min(at::cuda::getCurrentDeviceProperties()->maxGridSize[0], ceil_div(num_rows, int(threads.y))));
|
| 231 |
+
|
| 232 |
+
const uint32_t mem_size = 2 * num_threads * (sizeof(scalar_t) + sizeof(int64_t));
|
| 233 |
+
tensor_kernel_scan_innermost_dim_with_indices<scalar_t><<<grid, threads, mem_size,
|
| 234 |
+
at::cuda::getCurrentCUDAStream()>>>(
|
| 235 |
+
self.const_data_ptr<scalar_t>(), values.mutable_data_ptr<scalar_t>(), indices.mutable_data_ptr<int64_t>(),
|
| 236 |
+
num_rows, row_size, num_threads, log_num_threads_x, init, binary_op);
|
| 237 |
+
C10_CUDA_KERNEL_LAUNCH_CHECK();
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
template<typename scalar_t, typename BinaryFunction>
|
| 241 |
+
void scan_dim_with_indices(const TensorBase& self, const TensorBase& values, const TensorBase& indices, //int64_t dim) {
|
| 242 |
+
int64_t dim, scalar_t init, BinaryFunction binary_op) {
|
| 243 |
+
int ndim = self.dim();
|
| 244 |
+
auto self_ = self.expect_contiguous();
|
| 245 |
+
TORCH_INTERNAL_ASSERT(values.is_contiguous() && indices.is_contiguous());
|
| 246 |
+
if (dim == ndim - 1) {
|
| 247 |
+
scan_innermost_dim_with_indices<scalar_t>(*self_, values, indices, init, binary_op);
|
| 248 |
+
} else {
|
| 249 |
+
scan_outer_dim_with_indices<scalar_t>(*self_, values, indices, dim, init, binary_op);
|
| 250 |
+
}
|
| 251 |
+
}
|
| 252 |
+
|
| 253 |
+
// TODO: The implementation of `tensor_kernel_scan_outer_dim` and
|
| 254 |
+
// `tensor_kernel_scan_innermost_dim` is similar to
|
| 255 |
+
// `tensor_kernel_scan_outer_dim_with_indices`
|
| 256 |
+
// `tensor_kernel_scan_outer_dim_with_indices` and should be refactored to
|
| 257 |
+
// remove the duplication.
|
| 258 |
+
|
| 259 |
+
/* Perform an inclusive scan along an outer dimension of a tensor.
|
| 260 |
+
*
|
| 261 |
+
* - num_orows is the size of the flattened outer dimensions;
|
| 262 |
+
* - num_irows is the size of the flattened inner dimensions;
|
| 263 |
+
* - row_size is the size of the dimension along which to scan;
|
| 264 |
+
*
|
| 265 |
+
* The dimensions to the outside and inside of the specified dimension are considered as flattened.
|
| 266 |
+
* Thread blocks with the same blockIdx.y process an "outer row" (i.e. an element of the flattened
|
| 267 |
+
* outer dimensions, which contains several "inner rows").
|
| 268 |
+
* Each thread processes a single inner row at a time.
|
| 269 |
+
*/
|
| 270 |
+
template<typename scalar_t, class BinaryOp>
|
| 271 |
+
__global__ void tensor_kernel_scan_outer_dim(scalar_t *tgt_, const scalar_t *src_,
|
| 272 |
+
const uint32_t num_orows, const uint32_t num_irows, const uint32_t row_size,
|
| 273 |
+
const scalar_t init, BinaryOp binary_op)
|
| 274 |
+
{
|
| 275 |
+
for (uint32_t orow = blockIdx.x; orow < num_orows; orow += gridDim.x) {
|
| 276 |
+
for (uint32_t irow = blockIdx.y * blockDim.x + threadIdx.x; irow < num_irows; irow += gridDim.y * blockDim.x) {
|
| 277 |
+
const scalar_t *src = src_ + orow * row_size * num_irows + irow;
|
| 278 |
+
scalar_t *tgt = tgt_ + orow * row_size * num_irows + irow;
|
| 279 |
+
scalar_t acc = init;
|
| 280 |
+
|
| 281 |
+
for (uint32_t col = 0; col < row_size; ++col) {
|
| 282 |
+
acc = binary_op(acc, c10::load(src));
|
| 283 |
+
*tgt = acc;
|
| 284 |
+
|
| 285 |
+
src += num_irows;
|
| 286 |
+
tgt += num_irows;
|
| 287 |
+
}
|
| 288 |
+
}
|
| 289 |
+
}
|
| 290 |
+
}
|
| 291 |
+
|
| 292 |
+
/* Perform an inclusive scan along the innermost dimension of a tensor.
|
| 293 |
+
*
|
| 294 |
+
* - num_rows is the size of the flattened outer dimensions;
|
| 295 |
+
* - row_size is the size of the innermost dimension;
|
| 296 |
+
*
|
| 297 |
+
* The outer dimensions of the tensor are considered as a single dimension, i.e. the tensor is
|
| 298 |
+
* considered as having 'num_rows' rows of size 'row_size'.
|
| 299 |
+
* Each thread block processes one or more sets of contiguous rows (processing multiple rows
|
| 300 |
+
* per thread block is quicker than processing a single row, especially for short rows).
|
| 301 |
+
*/
|
| 302 |
+
template<typename T, class BinaryFunction>
|
| 303 |
+
__device__ void tensor_kernel_scan_innermost_dim_impl(T* row_buf, T *tgt_, const T *src_,
|
| 304 |
+
const uint32_t num_rows, const uint32_t row_size,
|
| 305 |
+
const uint32_t log_num_threads_x,
|
| 306 |
+
T init, BinaryFunction binary_op){
|
| 307 |
+
const uint32_t num_threads_x = 1 << log_num_threads_x;
|
| 308 |
+
for (uint32_t block_row = blockIdx.x * blockDim.y;
|
| 309 |
+
block_row < num_rows;
|
| 310 |
+
block_row += blockDim.y * gridDim.x) {
|
| 311 |
+
uint32_t row = block_row + threadIdx.y;
|
| 312 |
+
T block_total = init;
|
| 313 |
+
|
| 314 |
+
const T *row_src = src_ + row * row_size;
|
| 315 |
+
T *row_tgt = tgt_ + row * row_size;
|
| 316 |
+
const bool row_exists = row < num_rows;
|
| 317 |
+
|
| 318 |
+
// Perform scan on one block at a time, keeping track of the total value of
|
| 319 |
+
// all blocks processed so far.
|
| 320 |
+
for (uint32_t block_col = 0; block_col < row_size; block_col += 2 * num_threads_x) {
|
| 321 |
+
// Load data into shared memory (two values per thread).
|
| 322 |
+
uint32_t col1 = block_col + threadIdx.x;
|
| 323 |
+
uint32_t col2 = block_col + num_threads_x + threadIdx.x;
|
| 324 |
+
if (row_exists) {
|
| 325 |
+
if (col1 < row_size) {
|
| 326 |
+
row_buf[threadIdx.x] = row_src[col1];
|
| 327 |
+
} else {
|
| 328 |
+
row_buf[threadIdx.x] = init;
|
| 329 |
+
}
|
| 330 |
+
|
| 331 |
+
if (col2 < row_size) {
|
| 332 |
+
row_buf[num_threads_x + threadIdx.x] = row_src[col2];
|
| 333 |
+
} else {
|
| 334 |
+
row_buf[num_threads_x + threadIdx.x] = init;
|
| 335 |
+
}
|
| 336 |
+
|
| 337 |
+
// Add the total value of all previous blocks to the first value of this block.
|
| 338 |
+
if (threadIdx.x == 0) {
|
| 339 |
+
row_buf[0] = binary_op(row_buf[0], block_total);
|
| 340 |
+
}
|
| 341 |
+
}
|
| 342 |
+
__syncthreads();
|
| 343 |
+
|
| 344 |
+
// Parallel reduction with Sklansky method. The diagram can be seen on this paper:
|
| 345 |
+
// https://research.nvidia.com/publication/single-pass-parallel-prefix-scan-decoupled-look-back
|
| 346 |
+
for (uint32_t m = 0; m <= log_num_threads_x; ++m) {
|
| 347 |
+
if (row_exists) {
|
| 348 |
+
uint32_t s = 1 << m; // s = 2 ^ m
|
| 349 |
+
uint32_t a = ((threadIdx.x >> m) << (m + 1)) | s; // a = (threadIdx.x / s) * (2 * s) + s
|
| 350 |
+
uint32_t ti = a + (threadIdx.x % s);
|
| 351 |
+
uint32_t si = a - 1;
|
| 352 |
+
row_buf[ti] = binary_op(row_buf[ti], row_buf[si]);
|
| 353 |
+
}
|
| 354 |
+
__syncthreads();
|
| 355 |
+
}
|
| 356 |
+
|
| 357 |
+
// Write back to output.
|
| 358 |
+
if (row_exists) {
|
| 359 |
+
if (col1 < row_size) row_tgt[col1] = row_buf[threadIdx.x];
|
| 360 |
+
if (col2 < row_size) row_tgt[col2] = row_buf[num_threads_x + threadIdx.x];
|
| 361 |
+
}
|
| 362 |
+
block_total = row_buf[2 * num_threads_x - 1];
|
| 363 |
+
__syncthreads();
|
| 364 |
+
}
|
| 365 |
+
}
|
| 366 |
+
}
|
| 367 |
+
|
| 368 |
+
template <
|
| 369 |
+
typename T,
|
| 370 |
+
class BinaryFunction>
|
| 371 |
+
__global__ void tensor_kernel_scan_innermost_dim(
|
| 372 |
+
T* tgt_,
|
| 373 |
+
const T* src_,
|
| 374 |
+
const uint32_t num_rows,
|
| 375 |
+
const uint32_t row_size,
|
| 376 |
+
const uint32_t log_num_threads_x,
|
| 377 |
+
T init,
|
| 378 |
+
BinaryFunction binary_op) {
|
| 379 |
+
alignas(sizeof(double)) extern __shared__ char sbuf[];
|
| 380 |
+
T* sbuf2 = reinterpret_cast<T*>(sbuf);
|
| 381 |
+
const uint32_t num_threads_x = 1 << log_num_threads_x;
|
| 382 |
+
T* row_buf = reinterpret_cast<T*>(sbuf2 + num_threads_x * 2 * threadIdx.y);
|
| 383 |
+
|
| 384 |
+
tensor_kernel_scan_innermost_dim_impl<T>(
|
| 385 |
+
row_buf, tgt_, src_, num_rows, row_size, log_num_threads_x, init, binary_op);
|
| 386 |
+
}
|
| 387 |
+
|
| 388 |
+
|
| 389 |
+
template<typename scalar_t, class BinaryFunction>
|
| 390 |
+
__host__ void scan_outer_dim(const TensorBase& self, const TensorBase& result,
|
| 391 |
+
int dim, scalar_t init, BinaryFunction binary_op) {
|
| 392 |
+
const int64_t row_size = self.size(dim);
|
| 393 |
+
auto sizes = self.sizes();
|
| 394 |
+
|
| 395 |
+
// Treat all outer dimensions (i.e. dim_ < dim) as one.
|
| 396 |
+
const int64_t num_orows = c10::multiply_integers(sizes.begin(), sizes.begin() + dim);
|
| 397 |
+
|
| 398 |
+
// Treat all inner dimensions (i.e. dim > dimension) as one.
|
| 399 |
+
const int64_t num_irows = c10::multiply_integers(sizes.begin() + dim + 1, sizes.end());
|
| 400 |
+
|
| 401 |
+
dim3 threads(std::min(512, int(num_irows)));
|
| 402 |
+
int64_t maxGridDim = at::cuda::getCurrentDeviceProperties()->maxGridSize[1];
|
| 403 |
+
dim3 grid(std::min(maxGridDim, num_orows), std::min(maxGridDim, ceil_div(num_irows, int64_t{threads.x})));
|
| 404 |
+
|
| 405 |
+
check_fits_in_unsigned(num_irows, "num_irows");
|
| 406 |
+
check_fits_in_unsigned(num_orows, "num_orows");
|
| 407 |
+
check_fits_in_unsigned(row_size, "row_size");
|
| 408 |
+
|
| 409 |
+
tensor_kernel_scan_outer_dim<scalar_t><<<grid, threads, 0, at::cuda::getCurrentCUDAStream()>>>(
|
| 410 |
+
result.mutable_data_ptr<scalar_t>(), self.const_data_ptr<scalar_t>(),
|
| 411 |
+
num_orows, num_irows, row_size, init, binary_op);
|
| 412 |
+
C10_CUDA_KERNEL_LAUNCH_CHECK();
|
| 413 |
+
}
|
| 414 |
+
|
| 415 |
+
template <typename scalar_t, class BinaryFunction>
|
| 416 |
+
void scan_innermost_dim(const TensorBase& self, const TensorBase& result,
|
| 417 |
+
scalar_t init, BinaryFunction binary_op) {
|
| 418 |
+
int64_t ndim = self.dim();
|
| 419 |
+
// Treat all outer dimensions as a single dimension.
|
| 420 |
+
int64_t row_size = self.size(ndim - 1);
|
| 421 |
+
int64_t num_rows = self.numel() / row_size;
|
| 422 |
+
|
| 423 |
+
// assuming max_num_threads per block is 512
|
| 424 |
+
const uint32_t num_threads = 512;
|
| 425 |
+
const uint32_t log_num_threads_x = get_log_num_threads_x_inner_scan<uint32_t>(num_rows, row_size);
|
| 426 |
+
const uint32_t num_threads_x = (1 << log_num_threads_x);
|
| 427 |
+
const uint32_t num_threads_y = num_threads / num_threads_x;
|
| 428 |
+
dim3 threads(num_threads_x, num_threads_y);
|
| 429 |
+
int64_t maxGridDim = at::cuda::getCurrentDeviceProperties()->maxGridSize[0];
|
| 430 |
+
dim3 grid(std::min(maxGridDim, ceil_div(num_rows, int64_t{threads.y})));
|
| 431 |
+
|
| 432 |
+
check_fits_in_unsigned(num_rows, "Number of rows (self.numel()/self.size(self.dim()-1))");
|
| 433 |
+
check_fits_in_unsigned(row_size, "row_size");
|
| 434 |
+
|
| 435 |
+
tensor_kernel_scan_innermost_dim<scalar_t><<<grid, threads, num_threads * 2 * sizeof(scalar_t),
|
| 436 |
+
at::cuda::getCurrentCUDAStream()>>>(
|
| 437 |
+
result.mutable_data_ptr<scalar_t>(), self.const_data_ptr<scalar_t>(),
|
| 438 |
+
num_rows, row_size, log_num_threads_x, init, binary_op);
|
| 439 |
+
C10_CUDA_KERNEL_LAUNCH_CHECK();
|
| 440 |
+
}
|
| 441 |
+
|
| 442 |
+
template<typename scalar_t, typename BinaryFunction>
|
| 443 |
+
void scan_dim(const TensorBase& self, const TensorBase& result,
|
| 444 |
+
int64_t dim, scalar_t init, BinaryFunction binary_op) {
|
| 445 |
+
int ndim = self.dim();
|
| 446 |
+
auto self_ = self.expect_contiguous();
|
| 447 |
+
TORCH_INTERNAL_ASSERT(result.is_contiguous());
|
| 448 |
+
|
| 449 |
+
if (self.numel() == self.size(dim)) {
|
| 450 |
+
if constexpr (std::is_same<BinaryFunction, std::plus<scalar_t>>::value) {
|
| 451 |
+
if (C10_UNLIKELY(at::globalContext().deterministicAlgorithms()) && (self.is_floating_point() || self.is_complex())) {
|
| 452 |
+
# if (defined(CUDA_VERSION) && CUDA_VERSION > 11040) || defined(USE_ROCM)
|
| 453 |
+
cuda::cub::inclusive_deterministic_scan(self_->const_data_ptr<scalar_t>(), result.mutable_data_ptr<scalar_t>(), binary_op, self.numel());
|
| 454 |
+
#else
|
| 455 |
+
globalContext().alertNotDeterministic("cumsum_cuda_kernel");
|
| 456 |
+
cuda::cub::inclusive_scan(self_->const_data_ptr<scalar_t>(), result.mutable_data_ptr<scalar_t>(), binary_op, self.numel());
|
| 457 |
+
#endif
|
| 458 |
+
} else {
|
| 459 |
+
cuda::cub::inclusive_scan(self_->const_data_ptr<scalar_t>(), result.mutable_data_ptr<scalar_t>(), binary_op, self.numel());
|
| 460 |
+
}
|
| 461 |
+
} else {
|
| 462 |
+
cuda::cub::inclusive_scan(self_->const_data_ptr<scalar_t>(), result.mutable_data_ptr<scalar_t>(), binary_op, self.numel());
|
| 463 |
+
}
|
| 464 |
+
} else if (dim == ndim - 1) {
|
| 465 |
+
scan_innermost_dim<scalar_t>(*self_, result, init, binary_op);
|
| 466 |
+
} else {
|
| 467 |
+
scan_outer_dim<scalar_t>(*self_, result, dim, init, binary_op);
|
| 468 |
+
}
|
| 469 |
+
}
|
| 470 |
+
|
| 471 |
+
} // namespace at::native
|