WaveCut commited on
Commit
efb736c
·
verified ·
1 Parent(s): 57c2394

Sync the OrbitQuant 0.9.0 W3 CUDA pipeline

Browse files
orbitquant_packed_matmul_cuda/packed_matmul.cu CHANGED
@@ -28,7 +28,7 @@ inline bool orbitquant_mma64_pipeline_disabled() {
28
  }
29
 
30
  // ORBITQUANT_MMA64_FORCE_PIPELINE=1 extends the pipeline to every eligible
31
- // width (W4/W6) for re-benchmarking on other GPUs.
32
  inline bool orbitquant_mma64_pipeline_forced() {
33
  static const bool forced = []() {
34
  char const *value = std::getenv("ORBITQUANT_MMA64_FORCE_PIPELINE");
@@ -294,18 +294,32 @@ __global__ void orbitquant_packed_matmul_mma64_kernel(
294
  #endif // __CUDA_ARCH__ >= 800
295
  }
296
 
297
- __device__ __forceinline__ void copy_async_16(
 
298
  void *__restrict__ destination,
299
  void const *__restrict__ source) {
300
  #if __CUDA_ARCH__ >= 800
301
  const uint32_t shared_address =
302
  static_cast<uint32_t>(__cvta_generic_to_shared(destination));
303
- asm volatile(
304
- "cp.async.ca.shared.global [%0], [%1], 16;\n" : : "r"(shared_address),
305
- "l"(source));
 
 
 
 
 
 
 
306
  #else
307
- *reinterpret_cast<uint4 *>(destination) =
308
- *reinterpret_cast<uint4 const *>(source);
 
 
 
 
 
 
309
  #endif
310
  }
311
 
@@ -321,10 +335,9 @@ __device__ __forceinline__ void wait_for_async_copies() {
321
  #endif
322
  }
323
 
324
- // cp.async-pipelined variant of the mma64 kernel (sm80+, byte-aligned k-tile
325
- // strides, i.e. every supported width except W3). Interior blocks double-buffer
326
- // the X tile and the packed weight bytes so global loads overlap the MMA work;
327
- // edge blocks keep the guarded synchronous path.
328
  template <typename storage_t, typename mma_t, int Bits>
329
  __global__ void orbitquant_packed_matmul_mma64_pipelined_kernel(
330
  storage_t *__restrict__ out,
@@ -348,11 +361,13 @@ __global__ void orbitquant_packed_matmul_mma64_pipelined_kernel(
348
  constexpr int x_vector_values = 8;
349
  constexpr int x_vectors_per_row = tile_k / x_vector_values;
350
  constexpr int seg_stride = tile_k * Bits / 8;
351
- constexpr int weight_segment_values = 16;
352
  constexpr int weight_segments_per_row = tile_k / weight_segment_values;
353
  constexpr int segment_bytes = weight_segment_values * Bits / 8;
 
354
  static_assert(sizeof(storage_t) == sizeof(mma_t));
355
- static_assert(Bits == 2 || Bits == 4 || Bits == 6);
 
356
 
357
  extern __shared__ __align__(16) uint8_t dynamic_shared[];
358
  mma_t *x_tiles = reinterpret_cast<mma_t *>(dynamic_shared);
@@ -380,23 +395,23 @@ __global__ void orbitquant_packed_matmul_mma64_pipelined_kernel(
380
 
381
  if (interior) {
382
  constexpr int x_copies = tile_m * x_vectors_per_row;
383
- constexpr int w_copies = tile_n * seg_stride / 16;
384
  auto stage_tile = [&](int buffer, int64_t k_start) {
385
  mma_t *x_destination = x_tiles + buffer * tile_m * padded_k;
386
  for (int task = threadIdx.x; task < x_copies; task += blockDim.x) {
387
  const int local_row = task / x_vectors_per_row;
388
  const int local_vector = task - local_row * x_vectors_per_row;
389
- copy_async_16(
390
  x_destination + local_row * padded_k + local_vector * x_vector_values,
391
  x + (block_row + local_row) * in_features + k_start +
392
  local_vector * x_vector_values);
393
  }
394
  uint8_t *stage_destination = packed_stage + buffer * tile_n * seg_stride;
395
  for (int task = threadIdx.x; task < w_copies; task += blockDim.x) {
396
- const int byte_offset = task * 16;
397
  const int local_col = byte_offset / seg_stride;
398
  const int seg_byte = byte_offset - local_col * seg_stride;
399
- copy_async_16(
400
  stage_destination + byte_offset,
401
  packed_weight_indices + (block_col + local_col) * packed_row_bytes +
402
  (k_start * Bits) / 8 + seg_byte);
@@ -804,7 +819,7 @@ __global__ void orbitquant_packed_w4a4_int8_mma_kernel(
804
  const int byte_offset = vector * 16;
805
  const int local_row = byte_offset / packed_tile_k;
806
  const int local_k_byte = byte_offset - local_row * packed_tile_k;
807
- copy_async_16(
808
  packed_activation_stage + byte_offset,
809
  packed_activations +
810
  (block_row + local_row) * packed_row_stride + local_k_byte);
@@ -816,14 +831,14 @@ __global__ void orbitquant_packed_w4a4_int8_mma_kernel(
816
  if constexpr (KMajorWeight) {
817
  const int local_k_byte = byte_offset / TileN;
818
  const int local_col = byte_offset - local_k_byte * TileN;
819
- copy_async_16(
820
  packed_weight_stage + byte_offset,
821
  packed_weight_indices + local_k_byte * out_features + block_col +
822
  local_col);
823
  } else {
824
  const int local_col = byte_offset / packed_tile_k;
825
  const int local_k_byte = byte_offset - local_col * packed_tile_k;
826
- copy_async_16(
827
  packed_weight_stage + byte_offset,
828
  packed_weight_indices +
829
  (block_col + local_col) * packed_row_stride + local_k_byte);
@@ -890,7 +905,7 @@ __global__ void orbitquant_packed_w4a4_int8_mma_kernel(
890
  const int byte_offset = vector * 16;
891
  const int local_row = byte_offset / packed_tile_k;
892
  const int local_k_byte = byte_offset - local_row * packed_tile_k;
893
- copy_async_16(
894
  packed_activation_stage + byte_offset,
895
  packed_activations +
896
  (block_row + local_row) * packed_row_stride + next_k_byte +
@@ -903,7 +918,7 @@ __global__ void orbitquant_packed_w4a4_int8_mma_kernel(
903
  if constexpr (KMajorWeight) {
904
  const int local_k_byte = byte_offset / TileN;
905
  const int local_col = byte_offset - local_k_byte * TileN;
906
- copy_async_16(
907
  packed_weight_stage + byte_offset,
908
  packed_weight_indices +
909
  (next_k_byte + local_k_byte) * out_features + block_col +
@@ -911,7 +926,7 @@ __global__ void orbitquant_packed_w4a4_int8_mma_kernel(
911
  } else {
912
  const int local_col = byte_offset / packed_tile_k;
913
  const int local_k_byte = byte_offset - local_col * packed_tile_k;
914
- copy_async_16(
915
  packed_weight_stage + byte_offset,
916
  packed_weight_indices +
917
  (block_col + local_col) * packed_row_stride + next_k_byte +
@@ -1453,14 +1468,16 @@ void matmul_packed_weight(
1453
  // (each layer's weights are evicted between calls in a real model) it is
1454
  // 1.35-1.46x faster for W2/W4/W6 on RTX 4060 Ti, A40, and RTX 4090 whenever
1455
  // the grid fits in one wave (blocks <= SM count), and it regresses up to 15%
1456
- // once the grid oversubscribes the device. Gate on grid size; measured
1457
- // 2026-07 across the three architectures above.
 
1458
  // ORBITQUANT_MMA64_FORCE_PIPELINE=1 bypasses the grid gate,
1459
  // ORBITQUANT_MMA64_DISABLE_PIPELINE=1 forces the legacy kernel everywhere.
1460
  #define ORBITQUANT_MMA64_USE_PIPELINE(BITS_VALUE) \
1461
  ((orbitquant_mma64_pipeline_forced() || \
1462
- static_cast<int64_t>(mma_grid.x) * mma_grid.y <= \
1463
- mma_properties->multiProcessorCount) && \
 
1464
  !orbitquant_mma64_pipeline_disabled() && \
1465
  mma_properties->major >= 8 && \
1466
  (2 * 128 * 72 * 2 + 128 * 72 * 2 + 8 * 16 * 16 * 4 + \
@@ -1490,7 +1507,11 @@ void matmul_packed_weight(
1490
  }
1491
  break;
1492
  case 3:
1493
- ORBITQUANT_LAUNCH_MMA64_BF16(3);
 
 
 
 
1494
  break;
1495
  case 4:
1496
  if (ORBITQUANT_MMA64_USE_PIPELINE(4)) {
@@ -1594,7 +1615,11 @@ void matmul_packed_weight(
1594
  }
1595
  break;
1596
  case 3:
1597
- ORBITQUANT_LAUNCH_MMA64_HALF(3);
 
 
 
 
1598
  break;
1599
  case 4:
1600
  if (ORBITQUANT_MMA64_USE_PIPELINE(4)) {
 
28
  }
29
 
30
  // ORBITQUANT_MMA64_FORCE_PIPELINE=1 extends the pipeline to every eligible
31
+ // width for re-benchmarking on other GPUs.
32
  inline bool orbitquant_mma64_pipeline_forced() {
33
  static const bool forced = []() {
34
  char const *value = std::getenv("ORBITQUANT_MMA64_FORCE_PIPELINE");
 
294
  #endif // __CUDA_ARCH__ >= 800
295
  }
296
 
297
+ template <int Bytes>
298
+ __device__ __forceinline__ void copy_async(
299
  void *__restrict__ destination,
300
  void const *__restrict__ source) {
301
  #if __CUDA_ARCH__ >= 800
302
  const uint32_t shared_address =
303
  static_cast<uint32_t>(__cvta_generic_to_shared(destination));
304
+ if constexpr (Bytes == 16) {
305
+ asm volatile(
306
+ "cp.async.ca.shared.global [%0], [%1], 16;\n" : : "r"(shared_address),
307
+ "l"(source));
308
+ } else {
309
+ static_assert(Bytes == 8);
310
+ asm volatile(
311
+ "cp.async.ca.shared.global [%0], [%1], 8;\n" : : "r"(shared_address),
312
+ "l"(source));
313
+ }
314
  #else
315
+ if constexpr (Bytes == 16) {
316
+ *reinterpret_cast<uint4 *>(destination) =
317
+ *reinterpret_cast<uint4 const *>(source);
318
+ } else {
319
+ static_assert(Bytes == 8);
320
+ *reinterpret_cast<uint2 *>(destination) =
321
+ *reinterpret_cast<uint2 const *>(source);
322
+ }
323
  #endif
324
  }
325
 
 
335
  #endif
336
  }
337
 
338
+ // cp.async-pipelined variant of the mma64 kernel (sm80+). Interior blocks
339
+ // double-buffer the X tile and the packed weight bytes so global loads overlap
340
+ // the MMA work; edge blocks keep the guarded synchronous path.
 
341
  template <typename storage_t, typename mma_t, int Bits>
342
  __global__ void orbitquant_packed_matmul_mma64_pipelined_kernel(
343
  storage_t *__restrict__ out,
 
361
  constexpr int x_vector_values = 8;
362
  constexpr int x_vectors_per_row = tile_k / x_vector_values;
363
  constexpr int seg_stride = tile_k * Bits / 8;
364
+ constexpr int weight_segment_values = Bits == 3 ? 32 : 16;
365
  constexpr int weight_segments_per_row = tile_k / weight_segment_values;
366
  constexpr int segment_bytes = weight_segment_values * Bits / 8;
367
+ constexpr int weight_copy_bytes = Bits == 3 ? 8 : 16;
368
  static_assert(sizeof(storage_t) == sizeof(mma_t));
369
+ static_assert(Bits == 2 || Bits == 3 || Bits == 4 || Bits == 6);
370
+ static_assert(seg_stride % weight_copy_bytes == 0);
371
 
372
  extern __shared__ __align__(16) uint8_t dynamic_shared[];
373
  mma_t *x_tiles = reinterpret_cast<mma_t *>(dynamic_shared);
 
395
 
396
  if (interior) {
397
  constexpr int x_copies = tile_m * x_vectors_per_row;
398
+ constexpr int w_copies = tile_n * seg_stride / weight_copy_bytes;
399
  auto stage_tile = [&](int buffer, int64_t k_start) {
400
  mma_t *x_destination = x_tiles + buffer * tile_m * padded_k;
401
  for (int task = threadIdx.x; task < x_copies; task += blockDim.x) {
402
  const int local_row = task / x_vectors_per_row;
403
  const int local_vector = task - local_row * x_vectors_per_row;
404
+ copy_async<16>(
405
  x_destination + local_row * padded_k + local_vector * x_vector_values,
406
  x + (block_row + local_row) * in_features + k_start +
407
  local_vector * x_vector_values);
408
  }
409
  uint8_t *stage_destination = packed_stage + buffer * tile_n * seg_stride;
410
  for (int task = threadIdx.x; task < w_copies; task += blockDim.x) {
411
+ const int byte_offset = task * weight_copy_bytes;
412
  const int local_col = byte_offset / seg_stride;
413
  const int seg_byte = byte_offset - local_col * seg_stride;
414
+ copy_async<weight_copy_bytes>(
415
  stage_destination + byte_offset,
416
  packed_weight_indices + (block_col + local_col) * packed_row_bytes +
417
  (k_start * Bits) / 8 + seg_byte);
 
819
  const int byte_offset = vector * 16;
820
  const int local_row = byte_offset / packed_tile_k;
821
  const int local_k_byte = byte_offset - local_row * packed_tile_k;
822
+ copy_async<16>(
823
  packed_activation_stage + byte_offset,
824
  packed_activations +
825
  (block_row + local_row) * packed_row_stride + local_k_byte);
 
831
  if constexpr (KMajorWeight) {
832
  const int local_k_byte = byte_offset / TileN;
833
  const int local_col = byte_offset - local_k_byte * TileN;
834
+ copy_async<16>(
835
  packed_weight_stage + byte_offset,
836
  packed_weight_indices + local_k_byte * out_features + block_col +
837
  local_col);
838
  } else {
839
  const int local_col = byte_offset / packed_tile_k;
840
  const int local_k_byte = byte_offset - local_col * packed_tile_k;
841
+ copy_async<16>(
842
  packed_weight_stage + byte_offset,
843
  packed_weight_indices +
844
  (block_col + local_col) * packed_row_stride + local_k_byte);
 
905
  const int byte_offset = vector * 16;
906
  const int local_row = byte_offset / packed_tile_k;
907
  const int local_k_byte = byte_offset - local_row * packed_tile_k;
908
+ copy_async<16>(
909
  packed_activation_stage + byte_offset,
910
  packed_activations +
911
  (block_row + local_row) * packed_row_stride + next_k_byte +
 
918
  if constexpr (KMajorWeight) {
919
  const int local_k_byte = byte_offset / TileN;
920
  const int local_col = byte_offset - local_k_byte * TileN;
921
+ copy_async<16>(
922
  packed_weight_stage + byte_offset,
923
  packed_weight_indices +
924
  (next_k_byte + local_k_byte) * out_features + block_col +
 
926
  } else {
927
  const int local_col = byte_offset / packed_tile_k;
928
  const int local_k_byte = byte_offset - local_col * packed_tile_k;
929
+ copy_async<16>(
930
  packed_weight_stage + byte_offset,
931
  packed_weight_indices +
932
  (block_col + local_col) * packed_row_stride + next_k_byte +
 
1468
  // (each layer's weights are evicted between calls in a real model) it is
1469
  // 1.35-1.46x faster for W2/W4/W6 on RTX 4060 Ti, A40, and RTX 4090 whenever
1470
  // the grid fits in one wave (blocks <= SM count), and it regresses up to 15%
1471
+ // once the grid oversubscribes the device. W3 uses denser 8-byte copies and
1472
+ // wins from 128 rows even for oversubscribed grids on RTX 6000 Ada; shorter
1473
+ // W3 launches stay on the legacy kernel. Measured 2026-07.
1474
  // ORBITQUANT_MMA64_FORCE_PIPELINE=1 bypasses the grid gate,
1475
  // ORBITQUANT_MMA64_DISABLE_PIPELINE=1 forces the legacy kernel everywhere.
1476
  #define ORBITQUANT_MMA64_USE_PIPELINE(BITS_VALUE) \
1477
  ((orbitquant_mma64_pipeline_forced() || \
1478
+ ((BITS_VALUE) == 3 ? x.size(0) >= 128 \
1479
+ : static_cast<int64_t>(mma_grid.x) * mma_grid.y <= \
1480
+ mma_properties->multiProcessorCount)) && \
1481
  !orbitquant_mma64_pipeline_disabled() && \
1482
  mma_properties->major >= 8 && \
1483
  (2 * 128 * 72 * 2 + 128 * 72 * 2 + 8 * 16 * 16 * 4 + \
 
1507
  }
1508
  break;
1509
  case 3:
1510
+ if (ORBITQUANT_MMA64_USE_PIPELINE(3)) {
1511
+ ORBITQUANT_LAUNCH_MMA64_PIPELINED(c10::BFloat16, __nv_bfloat16, 3);
1512
+ } else {
1513
+ ORBITQUANT_LAUNCH_MMA64_BF16(3);
1514
+ }
1515
  break;
1516
  case 4:
1517
  if (ORBITQUANT_MMA64_USE_PIPELINE(4)) {
 
1615
  }
1616
  break;
1617
  case 3:
1618
+ if (ORBITQUANT_MMA64_USE_PIPELINE(3)) {
1619
+ ORBITQUANT_LAUNCH_MMA64_PIPELINED(c10::Half, half, 3);
1620
+ } else {
1621
+ ORBITQUANT_LAUNCH_MMA64_HALF(3);
1622
+ }
1623
  break;
1624
  case 4:
1625
  if (ORBITQUANT_MMA64_USE_PIPELINE(4)) {