| |
| |
| |
| |
| |
| #include <assert.h> |
| #include <stdint.h> |
| #include <utility> |
| #include <vector> |
| #include <algorithm> |
| #include <unordered_map> |
| |
| #include "cuda_common.h" |
| #include "cuda_utils.cuh" |
|
|
| |
| |
|
|
| __global__ void encoder_forward_kernel3(floatX* out, |
| const int* inp, const floatX* wte, const floatX* wpe, |
| int B, int T, int C) { |
| int idx = (blockIdx.x * blockDim.x + threadIdx.x) * x128::size; |
| int N = B * T * C; |
| if (idx >= N) { return; } |
|
|
| int bt = idx / C; |
| int b = bt / T; |
| int t = bt % T; |
| int c = idx % C; |
|
|
| int ix = inp[b * T + t]; |
|
|
| floatX* out_btc = out + b * T * C + t * C + c; |
| const floatX* wte_ix = wte + ix * C + c; |
| const floatX* wpe_tc = wpe + t * C + c; |
|
|
| x128 packed_out; |
| x128 wte128 = load128cs(wte_ix); |
| x128 wpe128 = load128cs(wpe_tc); |
| for (int k = 0; k < x128::size; k++) { |
| packed_out[k] = (floatX)((float)wte128[k] + (float)wpe128[k]); |
| } |
| store128(out_btc, packed_out); |
| } |
|
|
| template <int BLOCK_SIZE=256> |
| __global__ void wte_backward_kernel(floatX* dwte, |
| const int4* bucket_info, const int* workload_indices, const floatX* dout, const int* inp, |
| unsigned int seed, int B, int T, int C) { |
| |
| |
| |
| |
| |
| |
| |
| int bucket = blockIdx.x; |
| int warp_id = threadIdx.x / WARP_SIZE; |
| int lane_id = threadIdx.x % WARP_SIZE; |
| int c_per_warp = WARP_SIZE * x128::size; |
|
|
| int bucket_start_idx = bucket_info[bucket].x; |
| int bucket_size = bucket_info[bucket].y; |
| int bucket_ix = bucket_info[bucket].z; |
| int c = bucket_info[bucket].w * c_per_warp + (lane_id * x128::size); |
|
|
| |
| |
| if (c >= C) { return; } |
| |
| if (warp_id >= bucket_size) { return; } |
|
|
| float accum[x128::size] = {0.0f}; |
| __shared__ float accum_shared[x128::size * BLOCK_SIZE]; |
|
|
| for(int item = warp_id; item < bucket_size; item += BLOCK_SIZE/WARP_SIZE) { |
| int bt = workload_indices[bucket_start_idx + item]; |
|
|
| const floatX* dout_btc = dout + bt * C + c; |
| x128 packed_inp1 = load128cs(dout_btc); |
| for (int k = 0; k < packed_inp1.size; k++) { |
| accum[k] += (float)packed_inp1[k]; |
| } |
| } |
|
|
| if (warp_id != 0) { |
| |
| for (int k = 0; k < x128::size; k++) { |
| accum_shared[threadIdx.x + k * BLOCK_SIZE] = accum[k]; |
| } |
| return; |
| } |
|
|
| |
| floatX* dwte_ix = dwte + bucket_ix * C + c; |
| x128 packed_in_out = load128(dwte_ix); |
|
|
| |
| __syncthreads(); |
|
|
| |
| for (int i = threadIdx.x+WARP_SIZE; i < min(BLOCK_SIZE, bucket_size*WARP_SIZE); i += WARP_SIZE) { |
| for (int k = 0; k < x128::size; k++) { |
| accum[k] += accum_shared[i + k * BLOCK_SIZE]; |
| } |
| } |
|
|
| |
| for (unsigned int k = 0; k < x128::size; k++) { |
| |
| |
| |
| |
| stochastic_rounding(accum[k] + (float)packed_in_out[k], &packed_in_out[k], seed + bucket * WARP_SIZE + threadIdx.x + k); |
| } |
| store128(dwte_ix, packed_in_out); |
| } |
|
|
| __global__ void wpe_backward_kernel(floatX* dwpe, |
| const floatX* dout, const int* inp, |
| int B, int T, int C, unsigned int seed) { |
| |
| |
| |
| |
| |
| int idx = (blockIdx.x * blockDim.x + threadIdx.x) * x128::size; |
| if (idx >= T * C) { return; } |
|
|
| |
| int t = idx / C; |
| int c = idx % C; |
| float accum[x128::size] = {0.0f}; |
|
|
| for (int b = 0; b < B; b++) { |
| x128 packed_dout = load128cs(dout + (b * T * C) + (t * C) + c); |
| for (int k = 0; k < x128::size; k++) { |
| accum[k] += (float)packed_dout[k]; |
| } |
| } |
|
|
| floatX* dwpe_tc = dwpe + (t * C) + c; |
| x128 packed_dwpe = load128(dwpe_tc); |
| for (unsigned int k = 0; k < x128::size; k++) { |
| |
| |
| |
| |
| stochastic_rounding(accum[k] + (float)packed_dwpe[k], &packed_dwpe[k], seed + idx + k); |
| } |
| store128(dwpe_tc, packed_dwpe); |
| } |
|
|
| |
| |
|
|
| void encoder_forward(floatX* out, |
| const int* inp, const floatX* wte, const floatX* wpe, |
| int B, int T, int C, cudaStream_t stream) { |
| NVTX_RANGE_FN(); |
| const int block_size = 256; |
| const int N = B * T * C; |
| const int grid_size = CEIL_DIV(N, (int)(block_size * x128::size)); |
| encoder_forward_kernel3<<<grid_size, block_size, 0, stream>>>(out, inp, wte, wpe, B, T, C); |
| cudaCheck(cudaGetLastError()); |
| } |
|
|
| |
| void encoder_backward(floatX* dwte, floatX* dwpe, floatX* scratch, |
| int* workload_indices, int4* bucket_info, |
| const floatX* dout, const int* inp, const int* inputs_cpu, |
| int B, int T, int C, unsigned int seed, cudaStream_t stream) { |
| NVTX_RANGE_FN(); |
|
|
| |
| const int block_size = 256; |
| const int N = T * C / x128::size; |
| const int grid_size = CEIL_DIV(N, block_size); |
| wpe_backward_kernel<<<grid_size, block_size, 0, stream>>>(dwpe, dout, inp, B, T, C, seed); |
| cudaCheck(cudaGetLastError()); |
|
|
| |
| |
| int num_c_groups = CEIL_DIV(C, x128::size * WARP_SIZE); |
| assert(B*T*num_c_groups * (sizeof(int4)+sizeof(int)) <= B*T*3*C * sizeof(floatX)); |
|
|
| |
| int total_items = 0; |
| std::unordered_map<uint64_t, std::vector<uint64_t>> buckets; |
| for (uint64_t bt = 0; bt < B * T; bt++) { |
| for (uint64_t c_group = 0; c_group < num_c_groups; c_group++) { |
| |
| uint64_t data = bt + (c_group<<32ULL) + ((uint64_t)inputs_cpu[bt]<<42ULL); |
| buckets[c_group + num_c_groups * inputs_cpu[bt]].push_back(data); |
| total_items++; |
| } |
| } |
|
|
| |
| |
| |
| std::vector<std::pair<uint64_t, std::vector<uint64_t>>> sortedBuckets(buckets.begin(), buckets.end()); |
| std::sort(sortedBuckets.begin(), sortedBuckets.end(), |
| [](const std::pair<uint64_t, std::vector<uint64_t>>& a, const std::pair<uint64_t, std::vector<uint64_t>>& b) { |
| return a.second.size() > b.second.size(); |
| }); |
|
|
| int num_buckets = buckets.size(); |
| int bucket_index = 0; |
| int workload_index = 0; |
| for (const auto& bucket : sortedBuckets) { |
| bucket_info[bucket_index].x = workload_index; |
| bucket_info[bucket_index].y = bucket.second.size(); |
| bucket_info[bucket_index].z = (bucket.second[0] >> 42ULL) & ((1ULL<<20ULL)-1); |
| bucket_info[bucket_index].w = (bucket.second[0] >> 32ULL) & ((1ULL<<10ULL)-1); |
|
|
| for (uint64_t idx : bucket.second) { |
| workload_indices[workload_index++] = (int)(idx & ((1ULL<<31ULL)-1ULL)); |
| } |
| bucket_index++; |
| } |
|
|
| |
| |
| int4* d_bucket_info = (int4*)scratch; |
| int* d_workload_indices = (int*)(scratch + B*T*num_c_groups * sizeof(int4)); |
| cudaCheck(cudaMemcpyAsync(d_bucket_info, bucket_info, num_buckets * sizeof(int4), cudaMemcpyHostToDevice, stream)); |
| cudaCheck(cudaMemcpyAsync(d_workload_indices, workload_indices, total_items * sizeof(int), cudaMemcpyHostToDevice, stream)); |
|
|
| |
| |
| wte_backward_kernel<256><<<num_buckets, 256, 0, stream>>>(dwte, d_bucket_info, d_workload_indices, dout, inp, seed, B, T, C); |
| cudaCheck(cudaGetLastError()); |
| } |
|
|