| import cutlass |
| import cutlass.cute as cute |
| from cutlass.cute.runtime import from_dlpack |
|
|
| NEG_BIG = -3.0e38 |
|
|
|
|
| def transpose_matrix(T: cute.Tensor, i, j): |
| """Element (i, j) of T-transpose: a pure index swap, no data movement.""" |
| return T[j, i] |
|
|
|
|
| def matrix_multiplication(acc, A: cute.Tensor, B: cute.Tensor, row, col, k, |
| transpose_b=False): |
| """One MAC step of C[row, col] = sum_k A[row, k] * B[k, col]. |
| transpose_b is a Python bool, resolved at trace time.""" |
| b = transpose_matrix(B, k, col) if transpose_b else B[k, col] |
| return acc + A[row, k] * b |
|
|
|
|
| def softmax_computation(score, row_max, denom): |
| """One softmax probability, max-shifted for stability. Pass denom=1.0 for the |
| unnormalized numerator (online softmax normalizes once at the end).""" |
| return cute.math.exp(score - row_max) / denom |
|
|
|
|
| @cute.kernel |
| def softmax_attention( |
| Q: cute.Tensor, |
| K: cute.Tensor, |
| V: cute.Tensor, |
| output: cute.Tensor, |
| M: cutlass.Int32, |
| N: cutlass.Int32, |
| d: cutlass.Int32, |
| host_scale: cutlass.Float32, |
| ): |
| bx, _, _ = cute.arch.block_idx() |
| bdx, _, _ = cute.arch.block_dim() |
| tx, _, _ = cute.arch.thread_idx() |
|
|
| row = bx * bdx + tx |
|
|
| if row < M: |
| scale = host_scale |
|
|
| |
| for k in cutlass.range(d): |
| output[row, k] = cutlass.Float32(0.0) |
|
|
| running_max = cutlass.Float32(NEG_BIG) |
| running_sum = cutlass.Float32(0.0) |
|
|
| |
| for n in cutlass.range(N): |
| s = cutlass.Float32(0.0) |
| for k in cutlass.range(d): |
| s = matrix_multiplication(s, Q, K, row, n, k, transpose_b=True) |
| s = s * scale |
|
|
| new_max = running_max |
| if s > new_max: |
| new_max = s |
|
|
| |
| correction = softmax_computation(running_max, new_max, 1.0) |
| p = softmax_computation(s, new_max, 1.0) |
|
|
| running_sum = running_sum * correction + p |
| running_max = new_max |
|
|
| for k in cutlass.range(d): |
| output[row, k] = output[row, k] * correction + p * V[n, k] |
|
|
| |
| inv_sum = 1.0 / running_sum |
| for k in cutlass.range(d): |
| output[row, k] = output[row, k] * inv_sum |
|
|
|
|
| @cute.jit |
| def solve( |
| Q: cute.Tensor, |
| K: cute.Tensor, |
| V: cute.Tensor, |
| output: cute.Tensor, |
| M: cutlass.Int32, |
| N: cutlass.Int32, |
| d: cutlass.Int32, |
| host_scale: cutlass.Float32, |
| ): |
| block_size = 256 |
| grid_size = (M + block_size - 1) // block_size |
|
|
| softmax_attention(Q, K, V, output, M, N, d, host_scale).launch( |
| grid=(grid_size, 1, 1), |
| block=(block_size, 1, 1), |
| ) |
|
|