| |
| |
| |
| #include <assert.h> |
| |
| #include "cuda_common.h" |
| #include "cuda_utils.cuh" |
| #include "cublas_common.h" |
|
|
| |
| |
|
|
| |
| __global__ void permute_kernel(floatX* q, floatX* k, floatX* v, |
| const floatX* inp, |
| int B, int N, int NH, int d) { |
| |
| |
| int idx = blockIdx.x * blockDim.x + threadIdx.x; |
| if (idx >= B * NH * N * d) { return; } |
|
|
| |
| int b = idx / (NH * N * d); |
| int rest = idx % (NH * N * d); |
| int nh_ = rest / (N * d); |
| rest = rest % (N * d); |
| int n = rest / d; |
| int d_ = rest % d; |
| int inp_idx = (b * N * 3 * NH * d) + (n * 3 * NH * d) + (0 * NH * d) + (nh_ * d) + d_; |
| q[idx] = __ldcs(&inp[inp_idx]); |
| k[idx] = __ldcs(&inp[inp_idx + NH * d]); |
| v[idx] = __ldcs(&inp[inp_idx + 2 * (NH * d)]); |
| } |
|
|
| __global__ void permute_kernel_backward(floatX* dinp, |
| const floatX* dq, const floatX* dk, const floatX* dv, |
| int B, int N, int NH, int d) { |
| int idx = blockIdx.x * blockDim.x + threadIdx.x; |
| if (idx >= B * NH * N * d) { return; } |
|
|
| int b = idx / (NH * N * d); |
| int rest = idx % (NH * N * d); |
| int nh_ = rest / (N * d); |
| rest = rest % (N * d); |
| int n = rest / d; |
| int d_ = rest % d; |
|
|
| int inp_idx = (b * N * 3 * NH * d) + (n * 3 * NH * d) + (0 * NH * d) + (nh_ * d) + d_; |
| dinp[inp_idx] = dq[idx]; |
| dinp[inp_idx + NH * d] = dk[idx]; |
| dinp[inp_idx + 2 * (NH * d)] = dv[idx]; |
| } |
|
|
| __global__ void unpermute_kernel(floatX* inp, floatX *out, int B, int N, int NH, int d) { |
| |
|
|
| int idx = (blockIdx.x * blockDim.x + threadIdx.x); |
| |
| if (idx >= B * NH * N * d) { return; } |
|
|
| int b = idx / (NH * N * d); |
| int rest = idx % (NH * N * d); |
| int nh_ = rest / (N * d); |
| rest = rest % (N * d); |
| int n = rest / d; |
| int d_ = rest % d; |
| int other_idx = (b * NH * N * d) + (n * NH * d) + (nh_ * d) + d_; |
| out[other_idx] = __ldcs(&inp[idx]); |
| } |
|
|
| __global__ void unpermute_kernel_backward(floatX* dinp, const floatX *dout, int B, int N, int NH, int d) { |
| int idx = blockIdx.x * blockDim.x + threadIdx.x; |
| if (idx >= B * NH * N * d) { return; } |
|
|
| int b = idx / (NH * N * d); |
| int rest = idx % (NH * N * d); |
| int nh_ = rest / (N * d); |
| rest = rest % (N * d); |
| int n = rest / d; |
| int d_ = rest % d; |
| int other_idx = (b * NH * N * d) + (n * NH * d) + (nh_ * d) + d_; |
| dinp[idx] = (floatX)dout[other_idx]; |
| } |
|
|
| __global__ void softmax_forward_kernel5(floatX* out, float inv_temperature, const floatX* inp, int N, int T) { |
| |
| |
| |
| |
| assert(T % 4 == 0); |
| int lane_id = threadIdx.x % WARP_SIZE; |
| int warp_id = threadIdx.x / WARP_SIZE; |
| int num_warps = blockDim.x / WARP_SIZE; |
|
|
| |
| |
| |
| |
| |
| int idx = (gridDim.x - blockIdx.x - 1) * num_warps + warp_id; |
| if(idx >= N * T) { |
| return; |
| } |
| int own_pos = idx % T; |
| int pos_by_4 = own_pos / 4; |
|
|
| |
| const floatX* x = inp + idx * T; |
|
|
| |
| const float flt_max = 340282346638528859811704183484516925440.0f; |
| float maxval = -flt_max; |
| float sumval = 0.0f; |
|
|
| const floatX* x_aligned = reinterpret_cast<const floatX*>(__builtin_assume_aligned(x, 16)); |
| for (int i = lane_id; i < pos_by_4; i += WARP_SIZE) { |
| float regarray[4]; |
| for (int k = 0; k < 4; ++k) { |
| regarray[k] = (float)x_aligned[4*i + k]; |
| } |
| float old_maxval = maxval; |
| for(int k = 0; k < 4; ++k) { |
| maxval = fmaxf(maxval, regarray[k]); |
| } |
| sumval *= expf(inv_temperature * (old_maxval - maxval)); |
| for(int k = 0; k < 4; ++k) { |
| sumval += expf(inv_temperature * (regarray[k] - maxval)); |
| } |
| } |
|
|
| if(4*pos_by_4 + lane_id <= own_pos) { |
| float old_maxval = maxval; |
| maxval = fmaxf(maxval, (float)x[4*pos_by_4 + lane_id]); |
| sumval *= expf(inv_temperature * (old_maxval - maxval)); |
| sumval += expf(inv_temperature * ((float)x[4*pos_by_4 + lane_id] - maxval)); |
| } |
|
|
| float global_maxval = warpReduceMax(maxval); |
| sumval *= expf(inv_temperature * (maxval - global_maxval)); |
|
|
| float sum = warpReduceSum(sumval); |
| float norm = 1.f / sum; |
|
|
| |
| for (int i = lane_id; i <= own_pos; i += WARP_SIZE) { |
| |
| float ev = expf(inv_temperature * ((float)__ldcs(x + i) - global_maxval)); |
| __stcs(out + idx * T + i, (floatX)(ev * norm)); |
| } |
| } |
|
|
| __global__ void softmax_autoregressive_backward_inplace_kernel(floatX* datt, const floatX* att, |
| int B, int T, int C, float scale) { |
| constexpr const int BlockSize = 256; |
| constexpr int T_per_block = 4; |
|
|
| |
| int t0 = T - 1 - T_per_block*blockIdx.x; |
| int idx = blockIdx.y; |
|
|
| att += idx * T * T; |
| datt += idx * T * T; |
|
|
| for(int to = 0; to < T_per_block; ++to) { |
| int t = t0 - to; |
| if(t < 0) return; |
| const floatX* att_bth = att + t * T; |
| const floatX* datt_bth = datt + t * T; |
| floatX* dpreatt_bth = datt + t * T; |
|
|
| float local_sum = 0; |
| for (int t2 = threadIdx.x; t2 <= t; t2 += BlockSize) { |
| local_sum += (float)att_bth[t2] * (float)datt_bth[t2]; |
| } |
|
|
| local_sum = blockReduce<warpReduceSum>(local_sum); |
|
|
| for (int t3 = threadIdx.x; t3 < T; t3 += BlockSize) { |
| |
| |
| if(t3 <= t) { |
| float acc = (float) __ldcs(att_bth + t3) * ((float) __ldcs(datt_bth + t3) - local_sum); |
| __stcs(dpreatt_bth + t3, (floatX) (scale * acc)); |
| } else { |
| |
| __stcs(dpreatt_bth + t3, (floatX)0.f); |
| } |
| } |
| } |
| } |
|
|
| |
| |
|
|
| void attention_forward(floatX* out, floatX* qkvr, floatX* att, |
| floatX* inp, |
| int B, int T, int C, int NH, cudaStream_t stream) { |
| NVTX_RANGE_FN(); |
| |
| |
| const int block_size = 256; |
|
|
| |
| |
| |
| const int HS = C / NH; |
|
|
| |
| floatX *q, *k, *v; |
| q = qkvr + 0 * B * T * C; |
| k = qkvr + 1 * B * T * C; |
| v = qkvr + 2 * B * T * C; |
| int total_threads = B * NH * T * HS; |
| int num_blocks = CEIL_DIV(total_threads, block_size); |
| permute_kernel<<<num_blocks, block_size, 0, stream>>>(q, k, v, inp, B, T, NH, HS); |
|
|
| floatX* preatt = inp; |
| matmul_cublaslt(preatt, k, q, nullptr, T, T, HS, stream, true, false, B * NH, T * HS, T * HS, T * T); |
|
|
| |
| float scale = 1.f / sqrtf(HS); |
| int grid_size = CEIL_DIV(B * NH * T * WARP_SIZE, block_size); |
| softmax_forward_kernel5<<<grid_size, block_size, 0, stream>>>(att, scale, preatt, B * NH, T); |
|
|
| |
| floatX* vaccum = inp; |
| |
| matmul_cublaslt(vaccum, v, att, nullptr, HS, T, T, stream, false, false, B * NH, T * HS, T * T, T * HS); |
|
|
| |
| |
| num_blocks = CEIL_DIV(B * T * C, block_size); |
| unpermute_kernel<<<num_blocks, block_size, 0, stream>>>(vaccum, out, B, T, NH, HS); |
| cudaCheck(cudaGetLastError()); |
| } |
|
|
| |
| |
| void attention_backward(floatX* dinp, floatX* dqkvr, floatX* datt, floatX* scratch, |
| const floatX* dout, |
| const floatX* qkvr, const floatX* att, |
| int B, int T, int C, int NH, cudaStream_t stream) { |
| NVTX_RANGE_FN(); |
| const int block_size = 256; |
| const int HS = C / NH; |
|
|
| |
| const floatX *q, *k, *v; |
| q = qkvr + 0 * B * T * C; |
| k = qkvr + 1 * B * T * C; |
| v = qkvr + 2 * B * T * C; |
| floatX *dq, *dk, *dv; |
| dq = dqkvr + 0 * B * T * C; |
| dk = dqkvr + 1 * B * T * C; |
| dv = dqkvr + 2 * B * T * C; |
|
|
| |
| int num_blocks = CEIL_DIV(B * T * C, block_size); |
| unpermute_kernel_backward<<<num_blocks, block_size, 0, stream>>>(scratch, dout, B, T, NH, HS); |
| |
| matmul_cublaslt(datt, v, scratch, nullptr, T, T, HS, stream, true, false, B * NH, T * HS, T * HS, T * T); |
| |
| matmul_cublaslt(dv, scratch, att, nullptr, HS, T, T, stream, false, true, B * NH, T * HS, T * T, T * HS); |
| const float scale = 1.0f / sqrtf((float)HS); |
| |
| softmax_autoregressive_backward_inplace_kernel<<<dim3(T / 4, B * NH), 256>>>(datt, att, B, T, C, scale); |
| const floatX* dpreatt = datt; |
| |
| matmul_cublaslt(dq, k, dpreatt, nullptr, HS, T, T, stream, false, false, B * NH, T * HS, T * T, T * HS); |
| |
| matmul_cublaslt(dk, q, dpreatt, nullptr, HS, T, T, stream, false, true, B * NH, T * HS, T * T, T * HS); |
| |
| num_blocks = CEIL_DIV(B * NH * T * HS, block_size); |
| permute_kernel_backward<<<num_blocks, block_size, 0, stream>>>(dinp, dq, dk, dv, B, T, NH, HS); |
| cudaCheck(cudaGetLastError()); |
| } |
|
|