|
7 | 7 | */ |
8 | 8 |
|
9 | 9 | #include "common.cuh" |
| 10 | +#include "fbgemm_gpu/utils/cuda_utilities.cuh" |
10 | 11 |
|
11 | 12 | using Tensor = at::Tensor; |
12 | 13 |
|
@@ -35,78 +36,115 @@ __global__ inline void _float_to_paddedFP8rowwise_cuda_kernel( |
35 | 36 | const int output_columns = |
36 | 37 | ncols_aligned + (ncols + row_dim - 1) / row_dim * 8; |
37 | 38 |
|
| 39 | + // On ROCm the launch caps the grid (HIP 2^32 threads-per-launch limit); grid- |
| 40 | + // stride over rows/buckets. On CUDA the grid is not capped and the loop runs |
| 41 | + // exactly once. |
| 42 | + const int64_t num_items = |
| 43 | + (nrows == 1) ? ((ncols + row_dim - 1) / row_dim) : nrows; |
| 44 | +#ifdef USE_ROCM |
| 45 | + for (int64_t row = |
| 46 | + static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x; |
| 47 | + row < num_items; |
| 48 | + row += static_cast<int64_t>(gridDim.x) * blockDim.x) { |
| 49 | +#else |
38 | 50 | const int64_t row = |
39 | 51 | static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x; |
40 | | - // for 1D case, unsqueezing needed |
41 | | - if (nrows == 1) { |
42 | | - const auto threads = (ncols + row_dim - 1) / row_dim; |
43 | | - if (row >= threads) { |
| 52 | +#endif |
| 53 | + // for 1D case, unsqueezing needed |
| 54 | + if (nrows == 1) { |
| 55 | + const auto threads = (ncols + row_dim - 1) / row_dim; |
| 56 | + if (row >= threads) { |
| 57 | +#ifdef USE_ROCM |
| 58 | + continue; |
| 59 | +#else |
44 | 60 | return; |
45 | | - } |
46 | | - const input_t* const input_row = input + row * row_dim; |
47 | | - std::uint8_t* output_row = output + row * row_ext; |
48 | | - int last_buc_idx = row - (threads - 1); |
49 | | - float* output_row_scale = reinterpret_cast<float*>(output_row + row_dim); |
50 | | - const auto range = (row == threads - 1) ? row_dim - pad : row_dim; |
51 | | - float minimum_element = fbgemm_gpu::min(input_row, input_row + range); |
52 | | - float maximum_element = fbgemm_gpu::max(input_row, input_row + range); |
53 | | - auto scale = |
54 | | - max_pos / (kEpsilon + fmaxf(maximum_element, -minimum_element)); |
55 | | - output_row_scale[0] = scale; |
56 | | - // if no padding, the pad value is negative to indicate where the next |
57 | | - // non-zero pad value is for output size counting in host |
58 | | - output_row_scale[1] = |
59 | | - *reinterpret_cast<float*>((row == threads - 1) ? &pad : &last_buc_idx); |
60 | | - for (int col = 0; col < range; col += 1) { |
61 | | - output_row[col] = |
62 | | - float_to_hfp8(to_float(input_row[col]) * scale, ebit, bias, max_pos); |
63 | | - } |
| 61 | +#endif |
| 62 | + } |
| 63 | + const input_t* const input_row = input + row * row_dim; |
| 64 | + std::uint8_t* output_row = output + row * row_ext; |
| 65 | + int last_buc_idx = row - (threads - 1); |
| 66 | + float* output_row_scale = reinterpret_cast<float*>(output_row + row_dim); |
| 67 | + const auto range = (row == threads - 1) ? row_dim - pad : row_dim; |
| 68 | + float minimum_element = fbgemm_gpu::min(input_row, input_row + range); |
| 69 | + float maximum_element = fbgemm_gpu::max(input_row, input_row + range); |
| 70 | + auto scale = |
| 71 | + max_pos / (kEpsilon + fmaxf(maximum_element, -minimum_element)); |
| 72 | + output_row_scale[0] = scale; |
| 73 | + // if no padding, the pad value is negative to indicate where the next |
| 74 | + // non-zero pad value is for output size counting in host |
| 75 | + output_row_scale[1] = *reinterpret_cast<float*>( |
| 76 | + (row == threads - 1) ? &pad : &last_buc_idx); |
| 77 | + for (int col = 0; col < range; col += 1) { |
| 78 | + output_row[col] = float_to_hfp8( |
| 79 | + to_float(input_row[col]) * scale, ebit, bias, max_pos); |
| 80 | + } |
| 81 | +#ifdef USE_ROCM |
| 82 | + continue; |
| 83 | +#else |
64 | 84 | return; |
65 | | - } |
66 | | - // for 2D case |
| 85 | +#endif |
| 86 | + } |
| 87 | + // for 2D case |
67 | 88 |
|
68 | | - if (row >= nrows) { |
| 89 | + if (row >= nrows) { |
| 90 | +#ifdef USE_ROCM |
| 91 | + continue; |
| 92 | +#else |
69 | 93 | return; |
70 | | - } |
71 | | - const input_t* input_row = input + row * ncols; |
72 | | - std::uint8_t* output_row = output + row * output_columns; |
73 | | - for (int col = 0; col < ncols; col += row_dim) { |
74 | | - int col_offset = col / row_dim * 8; |
75 | | - int last_buc_idx = (ncols - col) / row_dim * -1; |
76 | | - float* output_row_scale = |
77 | | - reinterpret_cast<float*>(output_row + col + col_offset + row_dim); |
78 | | - int buc_end = (row_dim < ncols - col) ? row_dim : ncols - col; |
79 | | - float minimum_element = |
80 | | - fbgemm_gpu::min(input_row + col, input_row + buc_end + col); |
81 | | - float maximum_element = |
82 | | - fbgemm_gpu::max(input_row + col, input_row + buc_end + col); |
83 | | - auto scale = |
84 | | - max_pos / (kEpsilon + fmaxf(maximum_element, -minimum_element)); |
85 | | - output_row_scale[0] = scale; |
86 | | - output_row_scale[1] = *reinterpret_cast<float*>( |
87 | | - (ncols - col > row_dim) ? &last_buc_idx : &pad); |
88 | | - for (int bi = 0; bi < std::min(row_dim, (int)(ncols - col)); ++bi) { |
89 | | - output_row[col + bi + col_offset] = float_to_hfp8( |
90 | | - to_float(input_row[col + bi]) * scale, ebit, bias, max_pos); |
| 94 | +#endif |
91 | 95 | } |
92 | | - } |
| 96 | + const input_t* input_row = input + row * ncols; |
| 97 | + std::uint8_t* output_row = output + row * output_columns; |
| 98 | + for (int col = 0; col < ncols; col += row_dim) { |
| 99 | + int col_offset = col / row_dim * 8; |
| 100 | + int last_buc_idx = (ncols - col) / row_dim * -1; |
| 101 | + float* output_row_scale = |
| 102 | + reinterpret_cast<float*>(output_row + col + col_offset + row_dim); |
| 103 | + int buc_end = (row_dim < ncols - col) ? row_dim : ncols - col; |
| 104 | + float minimum_element = |
| 105 | + fbgemm_gpu::min(input_row + col, input_row + buc_end + col); |
| 106 | + float maximum_element = |
| 107 | + fbgemm_gpu::max(input_row + col, input_row + buc_end + col); |
| 108 | + auto scale = |
| 109 | + max_pos / (kEpsilon + fmaxf(maximum_element, -minimum_element)); |
| 110 | + output_row_scale[0] = scale; |
| 111 | + output_row_scale[1] = *reinterpret_cast<float*>( |
| 112 | + (ncols - col > row_dim) ? &last_buc_idx : &pad); |
| 113 | + for (int bi = 0; bi < std::min(row_dim, (int)(ncols - col)); ++bi) { |
| 114 | + output_row[col + bi + col_offset] = float_to_hfp8( |
| 115 | + to_float(input_row[col + bi]) * scale, ebit, bias, max_pos); |
| 116 | + } |
| 117 | + } |
| 118 | +#ifdef USE_ROCM |
| 119 | + } // for row (grid-stride loop, ROCm only) |
| 120 | +#endif |
93 | 121 | } |
94 | 122 |
|
95 | 123 | __global__ inline void _get_padding_value_kernel( |
96 | 124 | const int ncols, |
97 | 125 | const int row_dim, |
98 | 126 | const std::uint8_t* const __restrict__ input, |
99 | 127 | int* const __restrict__ offsets) { |
100 | | - const int64_t row = |
101 | | - static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x; |
102 | 128 | const int row_ext = row_dim + 8; |
103 | 129 | const auto threads = (ncols + row_ext - 1) / row_ext; |
| 130 | +#ifdef USE_ROCM |
| 131 | + for (int64_t row = |
| 132 | + static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x; |
| 133 | + row < threads; |
| 134 | + row += static_cast<int64_t>(gridDim.x) * blockDim.x) { |
| 135 | +#else |
| 136 | + const int64_t row = |
| 137 | + static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x; |
104 | 138 | if (row >= threads) |
105 | 139 | return; |
106 | | - const std::uint8_t* const input_row = input + row * row_ext; |
107 | | - int pad = *reinterpret_cast<const int*>(input_row + row_dim + 4); |
108 | | - pad = (pad > 0) ? pad : 0; |
109 | | - offsets[row] = pad; |
| 140 | +#endif |
| 141 | + const std::uint8_t* const input_row = input + row * row_ext; |
| 142 | + int pad = *reinterpret_cast<const int*>(input_row + row_dim + 4); |
| 143 | + pad = (pad > 0) ? pad : 0; |
| 144 | + offsets[row] = pad; |
| 145 | +#ifdef USE_ROCM |
| 146 | + } // for row (grid-stride loop, ROCm only) |
| 147 | +#endif |
110 | 148 | } |
111 | 149 |
|
112 | 150 | __global__ inline void _single_thread_sum_padding_kernel( |
@@ -183,28 +221,38 @@ __global__ inline void _PaddedFP8rowwise_to_float_2d_cuda_kernel( |
183 | 221 | const int ebit = forward ? 4 : 5; |
184 | 222 | const int bias = forward ? 15 : 31; |
185 | 223 |
|
| 224 | +#ifdef USE_ROCM |
| 225 | + for (int64_t row = |
| 226 | + static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x; |
| 227 | + row < nrows; |
| 228 | + row += static_cast<int64_t>(gridDim.x) * blockDim.x) { |
| 229 | +#else |
186 | 230 | const int64_t row = |
187 | 231 | static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x; |
188 | 232 | if (row >= nrows) { |
189 | 233 | return; |
190 | 234 | } |
191 | | - const std::uint8_t* const input_row = input + row * ncols; |
192 | | - output_t* output_row = output + row * output_columns; |
193 | | - int col_offset = 0; |
194 | | - for (int col = 0; col < ncols; col = col + row_ext) { |
195 | | - const float* input_row_scale = |
196 | | - reinterpret_cast<const float*>(input_row + col + row_ext - 8); |
197 | | - int pad = *reinterpret_cast<const int*>(&input_row_scale[1]); |
198 | | - // if pad is negative it's used to indidate indices of the next padded |
199 | | - // bucket |
200 | | - pad = ::max(0, ::min(pad, row_dim)); |
201 | | - for (int bi = 0; bi < row_dim - pad; ++bi) { |
202 | | - const auto output_ = |
203 | | - hfp8_to_float(input_row[col + bi], ebit, bias) / input_row_scale[0]; |
204 | | - quantize_float_store(&output_row[col + bi - col_offset], output_); |
| 235 | +#endif |
| 236 | + const std::uint8_t* const input_row = input + row * ncols; |
| 237 | + output_t* output_row = output + row * output_columns; |
| 238 | + int col_offset = 0; |
| 239 | + for (int col = 0; col < ncols; col = col + row_ext) { |
| 240 | + const float* input_row_scale = |
| 241 | + reinterpret_cast<const float*>(input_row + col + row_ext - 8); |
| 242 | + int pad = *reinterpret_cast<const int*>(&input_row_scale[1]); |
| 243 | + // if pad is negative it's used to indidate indices of the next padded |
| 244 | + // bucket |
| 245 | + pad = ::max(0, ::min(pad, row_dim)); |
| 246 | + for (int bi = 0; bi < row_dim - pad; ++bi) { |
| 247 | + const auto output_ = |
| 248 | + hfp8_to_float(input_row[col + bi], ebit, bias) / input_row_scale[0]; |
| 249 | + quantize_float_store(&output_row[col + bi - col_offset], output_); |
| 250 | + } |
| 251 | + col_offset = col_offset + 8 + pad; |
205 | 252 | } |
206 | | - col_offset = col_offset + 8 + pad; |
207 | | - } |
| 253 | +#ifdef USE_ROCM |
| 254 | + } // for row (grid-stride loop, ROCm only) |
| 255 | +#endif |
208 | 256 | } |
209 | 257 |
|
210 | 258 | } // namespace |
@@ -247,8 +295,12 @@ Tensor _float_to_paddedFP8rowwise_gpu_t( |
247 | 295 | } |
248 | 296 |
|
249 | 297 | constexpr int threads_per_block = 256; |
250 | | - const auto num_blocks = cuda_calc_xblock_count( |
251 | | - nrows == 1 ? (ncols + row_dim - 1) / row_dim : nrows, threads_per_block); |
| 298 | + const auto num_blocks = utils::cuda::cap_grid_dim_x( |
| 299 | + cuda_calc_xblock_count( |
| 300 | + nrows == 1 ? (ncols + row_dim - 1) / row_dim : nrows, |
| 301 | + threads_per_block), |
| 302 | + threads_per_block, |
| 303 | + at::cuda::getCurrentCUDAStream()); |
252 | 304 |
|
253 | 305 | FBGEMM_DISPATCH_FLOATING_TYPES( |
254 | 306 | input.scalar_type(), "_float_to_FP8rowwise_cuda_kernel", [&] { |
@@ -306,8 +358,11 @@ Tensor _paddedFP8rowwise_to_float_gpu_t( |
306 | 358 | auto output_dims = input_sizes.vec(); |
307 | 359 |
|
308 | 360 | constexpr int threads_per_block = 256; |
309 | | - const auto num_blocks = cuda_calc_xblock_count( |
310 | | - (nrows == 1) ? num_buckets : nrows, threads_per_block); |
| 361 | + const auto num_blocks = utils::cuda::cap_grid_dim_x( |
| 362 | + cuda_calc_xblock_count( |
| 363 | + (nrows == 1) ? num_buckets : nrows, threads_per_block), |
| 364 | + threads_per_block, |
| 365 | + at::cuda::getCurrentCUDAStream()); |
311 | 366 | Tensor offsets = at::empty( |
312 | 367 | (nrows == 1) ? num_buckets : 0, input.options().dtype(at::kInt)); |
313 | 368 | int total_pad = 0; |
|
0 commit comments