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 14, 05:30 UTC
[Kernel] Add GPTQv2 format support for low-bit or asymmetric quantization, by adapting gptq_gemm (#26092)
Xiangyu Li committed
Oct 24, 2025 at 11:26 UTC
5cc6bddb6ef5e8e5c10de8122a43fd6e8c1e3b4b
8 files changed
+295
-98
csrc/ops.h
+1
-1
@@ -307,7 +307,7 @@ void dynamic_scaled_int8_quant(torch::Tensor& out, torch::Tensor const& input,
307
torch::Tensor gptq_gemm(torch::Tensor a, torch::Tensor b_q_weight,
308
torch::Tensor b_gptq_qzeros,
309
torch::Tensor b_gptq_scales, torch::Tensor b_g_idx,
310
- bool use_exllama, int64_t bit);
310
+ bool use_exllama, bool use_v2_format, int64_t bit);
311
312
void gptq_shuffle(torch::Tensor q_weight, torch::Tensor q_perm, int64_t bit);
313
csrc/quantization/gptq/q_gemm.cu
+131
-92
@@ -185,7 +185,7 @@ typedef void (*fp_gemm_half_q_half_gptq_kernel)(const half*, const uint32_t*,
185
const uint32_t*, const half*,
186
half*, const int, const int,
187
const int, const int,
188
- const int*);
188
+ const bool, const int*);
189
190
template <bool first_block, int m_count>
191
__global__ void gemm_half_q_half_gptq_4bit_kernel(
@@ -193,12 +193,15 @@ __global__ void gemm_half_q_half_gptq_4bit_kernel(
193
const uint32_t* __restrict__ b_gptq_qzeros,
194
const half* __restrict__ b_gptq_scales, half* __restrict__ c,
195
const int size_m, const int size_n, const int size_k, const int groups,
196
- const int* __restrict__ b_q_perm) {
196
+ const bool use_v2_format, const int* __restrict__ b_q_perm) {
197
MatrixView_half a_(a, size_m, size_k);
198
MatrixView_half_rw c_(c, size_m, size_n);
199
MatrixView_q4_row b_gptq_qzeros_(b_gptq_qzeros, groups, size_n);
200
MatrixView_half b_gptq_scales_(b_gptq_scales, groups, size_n);
201
202
+ // GPTQv2 and GPTQv1 handles zero points differently
203
+ int zero_offset = use_v2_format ? 0 : 1;
204
+
205
auto t = threadIdx.x;
206
207
// Block
@@ -256,10 +259,10 @@ __global__ void gemm_half_q_half_gptq_4bit_kernel(
259
half2 y1y16[4][2];
260
b_gptq_qzeros_.item4(zeros, group, n);
261
b_gptq_scales_.item4_f(scales, group, n);
259
- dequant_4bit_8_prep_zero(zeros[0] + 1, z1z16[0], y1y16[0]);
260
- dequant_4bit_8_prep_zero(zeros[1] + 1, z1z16[1], y1y16[1]);
261
- dequant_4bit_8_prep_zero(zeros[2] + 1, z1z16[2], y1y16[2]);
262
- dequant_4bit_8_prep_zero(zeros[3] + 1, z1z16[3], y1y16[3]);
262
+ dequant_4bit_8_prep_zero(zeros[0] + zero_offset, z1z16[0], y1y16[0]);
263
+ dequant_4bit_8_prep_zero(zeros[1] + zero_offset, z1z16[1], y1y16[1]);
264
+ dequant_4bit_8_prep_zero(zeros[2] + zero_offset, z1z16[2], y1y16[2]);
265
+ dequant_4bit_8_prep_zero(zeros[3] + zero_offset, z1z16[3], y1y16[3]);
266
267
// Column result
268
float block_c[m_count][4] = {};
@@ -272,10 +275,10 @@ __global__ void gemm_half_q_half_gptq_4bit_kernel(
275
nextgroup += groupsize;
276
b_gptq_qzeros_.item4(zeros, group, n);
277
b_gptq_scales_.item4_f(scales, group, n);
275
- dequant_4bit_8_prep_zero(zeros[0] + 1, z1z16[0], y1y16[0]);
276
- dequant_4bit_8_prep_zero(zeros[1] + 1, z1z16[1], y1y16[1]);
277
- dequant_4bit_8_prep_zero(zeros[2] + 1, z1z16[2], y1y16[2]);
278
- dequant_4bit_8_prep_zero(zeros[3] + 1, z1z16[3], y1y16[3]);
278
+ dequant_4bit_8_prep_zero(zeros[0] + zero_offset, z1z16[0], y1y16[0]);
279
+ dequant_4bit_8_prep_zero(zeros[1] + zero_offset, z1z16[1], y1y16[1]);
280
+ dequant_4bit_8_prep_zero(zeros[2] + zero_offset, z1z16[2], y1y16[2]);
281
+ dequant_4bit_8_prep_zero(zeros[3] + zero_offset, z1z16[3], y1y16[3]);
282
}
283
284
#pragma unroll
@@ -329,12 +332,15 @@ __global__ void gemm_half_q_half_gptq_2bit_kernel(
332
const uint32_t* __restrict__ b_gptq_qzeros,
333
const half* __restrict__ b_gptq_scales, half* __restrict__ c,
334
const int size_m, const int size_n, const int size_k, const int groups,
332
- const int* __restrict__ b_q_perm) {
335
+ const bool use_v2_format, const int* __restrict__ b_q_perm) {
336
MatrixView_half a_(a, size_m, size_k);
337
MatrixView_half_rw c_(c, size_m, size_n);
338
MatrixView_q2_row b_gptq_qzeros_(b_gptq_qzeros, groups, size_n);
339
MatrixView_half b_gptq_scales_(b_gptq_scales, groups, size_n);
340
341
+ // GPTQv2 and GPTQv1 handles zero points differently
342
+ int zero_offset = use_v2_format ? 0 : 1;
343
+
344
auto t = threadIdx.x;
345
346
// Block
@@ -409,10 +415,10 @@ __global__ void gemm_half_q_half_gptq_2bit_kernel(
415
int4 load_int4 = *b_ptr4;
416
417
half2 dq[4][8];
412
- dequant_2bit_16(load_int4.x, dq[0], size_n, zeros[0] + 1);
413
- dequant_2bit_16(load_int4.y, dq[1], size_n, zeros[1] + 1);
414
- dequant_2bit_16(load_int4.z, dq[2], size_n, zeros[2] + 1);
415
- dequant_2bit_16(load_int4.w, dq[3], size_n, zeros[3] + 1);
418
+ dequant_2bit_16(load_int4.x, dq[0], size_n, zeros[0] + zero_offset);
419
+ dequant_2bit_16(load_int4.y, dq[1], size_n, zeros[1] + zero_offset);
420
+ dequant_2bit_16(load_int4.z, dq[2], size_n, zeros[2] + zero_offset);
421
+ dequant_2bit_16(load_int4.w, dq[3], size_n, zeros[3] + zero_offset);
422
423
#pragma unroll
424
for (int m = 0; m < m_count; m++) {
@@ -448,12 +454,15 @@ __global__ void gemm_half_q_half_gptq_3bit_kernel(
454
const uint32_t* __restrict__ b_gptq_qzeros,
455
const half* __restrict__ b_gptq_scales, half* __restrict__ c,
456
const int size_m, const int size_n, const int size_k, const int groups,
451
- const int* __restrict__ b_q_perm) {
457
+ const bool use_v2_format, const int* __restrict__ b_q_perm) {
458
MatrixView_half a_(a, size_m, size_k);
459
MatrixView_half_rw c_(c, size_m, size_n);
460
MatrixView_q3_row b_gptq_qzeros_(b_gptq_qzeros, groups, size_n);
461
MatrixView_half b_gptq_scales_(b_gptq_scales, groups, size_n);
462
463
+ // GPTQv2 and GPTQv1 handles zero points differently
464
+ int zero_offset = use_v2_format ? 0 : 1;
465
+
466
auto t = threadIdx.x;
467
468
// Block
@@ -534,13 +543,13 @@ __global__ void gemm_half_q_half_gptq_3bit_kernel(
543
544
half2 dq[4][16];
545
dequant_3bit_32(load_int4[0].x, load_int4[1].x, load_int4[2].x, dq[0],
537
- size_n, zeros[0] + 1);
546
+ size_n, zeros[0] + zero_offset);
547
dequant_3bit_32(load_int4[0].y, load_int4[1].y, load_int4[2].y, dq[1],
539
- size_n, zeros[1] + 1);
548
+ size_n, zeros[1] + zero_offset);
549
dequant_3bit_32(load_int4[0].z, load_int4[1].z, load_int4[2].z, dq[2],
541
- size_n, zeros[2] + 1);
550
+ size_n, zeros[2] + zero_offset);
551
dequant_3bit_32(load_int4[0].w, load_int4[1].w, load_int4[2].w, dq[3],
543
- size_n, zeros[3] + 1);
552
+ size_n, zeros[3] + zero_offset);
553
554
#pragma unroll
555
for (int m = 0; m < m_count; m++) {
@@ -574,12 +583,15 @@ __global__ void gemm_half_q_half_gptq_8bit_kernel(
583
const uint32_t* __restrict__ b_gptq_qzeros,
584
const half* __restrict__ b_gptq_scales, half* __restrict__ c,
585
const int size_m, const int size_n, const int size_k, const int groups,
577
- const int* __restrict__ b_q_perm) {
586
+ const bool use_v2_format, const int* __restrict__ b_q_perm) {
587
MatrixView_half a_(a, size_m, size_k);
588
MatrixView_half_rw c_(c, size_m, size_n);
589
MatrixView_q8_row b_gptq_qzeros_(b_gptq_qzeros, groups, size_n);
590
MatrixView_half b_gptq_scales_(b_gptq_scales, groups, size_n);
591
592
+ // GPTQv2 and GPTQv1 handles zero points differently
593
+ int zero_offset = use_v2_format ? 0 : 1;
594
+
595
auto t = threadIdx.x;
596
597
// Block
@@ -658,13 +670,13 @@ __global__ void gemm_half_q_half_gptq_8bit_kernel(
670
671
half2 dq[4][4];
672
dequant_8bit_8(load_int4[0].x, load_int4[1].x, dq[0], size_n,
661
- zeros[0] + 1);
673
+ zeros[0] + zero_offset);
674
dequant_8bit_8(load_int4[0].y, load_int4[1].y, dq[1], size_n,
663
- zeros[1] + 1);
675
+ zeros[1] + zero_offset);
676
dequant_8bit_8(load_int4[0].z, load_int4[1].z, dq[2], size_n,
665
- zeros[2] + 1);
677
+ zeros[2] + zero_offset);
678
dequant_8bit_8(load_int4[0].w, load_int4[1].w, dq[3], size_n,
667
- zeros[3] + 1);
679
+ zeros[3] + zero_offset);
680
681
for (int m = 0; m < m_count; m++) {
682
block_c[m][0] =
@@ -730,7 +742,8 @@ void gemm_half_q_half_cuda_part(const half* a, const uint32_t* b_q_weight,
742
const uint32_t* b_gptq_qzeros,
743
const half* b_gptq_scales, const int* b_q_perm,
744
half* c, int size_m, int size_n, int size_k,
733
- int m_count, int groups, int bit) {
745
+ int m_count, int groups, bool use_v2_format,
746
+ int bit) {
747
dim3 blockDim, gridDim;
748
blockDim.x = BLOCK_KN_SIZE;
749
blockDim.y = 1;
@@ -743,20 +756,23 @@ void gemm_half_q_half_cuda_part(const half* a, const uint32_t* b_q_weight,
756
pick_gemm_half_q_half_gptq_kernel(true, m_count, bit);
757
758
const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
746
- kernel<<<gridDim, blockDim, 0, stream>>>(a, b_q_weight, b_gptq_qzeros,
747
- b_gptq_scales, c, size_m, size_n,
748
- size_k, groups, b_q_perm);
759
+ kernel<<<gridDim, blockDim, 0, stream>>>(
760
+ a, b_q_weight, b_gptq_qzeros, b_gptq_scales, c, size_m, size_n, size_k,
761
+ groups, use_v2_format, b_q_perm);
762
}
763
764
__global__ void reconstruct_exllama_8bit_kernel(
765
const uint32_t* __restrict__ b_q_weight, const int* __restrict__ b_q_perm,
766
const uint32_t* __restrict__ b_gptq_qzeros,
767
const half* __restrict__ b_gptq_scales, const int size_k, const int size_n,
755
- const int groups, half* __restrict__ b) {
768
+ const int groups, const bool use_v2_format, half* __restrict__ b) {
769
MatrixView_half_rw b_(b, size_k, size_n);
770
MatrixView_q8_row b_gptq_qzeros_(b_gptq_qzeros, groups, size_n);
771
MatrixView_half b_gptq_scales_(b_gptq_scales, groups, size_n);
772
773
+ // GPTQv2 and GPTQv1 handles zero points differently
774
+ int zero_offset = use_v2_format ? 0 : 1;
775
+
776
auto offset_k = BLOCK_KN_SIZE * blockIdx.y;
777
auto offset_n = BLOCK_KN_SIZE * blockIdx.x * 4;
778
@@ -812,13 +828,13 @@ __global__ void reconstruct_exllama_8bit_kernel(
828
829
half2 dq[4][4];
830
dequant_8bit_8(load_int4[0].x, load_int4[1].x, dq[0], size_n,
815
- zeros[0] + 1);
831
+ zeros[0] + zero_offset);
832
dequant_8bit_8(load_int4[0].y, load_int4[1].y, dq[1], size_n,
817
- zeros[1] + 1);
833
+ zeros[1] + zero_offset);
834
dequant_8bit_8(load_int4[0].z, load_int4[1].z, dq[2], size_n,
819
- zeros[2] + 1);
835
+ zeros[2] + zero_offset);
836
dequant_8bit_8(load_int4[0].w, load_int4[1].w, dq[3], size_n,
821
- zeros[3] + 1);
837
+ zeros[3] + zero_offset);
838
839
// half* dqh = (half*)dq;
840
if (b_q_perm) {
@@ -849,11 +865,14 @@ __global__ void reconstruct_exllama_4bit_kernel(
865
const uint32_t* __restrict__ b_q_weight, const int* __restrict__ b_q_perm,
866
const uint32_t* __restrict__ b_gptq_qzeros,
867
const half* __restrict__ b_gptq_scales, const int size_k, const int size_n,
852
- const int groups, half* __restrict__ b) {
868
+ const int groups, const bool use_v2_format, half* __restrict__ b) {
869
MatrixView_half_rw b_(b, size_k, size_n);
870
MatrixView_q4_row b_gptq_qzeros_(b_gptq_qzeros, groups, size_n);
871
MatrixView_half b_gptq_scales_(b_gptq_scales, groups, size_n);
872
873
+ // GPTQv2 and GPTQv1 handles zero points differently
874
+ int zero_offset = use_v2_format ? 0 : 1;
875
+
876
auto offset_k = BLOCK_KN_SIZE * blockIdx.y;
877
auto offset_n = BLOCK_KN_SIZE * blockIdx.x * 4;
878
@@ -888,10 +907,10 @@ __global__ void reconstruct_exllama_4bit_kernel(
907
half2 y1y16[4][2];
908
b_gptq_qzeros_.item4(zeros, group, n);
909
b_gptq_scales_.item4_h2(scales, group, n);
891
- dequant_4bit_8_prep_zero(zeros[0] + 1, z1z16[0], y1y16[0]);
892
- dequant_4bit_8_prep_zero(zeros[1] + 1, z1z16[1], y1y16[1]);
893
- dequant_4bit_8_prep_zero(zeros[2] + 1, z1z16[2], y1y16[2]);
894
- dequant_4bit_8_prep_zero(zeros[3] + 1, z1z16[3], y1y16[3]);
910
+ dequant_4bit_8_prep_zero(zeros[0] + zero_offset, z1z16[0], y1y16[0]);
911
+ dequant_4bit_8_prep_zero(zeros[1] + zero_offset, z1z16[1], y1y16[1]);
912
+ dequant_4bit_8_prep_zero(zeros[2] + zero_offset, z1z16[2], y1y16[2]);
913
+ dequant_4bit_8_prep_zero(zeros[3] + zero_offset, z1z16[3], y1y16[3]);
914
915
__syncthreads();
916
@@ -904,10 +923,10 @@ __global__ void reconstruct_exllama_4bit_kernel(
923
nextgroup += groupsize;
924
b_gptq_qzeros_.item4(zeros, group, n);
925
b_gptq_scales_.item4_h2(scales, group, n);
907
- dequant_4bit_8_prep_zero(zeros[0] + 1, z1z16[0], y1y16[0]);
908
- dequant_4bit_8_prep_zero(zeros[1] + 1, z1z16[1], y1y16[1]);
909
- dequant_4bit_8_prep_zero(zeros[2] + 1, z1z16[2], y1y16[2]);
910
- dequant_4bit_8_prep_zero(zeros[3] + 1, z1z16[3], y1y16[3]);
926
+ dequant_4bit_8_prep_zero(zeros[0] + zero_offset, z1z16[0], y1y16[0]);
927
+ dequant_4bit_8_prep_zero(zeros[1] + zero_offset, z1z16[1], y1y16[1]);
928
+ dequant_4bit_8_prep_zero(zeros[2] + zero_offset, z1z16[2], y1y16[2]);
929
+ dequant_4bit_8_prep_zero(zeros[3] + zero_offset, z1z16[3], y1y16[3]);
930
}
931
932
for (int p = 0; p < 4; p++) {
@@ -954,11 +973,14 @@ __global__ void reconstruct_exllama_3bit_kernel(
973
const uint32_t* __restrict__ b_q_weight, const int* __restrict__ b_q_perm,
974
const uint32_t* __restrict__ b_gptq_qzeros,
975
const half* __restrict__ b_gptq_scales, const int size_k, const int size_n,
957
- const int groups, half* __restrict__ b) {
976
+ const int groups, const bool use_v2_format, half* __restrict__ b) {
977
MatrixView_half_rw b_(b, size_k, size_n);
978
MatrixView_q3_row b_gptq_qzeros_(b_gptq_qzeros, groups, size_n);
979
MatrixView_half b_gptq_scales_(b_gptq_scales, groups, size_n);
980
981
+ // GPTQv2 and GPTQv1 handles zero points differently
982
+ int zero_offset = use_v2_format ? 0 : 1;
983
+
984
auto offset_k = BLOCK_KN_SIZE * blockIdx.y;
985
auto offset_n = BLOCK_KN_SIZE * blockIdx.x * 4;
986
@@ -1016,13 +1038,13 @@ __global__ void reconstruct_exllama_3bit_kernel(
1038
1039
half2 dq[4][16];
1040
dequant_3bit_32(load_int4[0].x, load_int4[1].x, load_int4[2].x, dq[0],
1019
- size_n, zeros[0] + 1);
1041
+ size_n, zeros[0] + zero_offset);
1042
dequant_3bit_32(load_int4[0].y, load_int4[1].y, load_int4[2].y, dq[1],
1021
- size_n, zeros[1] + 1);
1043
+ size_n, zeros[1] + zero_offset);
1044
dequant_3bit_32(load_int4[0].z, load_int4[1].z, load_int4[2].z, dq[2],
1023
- size_n, zeros[2] + 1);
1045
+ size_n, zeros[2] + zero_offset);
1046
dequant_3bit_32(load_int4[0].w, load_int4[1].w, load_int4[2].w, dq[3],
1025
- size_n, zeros[3] + 1);
1047
+ size_n, zeros[3] + zero_offset);
1048
1049
if (b_q_perm) {
1050
for (int j = 0; j < 16; j++) {
@@ -1052,11 +1074,14 @@ __global__ void reconstruct_exllama_2bit_kernel(
1074
const uint32_t* __restrict__ b_q_weight, const int* __restrict__ b_q_perm,
1075
const uint32_t* __restrict__ b_gptq_qzeros,
1076
const half* __restrict__ b_gptq_scales, const int size_k, const int size_n,
1055
- const int groups, half* __restrict__ b) {
1077
+ const int groups, const bool use_v2_format, half* __restrict__ b) {
1078
MatrixView_half_rw b_(b, size_k, size_n);
1079
MatrixView_q2_row b_gptq_qzeros_(b_gptq_qzeros, groups, size_n);
1080
MatrixView_half b_gptq_scales_(b_gptq_scales, groups, size_n);
1081
1082
+ // GPTQv2 and GPTQv1 handles zero points differently
1083
+ int zero_offset = use_v2_format ? 0 : 1;
1084
+
1085
auto offset_k = BLOCK_KN_SIZE * blockIdx.y;
1086
auto offset_n = BLOCK_KN_SIZE * blockIdx.x * 4;
1087
@@ -1108,10 +1133,10 @@ __global__ void reconstruct_exllama_2bit_kernel(
1133
int4 load_int4 = *b_ptr4;
1134
1135
half2 dq[4][8];
1111
- dequant_2bit_16(load_int4.x, dq[0], size_n, zeros[0] + 1);
1112
- dequant_2bit_16(load_int4.y, dq[1], size_n, zeros[1] + 1);
1113
- dequant_2bit_16(load_int4.z, dq[2], size_n, zeros[2] + 1);
1114
- dequant_2bit_16(load_int4.w, dq[3], size_n, zeros[3] + 1);
1136
+ dequant_2bit_16(load_int4.x, dq[0], size_n, zeros[0] + zero_offset);
1137
+ dequant_2bit_16(load_int4.y, dq[1], size_n, zeros[1] + zero_offset);
1138
+ dequant_2bit_16(load_int4.z, dq[2], size_n, zeros[2] + zero_offset);
1139
+ dequant_2bit_16(load_int4.w, dq[3], size_n, zeros[3] + zero_offset);
1140
1141
b_ptr += size_n;
1142
// half* dqh = (half*)dq;
@@ -1143,7 +1168,7 @@ void reconstruct_exllama(const uint32_t* b_q_weight,
1168
const uint32_t* b_gptq_qzeros,
1169
const half* b_gptq_scales, const int* b_q_perm,
1170
half* out, int height, int width, int groups,
1146
- int bit) {
1171
+ bool use_v2_format, int bit) {
1172
dim3 blockDim, gridDim;
1173
blockDim.x = BLOCK_KN_SIZE;
1174
blockDim.y = 1;
@@ -1162,14 +1187,14 @@ void reconstruct_exllama(const uint32_t* b_q_weight,
1187
const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
1188
reconstruct_exllama_kernel<<<gridDim, blockDim, 0, stream>>>(
1189
b_q_weight, b_q_perm, b_gptq_qzeros, b_gptq_scales, height, width, groups,
1165
- out);
1190
+ use_v2_format, out);
1191
}
1192
1193
__global__ void gemm_half_q_half_alt_4bit_kernel(
1194
const half2* __restrict__ vec, const uint32_t* __restrict__ mat,
1195
half* __restrict__ mul, const half* __restrict__ scales,
1196
const uint32_t* __restrict__ zeros, const int* __restrict__ g_idx,
1172
- int batch, int height, int width) {
1197
+ int batch, int height, int width, bool use_v2_format) {
1198
int zero_width = width / 8;
1199
int vec_height = height * 4;
1200
const int blockwidth2 = BLOCK_KN_SIZE / 2;
@@ -1179,6 +1204,9 @@ __global__ void gemm_half_q_half_alt_4bit_kernel(
1204
int h_end = min(BLOCK_KN_SIZE / 8, height - h) * 4;
1205
auto w = BLOCK_KN_SIZE * blockIdx.x + threadIdx.x;
1206
1207
+ // GPTQv2 and GPTQv1 handles zero points differently
1208
+ int zero_offset = use_v2_format ? 0 : 1;
1209
+
1210
__shared__ half2 blockvec[BLOCK_M_SIZE_MAX][blockwidth2];
1211
if (threadIdx.x < h_end) {
1212
for (int m = 0; m < b_end; ++m) {
@@ -1223,10 +1251,11 @@ __global__ void gemm_half_q_half_alt_4bit_kernel(
1251
half2 zero = __halves2half2(
1252
__hmul(scale_f,
1253
__int2half_rn(-((zeros[g * zero_width + z_w] >> z_mod) & 0xF) -
1226
- 1)),
1227
- __hmul(scale_f2,
1228
- __int2half_rn(
1229
- -((zeros[g2 * zero_width + z_w] >> z_mod) & 0xF) - 1)));
1254
+ zero_offset)),
1255
+ __hmul(
1256
+ scale_f2,
1257
+ __int2half_rn(-((zeros[g2 * zero_width + z_w] >> z_mod) & 0xF) -
1258
+ zero_offset)));
1259
scales_tmp[tmp_k] = scale;
1260
zeros_tmp[tmp_k] = zero;
1261
}
@@ -1268,7 +1297,7 @@ __global__ void gemm_half_q_half_alt_8bit_kernel(
1297
const half2* __restrict__ vec, const uint32_t* __restrict__ mat,
1298
half* __restrict__ mul, const half* __restrict__ scales,
1299
const uint32_t* __restrict__ zeros, const int* __restrict__ g_idx,
1271
- int batch, int height, int width) {
1300
+ int batch, int height, int width, bool use_v2_format) {
1301
int zero_width = width / 4;
1302
int vec_height = height * 2;
1303
const int blockwidth2 = BLOCK_KN_SIZE / 2;
@@ -1278,6 +1307,9 @@ __global__ void gemm_half_q_half_alt_8bit_kernel(
1307
int h_end = min(BLOCK_KN_SIZE / 4, height - h) * 2;
1308
auto w = BLOCK_KN_SIZE * blockIdx.x + threadIdx.x;
1309
1310
+ // GPTQv2 and GPTQv1 handles zero points differently
1311
+ int zero_offset = use_v2_format ? 0 : 1;
1312
+
1313
__shared__ half2 blockvec[BLOCK_M_SIZE_MAX][blockwidth2];
1314
if (threadIdx.x < h_end) {
1315
for (int m = 0; m < b_end; ++m) {
@@ -1312,12 +1344,13 @@ __global__ void gemm_half_q_half_alt_8bit_kernel(
1344
half scale_f2 = scales[g2 * width + w];
1345
half2 scale = __halves2half2(scale_f, scale_f2);
1346
half2 zero = __halves2half2(
1315
- __hmul(scale_f,
1316
- __int2half_rn(
1317
- -((zeros[g * zero_width + z_w] >> z_mod) & 0xff) - 1)),
1318
- __hmul(scale_f2,
1319
- __int2half_rn(
1320
- -((zeros[g2 * zero_width + z_w] >> z_mod) & 0xff) - 1)));
1347
+ __hmul(scale_f, __int2half_rn(
1348
+ -((zeros[g * zero_width + z_w] >> z_mod) & 0xff) -
1349
+ zero_offset)),
1350
+ __hmul(
1351
+ scale_f2,
1352
+ __int2half_rn(-((zeros[g2 * zero_width + z_w] >> z_mod) & 0xff) -
1353
+ zero_offset)));
1354
scales_tmp[tmp_k] = scale;
1355
zeros_tmp[tmp_k] = zero;
1356
}
@@ -1355,7 +1388,7 @@ void gemm_half_q_half_alt(const half* a, const uint32_t* b_q_weight,
1388
const uint32_t* b_gptq_qzeros,
1389
const half* b_gptq_scales, const int* b_g_idx,
1390
half* c, int size_m, int size_n, int size_k,
1358
- int bit) {
1391
+ bool use_v2_format, int bit) {
1392
dim3 blockDim, gridDim;
1393
blockDim.x = BLOCK_KN_SIZE;
1394
blockDim.y = 1;
@@ -1372,17 +1405,15 @@ void gemm_half_q_half_alt(const half* a, const uint32_t* b_q_weight,
1405
const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
1406
kernel<<<gridDim, blockDim, 0, stream>>>(
1407
(const half2*)a, b_q_weight, c, b_gptq_scales, b_gptq_qzeros, b_g_idx,
1375
- size_m, size_k / 32 * bit, size_n);
1408
+ size_m, size_k / 32 * bit, size_n, use_v2_format);
1409
}
1410
1411
template <class T, int bit>
1379
-__global__ void reconstruct_gptq_kernel(const uint32_t* __restrict__ w,
1380
- const half* __restrict__ w_scales,
1381
- const uint32_t* __restrict__ w_zeros,
1382
- const int* __restrict__ g_idx,
1383
- const int height, const int width,
1384
- const int group,
1385
- half* __restrict__ out) {
1412
+__global__ void reconstruct_gptq_kernel(
1413
+ const uint32_t* __restrict__ w, const half* __restrict__ w_scales,
1414
+ const uint32_t* __restrict__ w_zeros, const int* __restrict__ g_idx,
1415
+ const int height, const int width, const int group,
1416
+ const bool use_v2_format, half* __restrict__ out) {
1417
// Start of block
1418
1419
auto column = BLOCK_KN_SIZE * blockIdx.x + threadIdx.x;
@@ -1395,6 +1426,9 @@ __global__ void reconstruct_gptq_kernel(const uint32_t* __restrict__ w,
1426
MatrixView_half w_scales_(w_scales, group, width);
1427
T w_zeros_(w_zeros, group, width);
1428
1429
+ // GPTQv2 and GPTQv1 handles zero points differently
1430
+ int zero_offset = use_v2_format ? 0 : 1;
1431
+
1432
uint32_t w_read = w[blockIdx.y * width + column];
1433
half* out_ptr = out_.item_ptr(row, column);
1434
@@ -1402,7 +1436,7 @@ __global__ void reconstruct_gptq_kernel(const uint32_t* __restrict__ w,
1436
for (int s = 0; s < 32; s += bit) {
1437
int group = g_idx[row + s / bit];
1438
half w_scale = w_scales_.item(group, column);
1405
- uint32_t w_zero = w_zeros_.item(group, column) + 1;
1439
+ uint32_t w_zero = w_zeros_.item(group, column) + zero_offset;
1440
half w_item =
1441
__hmul(__int2half_rn((int)((w_read >> s) & ((1 << bit) - 1)) - w_zero),
1442
w_scale);
@@ -1415,7 +1449,7 @@ __global__ void reconstruct_gptq_3bit_kernel(
1449
const uint32_t* __restrict__ w, const half* __restrict__ w_scales,
1450
const uint32_t* __restrict__ w_zeros, const int* __restrict__ g_idx,
1451
const int height, const int width, const int group,
1418
- half* __restrict__ out) {
1452
+ const bool use_v2_format, half* __restrict__ out) {
1453
// Start of block
1454
auto column = BLOCK_KN_SIZE * blockIdx.x + threadIdx.x;
1455
auto row = blockIdx.y * 32;
@@ -1427,6 +1461,9 @@ __global__ void reconstruct_gptq_3bit_kernel(
1461
MatrixView_half w_scales_(w_scales, group, width);
1462
MatrixView_q3_row w_zeros_(w_zeros, group, width);
1463
1464
+ // GPTQv2 and GPTQv1 handles zero points differently
1465
+ int zero_offset = use_v2_format ? 0 : 1;
1466
+
1467
uint32_t w1 = w[(blockIdx.y * 3) * width + column];
1468
uint32_t w2 = w[(blockIdx.y * 3 + 1) * width + column];
1469
uint32_t w3 = w[(blockIdx.y * 3 + 2) * width + column];
@@ -1436,7 +1473,7 @@ __global__ void reconstruct_gptq_3bit_kernel(
1473
for (int i = 0; i < 32; i += 1) {
1474
int group = g_idx[row + i];
1475
half w_scale = w_scales_.item(group, column);
1439
- uint32_t w_zero = w_zeros_.item(group, column) + 1;
1476
+ uint32_t w_zero = w_zeros_.item(group, column) + zero_offset;
1477
int w_item;
1478
if (i == 10) {
1479
w_item = (w1 >> 30) | ((w2 << 2) & 0x4);
@@ -1456,7 +1493,8 @@ __global__ void reconstruct_gptq_3bit_kernel(
1493
1494
void reconstruct_gptq(const uint32_t* b_q_weight, const uint32_t* b_gptq_qzeros,
1495
const half* b_gptq_scales, const int* b_g_idx, half* out,
1459
- int height, int width, int groups, int bit) {
1496
+ int height, int width, int groups, bool use_v2_format,
1497
+ int bit) {
1498
dim3 blockDim, gridDim;
1499
blockDim.x = BLOCK_KN_SIZE;
1500
blockDim.y = 1;
@@ -1476,7 +1514,7 @@ void reconstruct_gptq(const uint32_t* b_q_weight, const uint32_t* b_gptq_qzeros,
1514
const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
1515
kernel<<<gridDim, blockDim, 0, stream>>>(b_q_weight, b_gptq_scales,
1516
b_gptq_qzeros, b_g_idx, height,
1479
- width, groups, out);
1517
+ width, groups, use_v2_format, out);
1518
}
1519
1520
void gemm_half_q_half_cuda(cublasHandle_t cublas_handle, const half* a,
@@ -1484,7 +1522,8 @@ void gemm_half_q_half_cuda(cublasHandle_t cublas_handle, const half* a,
1522
const uint32_t* b_gptq_qzeros,
1523
const half* b_gptq_scales, const int* b_g_idx,
1524
half* c, half* temp_dq, int size_m, int size_n,
1487
- int size_k, int groups, bool use_exllama, int bit) {
1525
+ int size_k, int groups, bool use_exllama,
1526
+ bool use_v2_format, int bit) {
1527
bool use_reconstruct;
1528
if (use_exllama) {
1529
use_reconstruct = ((bit == 8 && size_m > MAX_Q_GEMM_ROWS_8BIT) ||
@@ -1498,10 +1537,10 @@ void gemm_half_q_half_cuda(cublasHandle_t cublas_handle, const half* a,
1537
// Reconstruct FP16 matrix, then cuBLAS
1538
if (use_exllama) {
1539
reconstruct_exllama(b_q_weight, b_gptq_qzeros, b_gptq_scales, b_g_idx,
1501
- temp_dq, size_k, size_n, groups, bit);
1540
+ temp_dq, size_k, size_n, groups, use_v2_format, bit);
1541
} else {
1542
reconstruct_gptq(b_q_weight, b_gptq_qzeros, b_gptq_scales, b_g_idx,
1504
- temp_dq, size_k, size_n, groups, bit);
1543
+ temp_dq, size_k, size_n, groups, use_v2_format, bit);
1544
}
1545
1546
const half alpha = __float2half(1.0f);
@@ -1517,18 +1556,18 @@ void gemm_half_q_half_cuda(cublasHandle_t cublas_handle, const half* a,
1556
if (max_chunks) {
1557
gemm_half_q_half_cuda_part(a, b_q_weight, b_gptq_qzeros, b_gptq_scales,
1558
b_g_idx, c, last_chunk, size_n, size_k,
1520
- BLOCK_M_SIZE_MAX, groups, bit);
1559
+ BLOCK_M_SIZE_MAX, groups, use_v2_format, bit);
1560
}
1561
1562
if (last_chunk_size) {
1524
- gemm_half_q_half_cuda_part(a + last_chunk * size_k, b_q_weight,
1525
- b_gptq_qzeros, b_gptq_scales, b_g_idx,
1526
- c + last_chunk * size_n, last_chunk_size,
1527
- size_n, size_k, last_chunk_size, groups, bit);
1563
+ gemm_half_q_half_cuda_part(
1564
+ a + last_chunk * size_k, b_q_weight, b_gptq_qzeros, b_gptq_scales,
1565
+ b_g_idx, c + last_chunk * size_n, last_chunk_size, size_n, size_k,
1566
+ last_chunk_size, groups, use_v2_format, bit);
1567
}
1568
} else {
1569
gemm_half_q_half_alt(a, b_q_weight, b_gptq_qzeros, b_gptq_scales, b_g_idx,
1531
- c, size_m, size_n, size_k, bit);
1570
+ c, size_m, size_n, size_k, use_v2_format, bit);
1571
}
1572
}
1573
@@ -1815,7 +1854,7 @@ void shuffle_exllama_weight(uint32_t* q_weight, int* q_perm, int height,
1854
torch::Tensor gptq_gemm(torch::Tensor a, torch::Tensor b_q_weight,
1855
torch::Tensor b_gptq_qzeros,
1856
torch::Tensor b_gptq_scales, torch::Tensor b_g_idx,
1818
- bool use_exllama, int64_t bit) {
1857
+ bool use_exllama, bool use_v2_format, int64_t bit) {
1858
const at::cuda::OptionalCUDAGuard device_guard(device_of(a));
1859
auto options = torch::TensorOptions().dtype(a.dtype()).device(a.device());
1860
at::Tensor c = torch::empty({a.size(0), b_q_weight.size(1)}, options);
@@ -1833,7 +1872,7 @@ torch::Tensor gptq_gemm(torch::Tensor a, torch::Tensor b_q_weight,
1872
c.size(1), // n
1873
a.size(1), // k
1874
b_gptq_qzeros.size(0), // group number
1836
- use_exllama, bit);
1875
+ use_exllama, use_v2_format, bit);
1876
return c;
1877
}
1878
csrc/torch_bindings.cpp
+2
-1
@@ -557,7 +557,8 @@ TORCH_LIBRARY_EXPAND(TORCH_EXTENSION_NAME, ops) {
557
// to prevent the meta function registry.
558
ops.def(
559
"gptq_gemm(Tensor a, Tensor b_q_weight, Tensor b_gptq_qzeros, "
560
- "Tensor b_gptq_scales, Tensor b_g_idx, bool use_exllama, int bit) "
560
+ "Tensor b_gptq_scales, Tensor b_g_idx, bool use_exllama, bool "
561
+ "use_v2_format, int bit) "
562
"-> Tensor",
563
{stride_tag});
564
ops.impl("gptq_gemm", torch::kCUDA, &gptq_gemm);
tests/kernels/quantization/test_gptq.py
+7
-1
@@ -26,4 +26,10 @@ def test_gptq_gemm_opcheck():
26
idx = torch.empty((0,), device="cuda", dtype=torch.int32)
27
use_exllama = True
28
bit = 4
29
- opcheck(torch.ops._C.gptq_gemm, (a, weight, zeros, scales, idx, use_exllama, bit))
29
+ # Test both GPTQv1 and GPTQv2 format
30
+ opcheck(
31
+ torch.ops._C.gptq_gemm, (a, weight, zeros, scales, idx, use_exllama, True, bit)
32
+ )
33
+ opcheck(
34
+ torch.ops._C.gptq_gemm, (a, weight, zeros, scales, idx, use_exllama, False, bit)
35
+ )
tests/quantization/test_gptq_v2.py
new
+109
@@ -0,0 +1,109 @@
1
+# SPDX-License-Identifier: Apache-2.0
2
+# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+"""Tests whether vllm correctly load and run gptq_v2 format checkpoints.
4
+
5
+Run `pytest tests/quantization/test_gptq_v2.py --forked`.
6
+"""
7
+
8
+import pytest
9
+import torch
10
+from transformers import AutoTokenizer
11
+
12
+from vllm import SamplingParams
13
+from vllm.model_executor.layers.quantization.gptq import GPTQLinearMethod
14
+
15
+# A dummy small model quantized by GPTQModel, stored in GPTQ v2 format
16
+MODELS = ["XXXXyu/Qwen3-1.7B-w2g64-gptq_v2"]
17
+
18
+# Generate multiple sequences for testing, because an 1.7B 2-bit model
19
+# cannot always generate normal texts.
20
+N_SEQ = 5
21
+
22
+
23
+@pytest.mark.parametrize("model_id", MODELS)
24
+def test_model_load(vllm_runner, model_id, monkeypatch):
25
+ # `LLM.apply_model` requires pickling a function.
26
+ monkeypatch.setenv("VLLM_ALLOW_INSECURE_SERIALIZATION", "1")
27
+
28
+ # Only check the default GPTQ linear method (used for 2/3-bit models).
29
+ # 4/8-bit linear methods like Marlin already support gptq_v2.
30
+ linear_method_cls = GPTQLinearMethod
31
+
32
+ with vllm_runner(model_id, dtype=torch.float16, max_model_len=512) as llm:
33
+
34
+ def check_model(model_id):
35
+ for name, submodule in model_id.named_modules():
36
+ # Could check more modules if necessary
37
+ if name == "model_id.layers.0.self_attn.qkv_proj":
38
+ assert isinstance(submodule.quant_method, linear_method_cls)
39
+
40
+ config = submodule.quant_method.quant_config
41
+ assert config.checkpoint_format == "gptq_v2"
42
+ assert submodule.quant_method.use_v2_format
43
+
44
+ # Just break since currently we only check 1 module
45
+ break
46
+
47
+ # Check if gptq_v2 format is correctly loaded
48
+ llm.apply_model(check_model)
49
+
50
+
51
+@pytest.mark.parametrize("model_id", MODELS)
52
+def test_model_inference(vllm_runner, model_id):
53
+ # Prepare prompt to test the model's generation result.
54
+ prompt = "What is the meaning of life?"
55
+ messages = [
56
+ {"role": "system", "content": "You are a helpful assistant."},
57
+ {"role": "user", "content": prompt},
58
+ ]
59
+ tokenizer = AutoTokenizer.from_pretrained(model_id)
60
+ text = tokenizer.apply_chat_template(
61
+ messages,
62
+ tokenize=False,
63
+ add_generation_prompt=True,
64
+ enable_thinking=False, # If thinking model, set it to false
65
+ )
66
+ sampling_params = SamplingParams(
67
+ n=N_SEQ,
68
+ max_tokens=128,
69
+ temperature=0.7,
70
+ top_p=0.8,
71
+ top_k=20,
72
+ min_p=0,
73
+ presence_penalty=2,
74
+ )
75
+
76
+ with vllm_runner(model_id, dtype=torch.float16, max_model_len=512) as llm:
77
+ # Generate a response to verify inference correctness
78
+ output = llm.generate(text, sampling_params)
79
+
80
+ # Make sure the output exists
81
+ assert output
82
+ assert output[0][1]
83
+ assert len(output[0][1]) == N_SEQ
84
+
85
+ def has_normal_char_distribution(texts, min_len):
86
+ for text in texts:
87
+ # Response too short
88
+ if len(text) < min_len:
89
+ return False
90
+
91
+ # Basic ratio checks
92
+ letters = sum(c.isalpha() for c in text)
93
+ spaces = sum(c.isspace() for c in text)
94
+ total = len(text)
95
+
96
+ letter_ratio = letters / total
97
+ space_ratio = spaces / total
98
+
99
+ # At least 1 normal text should exist within output sequences
100
+ # Normal text should be mostly letters with reasonable spacing
101
+ # Some magic numbers, could be adjusted
102
+ if 0.5 <= letter_ratio <= 0.9 and 0.01 <= space_ratio <= 0.3:
103
+ return True
104
+ # No sequence contains normal text, output might be broken
105
+ return False
106
+
107
+ # Apply some simple checks for giberish output
108
+ # Print the output sequences if failed
109
+ assert has_normal_char_distribution(output[0][1], 5), output[0][1]
vllm/_custom_ops.py
+10
-1
@@ -451,10 +451,18 @@ def gptq_gemm(
451
b_gptq_scales: torch.Tensor,
452
b_g_idx: torch.Tensor,
453
use_exllama: bool,
454
+ use_v2_format: bool,
455
bit: int,
456
) -> torch.Tensor:
457
return torch.ops._C.gptq_gemm(
457
- a, b_q_weight, b_gptq_qzeros, b_gptq_scales, b_g_idx, use_exllama, bit
458
+ a,
459
+ b_q_weight,
460
+ b_gptq_qzeros,
461
+ b_gptq_scales,
462
+ b_g_idx,
463
+ use_exllama,
464
+ use_v2_format,
465
+ bit,
466
)
467
468
@@ -468,6 +476,7 @@ if hasattr(torch.ops._C, "gptq_gemm"):
476
b_gptq_scales: torch.Tensor,
477
b_g_idx: torch.Tensor,
478
use_exllama: bool,
479
+ use_v2_format: bool,
480
bit: int,
481
) -> torch.Tensor:
482
return torch.empty(
vllm/model_executor/layers/quantization/gptq.py
+29
-1
@@ -11,6 +11,7 @@ from safetensors.torch import _TYPES as _SAFETENSORS_TO_TORCH_DTYPE
11
from torch.nn.parameter import Parameter
12
13
from vllm import _custom_ops as ops
14
+from vllm.logger import init_logger
15
from vllm.model_executor.layers.fused_moe.layer import FusedMoE
16
from vllm.model_executor.layers.linear import LinearMethodBase
17
from vllm.model_executor.layers.quantization.base_config import (
@@ -36,6 +37,8 @@ if TYPE_CHECKING:
37
else:
38
QuantizationMethods = str
39
40
+logger = init_logger(__name__)
41
+
42
43
class GPTQConfig(QuantizationConfig):
44
"""Config class for GPTQ.
@@ -52,6 +55,7 @@ class GPTQConfig(QuantizationConfig):
55
dynamic: dict[str, dict[str, int | bool]],
56
autoround_version: str = "",
57
modules_in_block_to_quantize: list[str] | None = None,
58
+ checkpoint_format: str = "",
59
) -> None:
60
# GPTQModel use `dynamic` config property to allow per module
61
# quantization config so each module can be individually optimized.
@@ -89,12 +93,24 @@ class GPTQConfig(QuantizationConfig):
93
"Currently, only 2/3/4/8-bit weight quantization is "
94
f"supported for GPTQ, but got {self.weight_bits} bits."
95
)
96
+ # Somehow gptq_gemm 4-bit is buggy, maybe fix it in the future.
97
+ # For now, show a warning, since gptq_marlin will be used by default.
98
+ if self.weight_bits == 4:
99
+ logger.warning_once(
100
+ "Currently, the 4-bit gptq_gemm kernel for GPTQ is buggy. "
101
+ "Please switch to gptq_marlin or gptq_bitblas."
102
+ )
103
104
self.modules_in_block_to_quantize = modules_in_block_to_quantize or []
105
106
# used to identify GPTQ model quantized by autoround
107
self.autoround_version = autoround_version
108
109
+ # GPTQ v1 and v2 format deals with zero points differently.
110
+ # Currently GPTQModel stores v1 format checkpoints by default,
111
+ # but provides the option to set `format="gptq_v2"` in `QuantizeConfig`.
112
+ self.checkpoint_format = checkpoint_format
113
+
114
def __repr__(self) -> str:
115
return (
116
f"GPTQConfig(weight_bits={self.weight_bits}, "
@@ -102,7 +118,8 @@ class GPTQConfig(QuantizationConfig):
118
f"desc_act={self.desc_act}), "
119
f"lm_head_quantized={self.lm_head_quantized}, "
120
f"dynamic={self.dynamic}, "
105
- f"modules_in_block_to_quantize={self.modules_in_block_to_quantize})"
121
+ f"modules_in_block_to_quantize={self.modules_in_block_to_quantize}), "
122
+ f"checkpoint_format={self.checkpoint_format})"
123
)
124
125
@classmethod
@@ -137,6 +154,9 @@ class GPTQConfig(QuantizationConfig):
154
modules_in_block_to_quantize = cls.get_from_keys_or(
155
config, ["modules_in_block_to_quantize"], default=None
156
)
157
+ checkpoint_format = cls.get_from_keys_or(
158
+ config, ["checkpoint_format"], default=""
159
+ )
160
return cls(
161
weight_bits,
162
group_size,
@@ -145,6 +165,7 @@ class GPTQConfig(QuantizationConfig):
165
dynamic,
166
autoround_version,
167
modules_in_block_to_quantize,
168
+ checkpoint_format,
169
)
170
171
def get_quant_method(
@@ -154,6 +175,7 @@ class GPTQConfig(QuantizationConfig):
175
# GPTQ MoE support: fall back to MoeWNA16 for broad compatibility
176
from .moe_wna16 import MoeWNA16Config
177
178
+ # TODO: maybe update this for GPTQv2 format checkpoints
179
config = {
180
"quant_method": "gptq",
181
"bits": self.weight_bits,
@@ -210,6 +232,9 @@ class GPTQLinearMethod(LinearMethodBase):
232
def __init__(self, quant_config: GPTQConfig):
233
self.quant_config = quant_config
234
235
+ # GPTQ v1 and v2 format deals with zero points differently
236
+ self.use_v2_format = quant_config.checkpoint_format == "gptq_v2"
237
+
238
def create_weights(
239
self,
240
layer: torch.nn.Module,
@@ -351,6 +376,8 @@ class GPTQLinearMethod(LinearMethodBase):
376
out_shape = x.shape[:-1] + (layer.qweight.shape[-1],)
377
reshaped_x = x.reshape(-1, x.shape[-1])
378
379
+ # GPTQ v1 and v2 format checkpoints deals with zero points differently,
380
+ # and require different gemm kernels.
381
output = ops.gptq_gemm(
382
reshaped_x,
383
layer.qweight,
@@ -358,6 +385,7 @@ class GPTQLinearMethod(LinearMethodBase):
385
layer.scales,
386
layer.g_idx,
387
layer.exllama_state == ExllamaState.READY,
388
+ self.use_v2_format,
389
self.quant_config.weight_bits,
390
)
391
if bias is not None:
vllm/model_executor/layers/quantization/kernels/mixed_precision/exllama.py
+6
-1
@@ -145,10 +145,15 @@ class ExllamaLinearKernel(MPLinearKernel):
145
146
w_q, w_s, w_zp, w_g_idx = self._get_weight_params(layer)
147
148
+ # gptq_gemm supports GPTQv2 format by passing use_v2_format=True.
149
+ # However, the MPLinearLayerConfig doesn't contain format info.
150
+ # So hardcode GPTQv1 format here, to keep its behavior unchanged.
151
+ use_v2_format = False
152
+
153
assert w_zp is not None, "Zero points are required by Exllama"
154
assert w_g_idx is not None, "Group index is required by Exllama"
155
output = ops.gptq_gemm(
151
- x_2d, w_q, w_zp, w_s, w_g_idx, True, c.weight_type.size_bits
156
+ x_2d, w_q, w_zp, w_s, w_g_idx, True, use_v2_format, c.weight_type.size_bits
157
)
158
159
if bias is not None: