| 1 | /* |
| 2 | Adapted from https://github.com/mit-han-lab/llm-awq |
| 3 | @article{lin2023awq, |
| 4 | title={AWQ: Activation-aware Weight Quantization for LLM Compression and |
| 5 | Acceleration}, author={Lin, Ji and Tang, Jiaming and Tang, Haotian and Yang, |
| 6 | Shang and Dang, Xingyu and Han, Song}, journal={arXiv}, year={2023} |
| 7 | } |
| 8 | */ |
| 9 | |
| 10 | #include <torch/csrc/stable/tensor.h> |
| 11 | #include <torch/csrc/stable/ops.h> |
| 12 | #include "libtorch_stable/torch_utils.h" |
| 13 | |
| 14 | #include "libtorch_stable/quantization/awq/dequantize.cuh" |
| 15 | |
| 16 | #include <cuda_fp16.h> |
| 17 | |
| 18 | namespace vllm { |
| 19 | namespace awq { |
| 20 | |
| 21 | template <int N> |
| 22 | __global__ void __launch_bounds__(64) |
| 23 | gemm_forward_4bit_cuda_m16nXk32(int G, int split_k_iters, |
| 24 | half* __restrict__ A, int* __restrict__ B, |
| 25 | half* __restrict__ scaling_factors, |
| 26 | int* __restrict__ zeros, int M, int IC, |
| 27 | int OC, half* __restrict__ C) { |
| 28 | // Only support matrix n = 64 or 128 |
| 29 | assert(N == 64 || N == 128); |
| 30 | #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 750 |
| 31 | assert(false); |
| 32 | #else |
| 33 | static constexpr uint32_t ZERO = 0x0; |
| 34 | float C_warp[32]; |
| 35 | __shared__ half A_shared[16 * (32 + 8)]; |
| 36 | __shared__ half B_shared[32 * (N + 8)]; |
| 37 | |
| 38 | int j_factors1 = ((OC + N - 1) / N); |
| 39 | int blockIdx_y = blockIdx.x % ((M + 16 - 1) / 16 * j_factors1); |
| 40 | int blockIdx_z = blockIdx.x / ((M + 16 - 1) / 16 * j_factors1); |
| 41 | |
| 42 | half A_shared_warp[8]; |
| 43 | half B_shared_warp[N / 4]; |
| 44 | for (int j_0_4_init = 0; j_0_4_init < N / 32; ++j_0_4_init) { |
| 45 | for (int i = 0; i < 8; ++i) { |
| 46 | C_warp[(j_0_4_init * 8) + i] = 0.0; |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | static constexpr int row_stride_warp = 32 * 8 / 32; |
| 51 | static constexpr int row_stride = 2 * 32 * 8 / N; |
| 52 | // TODO: Haotian: blockIdx_y / j_factors1 in A loading to support bsz > 16 |
| 53 | bool ld_A_flag = |
| 54 | (blockIdx_y / j_factors1 * 16 + threadIdx.y * row_stride_warp + |
| 55 | threadIdx.x * 8 / 32) < M; // threadIdx.y is warp_id |
| 56 | // bool wb_C_flag = (threadIdx.x / 4) < M; |
| 57 | |
| 58 | half* A_ptr = |
| 59 | A + |
| 60 | (((int)blockIdx_y) / j_factors1 * 16 + |
| 61 | (((int)threadIdx.y) * row_stride_warp) + ((int)threadIdx.x) / (32 / 8)) * |
| 62 | IC + |
| 63 | (((int)threadIdx.x) % (32 / 8)) * 8; |
| 64 | |
| 65 | int* B_ptr = B + ((int)threadIdx.y) * (OC / 8) * (256 / N) + |
| 66 | (((int)threadIdx.x) / (N / 8)) * (OC / 8) + |
| 67 | (((int)blockIdx_y) % j_factors1) * (N / 8) + |
| 68 | (((int)threadIdx.x) % (N / 8)) * 1; |
| 69 | // Why * 1 in the above line? |
| 70 | |
| 71 | half* A_shared_ptr = A_shared + |
| 72 | ((int)threadIdx.y) * row_stride_warp * (32 + 8) + |
| 73 | (((int)threadIdx.x) / (32 / 8)) * (32 + 8) + |
| 74 | (((int)threadIdx.x) % (32 / 8)) * 8; |
| 75 | |
| 76 | half* B_shared_ptr = B_shared + |
| 77 | ((int)threadIdx.y) * (row_stride / 2) * (N + 8) + |
| 78 | (((int)threadIdx.x) / (N / 8)) * (N + 8) + |
| 79 | (((int)threadIdx.x) % (N / 8)) * 8; |
| 80 | |
| 81 | int* zeros_ptr = zeros + (((int)blockIdx_y) % j_factors1) * (N / 8) + |
| 82 | ((int)threadIdx.x) % (N / 8); |
| 83 | |
| 84 | half* scaling_factors_ptr = scaling_factors + |
| 85 | (((int)blockIdx_y) % j_factors1) * N + |
| 86 | (((int)threadIdx.x) % (N / 8)) * 8; |
| 87 | |
| 88 | half* C_ptr = |
| 89 | C + |
| 90 | static_cast<long long>(blockIdx_z) * M * OC // blockIdz.x -> split_k dim |
| 91 | + (((int)blockIdx_y) % j_factors1) * N + ((int)threadIdx.y) * (N / 2) + |
| 92 | (((int)threadIdx.x) % 4) * 2; |
| 93 | |
| 94 | // preload s.f. and zeros |
| 95 | int k_bound = (IC / 32 + split_k_iters - 1) / split_k_iters; |
| 96 | if ((k_bound - 1) * split_k_iters * 32 + blockIdx_z * 32 >= IC) k_bound -= 1; |
| 97 | for (int _k_0_0 = 0; _k_0_0 < k_bound; ++_k_0_0) { |
| 98 | int k_0_0 = _k_0_0 * split_k_iters + blockIdx_z; |
| 99 | __syncthreads(); |
| 100 | // TODO: Haotian: blockIdx_y / j_factors1 in A loading to support bsz > 16 |
| 101 | if (ld_A_flag) { |
| 102 | *(uint4*)(A_shared_ptr) = *(uint4*)(A_ptr + (k_0_0 * 32)); |
| 103 | } else { |
| 104 | *(uint4*)(A_shared_ptr) = make_uint4(0, 0, 0, 0); |
| 105 | } |
| 106 | |
| 107 | // for (int ax0_ax1_fused_0 = 0; ax0_ax1_fused_0 < 2; ++ax0_ax1_fused_0) { |
| 108 | uint32_t zeros_loaded = *(uint32_t*)(zeros_ptr + k_0_0 * 32 / G * (OC / 8)); |
| 109 | uint4 B_loaded_zero = dequantize_s4_to_fp16x2(zeros_loaded); |
| 110 | uint4 B_loaded_scale = |
| 111 | *(uint4*)(scaling_factors_ptr + k_0_0 * 32 / G * (OC)); |
| 112 | /* |
| 113 | if (blockIdx_z == 0 && blockIdx_y == 0 && k_0_0 == 0 && threadIdx.x == 0 && |
| 114 | threadIdx.y == 0){ printf("%x %x %x %x %x %x %x %x\n", B_loaded_scale.x, |
| 115 | B_loaded_scale.y, B_loaded_scale.z, B_loaded_scale.w, B_loaded_zero.x, |
| 116 | B_loaded_zero.y, B_loaded_zero.z, B_loaded_zero.w); |
| 117 | } |
| 118 | */ |
| 119 | // uint4 B_loaded_scale = make_uint4(0, 0, 0, 0); |
| 120 | int* B_ptr_local = B_ptr + k_0_0 * 32 * (OC / 8); |
| 121 | |
| 122 | for (int ax0_ax1_fused_0 = 0; ax0_ax1_fused_0 < N / 16; ++ax0_ax1_fused_0) { |
| 123 | // B: 32 x 136 (128+8) float16 |
| 124 | // each warp: 32 x 4 |
| 125 | // each thr: read 32 bit -> convert to 8xFP16 (a UINT4) -> scale and minus |
| 126 | // zero -> WB UINT4 |
| 127 | // *(uint4*)(B_shared + ((((ax0_ax1_fused_0 * 544) + (((int)threadIdx.y) * |
| 128 | // 272)) + ((((int)threadIdx.x) >> 4) * 136)) + ((((int)threadIdx.x) & 15) |
| 129 | // * 8))) = *(uint4*)(B + ((((((k_0_0 * 163840) + (ax0_ax1_fused_0 * |
| 130 | // 20480)) + (((int)threadIdx.y) * 10240)) + ((((int)threadIdx.x) >> 4) * |
| 131 | // 5120)) + (((int)blockIdx_y) * 128)) + ((((int)threadIdx.x) & 15) * |
| 132 | // 8))); row stride in shared memory: (NWARPS * 32 * 8 / cta_N) |
| 133 | uint32_t B_loaded = |
| 134 | *(uint32_t*)(B_ptr_local + ax0_ax1_fused_0 * row_stride * (OC / 8)); |
| 135 | uint4 B_loaded_fp16 = dequantize_s4_to_fp16x2(B_loaded); |
| 136 | |
| 137 | // - zero and * scale |
| 138 | // TODO (Haotian): can save 4 assembly instructions if sormulate as deq = |
| 139 | // q * scale - zero * scale. |
| 140 | asm volatile("sub.f16x2 %0, %1, %2;\n" |
| 141 | : "=r"(B_loaded_fp16.x) |
| 142 | : "r"(B_loaded_fp16.x), "r"(B_loaded_zero.x)); |
| 143 | asm volatile("fma.rn.f16x2 %0, %1, %2, %3;\n" |
| 144 | : "=r"(B_loaded_fp16.x) |
| 145 | : "r"(B_loaded_fp16.x), "r"(B_loaded_scale.x), "r"(ZERO)); |
| 146 | asm volatile("sub.f16x2 %0, %1, %2;\n" |
| 147 | : "=r"(B_loaded_fp16.y) |
| 148 | : "r"(B_loaded_fp16.y), "r"(B_loaded_zero.y)); |
| 149 | asm volatile("fma.rn.f16x2 %0, %1, %2, %3;\n" |
| 150 | : "=r"(B_loaded_fp16.y) |
| 151 | : "r"(B_loaded_fp16.y), "r"(B_loaded_scale.y), "r"(ZERO)); |
| 152 | asm volatile("sub.f16x2 %0, %1, %2;\n" |
| 153 | : "=r"(B_loaded_fp16.z) |
| 154 | : "r"(B_loaded_fp16.z), "r"(B_loaded_zero.z)); |
| 155 | asm volatile("fma.rn.f16x2 %0, %1, %2, %3;\n" |
| 156 | : "=r"(B_loaded_fp16.z) |
| 157 | : "r"(B_loaded_fp16.z), "r"(B_loaded_scale.z), "r"(ZERO)); |
| 158 | asm volatile("sub.f16x2 %0, %1, %2;\n" |
| 159 | : "=r"(B_loaded_fp16.w) |
| 160 | : "r"(B_loaded_fp16.w), "r"(B_loaded_zero.w)); |
| 161 | asm volatile("fma.rn.f16x2 %0, %1, %2, %3;\n" |
| 162 | : "=r"(B_loaded_fp16.w) |
| 163 | : "r"(B_loaded_fp16.w), "r"(B_loaded_scale.w), "r"(ZERO)); |
| 164 | /* |
| 165 | if (ax0_ax1_fused_0 == 0 && blockIdx_z == 0 && blockIdx_y == 0 && k_0_0 == |
| 166 | 0 && threadIdx.x == 17 && threadIdx.y == 0){ printf("[x] %X %X %X %X\n", |
| 167 | B_loaded_fp16.x, B_loaded_fp16.y, B_loaded_fp16.z, B_loaded_fp16.w); |
| 168 | } |
| 169 | */ |
| 170 | |
| 171 | // write back |
| 172 | *(uint4*)(B_shared_ptr + ax0_ax1_fused_0 * row_stride * (N + 8)) = |
| 173 | B_loaded_fp16; |
| 174 | } |
| 175 | __syncthreads(); |
| 176 | |
| 177 | for (int k_0_1 = 0; k_0_1 < 2; ++k_0_1) { |
| 178 | { |
| 179 | unsigned int addr; |
| 180 | __asm__ __volatile__( |
| 181 | "{ .reg .u64 addr; cvta.to.shared.u64 addr, %1; cvt.u32.u64 %0, " |
| 182 | "addr; }\n" |
| 183 | : "=r"(addr) |
| 184 | : "l"((void*)((&(A_shared[(k_0_1 * 16)])) + |
| 185 | (((((int)threadIdx.x) & 15) * 40) + |
| 186 | ((((int)threadIdx.x) >> 4) * 8))))); |
| 187 | |
| 188 | __asm__ __volatile__( |
| 189 | "ldmatrix.sync.aligned.m8n8.x4.shared.b16" |
| 190 | "{%0, %1, %2, %3}, [%4];\n" |
| 191 | : "=r"(((unsigned*)(A_shared_warp + 0))[0]), |
| 192 | "=r"(((unsigned*)(A_shared_warp + 0))[1]), |
| 193 | "=r"(((unsigned*)(A_shared_warp + 0))[2]), |
| 194 | "=r"(((unsigned*)(A_shared_warp + 0))[3]) |
| 195 | : "r"(addr)); |
| 196 | } |
| 197 | |
| 198 | for (int ax1_0 = 0; ax1_0 < N / 32; ++ax1_0) { |
| 199 | { |
| 200 | unsigned int addr; |
| 201 | __asm__ __volatile__( |
| 202 | "{ .reg .u64 addr; cvta.to.shared.u64 addr, %1; cvt.u32.u64 %0, " |
| 203 | "addr; }\n" |
| 204 | : "=r"(addr) |
| 205 | : "l"((void*)((&(B_shared[(((k_0_1 * (N * 16 + 128)) + |
| 206 | (((int)threadIdx.y) * (N / 2))) + |
| 207 | (ax1_0 * 16))])) + |
| 208 | (((((int)threadIdx.x) & 15) * (N + 8)) + |
| 209 | ((((int)threadIdx.x) >> 4) * 8))))); |
| 210 | __asm__ __volatile__( |
| 211 | "ldmatrix.sync.aligned.m8n8.x4.trans.shared.b16" |
| 212 | "{%0, %1, %2, %3}, [%4];\n" |
| 213 | : "=r"(((unsigned*)(B_shared_warp + (ax1_0 * 8)))[0]), |
| 214 | "=r"(((unsigned*)(B_shared_warp + (ax1_0 * 8)))[1]), |
| 215 | "=r"(((unsigned*)(B_shared_warp + (ax1_0 * 8)))[2]), |
| 216 | "=r"(((unsigned*)(B_shared_warp + (ax1_0 * 8)))[3]) |
| 217 | : "r"(addr)); |
| 218 | } |
| 219 | } |
| 220 | for (int j_0_4 = 0; j_0_4 < N / 32; ++j_0_4) { |
| 221 | #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ == 750 |
| 222 | { |
| 223 | __asm__ __volatile__( |
| 224 | "mma.sync.aligned.m16n8k8.row.col.f32.f16.f16.f32" |
| 225 | "{%0, %1, %2, %3}, {%4, %5}, {%6}, {%7, %8, %9, %10};\n" |
| 226 | : "=f"(((float*)(C_warp + (j_0_4 * 8)))[0]), |
| 227 | "=f"(((float*)(C_warp + (j_0_4 * 8)))[1]), |
| 228 | "=f"(((float*)(C_warp + (j_0_4 * 8)))[2]), |
| 229 | "=f"(((float*)(C_warp + (j_0_4 * 8)))[3]) |
| 230 | : "r"(((unsigned*)(A_shared_warp + 0))[0]), |
| 231 | "r"(((unsigned*)(A_shared_warp + 0))[1]), |
| 232 | "r"(((unsigned*)(B_shared_warp + (j_0_4 * 8)))[0]), |
| 233 | "f"(((float*)(C_warp + (j_0_4 * 8)))[0]), |
| 234 | "f"(((float*)(C_warp + (j_0_4 * 8)))[1]), |
| 235 | "f"(((float*)(C_warp + (j_0_4 * 8)))[2]), |
| 236 | "f"(((float*)(C_warp + (j_0_4 * 8)))[3])); |
| 237 | } |
| 238 | |
| 239 | { |
| 240 | __asm__ __volatile__( |
| 241 | "mma.sync.aligned.m16n8k8.row.col.f32.f16.f16.f32" |
| 242 | "{%0, %1, %2, %3}, {%4, %5}, {%6}, {%7, %8, %9, %10};\n" |
| 243 | : "=f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[0]), |
| 244 | "=f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[1]), |
| 245 | "=f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[2]), |
| 246 | "=f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[3]) |
| 247 | : "r"(((unsigned*)(A_shared_warp + 0))[0]), |
| 248 | "r"(((unsigned*)(A_shared_warp + 0))[1]), |
| 249 | "r"(((unsigned*)(B_shared_warp + ((j_0_4 * 8) + 4)))[0]), |
| 250 | "f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[0]), |
| 251 | "f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[1]), |
| 252 | "f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[2]), |
| 253 | "f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[3])); |
| 254 | } |
| 255 | |
| 256 | { |
| 257 | __asm__ __volatile__( |
| 258 | "mma.sync.aligned.m16n8k8.row.col.f32.f16.f16.f32" |
| 259 | "{%0, %1, %2, %3}, {%4, %5}, {%6}, {%7, %8, %9, %10};\n" |
| 260 | : "=f"(((float*)(C_warp + (j_0_4 * 8)))[0]), |
| 261 | "=f"(((float*)(C_warp + (j_0_4 * 8)))[1]), |
| 262 | "=f"(((float*)(C_warp + (j_0_4 * 8)))[2]), |
| 263 | "=f"(((float*)(C_warp + (j_0_4 * 8)))[3]) |
| 264 | : "r"(((unsigned*)(A_shared_warp + 0))[2]), |
| 265 | "r"(((unsigned*)(A_shared_warp + 0))[3]), |
| 266 | "r"(((unsigned*)(B_shared_warp + (j_0_4 * 8)))[1]), |
| 267 | "f"(((float*)(C_warp + (j_0_4 * 8)))[0]), |
| 268 | "f"(((float*)(C_warp + (j_0_4 * 8)))[1]), |
| 269 | "f"(((float*)(C_warp + (j_0_4 * 8)))[2]), |
| 270 | "f"(((float*)(C_warp + (j_0_4 * 8)))[3])); |
| 271 | } |
| 272 | |
| 273 | { |
| 274 | __asm__ __volatile__( |
| 275 | "mma.sync.aligned.m16n8k8.row.col.f32.f16.f16.f32" |
| 276 | "{%0, %1, %2, %3}, {%4, %5}, {%6}, {%7, %8, %9, %10};\n" |
| 277 | : "=f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[0]), |
| 278 | "=f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[1]), |
| 279 | "=f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[2]), |
| 280 | "=f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[3]) |
| 281 | : "r"(((unsigned*)(A_shared_warp + 0))[2]), |
| 282 | "r"(((unsigned*)(A_shared_warp + 0))[3]), |
| 283 | "r"(((unsigned*)(B_shared_warp + ((j_0_4 * 8) + 4)))[1]), |
| 284 | "f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[0]), |
| 285 | "f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[1]), |
| 286 | "f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[2]), |
| 287 | "f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[3])); |
| 288 | } |
| 289 | #else |
| 290 | { |
| 291 | __asm__ __volatile__( |
| 292 | "mma.sync.aligned.m16n8k16.row.col.f32.f16.f16.f32" |
| 293 | "{%0, %1, %2, %3}, {%4, %5, %6, %7}, {%8, %9}, {%10, %11, %12, " |
| 294 | "%13};\n" |
| 295 | : "=f"(((float*)(C_warp + (j_0_4 * 8)))[0]), |
| 296 | "=f"(((float*)(C_warp + (j_0_4 * 8)))[1]), |
| 297 | "=f"(((float*)(C_warp + (j_0_4 * 8)))[2]), |
| 298 | "=f"(((float*)(C_warp + (j_0_4 * 8)))[3]) |
| 299 | : "r"(((unsigned*)(A_shared_warp + 0))[0]), |
| 300 | "r"(((unsigned*)(A_shared_warp + 0))[1]), |
| 301 | "r"(((unsigned*)(A_shared_warp + 0))[2]), |
| 302 | "r"(((unsigned*)(A_shared_warp + 0))[3]), |
| 303 | "r"(((unsigned*)(B_shared_warp + (j_0_4 * 8)))[0]), |
| 304 | "r"(((unsigned*)(B_shared_warp + (j_0_4 * 8)))[1]), |
| 305 | "f"(((float*)(C_warp + (j_0_4 * 8)))[0]), |
| 306 | "f"(((float*)(C_warp + (j_0_4 * 8)))[1]), |
| 307 | "f"(((float*)(C_warp + (j_0_4 * 8)))[2]), |
| 308 | "f"(((float*)(C_warp + (j_0_4 * 8)))[3])); |
| 309 | } |
| 310 | |
| 311 | { |
| 312 | __asm__ __volatile__( |
| 313 | "mma.sync.aligned.m16n8k16.row.col.f32.f16.f16.f32" |
| 314 | "{%0, %1, %2, %3}, {%4, %5, %6, %7}, {%8, %9}, {%10, %11, %12, " |
| 315 | "%13};\n" |
| 316 | : "=f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[0]), |
| 317 | "=f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[1]), |
| 318 | "=f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[2]), |
| 319 | "=f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[3]) |
| 320 | : "r"(((unsigned*)(A_shared_warp + 0))[0]), |
| 321 | "r"(((unsigned*)(A_shared_warp + 0))[1]), |
| 322 | "r"(((unsigned*)(A_shared_warp + 0))[2]), |
| 323 | "r"(((unsigned*)(A_shared_warp + 0))[3]), |
| 324 | "r"(((unsigned*)(B_shared_warp + ((j_0_4 * 8) + 4)))[0]), |
| 325 | "r"(((unsigned*)(B_shared_warp + ((j_0_4 * 8) + 4)))[1]), |
| 326 | "f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[0]), |
| 327 | "f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[1]), |
| 328 | "f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[2]), |
| 329 | "f"(((float*)(C_warp + ((j_0_4 * 8) + 4)))[3])); |
| 330 | } |
| 331 | |
| 332 | #endif |
| 333 | } |
| 334 | } |
| 335 | } |
| 336 | |
| 337 | // TODO: Shang: Hoist loop invariance. |
| 338 | for (int ax1_0_1 = 0; ax1_0_1 < (N / 32); ++ax1_0_1) { |
| 339 | for (int local_id = 0; local_id < 8; ++local_id) { |
| 340 | int row_offset = (((int)blockIdx_y) / j_factors1) * 16 + |
| 341 | ((int)threadIdx.x) / 4 + (local_id % 4) / 2 * 8; |
| 342 | if (row_offset < M) { |
| 343 | *(C_ptr + ax1_0_1 * 16 + row_offset * OC + (local_id / 4) * 8 + |
| 344 | local_id % 2) = __float2half(C_warp[(ax1_0_1 * 8) + local_id]); |
| 345 | } |
| 346 | } |
| 347 | } |
| 348 | #endif |
| 349 | } |
| 350 | |
| 351 | __global__ void __launch_bounds__(64) |
| 352 | dequantize_weights(int* __restrict__ B, half* __restrict__ scaling_factors, |
| 353 | int* __restrict__ zeros, half* __restrict__ C, int G) { |
| 354 | static constexpr uint32_t ZERO = 0x0; |
| 355 | half B_shared[32 * (128 + 8)]; |
| 356 | |
| 357 | half* B_shared_ptr2 = B_shared; |
| 358 | |
| 359 | int N = blockDim.x * gridDim.x; // 2 |
| 360 | int col = (blockIdx.x * blockDim.x + threadIdx.x); |
| 361 | int row = blockIdx.y * blockDim.y + threadIdx.y; |
| 362 | int index1 = 8 * col + 8 * row * N; |
| 363 | half* C_ptr2 = C + index1; |
| 364 | |
| 365 | int index2 = col + row * N; |
| 366 | int* B_ptr2 = B + index2; |
| 367 | |
| 368 | int index3 = col + (int)(row / G) * N; |
| 369 | int* zeros_ptr2 = zeros + index3; |
| 370 | int index4 = 8 * col + (int)(row / G) * N * 8; |
| 371 | half* scaling_factors_ptr2 = scaling_factors + index4; |
| 372 | |
| 373 | uint32_t zeros_loaded = *(uint32_t*)(zeros_ptr2); |
| 374 | uint4 B_loaded_zero = dequantize_s4_to_fp16x2(zeros_loaded); |
| 375 | uint4 B_loaded_scale = *(uint4*)(scaling_factors_ptr2); |
| 376 | |
| 377 | uint32_t B_loaded = *(uint32_t*)B_ptr2; |
| 378 | uint4 B_loaded_fp16 = dequantize_s4_to_fp16x2(B_loaded); |
| 379 | asm volatile("sub.f16x2 %0, %1, %2;\n" |
| 380 | : "=r"(B_loaded_fp16.x) |
| 381 | : "r"(B_loaded_fp16.x), "r"(B_loaded_zero.x)); |
| 382 | asm volatile("fma.rn.f16x2 %0, %1, %2, %3;\n" |
| 383 | : "=r"(B_loaded_fp16.x) |
| 384 | : "r"(B_loaded_fp16.x), "r"(B_loaded_scale.x), "r"(ZERO)); |
| 385 | asm volatile("sub.f16x2 %0, %1, %2;\n" |
| 386 | : "=r"(B_loaded_fp16.y) |
| 387 | : "r"(B_loaded_fp16.y), "r"(B_loaded_zero.y)); |
| 388 | asm volatile("fma.rn.f16x2 %0, %1, %2, %3;\n" |
| 389 | : "=r"(B_loaded_fp16.y) |
| 390 | : "r"(B_loaded_fp16.y), "r"(B_loaded_scale.y), "r"(ZERO)); |
| 391 | asm volatile("sub.f16x2 %0, %1, %2;\n" |
| 392 | : "=r"(B_loaded_fp16.z) |
| 393 | : "r"(B_loaded_fp16.z), "r"(B_loaded_zero.z)); |
| 394 | asm volatile("fma.rn.f16x2 %0, %1, %2, %3;\n" |
| 395 | : "=r"(B_loaded_fp16.z) |
| 396 | : "r"(B_loaded_fp16.z), "r"(B_loaded_scale.z), "r"(ZERO)); |
| 397 | asm volatile("sub.f16x2 %0, %1, %2;\n" |
| 398 | : "=r"(B_loaded_fp16.w) |
| 399 | : "r"(B_loaded_fp16.w), "r"(B_loaded_zero.w)); |
| 400 | asm volatile("fma.rn.f16x2 %0, %1, %2, %3;\n" |
| 401 | : "=r"(B_loaded_fp16.w) |
| 402 | : "r"(B_loaded_fp16.w), "r"(B_loaded_scale.w), "r"(ZERO)); |
| 403 | |
| 404 | *(uint4*)B_shared_ptr2 = B_loaded_fp16; |
| 405 | |
| 406 | for (int i = 0; i < 8; ++i) { |
| 407 | *(C_ptr2 + i) = B_shared[i]; |
| 408 | } |
| 409 | } |
| 410 | |
| 411 | } // namespace awq |
| 412 | } // namespace vllm |
| 413 | |
| 414 | torch::stable::Tensor awq_dequantize(torch::stable::Tensor _kernel, |
| 415 | torch::stable::Tensor _scaling_factors, |
| 416 | torch::stable::Tensor _zeros, |
| 417 | int64_t split_k_iters, int64_t thx, |
| 418 | int64_t thy) { |
| 419 | int in_c = _kernel.size(0); |
| 420 | int qout_c = _kernel.size(1); |
| 421 | int out_c = qout_c * 8; |
| 422 | int G = in_c / _scaling_factors.size(0); |
| 423 | |
| 424 | int x_thread = thx; |
| 425 | int y_thread = thy; |
| 426 | |
| 427 | int x_blocks = 1; |
| 428 | int y_blocks = 1; |
| 429 | if (thx == 0) { |
| 430 | x_thread = qout_c; |
| 431 | } |
| 432 | if (thy == 0) { |
| 433 | y_thread = in_c; |
| 434 | } |
| 435 | if (thx == 0 && thy == 0) { |
| 436 | x_thread = 8; |
| 437 | y_thread = 8; |
| 438 | x_blocks = (int)(qout_c / 8); |
| 439 | y_blocks = (int)(in_c / 8); |
| 440 | } |
| 441 | |
| 442 | const torch::stable::accelerator::DeviceGuard device_guard( |
| 443 | _scaling_factors.get_device_index()); |
| 444 | |
| 445 | auto _de_kernel = |
| 446 | torch::stable::empty({in_c, out_c}, _scaling_factors.scalar_type(), |
| 447 | std::nullopt, _scaling_factors.device()); |
| 448 | |
| 449 | auto kernel = reinterpret_cast<int*>(_kernel.mutable_data_ptr<int>()); |
| 450 | auto de_kernel = reinterpret_cast<half*>( |
| 451 | _de_kernel.mutable_data_ptr<torch::headeronly::Half>()); |
| 452 | auto scaling_factors = reinterpret_cast<half*>( |
| 453 | _scaling_factors.mutable_data_ptr<torch::headeronly::Half>()); |
| 454 | auto zeros = reinterpret_cast<int*>(_zeros.mutable_data_ptr<int>()); |
| 455 | |
| 456 | dim3 num_blocks(x_blocks, y_blocks); |
| 457 | dim3 threads_per_block(x_thread, y_thread); |
| 458 | |
| 459 | const cudaStream_t stream = get_current_cuda_stream(); |
| 460 | vllm::awq::dequantize_weights<<<num_blocks, threads_per_block, 0, stream>>>( |
| 461 | kernel, scaling_factors, zeros, de_kernel, G); |
| 462 | |
| 463 | return _de_kernel; |
| 464 | } |
| 465 | |
| 466 | // in_feats: M, IC [float16] |
| 467 | // kernel: IC, OC // 8 [int32] -> cast to IC, OC [uint4b] |
| 468 | // scaling_factors: IC // G, OC [float16] |
| 469 | // zeros: IC // G, OC // 8 [int32] -> cast to IC // G, OC [uint4b] |
| 470 | // assume that batch_size < 16 for now |
| 471 | |
| 472 | torch::stable::Tensor awq_gemm(torch::stable::Tensor _in_feats, |
| 473 | torch::stable::Tensor _kernel, |
| 474 | torch::stable::Tensor _scaling_factors, |
| 475 | torch::stable::Tensor _zeros, |
| 476 | int64_t split_k_iters) { |
| 477 | int num_in_feats = _in_feats.size(0); |
| 478 | int num_in_channels = _in_feats.size(1); |
| 479 | const torch::stable::accelerator::DeviceGuard device_guard( |
| 480 | _in_feats.get_device_index()); |
| 481 | |
| 482 | auto _out_feats = torch::stable::empty( |
| 483 | {split_k_iters, num_in_feats, _kernel.size(1) * 8}, |
| 484 | _in_feats.scalar_type(), std::nullopt, _in_feats.device()); |
| 485 | int num_out_feats = _out_feats.size(-2); |
| 486 | int num_out_channels = _out_feats.size(-1); |
| 487 | |
| 488 | auto in_feats = reinterpret_cast<half*>( |
| 489 | _in_feats.mutable_data_ptr<torch::headeronly::Half>()); |
| 490 | auto kernel = reinterpret_cast<int*>(_kernel.mutable_data_ptr<int>()); |
| 491 | auto out_feats = reinterpret_cast<half*>( |
| 492 | _out_feats.mutable_data_ptr<torch::headeronly::Half>()); |
| 493 | auto scaling_factors = reinterpret_cast<half*>( |
| 494 | _scaling_factors.mutable_data_ptr<torch::headeronly::Half>()); |
| 495 | auto zeros = reinterpret_cast<int*>(_zeros.mutable_data_ptr<int>()); |
| 496 | int group_size = num_in_channels / _scaling_factors.size(0); |
| 497 | |
| 498 | if (num_out_channels % 64 != 0) |
| 499 | throw std::invalid_argument("OC is not multiple of cta_N = 64"); |
| 500 | if (num_out_channels % 8 != 0) |
| 501 | throw std::invalid_argument("OC is not multiple of pack_num = 8"); |
| 502 | if (group_size % 32 != 0) |
| 503 | throw std::invalid_argument("Group size should be a multiple of 32"); |
| 504 | if (num_out_channels % group_size != 0) |
| 505 | throw std::invalid_argument("OC is not multiple of Group size"); |
| 506 | |
| 507 | const cudaStream_t stream = get_current_cuda_stream(); |
| 508 | if (num_out_channels % 128 == 0) { |
| 509 | int j_factors1 = num_out_channels / 128 / 1; |
| 510 | dim3 num_blocks((num_out_feats + 16 - 1) / 16 * j_factors1 * split_k_iters); |
| 511 | // threadIdx.x: 32 |
| 512 | // threadIdx.y: i_factors[2] * j_factors[2] |
| 513 | dim3 threads_per_block(32, 2); |
| 514 | vllm::awq::gemm_forward_4bit_cuda_m16nXk32<128> |
| 515 | <<<num_blocks, threads_per_block, 0, stream>>>( |
| 516 | group_size, split_k_iters, in_feats, kernel, scaling_factors, zeros, |
| 517 | num_in_feats, num_in_channels, num_out_channels, out_feats); |
| 518 | } else if (num_out_channels % 64 == 0) { |
| 519 | int j_factors1 = num_out_channels / 64 / 1; |
| 520 | dim3 num_blocks(1 * (num_out_feats + 16 - 1) / 16 * j_factors1 * |
| 521 | split_k_iters); |
| 522 | |
| 523 | // threadIdx.x: 32 |
| 524 | // threadIdx.y: i_factors[2] * j_factors[2] |
| 525 | dim3 threads_per_block(32, 2); |
| 526 | vllm::awq::gemm_forward_4bit_cuda_m16nXk32<64> |
| 527 | <<<num_blocks, threads_per_block, 0, stream>>>( |
| 528 | group_size, split_k_iters, in_feats, kernel, scaling_factors, zeros, |
| 529 | num_in_feats, num_in_channels, num_out_channels, out_feats); |
| 530 | } |
| 531 | // Pass the reduction dim via a named, lifetime-stable local rather than a |
| 532 | // bare `0`. |
| 533 | constexpr int64_t sum_dim = 0; |
| 534 | return torch::stable::sum( |
| 535 | _out_feats, torch::headeronly::IntHeaderOnlyArrayRef(&sum_dim, 1)); |
| 536 | } |