A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 15, 21:00 UTC
[Misc][Kernel]: Add GPTQAllSpark Quantization (#12931)
YajieWang committed
Mar 1, 2025 at 14:30 UTC
6a92ff93e1221ef0a5ff1528c2fdf05cecfb94ec
12 files changed
+2005
-4
CMakeLists.txt
+16
@@ -317,6 +317,22 @@ if(VLLM_GPU_LANG STREQUAL "CUDA")
317
" in CUDA target architectures")
318
endif()
319
320
+ # Only build AllSpark kernels if we are building for at least some compatible archs.
321
+ cuda_archs_loose_intersection(ALLSPARK_ARCHS "8.0;8.6;8.7;8.9" "${CUDA_ARCHS}")
322
+ if (ALLSPARK_ARCHS)
323
+ set(ALLSPARK_SRCS
324
+ "csrc/quantization/gptq_allspark/allspark_repack.cu"
325
+ "csrc/quantization/gptq_allspark/allspark_qgemm_w8a16.cu")
326
+ set_gencode_flags_for_srcs(
327
+ SRCS "${ALLSPARK_SRCS}"
328
+ CUDA_ARCHS "${ALLSPARK_ARCHS}")
329
+ list(APPEND VLLM_EXT_SRC "${ALLSPARK_SRCS}")
330
+ message(STATUS "Building AllSpark kernels for archs: ${ALLSPARK_ARCHS}")
331
+ else()
332
+ message(STATUS "Not building AllSpark kernels as no compatible archs found"
333
+ " in CUDA target architectures")
334
+ endif()
335
+
336
# The cutlass_scaled_mm kernels for Hopper (c3x, i.e. CUTLASS 3.x) require
337
# CUDA 12.0 or later (and only work on Hopper, 9.0a for now).
338
cuda_archs_loose_intersection(SCALED_MM_3X_ARCHS "9.0a" "${CUDA_ARCHS}")
benchmarks/kernels/benchmark_marlin.py
+45
-2
@@ -10,6 +10,8 @@ from vllm import _custom_ops as ops
10
from vllm.model_executor.layers.quantization.gptq_marlin_24 import (
11
GPTQ_MARLIN_24_MAX_PARALLEL, GPTQ_MARLIN_24_MIN_THREAD_N,
12
GPTQ_MARLIN_24_SUPPORTED_GROUP_SIZES, GPTQ_MARLIN_24_SUPPORTED_QUANT_TYPES)
13
+from vllm.model_executor.layers.quantization.utils.allspark_utils import (
14
+ ALLSPARK_AMPERE_M_CUBLAS_THRESHOLD, ALLSPARK_SUPPORTED_QUANT_TYPES)
15
from vllm.model_executor.layers.quantization.utils.marlin_utils import (
16
GPTQ_MARLIN_MAX_PARALLEL, GPTQ_MARLIN_MIN_THREAD_N,
17
MARLIN_SUPPORTED_GROUP_SIZES, query_marlin_supported_quant_types)
@@ -18,12 +20,12 @@ from vllm.model_executor.layers.quantization.utils.marlin_utils_test import (
20
from vllm.model_executor.layers.quantization.utils.marlin_utils_test_24 import (
21
marlin_24_quantize)
22
from vllm.model_executor.layers.quantization.utils.quant_utils import (
21
- gptq_pack, gptq_quantize_weights, sort_weights)
23
+ gptq_pack, gptq_quantize_weights, quantize_weights, sort_weights)
24
from vllm.scalar_type import ScalarType
25
from vllm.utils import FlexibleArgumentParser
26
27
DEFAULT_MODELS = ["meta-llama/Llama-2-7b-hf/TP1"]
26
-DEFAULT_BATCH_SIZES = [1, 16, 32, 64, 128, 256, 512]
28
+DEFAULT_BATCH_SIZES = [1, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192]
29
30
ACT_ORDER_OPTS = [False, True]
31
K_FULL_OPTS = [False, True]
@@ -81,6 +83,27 @@ def bench_run(results: List[benchmark.Measurement], model: str,
83
GPTQ_MARLIN_24_MAX_PARALLEL)
84
marlin_zp = torch.zeros_like(marlin_s, dtype=torch.int)
85
86
+ # AllSpark W8A16 quant
87
+ as_supported_case = (quant_type in ALLSPARK_SUPPORTED_QUANT_TYPES
88
+ and group_size == -1 and not act_order and is_k_full)
89
+ if as_supported_case:
90
+ properties = torch.cuda.get_device_properties(b.device.index)
91
+ sm_count = properties.multi_processor_count
92
+ sm_version = properties.major * 10 + properties.minor
93
+
94
+ supported_arch = (sm_version >= 80 and sm_version < 90)
95
+ as_supported_case = as_supported_case and supported_arch
96
+ if supported_arch:
97
+ has_zp = False
98
+ w_ref, qw, s, zp = quantize_weights(b, quant_type, group_size,
99
+ has_zp)
100
+ qw = qw.to(torch.uint8)
101
+
102
+ qw_reorder, s_reorder, zp_reorder = \
103
+ ops.allspark_repack_weight(
104
+ qw, s, zp, has_zp)
105
+ CUBLAS_M_THRESHOLD = ALLSPARK_AMPERE_M_CUBLAS_THRESHOLD
106
+
107
globals = {
108
# Gen params
109
"quant_type": quant_type,
@@ -109,10 +132,19 @@ def bench_run(results: List[benchmark.Measurement], model: str,
132
# GPTQ params
133
"q_w_gptq": q_w_gptq,
134
"repack_sort_indices": repack_sort_indices,
135
+ # AllSpark W8A16 params
136
+ "qw_reorder": qw_reorder if as_supported_case else None,
137
+ "s_reorder": s_reorder if as_supported_case else None,
138
+ "zp_reorder": zp_reorder if as_supported_case else None,
139
+ "sm_count": sm_count if as_supported_case else None,
140
+ "sm_version": sm_version if as_supported_case else None,
141
+ "CUBLAS_M_THRESHOLD":
142
+ CUBLAS_M_THRESHOLD if as_supported_case else None,
143
# Kernels
144
"gptq_marlin_gemm": ops.gptq_marlin_gemm,
145
"gptq_marlin_24_gemm": ops.gptq_marlin_24_gemm,
146
"gptq_marlin_repack": ops.gptq_marlin_repack,
147
+ "allspark_w8a16_gemm": ops.allspark_w8a16_gemm,
148
}
149
150
min_run_time = 1
@@ -172,6 +204,17 @@ def bench_run(results: List[benchmark.Measurement], model: str,
204
description="gptq_marlin_repack",
205
).blocked_autorange(min_run_time=min_run_time))
206
207
+ if as_supported_case:
208
+ results.append(
209
+ benchmark.Timer(
210
+ stmt=
211
+ "output = allspark_w8a16_gemm(a, qw_reorder, s_reorder, zp_reorder, size_n, group_size, sm_count, sm_version, CUBLAS_M_THRESHOLD, False, True)", # noqa: E501
212
+ globals=globals,
213
+ label=label,
214
+ sub_label=sub_label,
215
+ description="allspark_w8a16_gemm_fp32",
216
+ ).blocked_autorange(min_run_time=min_run_time))
217
+
218
219
def main(args):
220
print("Benchmarking models:")
csrc/quantization/gptq_allspark/allspark_qgemm_w8a16.cu
new
+1008
@@ -0,0 +1,1008 @@
1
+#include "allspark_utils.cuh"
2
+#include <torch/all.h>
3
+#include "core/registration.h"
4
+#include <cublas_v2.h>
5
+
6
+at::Tensor as_g_workspace;
7
+
8
+#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 800
9
+
10
+torch::Tensor allspark_w8a16_gemm(
11
+ torch::Tensor const& a, torch::Tensor const& b_qweight,
12
+ torch::Tensor const& b_scales, c10::optional<torch::Tensor> const& b_qzeros,
13
+ int64_t n, int64_t group_size, int64_t sm_count, int64_t sm_version,
14
+ int64_t CUBLAS_M_THRESHOLD, bool has_zp, bool n32k16_reorder) {
15
+ TORCH_CHECK_NOT_IMPLEMENTED(
16
+ false, "allspark_w8a16_gemm(..) requires CUDA_ARCH >= 8.0");
17
+ return torch::empty({1, 1});
18
+}
19
+
20
+#else
21
+namespace allspark {
22
+/*
23
+ * GemmTile manage data movement from Global Memory to Shared Memory
24
+ * requiring N % 8 == 0, K % 16 == 0 by loading uint
25
+ * BN is obtained by padding the original N to a multiple of 32
26
+ * weight B is rearranged as N32K16 order,
27
+ * i.e. a initial data block of size 32(n)x16(k) is reordered as n8k4n4k4,
28
+ * in order to put data loaded by the same thread of 32x16 data block together
29
+ * continuously (see
30
+ * https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#matrix-fragments-for-mma-m16n8k16-with-floating-point-type)
31
+ */
32
+template <typename FType, typename QType, int Mtile, int Ntile, int NStage,
33
+ int BLOCK>
34
+struct GmemTile_W8A16_PerC_MtilexNtilex32_multistage_SM8x_SplitK {
35
+ // element num loaded by a LDG inst.
36
+ static constexpr int LDG_ELEMENT_CNT_A = 8;
37
+ static constexpr int LDG_ELEMENT_CNT_B = 16;
38
+ static constexpr int WARP_SIZE = 32;
39
+ static constexpr int M_SIZE_ONE_LOAD = (BLOCK * LDG_ELEMENT_CNT_A) / 32;
40
+ static constexpr int N_SIZE_ONE_LOAD = (BLOCK * LDG_ELEMENT_CNT_B) / 32;
41
+
42
+ __device__ GmemTile_W8A16_PerC_MtilexNtilex32_multistage_SM8x_SplitK(
43
+ const SM8x_GEMM_W8A16_Splitk_Params<FType, QType>& k_params,
44
+ const uint32_t& A_smem_addr, const uint32_t& BQ_smem_addr,
45
+ const uint32_t& A_stage_stride, const uint32_t& BQ_stage_stride)
46
+ : params(k_params),
47
+ A_smem_base_addr(A_smem_addr),
48
+ BQ_smem_base_addr(BQ_smem_addr),
49
+ A_smem_stage_stride(A_stage_stride),
50
+ BQ_smem_stage_stride(BQ_stage_stride) {
51
+ this_block_A_base_ptr = params.A_ptr + blockIdx.x * Mtile * params.K +
52
+ blockIdx.z * params.SplitK;
53
+ // here B is rearranged as N32K16 order, i.e. 4 continuous N-direction
54
+ // 8(N)x16(K) size data blocks are packed together
55
+ this_block_B_base_ptr = params.B_ptr + blockIdx.y * Ntile * params.K +
56
+ blockIdx.z * params.SplitK * 4;
57
+
58
+ const int lane_id = threadIdx.x % WARP_SIZE;
59
+
60
+ // For matrix A, a block load/store Mtile(row) x 32(col) elements in
61
+ // multiple iters, 8x4 warp load/store 8(row) x 32(col) elements per iter
62
+ const int Aldg_row_base_idx = threadIdx.x / 4;
63
+ Aldg_col_idx = (threadIdx.x % 4) * LDG_ELEMENT_CNT_A;
64
+ const int Aldg_base_offset = Aldg_row_base_idx * params.K + Aldg_col_idx;
65
+
66
+ // For matrix B, a block load/store elements of (Ntile / 4) row x 128 col
67
+ // elements of N32K16 packing in multiple iters, 4x8 warp load/store 4(row)
68
+ // * 128(col) per iter
69
+ Bldg_col_idx = (threadIdx.x % 8) * LDG_ELEMENT_CNT_B;
70
+ const int Bldg_row_base_idx = threadIdx.x / 8;
71
+ const int Bldg_base_offset =
72
+ Bldg_row_base_idx * params.K * 4 + Bldg_col_idx;
73
+
74
+ this_block_A_base_ptr += Aldg_base_offset;
75
+ this_block_B_base_ptr += Bldg_base_offset;
76
+
77
+ const int sts_a_base_offset =
78
+ (threadIdx.x / 4) * 32 +
79
+ ((lane_id % 4) ^ ((lane_id / 4) % 4) ^ ((lane_id / 4) / 4)) *
80
+ LDG_ELEMENT_CNT_A;
81
+ const int sts_bq_base_offset =
82
+ Bldg_row_base_idx * 32 * 4 +
83
+ ((threadIdx.x % 8) ^ (((threadIdx.x / 8) % 2) * 4)) * LDG_ELEMENT_CNT_B;
84
+
85
+ A_smem_base_addr += sts_a_base_offset * sizeof(FType);
86
+ BQ_smem_base_addr += sts_bq_base_offset * sizeof(uint8_t);
87
+
88
+ A_ldg_guard = 0;
89
+ B_ldg_guard = 0;
90
+ #pragma unroll
91
+ for (int i = 0; i < (Mtile + M_SIZE_ONE_LOAD - 1) / M_SIZE_ONE_LOAD; ++i) {
92
+ int m_idx = blockIdx.x * Mtile + Aldg_row_base_idx + i * M_SIZE_ONE_LOAD;
93
+ if (m_idx < params.M) {
94
+ A_ldg_guard |= (1u << i);
95
+ }
96
+ }
97
+
98
+ const int N_padded = (params.N + 31) / 32 * 32;
99
+ #pragma unroll
100
+ for (int i = 0; i < (Ntile + N_SIZE_ONE_LOAD - 1) / N_SIZE_ONE_LOAD; ++i) {
101
+ int n_idx = blockIdx.y * Ntile + (Bldg_row_base_idx / 8) * 32 +
102
+ i * N_SIZE_ONE_LOAD;
103
+ if (n_idx < N_padded) {
104
+ B_ldg_guard |= (1u << i);
105
+ }
106
+ }
107
+ }
108
+
109
+ __device__ void ldgsts_first_ktiles(const int& first_k_tile,
110
+ const int& k_tiles) {
111
+ // load first k_tile
112
+ // load A
113
+ const int A_src_size = Aldg_col_idx < first_k_tile ? 16 : 0;
114
+ #pragma unroll
115
+ for (int i = 0; i < (Mtile + M_SIZE_ONE_LOAD - 1) / M_SIZE_ONE_LOAD; ++i) {
116
+ cp_async<16>(
117
+ A_smem_base_addr + (i * M_SIZE_ONE_LOAD * 32) * sizeof(FType),
118
+ this_block_A_base_ptr + i * M_SIZE_ONE_LOAD * params.K, A_src_size,
119
+ (A_ldg_guard & (1u << i)) != 0);
120
+ }
121
+
122
+ // load B
123
+ const int B_src_size = (Bldg_col_idx / 4) < first_k_tile ? 16 : 0;
124
+ #pragma unroll
125
+ for (int i = 0; i < (Ntile + N_SIZE_ONE_LOAD - 1) / N_SIZE_ONE_LOAD; ++i) {
126
+ cp_async<16>(
127
+ BQ_smem_base_addr + (i * N_SIZE_ONE_LOAD * 32) * sizeof(uint8_t),
128
+ this_block_B_base_ptr + i * N_SIZE_ONE_LOAD * params.K, B_src_size,
129
+ (B_ldg_guard & (1u << i)) != 0);
130
+ }
131
+
132
+ cp_async_commit_group();
133
+ this_block_A_base_ptr += first_k_tile;
134
+ this_block_B_base_ptr += (first_k_tile * 4);
135
+
136
+ // load second to (N-stage - 1) k_tiles
137
+ for (int stage_idx = 1; stage_idx < NStage - 1; ++stage_idx) {
138
+ if (stage_idx < k_tiles) {
139
+ #pragma unroll
140
+ for (int i = 0; i < (Mtile + M_SIZE_ONE_LOAD - 1) / M_SIZE_ONE_LOAD;
141
+ ++i) {
142
+ cp_async<16>(A_smem_base_addr + stage_idx * A_smem_stage_stride +
143
+ (i * M_SIZE_ONE_LOAD * 32) * sizeof(FType),
144
+ this_block_A_base_ptr + i * M_SIZE_ONE_LOAD * params.K,
145
+ 16, (A_ldg_guard & (1u << i)) != 0);
146
+ }
147
+
148
+ #pragma unroll
149
+ for (int i = 0; i < (Ntile + N_SIZE_ONE_LOAD - 1) / N_SIZE_ONE_LOAD;
150
+ ++i) {
151
+ cp_async<16>(BQ_smem_base_addr + stage_idx * BQ_smem_stage_stride +
152
+ (i * N_SIZE_ONE_LOAD * 32) * sizeof(uint8_t),
153
+ this_block_B_base_ptr + i * N_SIZE_ONE_LOAD * params.K,
154
+ 16, (B_ldg_guard & (1u << i)) != 0);
155
+ }
156
+
157
+ this_block_A_base_ptr += 32;
158
+ this_block_B_base_ptr += (32 * 4);
159
+ }
160
+ cp_async_commit_group();
161
+ }
162
+ }
163
+
164
+ __device__ void ldgsts(const int& sts_stage_idx) {
165
+ const int a_stage_offset = sts_stage_idx * A_smem_stage_stride;
166
+ const int bq_stage_offset = sts_stage_idx * BQ_smem_stage_stride;
167
+ #pragma unroll
168
+ for (int i = 0; i < (Mtile + M_SIZE_ONE_LOAD - 1) / M_SIZE_ONE_LOAD; ++i) {
169
+ cp_async<16>(A_smem_base_addr + a_stage_offset +
170
+ (i * M_SIZE_ONE_LOAD * 32) * sizeof(FType),
171
+ this_block_A_base_ptr + i * M_SIZE_ONE_LOAD * params.K, 16,
172
+ (A_ldg_guard & (1u << i)) != 0);
173
+ }
174
+
175
+ #pragma unroll
176
+ for (int i = 0; i < (Ntile + N_SIZE_ONE_LOAD - 1) / N_SIZE_ONE_LOAD; ++i) {
177
+ cp_async<16>(BQ_smem_base_addr + bq_stage_offset +
178
+ (i * N_SIZE_ONE_LOAD * 32) * sizeof(uint8_t),
179
+ this_block_B_base_ptr + i * N_SIZE_ONE_LOAD * params.K, 16,
180
+ (B_ldg_guard & (1u << i)) != 0);
181
+ }
182
+
183
+ cp_async_commit_group();
184
+ this_block_A_base_ptr += 32;
185
+ this_block_B_base_ptr += (32 * 4);
186
+ }
187
+
188
+ const FType* this_block_A_base_ptr = nullptr;
189
+ const QType* this_block_B_base_ptr = nullptr;
190
+
191
+ int Aldg_col_idx;
192
+ int Bldg_col_idx;
193
+
194
+ uint32_t A_ldg_guard;
195
+ uint32_t B_ldg_guard;
196
+
197
+ uint32_t A_smem_base_addr, BQ_smem_base_addr;
198
+ const uint32_t A_smem_stage_stride, BQ_smem_stage_stride;
199
+
200
+ const SM8x_GEMM_W8A16_Splitk_Params<FType, QType>& params;
201
+};
202
+
203
+/*
204
+ * requiring N % 8 == 0
205
+ */
206
+template <typename FType, typename QType, int Mtile, int Ntile, int BLOCK,
207
+ bool EnableFuse, bool has_zp>
208
+struct ComputeTile_W8A16_PerC_MtilexNtilex32_multistage_SM8x_SplitK {
209
+ static constexpr int WARP_SIZE = 32;
210
+ static constexpr int WARP_CNT = BLOCK / WARP_SIZE;
211
+ static constexpr int WARP_NTILE = Ntile / WARP_CNT;
212
+ static constexpr int WARP_NITER = WARP_NTILE / 8; // hmma16816
213
+ static_assert(WARP_NTILE == 32 or WARP_NTILE == 64,
214
+ "now only support WARP_NTILE = 32 or 64!");
215
+
216
+ __device__ ComputeTile_W8A16_PerC_MtilexNtilex32_multistage_SM8x_SplitK(
217
+ const SM8x_GEMM_W8A16_Splitk_Params<FType, QType>& k_params,
218
+ const uint32_t& A_smem_addr, const uint32_t& BQ_smem_addr,
219
+ const uint32_t& A_stage_stride, const uint32_t& BQ_stage_stride)
220
+ : params(k_params),
221
+ A_smem_base_addr(A_smem_addr),
222
+ BQ_smem_base_addr(BQ_smem_addr),
223
+ A_smem_stage_stride(A_stage_stride),
224
+ BQ_smem_stage_stride(BQ_stage_stride) {
225
+ warp_id = threadIdx.x / WARP_SIZE;
226
+ lane_id = threadIdx.x % WARP_SIZE;
227
+
228
+ load_a_base_offset[0] =
229
+ (lane_id % 16) * 32 +
230
+ ((lane_id / 16) ^ (lane_id % 4) ^ ((lane_id / 4) % 2)) * 8;
231
+ load_a_base_offset[1] =
232
+ (lane_id % 16) * 32 +
233
+ ((lane_id / 16 + 2) ^ (lane_id % 4) ^ ((lane_id / 4) % 2)) * 8;
234
+
235
+ load_b_base_offset[0] =
236
+ (lane_id / 4 + warp_id * (WARP_NTILE / 4)) * 32 * 4 +
237
+ (lane_id % 4) * 16 + ((lane_id / 4) % 2) * 16 * 4;
238
+ load_b_base_offset[1] =
239
+ (lane_id / 4 + warp_id * (WARP_NTILE / 4)) * 32 * 4 +
240
+ (lane_id % 4) * 16 + (((lane_id / 4) % 2) ^ 1) * 16 * 4;
241
+
242
+ sts_c_base_offset = warp_id * Mtile * WARP_NTILE +
243
+ (lane_id / 4) * WARP_NTILE + (lane_id % 4) * 2;
244
+
245
+ if (EnableFuse) {
246
+ this_block_C_base_ptr =
247
+ params.C_ptr + blockIdx.x * Mtile * params.N + blockIdx.y * Ntile;
248
+ } else {
249
+ this_block_C_base_ptr =
250
+ params.C_split_ptr + blockIdx.z * params.M * params.N +
251
+ blockIdx.x * Mtile * params.N + blockIdx.y * Ntile;
252
+ }
253
+ int store_thds_in_row = WARP_NTILE / 8;
254
+ store_c_row_base_idx = lane_id / store_thds_in_row;
255
+ store_c_col_idx = warp_id * WARP_NTILE + (lane_id % store_thds_in_row) * 8;
256
+ store_c_base_offset = store_c_row_base_idx * params.N + store_c_col_idx;
257
+
258
+ #pragma unroll
259
+ for (int i = 0; i < Mtile / 16; ++i) {
260
+ #pragma unroll
261
+ for (int j = 0; j < WARP_NITER; ++j) {
262
+ #pragma unroll
263
+ for (int k = 0; k < 4; ++k) {
264
+ C_frag[i][j][k] = 0.f;
265
+ }
266
+ }
267
+ }
268
+ params_n_idx =
269
+ blockIdx.y * Ntile + warp_id * WARP_NTILE + (lane_id / 4) * 4;
270
+ }
271
+
272
+ __device__ void lds(const int& smem_stage_idx, const int& reg_buf_idx,
273
+ const int& k_phase_idx) {
274
+ uint32_t A_smem_addr =
275
+ A_smem_base_addr + A_smem_stage_stride * smem_stage_idx;
276
+ uint32_t B_smem_addr =
277
+ BQ_smem_base_addr + BQ_smem_stage_stride * smem_stage_idx;
278
+
279
+ #pragma unroll
280
+ for (int i = 0; i < Mtile / 16; ++i) {
281
+ ldsm_4(A_frag[reg_buf_idx][i][0], A_frag[reg_buf_idx][i][1],
282
+ A_frag[reg_buf_idx][i][2], A_frag[reg_buf_idx][i][3],
283
+ A_smem_addr + (load_a_base_offset[k_phase_idx] + i * 16 * 32) *
284
+ sizeof(FType));
285
+ }
286
+ #pragma unroll
287
+ for (int i = 0; i < WARP_NTILE / 32; ++i) {
288
+ lds128(BQ_frag[reg_buf_idx][4 * i + 0], BQ_frag[reg_buf_idx][4 * i + 1],
289
+ BQ_frag[reg_buf_idx][4 * i + 2], BQ_frag[reg_buf_idx][4 * i + 3],
290
+ B_smem_addr + (load_b_base_offset[k_phase_idx] + i * 32 * 32) *
291
+ sizeof(uint8_t));
292
+ }
293
+
294
+ // dequant B
295
+ #pragma unroll
296
+ for (int i = 0; i < WARP_NITER / 2; ++i) {
297
+ cvt_8bx4_to_16bx4_bias128(BQ_frag[reg_buf_idx][2 * i],
298
+ BF_frag[reg_buf_idx][2 * i]);
299
+ if (has_zp) {
300
+ BF_frag[reg_buf_idx][2 * i][0] =
301
+ __hsub2(BF_frag[reg_buf_idx][2 * i][0], num2num2(B_zero[i].x));
302
+ BF_frag[reg_buf_idx][2 * i][1] =
303
+ __hsub2(BF_frag[reg_buf_idx][2 * i][1], num2num2(B_zero[i].x));
304
+ }
305
+
306
+ BF_frag[reg_buf_idx][2 * i][0] =
307
+ __hmul2(BF_frag[reg_buf_idx][2 * i][0], num2num2(B_scale[i].x));
308
+ BF_frag[reg_buf_idx][2 * i][1] =
309
+ __hmul2(BF_frag[reg_buf_idx][2 * i][1], num2num2(B_scale[i].x));
310
+
311
+ cvt_8bx4_to_16bx4_bias128(BQ_frag[reg_buf_idx][2 * i + 1],
312
+ BF_frag[reg_buf_idx][2 * i + 1]);
313
+ if (has_zp) {
314
+ BF_frag[reg_buf_idx][2 * i + 1][0] =
315
+ __hsub2(BF_frag[reg_buf_idx][2 * i + 1][0], num2num2(B_zero[i].y));
316
+ BF_frag[reg_buf_idx][2 * i + 1][1] =
317
+ __hsub2(BF_frag[reg_buf_idx][2 * i + 1][1], num2num2(B_zero[i].y));
318
+ }
319
+
320
+ BF_frag[reg_buf_idx][2 * i + 1][0] =
321
+ __hmul2(BF_frag[reg_buf_idx][2 * i + 1][0], num2num2(B_scale[i].y));
322
+ BF_frag[reg_buf_idx][2 * i + 1][1] =
323
+ __hmul2(BF_frag[reg_buf_idx][2 * i + 1][1], num2num2(B_scale[i].y));
324
+ }
325
+ }
326
+
327
+ __device__ void ldg_params() {
328
+ const int N_padded = (params.N + 31) / 32 * 32;
329
+ // load B scale and zero_point
330
+ #pragma unroll
331
+ for (int i = 0; i < WARP_NTILE / 32; ++i) {
332
+ ldg64_ca(B_scale[2 * i + 0], B_scale[2 * i + 1],
333
+ params.B_scale_ptr + params_n_idx + i * 32,
334
+ (params_n_idx + i * 32) < N_padded);
335
+ if (has_zp) {
336
+ ldg64_ca(B_zero[2 * i + 0], B_zero[2 * i + 1],
337
+ params.B_zero_ptr + params_n_idx + i * 32,
338
+ (params_n_idx + i * 32) < N_padded);
339
+ }
340
+ }
341
+ }
342
+
343
+ __device__ void mma(const int& reg_buf_idx) {
344
+ #pragma unroll
345
+ for (int m_idx = 0; m_idx < Mtile / 16; ++m_idx) {
346
+ #pragma unroll
347
+ for (int n_idx = 0; n_idx < WARP_NITER; ++n_idx) {
348
+ hmma16816_f32<FType>(
349
+ C_frag[m_idx][n_idx], A_frag[reg_buf_idx][m_idx],
350
+ reinterpret_cast<uint32_t(&)[2]>(BF_frag[reg_buf_idx][n_idx]));
351
+ }
352
+ }
353
+ }
354
+
355
+ __device__ void fused_splitk_reduce() {
356
+ // need splitk-reduce if enable splitk
357
+ if (gridDim.z > 1) {
358
+ int blk_red_idx = blockIdx.x * gridDim.y + blockIdx.y;
359
+ // Wait for all previous blocks in the splitk direction to accumulate the
360
+ // results into C_tmp
361
+ if (threadIdx.x == 0) {
362
+ uint32_t* red_count_ptr = params.red_count_ptr + blk_red_idx;
363
+ uint32_t count;
364
+ do {
365
+ // make sure the ld.cg inside the do-wile loop
366
+ __threadfence_block();
367
+ asm volatile("ld.global.cg.b32 %0, [%1];"
368
+ : "=r"(count)
369
+ : "l"(red_count_ptr));
370
+ } while (count != blockIdx.z);
371
+ }
372
+ __syncthreads();
373
+
374
+ int C_tmp_base_offset = blk_red_idx * Mtile * Ntile + threadIdx.x * 4;
375
+ if (blockIdx.z != 0) {
376
+ // expecting that temporary register here reuses the previous A&B frag
377
+ // register
378
+ float temp_frag[Mtile / 16][WARP_NITER][4];
379
+ #pragma unroll
380
+ for (int m_idx = 0; m_idx < Mtile / 16; ++m_idx) {
381
+ #pragma unroll
382
+ for (int n_idx = 0; n_idx < WARP_NITER; ++n_idx) {
383
+ int offset =
384
+ C_tmp_base_offset + (m_idx * WARP_NITER + n_idx) * BLOCK * 4;
385
+ *reinterpret_cast<int4*>(temp_frag[m_idx][n_idx]) =
386
+ *reinterpret_cast<int4*>(params.C_tmp_ptr + offset);
387
+ }
388
+ }
389
+ #pragma unroll
390
+ for (int m_idx = 0; m_idx < Mtile / 16; ++m_idx) {
391
+ #pragma unroll
392
+ for (int n_idx = 0; n_idx < WARP_NITER; ++n_idx) {
393
+ #pragma unroll
394
+ for (int idx = 0; idx < 4; ++idx) {
395
+ C_frag[m_idx][n_idx][idx] += temp_frag[m_idx][n_idx][idx];
396
+ }
397
+ }
398
+ }
399
+ }
400
+
401
+ // first splitk - 1 blocks need to write partial results into C_tmp
402
+ if (blockIdx.z != gridDim.z - 1) {
403
+ #pragma unroll
404
+ for (int m_idx = 0; m_idx < Mtile / 16; ++m_idx) {
405
+ #pragma unroll
406
+ for (int n_idx = 0; n_idx < WARP_NITER; ++n_idx) {
407
+ int offset =
408
+ C_tmp_base_offset + (m_idx * WARP_NITER + n_idx) * BLOCK * 4;
409
+ asm volatile(
410
+ "{st.global.cg.v4.b32 [%0], {%1, %2, %3, %4};}\n"
411
+ :
412
+ : "l"(params.C_tmp_ptr + offset), "f"(C_frag[m_idx][n_idx][0]),
413
+ "f"(C_frag[m_idx][n_idx][1]), "f"(C_frag[m_idx][n_idx][2]),
414
+ "f"(C_frag[m_idx][n_idx][3]));
415
+ }
416
+ }
417
+ __threadfence();
418
+ __syncthreads();
419
+ if (threadIdx.x == 0) {
420
+ uint32_t* red_count_ptr = params.red_count_ptr + blk_red_idx;
421
+ atomicInc(red_count_ptr, gridDim.z);
422
+ }
423
+ }
424
+ }
425
+ }
426
+
427
+ __device__ void stg(char* smem) {
428
+ if (EnableFuse) {
429
+ if (blockIdx.z != gridDim.z - 1) return;
430
+ }
431
+ uint32_t* C_sts_ptr =
432
+ reinterpret_cast<uint32_t*>(smem + sts_c_base_offset * sizeof(FType));
433
+ // C_tile sts
434
+ #pragma unroll
435
+ for (int m_idx = 0; m_idx < Mtile / 16; ++m_idx) {
436
+ #pragma unroll
437
+ for (int n_idx = 0; n_idx < WARP_NITER; ++n_idx) {
438
+ #pragma unroll
439
+ for (int k_idx = 0; k_idx < 2; ++k_idx) {
440
+ FType low16 = static_cast<FType>(C_frag[m_idx][n_idx][k_idx * 2]);
441
+ FType high16 =
442
+ static_cast<FType>(C_frag[m_idx][n_idx][k_idx * 2 + 1]);
443
+ uint32_t tmp = (reinterpret_cast<uint32_t&>(low16) & 0xffff) |
444
+ (reinterpret_cast<uint32_t&>(high16) << 16);
445
+ int sts_offset =
446
+ m_idx * 16 * (WARP_NTILE / 2) +
447
+ (((lane_id / (32 / WARP_NITER)) + n_idx) % WARP_NITER) * (8 / 2) +
448
+ k_idx * 8 * (WARP_NTILE / 2);
449
+ C_sts_ptr[sts_offset] = tmp;
450
+ }
451
+ }
452
+ }
453
+
454
+ __syncthreads();
455
+
456
+ FType* C_base_ptr = this_block_C_base_ptr + store_c_base_offset;
457
+ // C_tile lds and stg
458
+ int m_base_idx = store_c_row_base_idx + blockIdx.x * Mtile;
459
+ bool n_guard = (store_c_col_idx + blockIdx.y * Ntile) < params.N;
460
+ if (WARP_NTILE == 32) {
461
+ int lds_c_base_offset = warp_id * Mtile * WARP_NTILE +
462
+ (lane_id / 4) * WARP_NTILE +
463
+ ((lane_id % 4 + lane_id / 8) % 4) * 8;
464
+ uint4* C_lds_ptr =
465
+ reinterpret_cast<uint4*>(smem + lds_c_base_offset * sizeof(FType));
466
+ #pragma unroll
467
+ for (int i = 0; i < (Mtile / 16) * (WARP_NITER / 2); ++i) {
468
+ uint4 stg_reg = C_lds_ptr[i * 8 * 4];
469
+ stg128(stg_reg.x, stg_reg.y, stg_reg.z, stg_reg.w,
470
+ C_base_ptr + i * 8 * params.N,
471
+ (m_base_idx + i * 8) < params.M && n_guard);
472
+ }
473
+ } else if (WARP_NTILE == 64) {
474
+ int lds_c_base_offset =
475
+ warp_id * Mtile * WARP_NTILE + (lane_id / 8) * WARP_NTILE;
476
+ #pragma unroll
477
+ for (int i = 0; i < (Mtile / 16) * (WARP_NITER / 2); ++i) {
478
+ int lds_c_offset = lds_c_base_offset + i * 4 * WARP_NTILE +
479
+ ((lane_id % 8 + lane_id / 8 + (i % 2) * 4) % 8) * 8;
480
+ uint4 stg_reg =
481
+ *reinterpret_cast<uint4*>(smem + lds_c_offset * sizeof(FType));
482
+ stg128(stg_reg.x, stg_reg.y, stg_reg.z, stg_reg.w,
483
+ C_base_ptr + i * 4 * params.N,
484
+ (m_base_idx + i * 4) < params.M && n_guard);
485
+ }
486
+ }
487
+ }
488
+
489
+ const SM8x_GEMM_W8A16_Splitk_Params<FType, QType>& params;
490
+
491
+ int load_a_base_offset[2];
492
+ int load_b_base_offset[2];
493
+ int sts_c_base_offset;
494
+
495
+ int store_c_base_offset;
496
+
497
+ int store_c_row_base_idx, store_c_col_idx;
498
+ FType* this_block_C_base_ptr = nullptr;
499
+
500
+ int params_n_idx;
501
+ const uint32_t A_smem_base_addr, BQ_smem_base_addr;
502
+ const uint32_t A_smem_stage_stride, BQ_smem_stage_stride;
503
+
504
+ int lane_id;
505
+ int warp_id;
506
+ // first 2 denotes double buffer, second dim denotes M direction
507
+ uint32_t A_frag[2][Mtile / 16][4];
508
+
509
+ typename HalfType<FType>::T2 B_scale[WARP_NITER / 2];
510
+ typename HalfType<FType>::T2 B_zero[WARP_NITER / 2];
511
+ uint32_t BQ_frag[2][WARP_NITER];
512
+ // first 2 denotes double buffer, second dim denotes N direction, last 2
513
+ // denotes K direction
514
+ typename HalfType<FType>::T2 BF_frag[2][WARP_NITER][2];
515
+ // first dim denotes M direction, second dim denotes N direction
516
+ float C_frag[Mtile / 16][WARP_NITER][4];
517
+};
518
+
519
+/*
520
+ * @brief W8A16 Perchannel Quantization GEMM,
521
+ * requires N % 8 == 0, K % 16 == 0
522
+ * accumulator precision: FP32
523
+ * @tparam FType: DataType for A, B_scale, B_zero, and C, supports half or
524
+ * nv_bfloat16
525
+ * @tparam QType: DataType for B, support uint8(bias128)
526
+ * @tparam Mtile: M-dimensional size of the gemm block tile, supports 16, 32,
527
+ * 48 or 64
528
+ * @tparam Ntile: N-dimensional size of the gemm block tile, supports 128 or
529
+ * 256
530
+ * @tparam NStage: Num of stages for async copy
531
+ * @tparam BLOCK: BLOCK size
532
+ * @tparam EnableFuse: If true, use fused splitk-reduce, otherwise use
533
+ * non-fused splitk-reduce
534
+ * @tparam has_zp: whether to use zero_point
535
+ *
536
+ * @fparam params struct consists of following parameters:
537
+ * @param A_ptr: Matrix A value ptr, A = (M, K)
538
+ * @param B_ptr: Matrix B value ptr, B = (N32_align, K) (N32K16 special
539
+ * format), N32_align = (N + 32 - 1) / 32 * 32
540
+ * @param B_scale_ptr: B_scale value ptr, B_scale = (N32_align,) (N32K16
541
+ * special format)
542
+ * @param B_zero_ptr: B_zero value ptr, B_zero = (N32_align,) (N32K16
543
+ * special format)
544
+ * @param C_ptr: Matrix C value ptr, C = (M, N)
545
+ * @param M: dimnesion m
546
+ * @param N: dimnesion n
547
+ * @param K: dimnesion k
548
+ * @param SplitK: split size along K-dimension
549
+ * @param C_split_ptr: Matrix C_split value ptr, used only in non-fused
550
+ * splitk-reduce
551
+ * @param C_tmp_ptr: Matrix C_tmp value ptr, used only in fused
552
+ * splitk-reduce
553
+ * @param red_count_ptr: 1-D red_count value ptr, used only in fused
554
+ * splitk-reduce
555
+ */
556
+template <typename FType, typename QType, int Mtile, int Ntile, int NStage,
557
+ int BLOCK, bool EnableFuse, bool has_zp>
558
+__global__ void __launch_bounds__(BLOCK)
559
+ ampere_hgemm_W8A16_perc_f16_f16_MtilexNtilex32_hmma16816_multistage_AN_BTN32K16_CN_splitk_kernel(
560
+ const SM8x_GEMM_W8A16_Splitk_Params<FType, QType> params) {
561
+ // A smem size = 64 * 32 * 2B/elem * 4(stage) = 16KB
562
+ // B smem size = 128 * 32 * 1B/elem * 4(stage) = 16KB
563
+ constexpr int smem_size_one_stage = Mtile * 32 * 2 + Ntile * 32;
564
+ __shared__ char smem[NStage * smem_size_one_stage];
565
+ char* A_smem = smem;
566
+ char* BQ_smem = smem + Mtile * 32 * 2 * NStage;
567
+
568
+ uint32_t A_smem_addr = smem_u32addr(A_smem);
569
+ uint32_t BQ_smem_addr = smem_u32addr(BQ_smem);
570
+ uint32_t A_smem_stage_stride = Mtile * 32 * 2;
571
+ uint32_t BQ_smem_stage_stride = Ntile * 32;
572
+
573
+ // initialize the data move process from GM to SMEM for this block
574
+ GmemTile_W8A16_PerC_MtilexNtilex32_multistage_SM8x_SplitK<
575
+ FType, QType, Mtile, Ntile, NStage, BLOCK>
576
+ gmem_tile(params, A_smem_addr, BQ_smem_addr, A_smem_stage_stride,
577
+ BQ_smem_stage_stride);
578
+
579
+ int sts_stage_idx = 0;
580
+ int lds_stage_idx = 0;
581
+
582
+ int tb_k_slice = blockIdx.z * params.SplitK + params.SplitK <= params.K
583
+ ? params.SplitK
584
+ : params.K - blockIdx.z * params.SplitK;
585
+ int k_tiles = (tb_k_slice + 31) / 32;
586
+ int first_k_tile = tb_k_slice - (k_tiles - 1) * 32;
587
+
588
+ // load first three tiles to shared memory
589
+ gmem_tile.ldgsts_first_ktiles(first_k_tile, k_tiles);
590
+ sts_stage_idx += (NStage - 2);
591
+ ComputeTile_W8A16_PerC_MtilexNtilex32_multistage_SM8x_SplitK<
592
+ FType, QType, Mtile, Ntile, BLOCK, EnableFuse, has_zp>
593
+ compute_tile(params, A_smem_addr, BQ_smem_addr, A_smem_stage_stride,
594
+ BQ_smem_stage_stride);
595
+ compute_tile.ldg_params();
596
+ cp_asyc_wait_group<NStage - 2>();
597
+ __syncthreads();
598
+
599
+ compute_tile.lds(lds_stage_idx, 0, 0);
600
+ int reg_buf_idx = 1;
601
+
602
+ // main loop
603
+ for (; k_tiles > NStage - 1; --k_tiles) {
604
+ // load next A&B tile
605
+ sts_stage_idx = sts_stage_idx < NStage - 1 ? sts_stage_idx + 1 : 0;
606
+ gmem_tile.ldgsts(sts_stage_idx);
607
+
608
+ #pragma unroll
609
+ for (int k_phase_idx = 0; k_phase_idx < 2; k_phase_idx++) {
610
+ // dequantize next B tile
611
+ if (k_phase_idx == 1) {
612
+ cp_asyc_wait_group<NStage - 2>();
613
+ __syncthreads();
614
+ lds_stage_idx = lds_stage_idx < NStage - 1 ? lds_stage_idx + 1 : 0;
615
+ }
616
+
617
+ compute_tile.lds(lds_stage_idx, reg_buf_idx, (k_phase_idx + 1) % 2);
618
+
619
+ compute_tile.mma(reg_buf_idx ^ 1);
620
+ reg_buf_idx ^= 1;
621
+ }
622
+ }
623
+
624
+ // last NStage-1 tiles
625
+ for (; k_tiles > 0; --k_tiles) {
626
+ cp_async_commit_group();
627
+ #pragma unroll
628
+ for (int k_phase_idx = 0; k_phase_idx < 2; k_phase_idx++) {
629
+ // dequantize next B tile
630
+ if (k_phase_idx == 1) {
631
+ cp_asyc_wait_group<NStage - 2>();
632
+ __syncthreads();
633
+ lds_stage_idx = lds_stage_idx < NStage - 1 ? lds_stage_idx + 1 : 0;
634
+ }
635
+
636
+ compute_tile.lds(lds_stage_idx, reg_buf_idx, (k_phase_idx + 1) % 2);
637
+
638
+ compute_tile.mma(reg_buf_idx ^ 1);
639
+ reg_buf_idx ^= 1;
640
+ }
641
+ }
642
+
643
+ if (EnableFuse) {
644
+ compute_tile.fused_splitk_reduce();
645
+ }
646
+ compute_tile.stg(smem);
647
+}
648
+
649
+ #define __CALL_IF(MTILE, NTILE, NUM_THREADS, ENABLE_FUSE, HAS_ZP) \
650
+ else if (Mtile == MTILE && Ntile == NTILE && BLOCK == NUM_THREADS && \
651
+ enable_fuse == ENABLE_FUSE && has_zp == HAS_ZP) { \
652
+ ampere_hgemm_W8A16_perc_f16_f16_MtilexNtilex32_hmma16816_multistage_AN_BTN32K16_CN_splitk_kernel< \
653
+ FType, QType, MTILE, NTILE, 4, NUM_THREADS, ENABLE_FUSE, HAS_ZP> \
654
+ <<<grid, block, 0, stream>>>(params); \
655
+ }
656
+
657
+template <typename FType, typename QType>
658
+void ampere_hgemm_W8A16_perc_f16_f16_MtilexNtilex32_mma16816_multistage_AN_BTN32K16_CN_splitk(
659
+ const FType* A, const QType* B, const FType* B_scale, const FType* B_zero,
660
+ FType* C, const int M, const int N, const int K, void* workspace,
661
+ const int sm_version, const BlockTileSplitkParams& fused_gemm_params,
662
+ cudaStream_t stream) {
663
+ int Mtile = fused_gemm_params.Mtile;
664
+ int grid_x = (M + Mtile - 1) / Mtile;
665
+ int Ntile = fused_gemm_params.Ntile;
666
+ int grid_y = (N + Ntile - 1) / Ntile;
667
+ int SplitK = fused_gemm_params.SplitK;
668
+ int grid_z = (K + SplitK - 1) / SplitK;
669
+
670
+ int BLOCK = (Ntile == 256) ? 256 : 128;
671
+
672
+ dim3 grid(grid_x, grid_y, grid_z);
673
+ dim3 block(BLOCK);
674
+
675
+ bool enable_fuse = fused_gemm_params.EnableFuse;
676
+ bool has_zp = B_zero != nullptr;
677
+ if (enable_fuse) {
678
+ float* C_tmp = reinterpret_cast<float*>(workspace);
679
+ uint32_t* red_count = reinterpret_cast<uint32_t*>(
680
+ (char*)workspace + grid_x * Mtile * grid_y * Ntile * sizeof(float));
681
+ CHECK_CUDA(cudaMemsetAsync(red_count, 0, grid_x * grid_y * sizeof(uint32_t),
682
+ stream));
683
+ SM8x_GEMM_W8A16_Splitk_Params<FType, QType> params{
684
+ A, B, B_scale, B_zero, C, M, N,
685
+ K, SplitK, 0, -1, nullptr, C_tmp, red_count};
686
+
687
+ if (false) {
688
+ }
689
+ // Select the template parameters for kernel launch
690
+ // according to the above settings. Tuning is not supported.
691
+ __CALL_IF(16, 256, 256, true, false)
692
+ __CALL_IF(32, 256, 256, true, false)
693
+ __CALL_IF(48, 256, 256, true, false)
694
+ __CALL_IF(64, 128, 128, true, false)
695
+ __CALL_IF(64, 256, 256, true, false)
696
+ __CALL_IF(16, 256, 256, true, true)
697
+ __CALL_IF(32, 256, 256, true, true)
698
+ __CALL_IF(48, 256, 256, true, true)
699
+ __CALL_IF(64, 128, 128, true, true)
700
+ __CALL_IF(64, 256, 256, true, true)
701
+ } else {
702
+ FType* C_split = reinterpret_cast<FType*>(workspace);
703
+ SM8x_GEMM_W8A16_Splitk_Params<FType, QType> params{
704
+ A, B, B_scale, B_zero, C, M, N,
705
+ K, SplitK, 0, -1, C_split, nullptr, nullptr};
706
+
707
+ if (false) {
708
+ }
709
+ // Select the template parameters for kernel launch
710
+ // according to the above settings. Tuning is not supported.
711
+ __CALL_IF(16, 256, 256, false, false)
712
+ __CALL_IF(32, 256, 256, false, false)
713
+ __CALL_IF(48, 256, 256, false, false)
714
+ __CALL_IF(64, 128, 128, false, false)
715
+ __CALL_IF(64, 256, 256, false, false)
716
+ __CALL_IF(16, 256, 256, false, true)
717
+ __CALL_IF(32, 256, 256, false, true)
718
+ __CALL_IF(48, 256, 256, false, true)
719
+ __CALL_IF(64, 128, 128, false, true)
720
+ __CALL_IF(64, 256, 256, false, true)
721
+
722
+ // SplitK reduce
723
+ f16_gemm_splitk_reduce(C_split, C, M, N, grid_z, stream);
724
+ }
725
+}
726
+
727
+size_t allspark_qgemm_w8a16_perc_n32k16_ampere_workspace_size(
728
+ int m, int n, int k, int sm_count,
729
+ BlockTileSplitkParams& fused_gemm_params) {
730
+ // Determine the block tile and splitk strategy
731
+ int m16_times = (m + 16 - 1) / 16;
732
+ int Mtile = m16_times <= 4 ? m16_times * 16 : 64;
733
+ int grid_x = (m + Mtile - 1) / Mtile;
734
+ int Ntile =
735
+ (float(grid_x * ((n + 127) / 128)) / sm_count > 10) || (Mtile < 64) ? 256
736
+ : 128;
737
+ int grid_y = (n + Ntile - 1) / Ntile;
738
+ int grid_z;
739
+
740
+ // split-k
741
+ const float SPLIT_THRESHOLD = 0.8;
742
+ int n_slice;
743
+ for (n_slice = 1; n_slice < k / 256; ++n_slice) {
744
+ int n_block = grid_x * grid_y * n_slice;
745
+ if (n_block >= sm_count * SPLIT_THRESHOLD &&
746
+ (n_block % sm_count == 0 || n_block % sm_count >= sm_count * 0.5)) {
747
+ break;
748
+ }
749
+ }
750
+
751
+ int k_slice =
752
+ (k / n_slice) % 32 == 0 ? k / n_slice : k / n_slice / 32 * 32 + 32;
753
+ grid_z = (k + k_slice - 1) / k_slice;
754
+ bool enable_fuse = float(grid_x * grid_y) / sm_count >= 0.5 ? 1 : 0;
755
+
756
+ size_t ws_size;
757
+ if (enable_fuse) {
758
+ ws_size = grid_x * Mtile * grid_y * Ntile * sizeof(float) // For C_tmp
759
+ + grid_x * grid_y * sizeof(uint32_t); // For red_count
760
+ } else {
761
+ ws_size = grid_z * m * n * sizeof(__half);
762
+ }
763
+
764
+ fused_gemm_params.Mtile = Mtile;
765
+ fused_gemm_params.Ntile = Ntile;
766
+ fused_gemm_params.SplitK = k_slice;
767
+ fused_gemm_params.EnableFuse = enable_fuse;
768
+ return ws_size;
769
+}
770
+
771
+// restore from N32K16 order to original N-major order
772
+// K % 16 == 0, N % 8 == 0
773
+// each block process 64(k) * 32(n) result elements
774
+template <typename FT, typename QT>
775
+__global__ void restore_N32_K16_dequantize_rhs_w8a16_perc_kernel(
776
+ const QT* qdata, const FT* scales, const FT* zeros, FT* fdata,
777
+ const int N_32align, const int N, const int K) {
778
+ __shared__ FT smem[64 * 32];
779
+ int warp_id = threadIdx.x / 32;
780
+ int lane_id = threadIdx.x % 32;
781
+ const int src_row_idx = blockIdx.x * 8 + lane_id / 4;
782
+ const int src_col_idx =
783
+ blockIdx.y * 64 * 4 + warp_id * 16 * 4 + (lane_id % 4) * 16;
784
+ const int src_offset = src_row_idx * K * 4 + src_col_idx;
785
+ int params_nidx = blockIdx.x * 32 + (lane_id / 4) * 4;
786
+
787
+ QT qval_reg[16];
788
+ const QT* pdata = qdata + src_offset;
789
+ if (src_col_idx < (K * 4)) {
790
+ *(reinterpret_cast<uint4*>(qval_reg)) =
791
+ *(reinterpret_cast<const uint4*>(qdata + src_offset));
792
+ }
793
+ FT scale_reg[4];
794
+ *(reinterpret_cast<uint2*>(scale_reg)) =
795
+ *(reinterpret_cast<const uint2*>(scales + params_nidx));
796
+ FT zero_reg[4] = {0};
797
+ if (zeros != nullptr) {
798
+ *(reinterpret_cast<uint2*>(zero_reg)) =
799
+ *(reinterpret_cast<const uint2*>(zeros + params_nidx));
800
+ }
801
+ FT fval_reg[16];
802
+
803
+ const int sts_base_offset =
804
+ (warp_id * 16 + (lane_id % 4) * 2) * 32 + lane_id / 4;
805
+ #pragma unroll
806
+ for (int ni = 0; ni < 4; ++ni) {
807
+ cvt_8bx4_to_16bx4_bias128(
808
+ *reinterpret_cast<uint32_t*>(&qval_reg[ni * 4]),
809
+ reinterpret_cast<typename HalfType<FT>::T2*>(&(fval_reg[ni * 4])));
810
+ #pragma unroll
811
+ for (int ki = 0; ki < 4; ++ki) {
812
+ fval_reg[ni * 4 + ki] =
813
+ (fval_reg[ni * 4 + ki] - zero_reg[ni]) * scale_reg[ni];
814
+ int sts_offset = sts_base_offset + ((ki / 2) * 8 + (ki % 2)) * 32 +
815
+ ((ni + lane_id % 4) % 4) * 8;
816
+ smem[sts_offset] = fval_reg[ni * 4 + ki];
817
+ }
818
+ }
819
+ __syncthreads();
820
+
821
+ const int lds_base_offset =
822
+ (threadIdx.x / 4) * 32 + ((threadIdx.x % 4 + threadIdx.x / 8) % 4) * 8;
823
+ #pragma unroll
824
+ for (int i = 0; i < 2; ++i) {
825
+ *reinterpret_cast<uint4*>(fval_reg + i * 8) =
826
+ *reinterpret_cast<uint4*>(smem + lds_base_offset + i * 32 * 32);
827
+ }
828
+
829
+ const int dst_row_base_kidx = blockIdx.y * 64 + threadIdx.x / 4;
830
+ const int dst_col_nidx = blockIdx.x * 32 + (threadIdx.x % 4) * 8;
831
+ #pragma unroll
832
+ for (int i = 0; i < 2; ++i) {
833
+ int dst_row_kidx = dst_row_base_kidx + i * 32;
834
+ int dst_offset = dst_row_kidx * N + dst_col_nidx;
835
+ if (dst_row_kidx < K && dst_col_nidx < N) {
836
+ *reinterpret_cast<uint4*>(fdata + dst_offset) =
837
+ *reinterpret_cast<uint4*>(fval_reg + i * 8);
838
+ }
839
+ }
840
+}
841
+
842
+template <typename FT, typename QT>
843
+void restore_N32_K16_dequantize_rhs_w8a16(const QT* qdata, const FT* scales,
844
+ const FT* zeros, FT* fdata,
845
+ const int N_32align, const int N,
846
+ const int K, const int GroupSize,
847
+ cudaStream_t stream) {
848
+ TORCH_CHECK(N % 8 == 0 && K % 16 == 0 && N_32align % 32 == 0,
849
+ "Unsupported shape");
850
+ if (GroupSize == -1) {
851
+ const int BLOCK = 128;
852
+ dim3 grid(N_32align / 32, ((K / 16) + 3) / 4);
853
+ restore_N32_K16_dequantize_rhs_w8a16_perc_kernel<FT, QT>
854
+ <<<grid, BLOCK, 0, stream>>>(qdata, scales, zeros, fdata, N_32align, N,
855
+ K);
856
+ }
857
+ // TODO: Support SubChannel
858
+ else {
859
+ TORCH_CHECK(false, "Now only support PerChannel");
860
+ }
861
+}
862
+
863
+template <typename FT, typename QT>
864
+void w8a16_gemm_dq_cublas(const FT* in, const QT* rhs_qdata_ptr,
865
+ const FT* rhs_scales_ptr, const FT* rhs_zeros_ptr,
866
+ FT* out, void* workspace, const int M,
867
+ const int N_32align, const int N, const int K,
868
+ const int group_size, cudaStream_t stream,
869
+ cublasHandle_t handle) {
870
+ static_assert(
871
+ std::is_same<FT, half>::value || std::is_same<FT, nv_bfloat16>::value,
872
+ "only float16 and bfloat16 is supported");
873
+ // Dequant
874
+ FT* rhs_fdata_ptr = static_cast<FT*>(workspace);
875
+ restore_N32_K16_dequantize_rhs_w8a16(rhs_qdata_ptr, rhs_scales_ptr,
876
+ rhs_zeros_ptr, rhs_fdata_ptr, N_32align,
877
+ N, K, group_size, stream);
878
+ // cuBLAS GEMM
879
+ int lda = K;
880
+ int ldb = N;
881
+ int ldc = N;
882
+ const float alpha = 1.0f;
883
+ const float beta = 0.0f;
884
+ cudaDataType_t cuda_type;
885
+ if (std::is_same<FT, __half>::value) {
886
+ cuda_type = CUDA_R_16F;
887
+ } else {
888
+ cuda_type = CUDA_R_16BF;
889
+ }
890
+ CHECK_CUBLAS(cublasGemmEx(handle, CUBLAS_OP_N, CUBLAS_OP_N, N, M, K, &alpha,
891
+ rhs_fdata_ptr, cuda_type, ldb, in, cuda_type, lda,
892
+ &beta, out, cuda_type, ldc, CUDA_R_32F,
893
+ CUBLAS_GEMM_DEFAULT_TENSOR_OP));
894
+}
895
+
896
+template <typename FType, typename QType>
897
+void allspark_qgemm_w8a16_perc_ampere(
898
+ const FType* A, const QType* B, const FType* B_scale, const FType* B_zero,
899
+ FType* C, const int M, const int N_32align, const int N, const int K,
900
+ void* workspace, const BlockTileSplitkParams& fused_gemm_params,
901
+ const int group_size, int CUBLAS_M_THRESHOLD, const int sm_version,
902
+ cudaStream_t stream, cublasHandle_t handle) {
903
+ if (M > CUBLAS_M_THRESHOLD) {
904
+ w8a16_gemm_dq_cublas<FType, QType>(A, B, B_scale, B_zero, C, workspace, M,
905
+ N_32align, N, K, group_size, stream,
906
+ handle);
907
+ } else {
908
+ ampere_hgemm_W8A16_perc_f16_f16_MtilexNtilex32_mma16816_multistage_AN_BTN32K16_CN_splitk<
909
+ FType, QType>(A, B, B_scale, B_zero, C, M, N, K, workspace, sm_version,
910
+ fused_gemm_params, stream);
911
+ }
912
+}
913
+
914
+} // namespace allspark
915
+
916
+torch::Tensor allspark_w8a16_gemm(
917
+ torch::Tensor const& a, torch::Tensor const& b_qweight,
918
+ torch::Tensor const& b_scales, c10::optional<torch::Tensor> const& b_qzeros,
919
+ int64_t n, int64_t group_size, int64_t sm_count, int64_t sm_version,
920
+ int64_t CUBLAS_M_THRESHOLD, bool has_zp, bool n32k16_reorder) {
921
+ // Verify device and strides
922
+ TORCH_CHECK(a.device().is_cuda(), "A is not on GPU");
923
+ TORCH_CHECK(a.is_contiguous(), "A is not contiguous");
924
+
925
+ TORCH_CHECK(b_qweight.device().is_cuda(), "b_qweight is not on GPU");
926
+ TORCH_CHECK(b_qweight.is_contiguous(), "b_qweight is not contiguous");
927
+
928
+ TORCH_CHECK(b_scales.device().is_cuda(), "b_scales is not on GPU");
929
+ TORCH_CHECK(b_scales.is_contiguous(), "b_scales is not contiguous");
930
+
931
+ if (has_zp) {
932
+ TORCH_CHECK(b_qzeros.value().device().is_cuda(), "b_qzeros is not on GPU");
933
+ TORCH_CHECK(b_qzeros.value().is_contiguous(), "b_qzeros is not contiguous");
934
+ }
935
+
936
+ int m = a.size(0);
937
+ int n_32align = (n + 32 - 1) / 32 * 32;
938
+ int k = a.size(1);
939
+
940
+ // Verify shape
941
+ TORCH_CHECK(b_qweight.size(0) == n_32align,
942
+ "Shape mismatch: b_qweight.size(0) = ", b_qweight.size(0),
943
+ ", n_32align = ", n_32align);
944
+ TORCH_CHECK(b_qweight.size(1) == k,
945
+ "Shape mismatch: b_qweight.size(1) = ", b_qweight.size(1),
946
+ ", k = ", k);
947
+
948
+ TORCH_CHECK(group_size == -1, "Currently only supports group_size = -1");
949
+
950
+ const at::cuda::OptionalCUDAGuard device_guard(device_of(a));
951
+ const void* a_ptr = reinterpret_cast<const void*>(a.data_ptr());
952
+ const uint8_t* b_ptr = reinterpret_cast<const uint8_t*>(b_qweight.data_ptr());
953
+ const void* b_scale_ptr = reinterpret_cast<const void*>(b_scales.data_ptr());
954
+ const void* b_zero_ptr = nullptr;
955
+ if (b_qzeros.has_value()) {
956
+ b_zero_ptr = reinterpret_cast<const void*>(b_qzeros.value().data_ptr());
957
+ }
958
+
959
+ auto c_options = torch::TensorOptions().dtype(a.dtype()).device(a.device());
960
+ torch::Tensor c = torch::empty({m, n}, c_options);
961
+ void* c_ptr = reinterpret_cast<void*>(c.data_ptr());
962
+
963
+ cudaStream_t stream = at::cuda::getCurrentCUDAStream();
964
+ cublasHandle_t handle = at::cuda::getCurrentCUDABlasHandle();
965
+
966
+ allspark::BlockTileSplitkParams fused_gemm_params;
967
+
968
+ size_t ws_size = 0;
969
+ if (m > CUBLAS_M_THRESHOLD) {
970
+ ws_size = k * n * 2; // sizeof(f16)==2
971
+ } else {
972
+ ws_size = allspark::allspark_qgemm_w8a16_perc_n32k16_ampere_workspace_size(
973
+ m, n, k, sm_count, fused_gemm_params);
974
+ }
975
+
976
+ auto ws_options = torch::TensorOptions().dtype(at::kChar).device(a.device());
977
+ if (as_g_workspace.numel() <
978
+ ws_size) { // ws_options: kChar, so numel() is bytes
979
+ as_g_workspace = torch::empty({long(ws_size)}, ws_options);
980
+ }
981
+ void* ws = reinterpret_cast<void*>(as_g_workspace.data_ptr());
982
+
983
+ if (a.dtype() == at::ScalarType::Half) {
984
+ allspark::allspark_qgemm_w8a16_perc_ampere<__half, uint8_t>(
985
+ reinterpret_cast<const __half*>(a_ptr), b_ptr,
986
+ reinterpret_cast<const __half*>(b_scale_ptr),
987
+ reinterpret_cast<const __half*>(b_zero_ptr),
988
+ reinterpret_cast<__half*>(c_ptr), m, n_32align, n, k, ws,
989
+ fused_gemm_params, group_size, CUBLAS_M_THRESHOLD, sm_version, stream,
990
+ handle);
991
+ } else if (a.dtype() == at::ScalarType::BFloat16) {
992
+ allspark::allspark_qgemm_w8a16_perc_ampere<__nv_bfloat16, uint8_t>(
993
+ reinterpret_cast<const __nv_bfloat16*>(a_ptr), b_ptr,
994
+ reinterpret_cast<const __nv_bfloat16*>(b_scale_ptr),
995
+ reinterpret_cast<const __nv_bfloat16*>(b_zero_ptr),
996
+ reinterpret_cast<__nv_bfloat16*>(c_ptr), m, n_32align, n, k, ws,
997
+ fused_gemm_params, group_size, CUBLAS_M_THRESHOLD, sm_version, stream,
998
+ handle);
999
+ }
1000
+
1001
+ return c;
1002
+}
1003
+
1004
+#endif
1005
+
1006
+TORCH_LIBRARY_IMPL_EXPAND(TORCH_EXTENSION_NAME, CUDA, m) {
1007
+ m.impl("allspark_w8a16_gemm", &allspark_w8a16_gemm);
1008
+}
\ No newline at end of file
csrc/quantization/gptq_allspark/allspark_repack.cu
new
+163
@@ -0,0 +1,163 @@
1
+#include "allspark_utils.cuh"
2
+#include <torch/all.h>
3
+#include "core/registration.h"
4
+
5
+namespace allspark {
6
+
7
+// Rearrange B to facilitate Ampere Tensor Core load data
8
+// reorder B from (K, N) to (N_32align / 4, K * 4)
9
+// K % 16 == 0, N % 16 == 0, N_32align % 32 == 0
10
+template <typename FType>
11
+__global__ void __launch_bounds__(128)
12
+ rearrange_kn_weight_as_n32k16_order_ldg16_kernel(
13
+ const uint8_t* B, const FType* B_scale, const FType* B_zero,
14
+ uint8_t* B_result, FType* B_scale_result, FType* B_zero_result,
15
+ const int K, const int N, const int N_32align) {
16
+ const int lane_id = threadIdx.x % 32;
17
+ const int warp_id = threadIdx.x / 32;
18
+
19
+ if (blockIdx.x != gridDim.x - 1) {
20
+ // Load B
21
+ // per block process 64(k) * 128(n) B elements
22
+ // per warp process 16(k) * 128 B elements
23
+ const int src_row_base_idx =
24
+ blockIdx.x * 64 + warp_id * 16 + ((lane_id % 8) / 2) * 2;
25
+ const int src_col_idx =
26
+ blockIdx.y * 128 + (lane_id / 8) * 32 + (lane_id % 2) * 16;
27
+ uint8_t B_frag[4][16];
28
+#pragma unroll
29
+ for (int i = 0; i < 4; ++i) {
30
+ int src_row_idx = src_row_base_idx + (i / 2) * 8 + (i % 2);
31
+ int src_offset = src_row_idx * N + src_col_idx;
32
+ bool guard = src_row_idx < K && src_col_idx < N;
33
+ ldg128_cg_0(*reinterpret_cast<uint32_t*>(B_frag[i]),
34
+ *(reinterpret_cast<uint32_t*>(B_frag[i]) + 1),
35
+ *(reinterpret_cast<uint32_t*>(B_frag[i]) + 2),
36
+ *(reinterpret_cast<uint32_t*>(B_frag[i]) + 3), B + src_offset,
37
+ guard);
38
+ }
39
+
40
+ // reorder B
41
+ uint8_t B_reorder_frag[8][8];
42
+#pragma unroll
43
+ for (int i = 0; i < 4; ++i) {
44
+#pragma unroll
45
+ for (int j = 0; j < 16; ++j) {
46
+ int dst_i = j % 8;
47
+ int dst_j = i + (j / 8) * 4;
48
+ B_reorder_frag[dst_i][dst_j] = B_frag[i][j];
49
+ }
50
+ }
51
+
52
+ // Store B
53
+ const int dst_row_base_idx = blockIdx.y * (128 / 4) + (lane_id / 8) * 8;
54
+ const int dst_col_idx =
55
+ blockIdx.x * (64 * 4) + warp_id * 64 + (lane_id % 8) * 8;
56
+ for (int i = 0; i < 8; ++i) {
57
+ int dst_row_idx = dst_row_base_idx + i;
58
+ int dst_offset = dst_row_idx * K * 4 + dst_col_idx;
59
+ bool guard = (dst_row_base_idx < N_32align / 4) && (dst_col_idx < K * 4);
60
+ if (guard) {
61
+ *reinterpret_cast<int2*>(B_result + dst_offset) =
62
+ *reinterpret_cast<int2*>(B_reorder_frag[i]);
63
+ }
64
+ }
65
+ } else {
66
+ // Load B_scale and B_zero
67
+ FType b_scale_reg, b_zero_reg;
68
+ int src_offset = blockIdx.y * 128 + threadIdx.x;
69
+ ldg16_cg_0(b_scale_reg, B_scale + src_offset, src_offset < N);
70
+ if (B_zero != nullptr)
71
+ ldg16_cg_0(b_zero_reg, B_zero + src_offset, src_offset < N);
72
+ int dst_offset =
73
+ blockIdx.y * 128 + warp_id * 32 + (lane_id % 8) * 4 + lane_id / 8;
74
+ if (dst_offset < N_32align) {
75
+ B_scale_result[dst_offset] = b_scale_reg;
76
+ if (B_zero != nullptr) B_zero_result[dst_offset] = b_zero_reg;
77
+ }
78
+ }
79
+}
80
+
81
+template <typename FType>
82
+void rearrange_kn_weight_as_n32k16_order_ldg16(
83
+ const uint8_t* B, const FType* B_scale, const FType* B_zero,
84
+ uint8_t* B_result, FType* B_scale_result, FType* B_zero_result,
85
+ const int64_t K, const int64_t N, const int64_t N_32align,
86
+ cudaStream_t stream) {
87
+ if (N % 16 != 0 || K % 16 != 0) {
88
+ std::cerr << "Now only support N and K is multiples of 16" << std::endl;
89
+ }
90
+ const int BLOCK = 128;
91
+ int grid_x = (K + 64 - 1) / 64 + 1;
92
+ int grid_y = (N + 128 - 1) / 128;
93
+ dim3 grid(grid_x, grid_y);
94
+
95
+ rearrange_kn_weight_as_n32k16_order_ldg16_kernel<FType>
96
+ <<<grid, BLOCK, 0, stream>>>(B, B_scale, B_zero, B_result, B_scale_result,
97
+ B_zero_result, K, N, N_32align);
98
+}
99
+} // namespace allspark
100
+
101
+void rearrange_kn_weight_as_n32k16_order(
102
+ torch::Tensor const& b_qweight, torch::Tensor const& b_scales,
103
+ c10::optional<torch::Tensor> const& b_zeros, bool has_zp,
104
+ torch::Tensor& b_qweight_reorder, torch::Tensor& b_scales_reorder,
105
+ c10::optional<torch::Tensor> const& b_zeros_reorder, const int64_t K,
106
+ const int64_t N, const int64_t N_32align) {
107
+ // Verify device and strides
108
+ TORCH_CHECK(b_qweight.device().is_cuda(), "b_qweight is not on GPU");
109
+ TORCH_CHECK(b_qweight.is_contiguous(), "b_qweight is not contiguous");
110
+
111
+ TORCH_CHECK(b_scales.device().is_cuda(), "b_scales is not on GPU");
112
+ TORCH_CHECK(b_scales.is_contiguous(), "b_scales is not contiguous");
113
+
114
+ TORCH_CHECK(b_qweight_reorder.device().is_cuda(),
115
+ "b_qweight_reorder is not on GPU");
116
+ TORCH_CHECK(b_qweight_reorder.is_contiguous(),
117
+ "b_qweight_reorder is not contiguous");
118
+
119
+ TORCH_CHECK(b_scales_reorder.device().is_cuda(),
120
+ "b_scales_reorder is not on GPU");
121
+ TORCH_CHECK(b_scales_reorder.is_contiguous(),
122
+ "b_scales_reorder is not contiguous");
123
+
124
+ if (has_zp) {
125
+ TORCH_CHECK(b_zeros.value().device().is_cuda(), "b_zeros is not on GPU");
126
+ TORCH_CHECK(b_zeros.value().is_contiguous(), "b_zeros is not contiguous");
127
+
128
+ TORCH_CHECK(b_zeros_reorder.value().device().is_cuda(),
129
+ "b_zeros_reorder is not on GPU");
130
+ TORCH_CHECK(b_zeros_reorder.value().is_contiguous(),
131
+ "b_zeros_reorder is not contiguous");
132
+ }
133
+
134
+ const uint8_t* matB = reinterpret_cast<const uint8_t*>(b_qweight.data_ptr());
135
+ const void* b_scale = b_scales.data_ptr();
136
+ const void* b_zero = has_zp ? b_zeros.value().data_ptr() : nullptr;
137
+
138
+ uint8_t* matB_reorder =
139
+ reinterpret_cast<uint8_t*>(b_qweight_reorder.data_ptr());
140
+ void* b_scale_reorder = b_scales_reorder.data_ptr();
141
+ void* b_zero_reorder = has_zp ? b_zeros_reorder.value().data_ptr() : nullptr;
142
+
143
+ cudaStream_t stream = at::cuda::getCurrentCUDAStream();
144
+ if (b_scales.dtype() == at::ScalarType::Half) {
145
+ allspark::rearrange_kn_weight_as_n32k16_order_ldg16<__half>(
146
+ matB, reinterpret_cast<const __half*>(b_scale),
147
+ reinterpret_cast<const __half*>(b_zero), matB_reorder,
148
+ reinterpret_cast<__half*>(b_scale_reorder),
149
+ reinterpret_cast<__half*>(b_zero_reorder), K, N, N_32align, stream);
150
+ } else if (b_scales.dtype() == at::ScalarType::BFloat16) {
151
+ allspark::rearrange_kn_weight_as_n32k16_order_ldg16<__nv_bfloat16>(
152
+ matB, reinterpret_cast<const __nv_bfloat16*>(b_scale),
153
+ reinterpret_cast<const __nv_bfloat16*>(b_zero), matB_reorder,
154
+ reinterpret_cast<__nv_bfloat16*>(b_scale_reorder),
155
+ reinterpret_cast<__nv_bfloat16*>(b_zero_reorder), K, N, N_32align,
156
+ stream);
157
+ }
158
+}
159
+
160
+TORCH_LIBRARY_IMPL_EXPAND(TORCH_EXTENSION_NAME, CUDA, m) {
161
+ m.impl("rearrange_kn_weight_as_n32k16_order",
162
+ &rearrange_kn_weight_as_n32k16_order);
163
+}
csrc/quantization/gptq_allspark/allspark_utils.cuh
new
+408
@@ -0,0 +1,408 @@
1
+#pragma once
2
+
3
+#include <torch/all.h>
4
+#include <c10/cuda/CUDAGuard.h>
5
+#include <ATen/cuda/CUDAContext.h>
6
+#include <cuda_runtime.h>
7
+#include <cuda_fp16.h>
8
+#include <cuda_bf16.h>
9
+#include <iostream>
10
+
11
+namespace allspark {
12
+
13
+#define CHECK_CUDA(cmd) \
14
+ do { \
15
+ cudaError_t cuda_status = cmd; \
16
+ if (cuda_status != cudaSuccess) { \
17
+ std::string err_str = cudaGetErrorString(cuda_status); \
18
+ std::cerr << "Failed: " << __FILE__ << ":" << __LINE__ << " " \
19
+ << err_str; \
20
+ exit(-1); \
21
+ } \
22
+ } while (0)
23
+
24
+#define CHECK_CUBLAS(cmd) \
25
+ do { \
26
+ cublasStatus_t cublas_status = cmd; \
27
+ if (cublas_status != CUBLAS_STATUS_SUCCESS) { \
28
+ std::cerr << "Failed: " << __FILE__ << ":" << __LINE__ << " " \
29
+ << cublas_status << std::endl; \
30
+ exit(-1); \
31
+ } \
32
+ } while (0)
33
+
34
+template <typename FType, typename QType>
35
+struct SM8x_GEMM_W8A16_Splitk_Params {
36
+ const FType* A_ptr;
37
+ const QType* B_ptr;
38
+ const FType* B_scale_ptr;
39
+ const FType* B_zero_ptr;
40
+ FType* C_ptr;
41
+ int M;
42
+ int N;
43
+ int K;
44
+ int SplitK;
45
+ int GroupCnt;
46
+ int GroupSize;
47
+ FType* C_split_ptr; // for non-fused splitk reduce
48
+ float* C_tmp_ptr; // for fused splitk reduce
49
+ uint32_t* red_count_ptr; // for fused splitk reduce
50
+};
51
+
52
+struct alignas(16) BlockTileSplitkParams {
53
+ int Mtile;
54
+ int Ntile;
55
+ int SplitK;
56
+ bool EnableFuse;
57
+};
58
+
59
+template <typename FType, int BLOCK, int N_MATRIX>
60
+__global__ void f16_gemm_splitk_reduce_kernel(const FType* C_split, FType* C,
61
+ uint32_t n, uint32_t n_matrix,
62
+ uint32_t matrix_size) {
63
+ int idx = blockIdx.x * BLOCK + threadIdx.x;
64
+
65
+ if (idx >= matrix_size) {
66
+ return;
67
+ }
68
+
69
+ FType sum(0);
70
+
71
+ int n_mat = N_MATRIX > 0 ? N_MATRIX : (int)n_matrix;
72
+ for (int i = 0; i < n_mat; ++i) {
73
+ sum += C_split[idx + i * matrix_size];
74
+ }
75
+
76
+ C[idx] = sum;
77
+}
78
+
79
+template <typename FType>
80
+void f16_gemm_splitk_reduce(const FType* C_split, FType* C, const uint32_t m,
81
+ const uint32_t n, const uint32_t n_matrix,
82
+ cudaStream_t stream) {
83
+ const int BLOCK = 128;
84
+ uint32_t matrix_size = m * n;
85
+ int grid = (matrix_size + BLOCK - 1) / BLOCK;
86
+
87
+ void (*kernel)(const FType*, FType*, uint32_t, uint32_t, uint32_t) = nullptr;
88
+
89
+ switch (n_matrix) {
90
+ case 4:
91
+ kernel = f16_gemm_splitk_reduce_kernel<FType, BLOCK, 4>;
92
+ break;
93
+ case 5:
94
+ kernel = f16_gemm_splitk_reduce_kernel<FType, BLOCK, 5>;
95
+ break;
96
+ case 6:
97
+ kernel = f16_gemm_splitk_reduce_kernel<FType, BLOCK, 6>;
98
+ break;
99
+ case 7:
100
+ kernel = f16_gemm_splitk_reduce_kernel<FType, BLOCK, 7>;
101
+ break;
102
+ case 8:
103
+ kernel = f16_gemm_splitk_reduce_kernel<FType, BLOCK, 8>;
104
+ break;
105
+ case 9:
106
+ kernel = f16_gemm_splitk_reduce_kernel<FType, BLOCK, 9>;
107
+ break;
108
+ case 10:
109
+ kernel = f16_gemm_splitk_reduce_kernel<FType, BLOCK, 10>;
110
+ break;
111
+ case 11:
112
+ kernel = f16_gemm_splitk_reduce_kernel<FType, BLOCK, 11>;
113
+ break;
114
+ case 12:
115
+ kernel = f16_gemm_splitk_reduce_kernel<FType, BLOCK, 12>;
116
+ break;
117
+ default:
118
+ kernel = f16_gemm_splitk_reduce_kernel<FType, BLOCK, -1>;
119
+ break;
120
+ }
121
+
122
+ kernel<<<grid, BLOCK, 0, stream>>>(C_split, C, n, n_matrix, matrix_size);
123
+}
124
+
125
+template <typename T>
126
+struct HalfType;
127
+template <>
128
+struct HalfType<half> {
129
+ using T1 = __half;
130
+ using T2 = __half2;
131
+};
132
+template <>
133
+struct HalfType<__nv_bfloat16> {
134
+ using T1 = __nv_bfloat16;
135
+ using T2 = __nv_bfloat162;
136
+};
137
+
138
+// convert 64-bit pointer to 32-bit smem addr
139
+__device__ __forceinline__ uint32_t smem_u32addr(const void* smem_ptr) {
140
+ uint32_t addr;
141
+ asm("{.reg .u64 u64addr;\n"
142
+ " cvta.to.shared.u64 u64addr, %1;\n"
143
+ " cvt.u32.u64 %0, u64addr;}\n"
144
+ : "=r"(addr)
145
+ : "l"(smem_ptr));
146
+
147
+ return addr;
148
+}
149
+
150
+template <typename T>
151
+__device__ __forceinline__ void ldg16_cg_0(T& r0, const void* ptr, bool guard) {
152
+ static_assert(sizeof(T) == 2, "ldg16_cg_0: invalid T");
153
+
154
+ asm volatile(
155
+ "{.reg .pred p;\n"
156
+ " setp.ne.b32 p, %2, 0;\n"
157
+ " @!p mov.b16 %0, 0;\n"
158
+#if __CUDACC_VER_MAJOR__ >= 11 && __CUDACC_VER_MINOR__ >= 4 && \
159
+ __CUDA_ARCH__ >= 750
160
+ " @p ld.global.cg.L2::128B.b16 {%0}, [%1];}\n"
161
+#else
162
+ " @p ld.global.ca.b16 {%0}, [%1];}\n"
163
+#endif
164
+ : "=h"(reinterpret_cast<uint16_t&>(r0))
165
+ : "l"(ptr), "r"((int)guard));
166
+}
167
+
168
+template <typename T>
169
+__device__ __forceinline__ void ldg64_ca(T& r0, T& r1, const void* ptr,
170
+ bool guard) {
171
+ static_assert(sizeof(T) == 4, "ldg64_ca: invalid T");
172
+
173
+ asm volatile(
174
+ "{.reg .pred p;\n"
175
+ " setp.ne.b32 p, %3, 0;\n"
176
+#if __CUDACC_VER_MAJOR__ >= 11 && __CUDACC_VER_MINOR__ >= 4 && \
177
+ __CUDA_ARCH__ >= 750
178
+ " @p ld.global.ca.L2::128B.v2.b32 {%0, %1}, [%2];}\n"
179
+#else
180
+ " @p ld.global.ca.v2.b32 {%0, %1}, [%2];}\n"
181
+#endif
182
+ : "=r"(reinterpret_cast<uint32_t&>(r0)),
183
+ "=r"(reinterpret_cast<uint32_t&>(r1))
184
+ : "l"(ptr), "r"((int)guard));
185
+}
186
+
187
+template <typename T>
188
+__device__ __forceinline__ void ldg128_cg_0(T& r0, T& r1, T& r2, T& r3,
189
+ const void* ptr, bool guard) {
190
+ static_assert(sizeof(T) == 4, "ldg128_cg_0: invalid T");
191
+
192
+ asm volatile(
193
+ "{.reg .pred p;\n"
194
+ " setp.ne.b32 p, %5, 0;\n"
195
+ " @!p mov.b32 %0, 0;\n"
196
+ " @!p mov.b32 %1, 0;\n"
197
+ " @!p mov.b32 %2, 0;\n"
198
+ " @!p mov.b32 %3, 0;\n"
199
+#if __CUDACC_VER_MAJOR__ >= 11 && __CUDACC_VER_MINOR__ >= 4 && \
200
+ __CUDA_ARCH__ >= 750
201
+ " @p ld.global.cg.L2::128B.v4.b32 {%0, %1, %2, %3}, [%4];}\n"
202
+#else
203
+ " @p ld.global.cg.v4.b32 {%0, %1, %2, %3}, [%4];}\n"
204
+#endif
205
+ : "=r"(reinterpret_cast<uint32_t&>(r0)),
206
+ "=r"(reinterpret_cast<uint32_t&>(r1)),
207
+ "=r"(reinterpret_cast<uint32_t&>(r2)),
208
+ "=r"(reinterpret_cast<uint32_t&>(r3))
209
+ : "l"(ptr), "r"((int)guard));
210
+}
211
+
212
+template <typename T>
213
+__device__ __forceinline__ void lds128(T& reg0, T& reg1, T& reg2, T& reg3,
214
+ const uint32_t addr) {
215
+ static_assert(sizeof(T) == 4, "lds128: invalid T");
216
+
217
+ asm volatile("ld.shared.v4.b32 {%0, %1, %2, %3}, [%4];\n"
218
+ : "=r"(reinterpret_cast<uint32_t&>(reg0)),
219
+ "=r"(reinterpret_cast<uint32_t&>(reg1)),
220
+ "=r"(reinterpret_cast<uint32_t&>(reg2)),
221
+ "=r"(reinterpret_cast<uint32_t&>(reg3))
222
+ : "r"(addr));
223
+}
224
+
225
+template <typename T>
226
+__device__ __forceinline__ void stg128(const T& r0, const T& r1, const T& r2,
227
+ const T& r3, const void* ptr,
228
+ bool guard) {
229
+ static_assert(sizeof(T) == 4, "stg128: invalid T");
230
+
231
+ asm volatile(
232
+ "{.reg .pred p;\n"
233
+ " setp.ne.b32 p, %1, 0;\n"
234
+ " @p st.global.v4.b32 [%0], {%2, %3, %4, %5};}\n"
235
+ :
236
+ : "l"(ptr), "r"((int)guard), "r"(reinterpret_cast<const uint32_t&>(r0)),
237
+ "r"(reinterpret_cast<const uint32_t&>(r1)),
238
+ "r"(reinterpret_cast<const uint32_t&>(r2)),
239
+ "r"(reinterpret_cast<const uint32_t&>(r3)));
240
+}
241
+
242
+template <typename T>
243
+__device__ __forceinline__ void ldsm_4(T& r0, T& r1, T& r2, T& r3,
244
+ const uint32_t& addr) {
245
+ static_assert(sizeof(T) == 4, "ldsm_4: invalid T");
246
+#if (__CUDA_ARCH__ >= 750) && (__CUDACC_VER_MAJOR__ >= 11)
247
+ asm volatile(
248
+ "ldmatrix.sync.aligned.m8n8.x4.shared.b16 {%0, %1, %2, %3}, [%4];\n"
249
+ : "=r"(reinterpret_cast<uint32_t&>(r0)),
250
+ "=r"(reinterpret_cast<uint32_t&>(r1)),
251
+ "=r"(reinterpret_cast<uint32_t&>(r2)),
252
+ "=r"(reinterpret_cast<uint32_t&>(r3))
253
+ : "r"(addr));
254
+#endif
255
+}
256
+
257
+template <typename FType>
258
+__device__ __forceinline__ void hmma16816_f32(float (&d)[4],
259
+ const uint32_t (&a)[4],
260
+ const uint32_t (&b)[2]);
261
+
262
+template <>
263
+__device__ __forceinline__ void hmma16816_f32<__half>(float (&d)[4],
264
+ const uint32_t (&a)[4],
265
+ const uint32_t (&b)[2]) {
266
+#if (__CUDA_ARCH__ >= 800) && (__CUDACC_VER_MAJOR__ >= 11)
267
+ asm volatile(
268
+ "mma.sync.aligned.m16n8k16.row.col.f32.f16.f16.f32 {%0, %1, %2, %3}, "
269
+ "{%4, %5, %6, %7}, {%8, %9}, {%0, %1, %2, %3};\n"
270
+ : "+f"(d[0]), "+f"(d[1]), "+f"(d[2]), "+f"(d[3])
271
+ : "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]), "r"(b[0]), "r"(b[1]));
272
+#endif
273
+}
274
+
275
+template <>
276
+__device__ __forceinline__ void hmma16816_f32<__nv_bfloat16>(
277
+ float (&d)[4], const uint32_t (&a)[4], const uint32_t (&b)[2]) {
278
+#if (__CUDA_ARCH__ >= 800) && (__CUDACC_VER_MAJOR__ >= 11)
279
+ asm volatile(
280
+ "mma.sync.aligned.m16n8k16.row.col.f32.bf16.bf16.f32 {%0, %1, %2, %3}, "
281
+ "{%4, %5, %6, %7}, {%8, %9}, {%0, %1, %2, %3};\n"
282
+ : "+f"(d[0]), "+f"(d[1]), "+f"(d[2]), "+f"(d[3])
283
+ : "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]), "r"(b[0]), "r"(b[1]));
284
+#endif
285
+}
286
+
287
+template <int SIZE_IN_BYTES>
288
+__device__ __forceinline__ void cp_async(const uint32_t smem_addr,
289
+ const void* gmem_ptr,
290
+ const int src_in_bytes, bool guard) {
291
+ static_assert(
292
+ (SIZE_IN_BYTES == 4 || SIZE_IN_BYTES == 8 || SIZE_IN_BYTES == 16),
293
+ "Size is not supported");
294
+#if __CUDACC_VER_MAJOR__ >= 11 && __CUDA_ARCH__ >= 800
295
+ asm volatile(
296
+ "{.reg.pred p;\n"
297
+ " setp.ne.b32 p, %4, 0;\n"
298
+ #if __CUDACC_VER_MINOR__ >= 4
299
+ " @p cp.async.cg.shared.global.L2::256B [%0], [%1], %2, %3;}\n"
300
+ #else
301
+ " @p cp.async.cg.shared.global [%0], [%1], %2, %3;}\n"
302
+ #endif
303
+ ::"r"(smem_addr),
304
+ "l"(gmem_ptr), "n"(SIZE_IN_BYTES), "r"(src_in_bytes), "r"((int)guard));
305
+#endif
306
+}
307
+
308
+template <int SIZE_IN_BYTES>
309
+__device__ __forceinline__ void cp_async_ca(const uint32_t smem_addr,
310
+ const void* gmem_ptr,
311
+ const int src_in_bytes,
312
+ bool guard) {
313
+ static_assert(
314
+ (SIZE_IN_BYTES == 4 || SIZE_IN_BYTES == 8 || SIZE_IN_BYTES == 16),
315
+ "Size is not supported");
316
+#if __CUDACC_VER_MAJOR__ >= 11 && __CUDA_ARCH__ >= 800
317
+ asm volatile(
318
+ "{.reg.pred p;\n"
319
+ " setp.ne.b32 p, %4, 0;\n"
320
+ #if __CUDACC_VER_MINOR__ >= 4
321
+ " @p cp.async.ca.shared.global.L2::256B [%0], [%1], %2, %3;}\n"
322
+ #else
323
+ " @p cp.async.ca.shared.global [%0], [%1], %2, %3;}\n"
324
+ #endif
325
+ ::"r"(smem_addr),
326
+ "l"(gmem_ptr), "n"(SIZE_IN_BYTES), "r"(src_in_bytes), "r"((int)guard));
327
+#endif
328
+}
329
+
330
+__device__ __forceinline__ void cp_async_commit_group() {
331
+#if __CUDACC_VER_MAJOR__ >= 11 && __CUDA_ARCH__ >= 800
332
+ asm volatile("cp.async.commit_group;\n");
333
+#endif
334
+}
335
+
336
+template <int N>
337
+__device__ __forceinline__ void cp_asyc_wait_group() {
338
+#if __CUDACC_VER_MAJOR__ >= 11 && __CUDA_ARCH__ >= 800
339
+ asm volatile("cp.async.wait_group %0;\n" : : "n"(N));
340
+#endif
341
+}
342
+
343
+template <typename T>
344
+__device__ __forceinline__ void cvt_8bx4_to_16bx4_bias128(const uint32_t& idata,
345
+ T* fdata);
346
+
347
+template <>
348
+// fast conversion: 4xuint8 to 4xhalf, subtracting bias = 128
349
+__device__ __forceinline__ void cvt_8bx4_to_16bx4_bias128<__half2>(
350
+ const uint32_t& idata, __half2* fdata) {
351
+ uint32_t i10, i32;
352
+ asm volatile(
353
+ "prmt.b32 %0, %2, 0x64, 0x4140;"
354
+ "prmt.b32 %1, %2, 0x64, 0x4342;"
355
+ : "=r"(i10), "=r"(i32)
356
+ : "r"(idata));
357
+
358
+ static constexpr uint32_t MAGIC_NUM = 0x64806480;
359
+ fdata[0] = __hsub2(reinterpret_cast<const __half2&>(i10),
360
+ reinterpret_cast<const __half2&>(MAGIC_NUM));
361
+ fdata[1] = __hsub2(reinterpret_cast<const __half2&>(i32),
362
+ reinterpret_cast<const __half2&>(MAGIC_NUM));
363
+}
364
+
365
+template <>
366
+// fast conversion: 4xuint8 to 4xbfloat16, subtracting bias = 128
367
+// reference from marlin fast implementation
368
+__device__ __forceinline__ void cvt_8bx4_to_16bx4_bias128<__nv_bfloat162>(
369
+ const uint32_t& idata, __nv_bfloat162* fdata) {
370
+ float fp32_imd[4];
371
+ uint32_t* fp32_imd_casted = reinterpret_cast<uint32_t*>(fp32_imd);
372
+ asm volatile(
373
+ "prmt.b32 %0, %4, 0x4B000000, 0x7650;"
374
+ "prmt.b32 %1, %4, 0x4B000000, 0x7651;"
375
+ "prmt.b32 %2, %4, 0x4B000000, 0x7652;"
376
+ "prmt.b32 %3, %4, 0x4B000000, 0x7653;"
377
+ : "=r"(fp32_imd_casted[0]), "=r"(fp32_imd_casted[1]),
378
+ "=r"(fp32_imd_casted[2]), "=r"(fp32_imd_casted[3])
379
+ : "r"(idata));
380
+
381
+ fp32_imd[0] -= 8388736.f;
382
+ fp32_imd[1] -= 8388736.f;
383
+ fp32_imd[2] -= 8388736.f;
384
+ fp32_imd[3] -= 8388736.f;
385
+
386
+ uint32_t* bf16_res = reinterpret_cast<uint32_t*>(fdata);
387
+ asm volatile(
388
+ "prmt.b32 %0, %2, %3, 0x7632;"
389
+ "prmt.b32 %1, %4, %5, 0x7632;"
390
+ : "=r"(bf16_res[0]), "=r"(bf16_res[1])
391
+ : "r"(fp32_imd_casted[0]), "r"(fp32_imd_casted[1]),
392
+ "r"(fp32_imd_casted[2]), "r"(fp32_imd_casted[3]));
393
+}
394
+
395
+static __device__ nv_bfloat162 inline num2num2(const nv_bfloat16 x) {
396
+#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 800
397
+ assert(false);
398
+#else
399
+ return __bfloat162bfloat162(x);
400
+#endif
401
+ __builtin_unreachable(); // Suppress missing return statement warning
402
+}
403
+
404
+static __device__ half2 inline num2num2(const half x) {
405
+ return __half2half2(x);
406
+}
407
+
408
+} // namespace allspark
\ No newline at end of file
csrc/torch_bindings.cpp
+19
@@ -447,6 +447,25 @@ TORCH_LIBRARY_EXPAND(TORCH_EXTENSION_NAME, ops) {
447
"Tensor!? azp) -> ()");
448
ops.impl("dynamic_scaled_int8_quant", torch::kCUDA,
449
&dynamic_scaled_int8_quant);
450
+
451
+#ifndef USE_ROCM
452
+ // reorder weight for AllSpark Ampere W8A16 Fused Gemm kernel
453
+ ops.def(
454
+ "rearrange_kn_weight_as_n32k16_order(Tensor b_qweight, Tensor b_scales, "
455
+ "Tensor? b_zeros, "
456
+ "bool has_zp, Tensor! b_qweight_reorder, Tensor! b_scales_reorder, "
457
+ "Tensor!? b_zeros_reorder, "
458
+ "int K, int N, int N_32align) -> ()");
459
+ // conditionally compiled so impl in source file
460
+
461
+ // AllSpark quantization ops
462
+ ops.def(
463
+ "allspark_w8a16_gemm(Tensor a, Tensor b_qweight, Tensor b_scales, "
464
+ "Tensor? b_qzeros, "
465
+ "SymInt n, SymInt group_size, SymInt sm_count, SymInt sm_version, SymInt "
466
+ "CUBLAS_M_THRESHOLD, bool has_zp, bool n32k16_reorder) -> Tensor");
467
+ // conditionally compiled so impl in source file
468
+#endif
469
}
470
471
TORCH_LIBRARY_EXPAND(CONCAT(TORCH_EXTENSION_NAME, _cache_ops), cache_ops) {
tests/kernels/test_allspark_gemm.py
new
+100
@@ -0,0 +1,100 @@
1
+# SPDX-License-Identifier: Apache-2.0
2
+import pytest
3
+import torch
4
+
5
+from tests.kernels.utils import DEFAULT_OPCHECK_TEST_UTILS, opcheck
6
+from vllm import _custom_ops as ops
7
+from vllm.model_executor.layers.quantization.utils.allspark_utils import (
8
+ ALLSPARK_AMPERE_K_ALIGN, ALLSPARK_AMPERE_M_CUBLAS_THRESHOLD,
9
+ ALLSPARK_AMPERE_N_ALIGN)
10
+from vllm.model_executor.layers.quantization.utils.quant_utils import (
11
+ quantize_weights)
12
+from vllm.platforms import current_platform
13
+from vllm.scalar_type import scalar_types
14
+
15
+
16
+def is_gptq_allspark_supported(min_capability: int,
17
+ max_capability: int) -> bool:
18
+ if not current_platform.is_cuda():
19
+ return False
20
+
21
+ capability = current_platform.get_device_capability()
22
+ assert capability is not None
23
+
24
+ return capability.to_int() >= min_capability \
25
+ and capability.to_int() <= max_capability
26
+
27
+
28
+MNK_FACTORS = [
29
+ (1, 4, 8),
30
+ (13, 17, 67),
31
+ (26, 37, 13),
32
+ (48, 16, 24),
33
+ (67, 13, 88),
34
+ (257, 13, 11),
35
+ (658, 13, 11),
36
+ (1033, 9, 17),
37
+]
38
+
39
+DTYPES = [torch.float16, torch.bfloat16]
40
+HAS_ZP_OPTS = [False, True]
41
+
42
+
43
+def compute_max_diff(output, output_ref):
44
+ return torch.mean(torch.abs(output - output_ref)) / torch.mean(
45
+ torch.abs(output_ref))
46
+
47
+
48
+def rand_data(shape, dtype=torch.float16):
49
+ return torch.randn(shape, dtype=dtype, device="cuda")
50
+
51
+
52
+@pytest.mark.skipif(
53
+ not is_gptq_allspark_supported(80, 89),
54
+ reason="AllSpark Ampere kernel is not supported on this GPU type.")
55
+@pytest.mark.parametrize("mnk_factors", MNK_FACTORS)
56
+@pytest.mark.parametrize("group_size", [-1])
57
+@pytest.mark.parametrize("has_zp", HAS_ZP_OPTS)
58
+@pytest.mark.parametrize("dtype", DTYPES)
59
+def test_gptq_allspark_gemm_ampere(mnk_factors, group_size, has_zp, dtype):
60
+ m_factor, n_factor, k_factor = mnk_factors
61
+ m = m_factor
62
+ n = n_factor * ALLSPARK_AMPERE_N_ALIGN
63
+ k = k_factor * ALLSPARK_AMPERE_K_ALIGN
64
+
65
+ input = rand_data((m, k), dtype=dtype)
66
+ weight = rand_data((k, n), dtype=dtype)
67
+
68
+ # Quantize (and apply act_order if provided)
69
+ w_ref, qw, s, zp = quantize_weights(weight, scalar_types.uint8b128,
70
+ group_size, has_zp)
71
+
72
+ qw = qw.to(torch.uint8)
73
+ if has_zp:
74
+ zp = zp.to(dtype)
75
+ properties = torch.cuda.get_device_properties(qw.device.index)
76
+ sm_count = properties.multi_processor_count
77
+ sm_version = properties.major * 10 + properties.minor
78
+
79
+ n_32align = (n + 32 - 1) // 32 * 32
80
+
81
+ qw_reorder, s_reorder, zp_reorder = ops.allspark_repack_weight(
82
+ qw, s, zp, has_zp)
83
+ opcheck(torch.ops._C.rearrange_kn_weight_as_n32k16_order,
84
+ (qw, s, zp, has_zp, qw_reorder, s_reorder, zp_reorder, k, n,
85
+ n_32align))
86
+
87
+ opcheck(torch.ops._C.allspark_w8a16_gemm,
88
+ (input, qw_reorder, s_reorder, zp_reorder, n, group_size, sm_count,
89
+ sm_version, ALLSPARK_AMPERE_M_CUBLAS_THRESHOLD, has_zp, True),
90
+ test_utils=DEFAULT_OPCHECK_TEST_UTILS)
91
+ output = ops.allspark_w8a16_gemm(input, qw_reorder, s_reorder, zp_reorder,
92
+ n, group_size, sm_count, sm_version,
93
+ ALLSPARK_AMPERE_M_CUBLAS_THRESHOLD,
94
+ has_zp, True)
95
+
96
+ output_ref = torch.matmul(input, w_ref)
97
+ torch.cuda.synchronize()
98
+ max_diff = compute_max_diff(output, output_ref)
99
+
100
+ assert max_diff < 0.04
tests/quantization/test_compressed_tensors.py
-2
@@ -215,8 +215,6 @@ def test_compressed_tensors_wNa16(vllm_runner, wNa16_args):
215
assert qkv_proj.scheme.group_size == (-1
216
if group is None else group)
217
218
- assert qkv_proj.weight_packed.dtype is torch.int32
219
- assert qkv_proj.weight_scale.dtype is torch.float16
218
assert qkv_proj.scheme.pack_factor == pack_factor
219
220
llm.apply_model(check_model)
vllm/_custom_ops.py
+77
@@ -404,6 +404,22 @@ if hasattr(torch.ops._C, "gptq_marlin_24_gemm"):
404
memory_format=torch.contiguous_format)
405
406
407
+if hasattr(torch.ops._C, "allspark_w8a16_gemm"):
408
+
409
+ @register_fake("_C::allspark_w8a16_gemm")
410
+ def _allspark_w8a16_gemm_fake(a: torch.Tensor, b_qweight: torch.Tensor,
411
+ b_scales: torch.Tensor,
412
+ b_qzeros: Optional[torch.Tensor],
413
+ n: torch.SymInt, group_size: torch.SymInt,
414
+ sm_count: torch.SymInt,
415
+ sm_version: torch.SymInt,
416
+ CUBLAS_M_THRESHOLD: torch.SymInt,
417
+ has_zp: bool,
418
+ n32k16_reorder: bool) -> torch.Tensor:
419
+ m = a.size(0)
420
+ return torch.empty((m, n), device=a.device, dtype=a.dtype)
421
+
422
+
423
if hasattr(torch.ops._C, "ggml_dequantize"):
424
425
@register_fake("_C::ggml_dequantize")
@@ -881,6 +897,67 @@ def scaled_fp8_quant(
897
return output, scale
898
899
900
+# gptq allspark
901
+def allspark_repack_weight(
902
+ qweight: torch.Tensor,
903
+ scale: torch.Tensor,
904
+ zero_point: Optional[torch.Tensor] = None,
905
+ has_zp: bool = False
906
+) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
907
+ """
908
+ Rearrange qweight, scale, and zero_point(if asymmetric) to n32k16 format
909
+ for Ampere W8A16 Fused Gemm kernel
910
+
911
+ Args:
912
+ qweight: uint8 weight tensor, original k x n format.
913
+ scale: fp16/bf16 weight scale tensor, 1 x n format.
914
+ zero_point: fp16/bf16 weight zero_point tensor, 1 x n format.
915
+ Must be provided for asymmetric quantization.
916
+ has_zp: if use symmetric quantization, has_zp = False.
917
+ if use asymmetric quantization, has_zp = True.
918
+
919
+ Returns:
920
+ Tuple[torch.Tensor, torch.Tensor, Optional[torch.Tensor]] :
921
+ rearranged weight, scale, and optionally zero_point.
922
+ """
923
+ K = qweight.shape[0]
924
+ N = qweight.shape[1]
925
+ N_32align = (N + 32 - 1) // 32 * 32
926
+
927
+ qweight_reorder = torch.empty((N_32align, K),
928
+ device=qweight.device,
929
+ dtype=qweight.dtype)
930
+ scale_reorder = torch.empty((1, N_32align),
931
+ device=scale.device,
932
+ dtype=scale.dtype)
933
+ zero_point_reorder = None
934
+ if has_zp:
935
+ assert zero_point is not None, (
936
+ "zero_point must be provided for asymmetric quantization.")
937
+ zero_point_reorder = torch.empty((1, N_32align),
938
+ device=zero_point.device,
939
+ dtype=zero_point.dtype)
940
+
941
+ torch.ops._C.rearrange_kn_weight_as_n32k16_order(
942
+ qweight, scale, zero_point, has_zp, qweight_reorder, scale_reorder,
943
+ zero_point_reorder, K, N, N_32align)
944
+
945
+ return qweight_reorder, scale_reorder, zero_point_reorder
946
+
947
+
948
+def allspark_w8a16_gemm(a: torch.Tensor, b_qweight: torch.Tensor,
949
+ b_scales: torch.Tensor,
950
+ b_qzeros: Optional[torch.Tensor], n: int,
951
+ group_size: int, sm_count: int, sm_version: int,
952
+ CUBLAS_M_THRESHOLD: int, has_zp: bool,
953
+ n32k16_reorder: bool) -> torch.Tensor:
954
+
955
+ return torch.ops._C.allspark_w8a16_gemm(a, b_qweight, b_scales, b_qzeros,
956
+ n, group_size, sm_count,
957
+ sm_version, CUBLAS_M_THRESHOLD,
958
+ has_zp, n32k16_reorder)
959
+
960
+
961
# int8
962
def scaled_int8_quant(
963
input: torch.Tensor,
vllm/model_executor/layers/quantization/kernels/mixed_precision/__init__.py
+3
@@ -3,6 +3,8 @@
3
from typing import List, Optional, Type
4
5
import vllm.envs as envs
6
+from vllm.model_executor.layers.quantization.kernels.mixed_precision.allspark import ( # noqa: E501
7
+ AllSparkLinearKernel)
8
from vllm.model_executor.layers.quantization.kernels.mixed_precision.exllama import ( # noqa: E501
9
ExllamaLinearKernel)
10
from vllm.model_executor.layers.quantization.kernels.mixed_precision.machete import ( # noqa: E501
@@ -16,6 +18,7 @@ from vllm.platforms import current_platform
18
# in priority/performance order (when available)
19
_POSSIBLE_KERNELS: List[Type[MPLinearKernel]] = [
20
MacheteLinearKernel,
21
+ AllSparkLinearKernel,
22
MarlinLinearKernel,
23
ExllamaLinearKernel,
24
]
vllm/model_executor/layers/quantization/kernels/mixed_precision/allspark.py
new
+115
@@ -0,0 +1,115 @@
1
+# SPDX-License-Identifier: Apache-2.0
2
+
3
+from typing import Optional, Tuple
4
+
5
+import torch
6
+
7
+from vllm import _custom_ops as ops
8
+from vllm.model_executor.layers.quantization.utils import replace_parameter
9
+from vllm.model_executor.layers.quantization.utils.allspark_utils import (
10
+ ALLSPARK_AMPERE_M_CUBLAS_THRESHOLD, check_allspark_supported_dtype_shape)
11
+from vllm.model_executor.parameter import (BasevLLMParameter,
12
+ permute_param_layout_)
13
+
14
+from .MPLinearKernel import MPLinearKernel, MPLinearLayerConfig
15
+
16
+
17
+class AllSparkLinearKernel(MPLinearKernel):
18
+
19
+ @classmethod
20
+ def get_min_capability(cls) -> int:
21
+ return 80
22
+
23
+ @classmethod
24
+ def can_implement(cls,
25
+ c: MPLinearLayerConfig) -> Tuple[bool, Optional[str]]:
26
+ if c.has_g_idx:
27
+ return False, "Act reordering currently not supported by AllSpark"
28
+
29
+ if c.zero_points:
30
+ return False, "Zero points currently not supported by AllSpark"
31
+
32
+ return check_allspark_supported_dtype_shape(
33
+ c.partition_weight_shape[0], # in_features
34
+ c.partition_weight_shape[1], # out_features
35
+ c.group_size,
36
+ c.weight_type,
37
+ c.act_type)
38
+
39
+ # note assumes that
40
+ # `weight_packed` is: {input_dim = 0, output_dim = 1, packed_dim = 0}
41
+ # `weight_scale` is: {input_dim = 0, output_dim = 1}
42
+ def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
43
+ device = getattr(layer, self.w_q_name).device
44
+ c = self.config
45
+
46
+ # prepare the parameters required for the kernel
47
+ properties = torch.cuda.get_device_properties(device.index)
48
+ sm_count = properties.multi_processor_count
49
+ sm_version = properties.major * 10 + properties.minor
50
+ gemm_args = {}
51
+ gemm_args['sm_count'] = sm_count
52
+ gemm_args['sm_version'] = sm_version
53
+
54
+ self.gemm_args = gemm_args
55
+
56
+ # transform param weight, scale
57
+ old_weight_param = getattr(layer, self.w_q_name)
58
+ old_scale_param = getattr(layer, self.w_s_name)
59
+
60
+ assert isinstance(old_weight_param, BasevLLMParameter)
61
+ permute_param_layout_(old_weight_param,
62
+ input_dim=0,
63
+ output_dim=1,
64
+ packed_dim=0)
65
+
66
+ assert isinstance(old_scale_param, BasevLLMParameter)
67
+ permute_param_layout_(old_scale_param, input_dim=0, output_dim=1)
68
+
69
+ # unpack weight from K / 4 x N int32 to K x N uint8
70
+ new_weight_param = torch.nn.Parameter(old_weight_param.data,
71
+ requires_grad=False)
72
+ new_weight_param.data = new_weight_param.data.t().contiguous().view(
73
+ dtype=torch.uint8)
74
+ new_weight_param.data = new_weight_param.data.t().contiguous()
75
+
76
+ new_scale_param = torch.nn.Parameter(old_scale_param.data,
77
+ requires_grad=False)
78
+
79
+ # reorder K x N weight as N32K16 format for Ampere W8A16
80
+ new_weight_param.data, new_scale_param.data, _ = \
81
+ ops.allspark_repack_weight(
82
+ new_weight_param.data, new_scale_param.data, None,
83
+ c.zero_points)
84
+
85
+ replace_parameter(layer, self.w_q_name, new_weight_param.data)
86
+ replace_parameter(layer, self.w_s_name, new_scale_param.data)
87
+
88
+ def apply_weights(self,
89
+ layer: torch.nn.Module,
90
+ x: torch.Tensor,
91
+ bias: Optional[torch.Tensor] = None) -> torch.Tensor:
92
+ c = self.config
93
+ gemm_args = self.gemm_args
94
+ w_q, w_s, _, _ = self._get_weight_params(layer)
95
+
96
+ reshaped_x = x.reshape(-1, x.shape[-1])
97
+ out_shape = x.shape[:-1] + (c.partition_weight_shape[1], )
98
+
99
+ output = ops.allspark_w8a16_gemm(
100
+ a=reshaped_x,
101
+ b_qweight=w_q,
102
+ b_scales=w_s,
103
+ b_qzeros=None,
104
+ n=c.partition_weight_shape[1],
105
+ group_size=c.group_size,
106
+ sm_count=gemm_args['sm_count'],
107
+ sm_version=gemm_args['sm_version'],
108
+ CUBLAS_M_THRESHOLD=ALLSPARK_AMPERE_M_CUBLAS_THRESHOLD,
109
+ has_zp=c.zero_points,
110
+ n32k16_reorder=True)
111
+
112
+ if bias is not None:
113
+ output.add_(bias) # In-place add
114
+
115
+ return output.reshape(out_shape)
vllm/model_executor/layers/quantization/utils/allspark_utils.py
new
+51
@@ -0,0 +1,51 @@
1
+# SPDX-License-Identifier: Apache-2.0
2
+
3
+import torch
4
+
5
+from vllm.platforms import current_platform
6
+from vllm.scalar_type import ScalarType, scalar_types
7
+
8
+ALLSPARK_AMPERE_M_CUBLAS_THRESHOLD = 1024
9
+ALLSPARK_SUPPORTED_QUANT_TYPES = [scalar_types.uint8b128]
10
+ALLSPARK_AMPERE_N_ALIGN = 16
11
+ALLSPARK_AMPERE_K_ALIGN = 16
12
+
13
+
14
+def check_allspark_supported_dtype_shape(input_size_per_partition: int,
15
+ output_size_per_partition: int,
16
+ group_size: int,
17
+ weight_dtype: ScalarType,
18
+ act_dtype: torch.dtype):
19
+ capability_tuple = current_platform.get_device_capability()
20
+ device_capability = (-1 if capability_tuple is None else
21
+ capability_tuple.to_int())
22
+
23
+ # For Ampere GPU
24
+ if device_capability >= 80 and device_capability < 90:
25
+ if group_size != -1:
26
+ return False, \
27
+ "For Ampere GPU, AllSpark does not support group_size "\
28
+ f"= {group_size}. Only group_size = -1 are supported."
29
+
30
+ if weight_dtype not in ALLSPARK_SUPPORTED_QUANT_TYPES:
31
+ return False, "For Ampere GPU, AllSpark does not support "\
32
+ f"quant type ({weight_dtype}). Only quant type "\
33
+ f"({ALLSPARK_SUPPORTED_QUANT_TYPES}) are supported."
34
+
35
+ if input_size_per_partition % ALLSPARK_AMPERE_K_ALIGN != 0 \
36
+ or output_size_per_partition % ALLSPARK_AMPERE_N_ALIGN != 0:
37
+ return False, \
38
+ "AllSpark needs input_size_per_partition % "\
39
+ f"{ALLSPARK_AMPERE_K_ALIGN} = 0 and "\
40
+ f"output_size_per_partition % {ALLSPARK_AMPERE_N_ALIGN} = 0 "\
41
+ "for Ampere GPU optimized kernels."
42
+
43
+ if act_dtype != torch.float16 and act_dtype != torch.bfloat16:
44
+ return False, \
45
+ "AllSpark only supports act_dtype = float16 or bfloat16,"\
46
+ f"for Ampere GPU, but got act_dtype = {act_dtype}."
47
+ else:
48
+ return False, "AllSpark currently does not support "\
49
+ f"device_capability = {device_capability}."
50
+
51
+ return True, None