v3.8.0 update (#2082)
* 3.8 update * fix Markus' name --------- Co-authored-by: yuzhai <yuzhai@nvidia.com>
This commit is contained in:
@@ -45,12 +45,21 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if (__CUDACC_VER_MAJOR__ > 12) || (__CUDACC_VER_MAJOR__ == 12 && __CUDACC_VER_MINOR__ >= 4)
|
||||
|
||||
# define CUTLASS_ARCH_MMA_SM89_SUPPORTED 1
|
||||
# define CUTLASS_ARCH_MMA_F32_SM89_SUPPORTED
|
||||
#endif
|
||||
|
||||
#if defined(CUTLASS_ARCH_MMA_SM89_SUPPORTED) && defined(__CUDA_ARCH__) && (__CUDA_ARCH__ == 890)
|
||||
# define CUTLASS_ARCH_MMA_SM89_ENABLED
|
||||
#if (__CUDACC_VER_MAJOR__ > 12) || (__CUDACC_VER_MAJOR__ == 12 && __CUDACC_VER_MINOR__ >= 8)
|
||||
# define CUTLASS_ARCH_MMA_F16_SM89_SUPPORTED
|
||||
#endif
|
||||
|
||||
#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 890)
|
||||
# if defined(CUTLASS_ARCH_MMA_F32_SM89_SUPPORTED)
|
||||
# define CUTLASS_ARCH_MMA_F32_SM89_ENABLED
|
||||
# endif
|
||||
|
||||
# if defined(CUTLASS_ARCH_MMA_F16_SM89_SUPPORTED)
|
||||
# define CUTLASS_ARCH_MMA_F16_SM89_ENABLED
|
||||
# endif
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -132,7 +141,7 @@ struct Mma<
|
||||
void operator()(FragmentC &d, FragmentA const &a, FragmentB const &b,
|
||||
FragmentC const &c) const {
|
||||
|
||||
#if defined(CUTLASS_ARCH_MMA_SM89_ENABLED)
|
||||
#if defined(CUTLASS_ARCH_MMA_F32_SM89_ENABLED)
|
||||
|
||||
uint32_t const *A = reinterpret_cast<uint32_t const *>(&a);
|
||||
uint32_t const *B = reinterpret_cast<uint32_t const *>(&b);
|
||||
@@ -198,7 +207,7 @@ struct Mma<
|
||||
void operator()(FragmentC &d, FragmentA const &a, FragmentB const &b,
|
||||
FragmentC const &c) const {
|
||||
|
||||
#if defined(CUTLASS_ARCH_MMA_SM89_ENABLED)
|
||||
#if defined(CUTLASS_ARCH_MMA_F32_SM89_ENABLED)
|
||||
|
||||
uint32_t const *A = reinterpret_cast<uint32_t const *>(&a);
|
||||
uint32_t const *B = reinterpret_cast<uint32_t const *>(&b);
|
||||
@@ -264,7 +273,7 @@ struct Mma<
|
||||
void operator()(FragmentC &d, FragmentA const &a, FragmentB const &b,
|
||||
FragmentC const &c) const {
|
||||
|
||||
#if defined(CUTLASS_ARCH_MMA_SM89_ENABLED)
|
||||
#if defined(CUTLASS_ARCH_MMA_F32_SM89_ENABLED)
|
||||
|
||||
uint32_t const *A = reinterpret_cast<uint32_t const *>(&a);
|
||||
uint32_t const *B = reinterpret_cast<uint32_t const *>(&b);
|
||||
@@ -330,7 +339,7 @@ struct Mma<
|
||||
void operator()(FragmentC &d, FragmentA const &a, FragmentB const &b,
|
||||
FragmentC const &c) const {
|
||||
|
||||
#if defined(CUTLASS_ARCH_MMA_SM89_ENABLED)
|
||||
#if defined(CUTLASS_ARCH_MMA_F32_SM89_ENABLED)
|
||||
|
||||
uint32_t const *A = reinterpret_cast<uint32_t const *>(&a);
|
||||
uint32_t const *B = reinterpret_cast<uint32_t const *>(&b);
|
||||
@@ -359,5 +368,275 @@ struct Mma<
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Matrix Multiply 16832 - Float {E4M3, E5M2}, FP16 accumulation
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Matrix multiply-add operation - F16 = fe4m3 * fe4m3 + F16
|
||||
template <typename Operator_>
|
||||
struct Mma<
|
||||
gemm::GemmShape<16, 8, 32>,
|
||||
32,
|
||||
cutlass::float_e4m3_t,
|
||||
layout::RowMajor,
|
||||
cutlass::float_e4m3_t,
|
||||
layout::ColumnMajor,
|
||||
cutlass::half_t,
|
||||
layout::RowMajor,
|
||||
Operator_> {
|
||||
static_assert(platform::is_same<Operator_, OpMultiplyAdd>::value ||
|
||||
platform::is_same<Operator_, OpMultiplyAddFastAccum>::value,
|
||||
"Invalid operator for SM89 FP8 instruction");
|
||||
|
||||
using Shape = gemm::GemmShape<16, 8, 32>;
|
||||
|
||||
using ElementA = cutlass::float_e4m3_t;
|
||||
using LayoutA = layout::RowMajor;
|
||||
using FragmentA = Array<ElementA, 16>;
|
||||
|
||||
using ElementB = cutlass::float_e4m3_t;
|
||||
using LayoutB = layout::ColumnMajor;
|
||||
using FragmentB = Array<ElementB, 8>;
|
||||
|
||||
using ElementC = cutlass::half_t;
|
||||
using LayoutC = layout::RowMajor;
|
||||
using FragmentC = Array<cutlass::half_t, 4>;
|
||||
|
||||
using Operator = Operator_;
|
||||
using ArchTag = arch::Sm89;
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
void operator()(FragmentC &d, FragmentA const &a, FragmentB const &b,
|
||||
FragmentC const &c) const {
|
||||
|
||||
#if defined(CUTLASS_ARCH_MMA_F16_SM89_ENABLED)
|
||||
|
||||
uint32_t const *A = reinterpret_cast<uint32_t const *>(&a);
|
||||
uint32_t const *B = reinterpret_cast<uint32_t const *>(&b);
|
||||
uint32_t const *C = reinterpret_cast<uint32_t const *>(&c);
|
||||
uint32_t *D = reinterpret_cast<uint32_t *>(&d);
|
||||
|
||||
asm(
|
||||
"mma.sync.aligned.m16n8k32.row.col.f16.e4m3.e4m3.f16 "
|
||||
"{%0,%1}, {%2,%3,%4,%5}, {%6,%7}, {%8,%9};\n"
|
||||
: "=r"(D[0]), "=r"(D[1])
|
||||
:
|
||||
"r"(A[0]), "r"(A[1]), "r"(A[2]), "r"(A[3]),
|
||||
"r"(B[0]), "r"(B[1]),
|
||||
"r"(C[0]), "r"(C[1])
|
||||
);
|
||||
|
||||
#else
|
||||
|
||||
CUTLASS_UNUSED(d);
|
||||
CUTLASS_UNUSED(a);
|
||||
CUTLASS_UNUSED(b);
|
||||
CUTLASS_UNUSED(c);
|
||||
CUTLASS_NOT_IMPLEMENTED();
|
||||
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
/// Matrix multiply-add operation - F16 = fe4m3 * fe5m2 + F16
|
||||
template <typename Operator_>
|
||||
struct Mma<
|
||||
gemm::GemmShape<16, 8, 32>,
|
||||
32,
|
||||
cutlass::float_e4m3_t,
|
||||
layout::RowMajor,
|
||||
cutlass::float_e5m2_t,
|
||||
layout::ColumnMajor,
|
||||
cutlass::half_t,
|
||||
layout::RowMajor,
|
||||
Operator_> {
|
||||
static_assert(platform::is_same<Operator_, OpMultiplyAdd>::value ||
|
||||
platform::is_same<Operator_, OpMultiplyAddFastAccum>::value,
|
||||
"Invalid operator for SM89 FP8 instruction");
|
||||
|
||||
using Shape = gemm::GemmShape<16, 8, 32>;
|
||||
|
||||
using ElementA = cutlass::float_e4m3_t;
|
||||
using LayoutA = layout::RowMajor;
|
||||
using FragmentA = Array<ElementA, 16>;
|
||||
|
||||
using ElementB = cutlass::float_e5m2_t;
|
||||
using LayoutB = layout::ColumnMajor;
|
||||
using FragmentB = Array<ElementB, 8>;
|
||||
|
||||
using ElementC = cutlass::half_t;
|
||||
using LayoutC = layout::RowMajor;
|
||||
using FragmentC = Array<cutlass::half_t, 4>;
|
||||
|
||||
using Operator = Operator_;
|
||||
using ArchTag = arch::Sm89;
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
void operator()(FragmentC &d, FragmentA const &a, FragmentB const &b,
|
||||
FragmentC const &c) const {
|
||||
|
||||
#if defined(CUTLASS_ARCH_MMA_F16_SM89_ENABLED)
|
||||
|
||||
uint32_t const *A = reinterpret_cast<uint32_t const *>(&a);
|
||||
uint32_t const *B = reinterpret_cast<uint32_t const *>(&b);
|
||||
uint32_t const *C = reinterpret_cast<uint32_t const *>(&c);
|
||||
uint32_t *D = reinterpret_cast<uint32_t *>(&d);
|
||||
|
||||
asm(
|
||||
"mma.sync.aligned.m16n8k32.row.col.f16.e4m3.e5m2.f16 "
|
||||
"{%0,%1}, {%2,%3,%4,%5}, {%6,%7}, {%8,%9};\n"
|
||||
: "=r"(D[0]), "=r"(D[1])
|
||||
:
|
||||
"r"(A[0]), "r"(A[1]), "r"(A[2]), "r"(A[3]),
|
||||
"r"(B[0]), "r"(B[1]),
|
||||
"r"(C[0]), "r"(C[1])
|
||||
);
|
||||
|
||||
#else
|
||||
|
||||
CUTLASS_UNUSED(d);
|
||||
CUTLASS_UNUSED(a);
|
||||
CUTLASS_UNUSED(b);
|
||||
CUTLASS_UNUSED(c);
|
||||
CUTLASS_NOT_IMPLEMENTED();
|
||||
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
/// Matrix multiply-add operation - F16 = fe5m2 * fe4m3 + F16
|
||||
template <typename Operator_>
|
||||
struct Mma<
|
||||
gemm::GemmShape<16, 8, 32>,
|
||||
32,
|
||||
cutlass::float_e5m2_t,
|
||||
layout::RowMajor,
|
||||
cutlass::float_e4m3_t,
|
||||
layout::ColumnMajor,
|
||||
cutlass::half_t,
|
||||
layout::RowMajor,
|
||||
Operator_> {
|
||||
static_assert(platform::is_same<Operator_, OpMultiplyAdd>::value ||
|
||||
platform::is_same<Operator_, OpMultiplyAddFastAccum>::value,
|
||||
"Invalid operator for SM89 FP8 instruction");
|
||||
|
||||
using Shape = gemm::GemmShape<16, 8, 32>;
|
||||
|
||||
using ElementA = cutlass::float_e5m2_t;
|
||||
using LayoutA = layout::RowMajor;
|
||||
using FragmentA = Array<ElementA, 16>;
|
||||
|
||||
using ElementB = cutlass::float_e4m3_t;
|
||||
using LayoutB = layout::ColumnMajor;
|
||||
using FragmentB = Array<ElementB, 8>;
|
||||
|
||||
using ElementC = cutlass::half_t;
|
||||
using LayoutC = layout::RowMajor;
|
||||
using FragmentC = Array<cutlass::half_t, 4>;
|
||||
|
||||
using Operator = Operator_;
|
||||
using ArchTag = arch::Sm89;
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
void operator()(FragmentC &d, FragmentA const &a, FragmentB const &b,
|
||||
FragmentC const &c) const {
|
||||
|
||||
#if defined(CUTLASS_ARCH_MMA_F16_SM89_ENABLED)
|
||||
|
||||
uint32_t const *A = reinterpret_cast<uint32_t const *>(&a);
|
||||
uint32_t const *B = reinterpret_cast<uint32_t const *>(&b);
|
||||
uint32_t const *C = reinterpret_cast<uint32_t const *>(&c);
|
||||
uint32_t *D = reinterpret_cast<uint32_t *>(&d);
|
||||
|
||||
asm(
|
||||
"mma.sync.aligned.m16n8k32.row.col.f16.e5m2.e4m3.f16 "
|
||||
"{%0,%1}, {%2,%3,%4,%5}, {%6,%7}, {%8,%9};\n"
|
||||
: "=r"(D[0]), "=r"(D[1])
|
||||
:
|
||||
"r"(A[0]), "r"(A[1]), "r"(A[2]), "r"(A[3]),
|
||||
"r"(B[0]), "r"(B[1]),
|
||||
"r"(C[0]), "r"(C[1])
|
||||
);
|
||||
|
||||
#else
|
||||
|
||||
CUTLASS_UNUSED(d);
|
||||
CUTLASS_UNUSED(a);
|
||||
CUTLASS_UNUSED(b);
|
||||
CUTLASS_UNUSED(c);
|
||||
CUTLASS_NOT_IMPLEMENTED();
|
||||
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
/// Matrix multiply-add operation - F16 = fe5m2 * fe5m2 + F16
|
||||
template <typename Operator_>
|
||||
struct Mma<
|
||||
gemm::GemmShape<16, 8, 32>,
|
||||
32,
|
||||
cutlass::float_e5m2_t,
|
||||
layout::RowMajor,
|
||||
cutlass::float_e5m2_t,
|
||||
layout::ColumnMajor,
|
||||
cutlass::half_t,
|
||||
layout::RowMajor,
|
||||
Operator_> {
|
||||
static_assert(platform::is_same<Operator_, OpMultiplyAdd>::value ||
|
||||
platform::is_same<Operator_, OpMultiplyAddFastAccum>::value,
|
||||
"Invalid operator for SM89 FP8 instruction");
|
||||
|
||||
using Shape = gemm::GemmShape<16, 8, 32>;
|
||||
|
||||
using ElementA = cutlass::float_e5m2_t;
|
||||
using LayoutA = layout::RowMajor;
|
||||
using FragmentA = Array<ElementA, 16>;
|
||||
|
||||
using ElementB = cutlass::float_e5m2_t;
|
||||
using LayoutB = layout::ColumnMajor;
|
||||
using FragmentB = Array<ElementB, 8>;
|
||||
|
||||
using ElementC = cutlass::half_t;
|
||||
using LayoutC = layout::RowMajor;
|
||||
using FragmentC = Array<cutlass::half_t, 4>;
|
||||
|
||||
using Operator = Operator_;
|
||||
using ArchTag = arch::Sm89;
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
void operator()(FragmentC &d, FragmentA const &a, FragmentB const &b,
|
||||
FragmentC const &c) const {
|
||||
|
||||
#if defined(CUTLASS_ARCH_MMA_F16_SM89_ENABLED)
|
||||
|
||||
uint32_t const *A = reinterpret_cast<uint32_t const *>(&a);
|
||||
uint32_t const *B = reinterpret_cast<uint32_t const *>(&b);
|
||||
uint32_t const *C = reinterpret_cast<uint32_t const *>(&c);
|
||||
uint32_t *D = reinterpret_cast<uint32_t *>(&d);
|
||||
|
||||
asm(
|
||||
"mma.sync.aligned.m16n8k32.row.col.f16.e5m2.e5m2.f16 "
|
||||
"{%0,%1}, {%2,%3,%4,%5}, {%6,%7}, {%8,%9};\n"
|
||||
: "=r"(D[0]), "=r"(D[1])
|
||||
:
|
||||
"r"(A[0]), "r"(A[1]), "r"(A[2]), "r"(A[3]),
|
||||
"r"(B[0]), "r"(B[1]),
|
||||
"r"(C[0]), "r"(C[1])
|
||||
);
|
||||
|
||||
#else
|
||||
|
||||
CUTLASS_UNUSED(d);
|
||||
CUTLASS_UNUSED(a);
|
||||
CUTLASS_UNUSED(b);
|
||||
CUTLASS_UNUSED(c);
|
||||
CUTLASS_NOT_IMPLEMENTED();
|
||||
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace arch
|
||||
} // namespace cutlass
|
||||
|
||||
@@ -44,12 +44,13 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if (__CUDACC_VER_MAJOR__ > 12) || (__CUDACC_VER_MAJOR__ == 12 && __CUDACC_VER_MINOR__ >= 4)
|
||||
|
||||
# define CUTLASS_ARCH_SPARSE_MMA_SM89_SUPPORTED 1
|
||||
# define CUTLASS_ARCH_SPARSE_MMA_F32_SM89_SUPPORTED
|
||||
#endif
|
||||
|
||||
#if defined(CUTLASS_ARCH_SPARSE_MMA_SM89_SUPPORTED) && defined(__CUDA_ARCH__) && (__CUDA_ARCH__ == 890)
|
||||
# define CUTLASS_ARCH_SPARSE_MMA_SM89_ENABLED
|
||||
#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 890)
|
||||
# if defined(CUTLASS_ARCH_SPARSE_MMA_F32_SM89_SUPPORTED)
|
||||
# define CUTLASS_ARCH_SPARSE_MMA_F32_SM89_ENABLED
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -113,7 +114,7 @@ struct SparseMma<
|
||||
int const id2
|
||||
) const {
|
||||
|
||||
#if defined(CUTLASS_ARCH_SPARSE_MMA_SM89_ENABLED)
|
||||
#if defined(CUTLASS_ARCH_SPARSE_MMA_F32_SM89_ENABLED)
|
||||
|
||||
uint32_t const *A = reinterpret_cast<uint32_t const *>(&a);
|
||||
uint32_t const *B = reinterpret_cast<uint32_t const *>(&b);
|
||||
@@ -198,7 +199,7 @@ struct SparseMma<
|
||||
int const id2
|
||||
) const {
|
||||
|
||||
#if defined(CUTLASS_ARCH_SPARSE_MMA_SM89_ENABLED)
|
||||
#if defined(CUTLASS_ARCH_SPARSE_MMA_F32_SM89_ENABLED)
|
||||
|
||||
uint32_t const *A = reinterpret_cast<uint32_t const *>(&a);
|
||||
uint32_t const *B = reinterpret_cast<uint32_t const *>(&b);
|
||||
@@ -283,7 +284,7 @@ struct SparseMma<
|
||||
int const id2
|
||||
) const {
|
||||
|
||||
#if defined(CUTLASS_ARCH_SPARSE_MMA_SM89_ENABLED)
|
||||
#if defined(CUTLASS_ARCH_SPARSE_MMA_F32_SM89_ENABLED)
|
||||
|
||||
uint32_t const *A = reinterpret_cast<uint32_t const *>(&a);
|
||||
uint32_t const *B = reinterpret_cast<uint32_t const *>(&b);
|
||||
@@ -368,7 +369,7 @@ struct SparseMma<
|
||||
int const id2
|
||||
) const {
|
||||
|
||||
#if defined(CUTLASS_ARCH_SPARSE_MMA_SM89_ENABLED)
|
||||
#if defined(CUTLASS_ARCH_SPARSE_MMA_F32_SM89_ENABLED)
|
||||
|
||||
uint32_t const *A = reinterpret_cast<uint32_t const *>(&a);
|
||||
uint32_t const *B = reinterpret_cast<uint32_t const *>(&b);
|
||||
|
||||
@@ -51,10 +51,8 @@
|
||||
# define CUTLASS_SM90_CLUSTER_LAUNCH_ENABLED
|
||||
#endif
|
||||
|
||||
#ifndef CUDA_ENABLE_PREFERRED_CLUSTER
|
||||
#if (__CUDACC_VER_MAJOR__ > 12 || (__CUDACC_VER_MAJOR__ == 12 && __CUDACC_VER_MINOR__ >= 8))
|
||||
#if (__CUDACC_VER_MAJOR__ > 12 || (__CUDACC_VER_MAJOR__ == 12 && __CUDACC_VER_MINOR__ >= 8))
|
||||
# define CUDA_ENABLE_PREFERRED_CLUSTER
|
||||
#endif
|
||||
#endif
|
||||
namespace cutlass {
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ sm90_cluster_shape_to_im2col_tma_atom(UnimodalClusterShape unimodal_cluster_shap
|
||||
static_assert(cute::rank(unimodal_cluster_shape) == 1,
|
||||
"Use this function to figure out TMA for each mode individually.");
|
||||
|
||||
if constexpr (cute::size(unimodal_cluster_shape) == 1) {
|
||||
if constexpr (UnimodalClusterShape::value == 1) {
|
||||
return cute::SM90_TMA_LOAD_IM2COL{};
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -516,13 +516,13 @@ public:
|
||||
}
|
||||
|
||||
if (is_im2col_A || is_im2col_B) {
|
||||
// Check valid filter offsets for TMA_LOAD_IM2COL, unsigned int ranging from [0, offset_limit - 1]
|
||||
constexpr int32_t offset_limit = 1 << (16 / NumSpatialDimensions);
|
||||
// Check valid filter offsets for TMA_LOAD_IM2COL, unsigned int ranging from [0, offset_limit]
|
||||
constexpr int32_t offset_limit = (1 << (16 / NumSpatialDimensions)) - 1;
|
||||
auto flt_data = (ConvOp == conv::Operator::kWgrad) ? problem_shape.shape_C : problem_shape.shape_B;
|
||||
for (int i = 0; i < problem_shape.RankS; ++i) {
|
||||
// flt_data array contains [K, T, R, S, C], so pure filter [T, R, S] starts from the second position in the array
|
||||
implementable = implementable && (flt_data[i+1] * problem_shape.dilation[i] >= 0)
|
||||
&& (flt_data[i+1] * problem_shape.dilation[i] <= (offset_limit - 1));
|
||||
implementable = implementable && ((flt_data[i+1] - 1) * problem_shape.dilation[i] >= 0)
|
||||
&& ((flt_data[i+1] - 1) * problem_shape.dilation[i] <= offset_limit);
|
||||
}
|
||||
|
||||
if (!implementable) {
|
||||
|
||||
@@ -392,12 +392,12 @@ public:
|
||||
|
||||
if (is_im2col_A || is_im2col_B) {
|
||||
// Check valid filter offsets for TMA_LOAD_IM2COL, unsigned int ranging from [0, offset_limit - 1]
|
||||
constexpr int32_t offset_limit = 1 << (16 / NumSpatialDimensions);
|
||||
constexpr int32_t offset_limit = (1 << (16 / NumSpatialDimensions)) - 1;
|
||||
auto flt_data = (ConvOp == conv::Operator::kWgrad) ? problem_shape.shape_C : problem_shape.shape_B;
|
||||
for (int i = 0; i < problem_shape.RankS; ++i) {
|
||||
// flt_data array contains [K, T, R, S, C], so pure filter [T, R, S] starts from the second position in the array
|
||||
implementable = implementable && (flt_data[i+1] * problem_shape.dilation[i] >= 0)
|
||||
&& (flt_data[i+1] * problem_shape.dilation[i] < offset_limit);
|
||||
implementable = implementable && ((flt_data[i+1] - 1) * problem_shape.dilation[i] >= 0)
|
||||
&& ((flt_data[i+1] - 1) * problem_shape.dilation[i] < offset_limit);
|
||||
}
|
||||
|
||||
if (!implementable) {
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cutlass/conv/kernel/conv_universal.hpp"
|
||||
#include "cutlass/gemm/kernel/tile_scheduler.hpp"
|
||||
#include "cutlass/fast_math.h"
|
||||
#include "cutlass/workspace.h"
|
||||
|
||||
#include <cute/util/type_traits.hpp>
|
||||
#include <cute/int_tuple.hpp>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cutlass::conv::kernel {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
enum class DispatchMode {
|
||||
VoidC // Select between voidC and non-voidC kernel based on beta scaling
|
||||
};
|
||||
|
||||
// Dispatch between two ConvUniversal kernels
|
||||
template <DispatchMode Mode, class KernelA, class KernelB, class = void>
|
||||
class ConvUniversalDispatch;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <
|
||||
class ProblemShape_,
|
||||
class MainloopWithC_, class EpilogueWithC_,
|
||||
class MainloopVoidC_, class EpilogueVoidC_,
|
||||
class TileScheduler_
|
||||
>
|
||||
class ConvUniversalDispatch<
|
||||
DispatchMode::VoidC,
|
||||
ConvUniversal<ProblemShape_, MainloopWithC_, EpilogueWithC_, TileScheduler_>,
|
||||
ConvUniversal<ProblemShape_, MainloopVoidC_, EpilogueVoidC_, TileScheduler_>,
|
||||
cute::void_t<decltype(typename EpilogueWithC_::Arguments{}.thread.dBeta),
|
||||
decltype(typename EpilogueVoidC_::Arguments{}.thread.dBeta)>
|
||||
> : public ConvUniversal<ProblemShape_, MainloopWithC_, EpilogueWithC_, TileScheduler_> {
|
||||
private:
|
||||
using KernelWithC = ConvUniversal<ProblemShape_, MainloopWithC_, EpilogueWithC_, TileScheduler_>;
|
||||
using KernelVoidC = ConvUniversal<ProblemShape_, MainloopVoidC_, EpilogueVoidC_, TileScheduler_>;
|
||||
using FusionArguments = cute::remove_cvref_t<decltype(typename EpilogueWithC_::Arguments{}.thread)>;
|
||||
|
||||
public:
|
||||
// Mainloop derived types
|
||||
static_assert(cute::is_same_v<typename KernelWithC::TileShape, typename KernelVoidC::TileShape>);
|
||||
static_assert(cute::is_same_v<typename KernelWithC::TiledMma, typename KernelVoidC::TiledMma>);
|
||||
static_assert(cute::is_same_v<typename KernelWithC::ArchTag, typename KernelVoidC::ArchTag>);
|
||||
static_assert(cute::is_same_v<typename KernelWithC::ElementA, typename KernelVoidC::ElementA>);
|
||||
static_assert(cute::is_same_v<typename KernelWithC::StrideA, typename KernelVoidC::StrideA>);
|
||||
static_assert(cute::is_same_v<typename KernelWithC::ElementB, typename KernelVoidC::ElementB>);
|
||||
static_assert(cute::is_same_v<typename KernelWithC::StrideB, typename KernelVoidC::StrideB>);
|
||||
static_assert(cute::is_same_v<typename KernelWithC::ElementAccumulator, typename KernelVoidC::ElementAccumulator>);
|
||||
static_assert(cute::is_same_v<typename KernelWithC::ClusterShape, typename KernelVoidC::ClusterShape>);
|
||||
|
||||
// Epilogue derived types
|
||||
static_assert(not cute::is_void_v<typename KernelWithC::ElementC>);
|
||||
static_assert( cute::is_void_v<typename KernelVoidC::ElementC>);
|
||||
static_assert(cute::is_same_v<typename KernelWithC::StrideC, typename KernelVoidC::StrideC>);
|
||||
static_assert(cute::is_same_v<typename KernelWithC::ElementD, typename KernelVoidC::ElementD>);
|
||||
static_assert(cute::is_same_v<typename KernelWithC::StrideD, typename KernelVoidC::StrideD>);
|
||||
|
||||
// TileID scheduler
|
||||
static_assert(cute::is_same_v<typename KernelWithC::TileScheduler, typename KernelVoidC::TileScheduler>);
|
||||
|
||||
static constexpr int SharedStorageSize = cute::max(KernelWithC::SharedStorageSize, KernelVoidC::SharedStorageSize);
|
||||
|
||||
static_assert(KernelWithC::MaxThreadsPerBlock == KernelVoidC::MaxThreadsPerBlock);
|
||||
|
||||
static_assert(KernelWithC::MinBlocksPerMultiprocessor == KernelVoidC::MinBlocksPerMultiprocessor);
|
||||
|
||||
using Arguments = typename KernelWithC::Arguments;
|
||||
|
||||
struct Params {
|
||||
typename KernelWithC::Params withC;
|
||||
typename KernelVoidC::Params voidC;
|
||||
|
||||
void const* ptr_C;
|
||||
decltype(FusionArguments{}.beta) beta;
|
||||
decltype(FusionArguments{}.beta_ptr) beta_ptr;
|
||||
decltype(FusionArguments{}.dBeta) dBeta;
|
||||
cutlass::KernelHardwareInfo hw_info{};
|
||||
};
|
||||
|
||||
static size_t
|
||||
get_workspace_size(Arguments const& args) {
|
||||
return KernelWithC::get_workspace_size(args);
|
||||
}
|
||||
|
||||
static cutlass::Status
|
||||
initialize_workspace(Arguments const& args, void* workspace = nullptr, cudaStream_t stream = nullptr, CudaHostAdapter* cuda_adapter = nullptr) {
|
||||
return KernelWithC::initialize_workspace(args, workspace, stream, cuda_adapter);
|
||||
}
|
||||
|
||||
static Params
|
||||
to_underlying_arguments(Arguments const& args, void* workspace) {
|
||||
return {
|
||||
KernelWithC::to_underlying_arguments(args, workspace),
|
||||
KernelVoidC::to_underlying_arguments(reinterpret_cast<typename KernelVoidC::Arguments const&>(args), workspace),
|
||||
args.epilogue.ptr_C,
|
||||
args.epilogue.thread.beta,
|
||||
args.epilogue.thread.beta_ptr,
|
||||
args.epilogue.thread.dBeta,
|
||||
args.hw_info
|
||||
};
|
||||
}
|
||||
|
||||
static dim3
|
||||
get_grid_shape(Params const& params) {
|
||||
return KernelWithC::get_grid_shape(params.withC);
|
||||
}
|
||||
|
||||
CUTLASS_DEVICE
|
||||
void
|
||||
operator()(Params const& params, char* smem_buf) {
|
||||
using namespace cute;
|
||||
|
||||
bool run_voidC = false;
|
||||
if (params.ptr_C == nullptr) {
|
||||
run_voidC = true;
|
||||
}
|
||||
else if (params.beta_ptr == nullptr) { // Host scalar beta
|
||||
run_voidC = params.beta == 0;
|
||||
}
|
||||
else if (get<0>(params.dBeta) == 0 && get<1>(params.dBeta) == 0) { // Device scalar beta
|
||||
auto L = get<3>(append<4>(params.withC.problem_shape, _1{}));
|
||||
if (get<2>(params.dBeta) == repeat_like(L, 0) || size(L) == 1) { // Non-batched
|
||||
run_voidC = *params.beta_ptr == 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (run_voidC) {
|
||||
return kernel_voidC(params.voidC, smem_buf);
|
||||
}
|
||||
else {
|
||||
return KernelWithC::operator()(params.withC, smem_buf);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
KernelVoidC kernel_voidC;
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace cutlass::conv::kernel
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -81,7 +81,6 @@ is_sm10x_f8f6f4_inputs() {
|
||||
|
||||
cute::is_same_v<ElementA, cute::float_e4m3_t> ||
|
||||
cute::is_same_v<ElementA, cute::float_e5m2_t>
|
||||
|
||||
|| cute::is_same_v<ElementA, cute::float_e3m2_t> ||
|
||||
cute::is_same_v<ElementA, cute::float_e2m3_t> ||
|
||||
cute::is_same_v<ElementA, cute::float_e2m1_t>
|
||||
@@ -95,7 +94,6 @@ is_sm10x_f8f6f4_inputs() {
|
||||
|
||||
cute::is_same_v<ElementB, cute::float_e4m3_t> ||
|
||||
cute::is_same_v<ElementB, cute::float_e5m2_t>
|
||||
|
||||
|| cute::is_same_v<ElementB, cute::float_e3m2_t> ||
|
||||
cute::is_same_v<ElementB, cute::float_e2m3_t> ||
|
||||
cute::is_same_v<ElementB, cute::float_e2m1_t>
|
||||
@@ -116,7 +114,6 @@ static constexpr bool
|
||||
is_sm10x_f8f6f4_element() {
|
||||
return (cute::is_same_v<Element, cute::float_e4m3_t>
|
||||
|| cute::is_same_v<Element, cute::float_e5m2_t>
|
||||
|
||||
|| cute::is_same_v<Element, cute::float_e3m2_t>
|
||||
|| cute::is_same_v<Element, cute::float_e2m3_t>
|
||||
|| cute::is_same_v<Element, cute::float_e2m1_t>
|
||||
@@ -129,7 +126,7 @@ is_sm10x_f8f6f4_element() {
|
||||
template <class ElementType>
|
||||
CUTLASS_HOST_DEVICE
|
||||
static constexpr bool
|
||||
is_sm10x_block_scale_mxf8f6f4_input() {
|
||||
is_sm10x_mxf8f6f4_input() {
|
||||
// ElementType must be F8, F6, or F4
|
||||
return ( cute::is_same_v<ElementType, cutlass::type_erased_dynamic_float8_t> ||
|
||||
cute::is_same_v<ElementType, cutlass::detail::type_erased_dynamic_float6_unpacksmem_t> ||
|
||||
@@ -144,7 +141,7 @@ is_sm10x_block_scale_mxf8f6f4_input() {
|
||||
template <class ElementType>
|
||||
CUTLASS_HOST_DEVICE
|
||||
static constexpr bool
|
||||
is_sm10x_block_scale_mxf4nvf4_input() {
|
||||
is_sm10x_mxf4nvf4_input() {
|
||||
// ElementType must be F4
|
||||
return ( cute::is_same_v<ElementType, cute::type_erased_dynamic_float4_t> ||
|
||||
cute::is_same_v<ElementType, cute::float_e2m1_t>
|
||||
@@ -153,12 +150,12 @@ is_sm10x_block_scale_mxf4nvf4_input() {
|
||||
|
||||
template <class ElementType, bool IsRuntimeDataType>
|
||||
struct sm10x_block_scale_runtime_input_t {
|
||||
static constexpr bool IsMxF8F6F4MmaInput = is_sm10x_block_scale_mxf8f6f4_input<ElementType>();
|
||||
static constexpr bool IsMxF4NvF4MmaInput = is_sm10x_block_scale_mxf4nvf4_input<ElementType>();
|
||||
static constexpr bool IsF8F6F4MmaInput = is_sm10x_mxf8f6f4_input<ElementType>();
|
||||
static constexpr bool IsF4MmaInput = is_sm10x_mxf4nvf4_input<ElementType>();
|
||||
|
||||
using Type = cute::conditional_t<IsRuntimeDataType && IsMxF8F6F4MmaInput,
|
||||
using Type = cute::conditional_t<IsRuntimeDataType && IsF8F6F4MmaInput,
|
||||
cute::UMMA::MXF8F6F4Format,
|
||||
cute::conditional_t<IsRuntimeDataType && IsMxF4NvF4MmaInput,
|
||||
cute::conditional_t<IsRuntimeDataType && IsF4MmaInput,
|
||||
cute::UMMA::MXF4Format,
|
||||
void*
|
||||
>
|
||||
|
||||
@@ -301,7 +301,7 @@ struct LayoutAwareConvertImpl<
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
// Specialization for E5M2 -> FP16 with [3120] value order
|
||||
template <>
|
||||
struct LayoutAwareConvertImpl<
|
||||
@@ -343,12 +343,12 @@ struct LayoutAwareConvertImpl<
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
*/
|
||||
// Specialization for INT8 -> BF16 with [3120] value order
|
||||
template <>
|
||||
struct LayoutAwareConvertImpl<
|
||||
cutlass::int8_t,
|
||||
cutlass::half_t,
|
||||
cutlass::bfloat16_t,
|
||||
cute::Layout<cute::Shape<_2,_2>, cute::Stride<_2,_1>>,
|
||||
cute::Layout<_4>
|
||||
> {
|
||||
@@ -363,9 +363,9 @@ struct LayoutAwareConvertImpl<
|
||||
>& dst) {
|
||||
|
||||
static_assert(cute::is_same_v<cutlass::int8_t, typename EngineIn::value_type> &&
|
||||
cute::is_same_v<cutlass::half_t, typename EngineOut::value_type>);
|
||||
cute::is_same_v<cutlass::bfloat16_t, typename EngineOut::value_type>);
|
||||
using SrcArray = cutlass::Array<cutlass::int8_t, 8>;
|
||||
using DstArray = cutlass::Array<cutlass::half_t, 8>;
|
||||
using DstArray = cutlass::Array<cutlass::bfloat16_t, 8>;
|
||||
using RegArray = cutlass::AlignedArray<uint32_t, 4, sizeof(DstArray)>;
|
||||
|
||||
auto&& src_reg = cute::recast<uint32_t>(src)(0);
|
||||
@@ -403,7 +403,7 @@ struct LayoutAwareConvertImpl<
|
||||
template <>
|
||||
struct LayoutAwareConvertImpl<
|
||||
cutlass::int8_t,
|
||||
cutlass::bfloat16_t,
|
||||
cutlass::half_t,
|
||||
cute::Layout<cute::Shape<_2,_2>, cute::Stride<_2,_1>>,
|
||||
cute::Layout<_4>
|
||||
> {
|
||||
@@ -418,9 +418,9 @@ struct LayoutAwareConvertImpl<
|
||||
>& dst) {
|
||||
|
||||
static_assert(cute::is_same_v<cutlass::int8_t, typename EngineIn::value_type> &&
|
||||
cute::is_same_v<cutlass::bfloat16_t, typename EngineOut::value_type>);
|
||||
cute::is_same_v<cutlass::half_t, typename EngineOut::value_type>);
|
||||
using SrcArray = cutlass::Array<cutlass::int8_t, 8>;
|
||||
using DstArray = cutlass::Array<cutlass::bfloat16_t, 8>;
|
||||
using DstArray = cutlass::Array<cutlass::half_t, 8>;
|
||||
using RegArray = cutlass::AlignedArray<uint32_t, 4, sizeof(DstArray)>;
|
||||
|
||||
auto&& src_reg = cute::recast<uint32_t>(src)(0);
|
||||
|
||||
@@ -506,7 +506,6 @@ sm100_get_smem_load_op() {
|
||||
template <class Schedule, class LayoutTag>
|
||||
constexpr auto
|
||||
sm100_get_gmem_load_op() {
|
||||
|
||||
if constexpr (detail::is_im2col_mode<LayoutTag>) {
|
||||
return SM90_TMA_LOAD_IM2COL{};
|
||||
}
|
||||
@@ -519,7 +518,6 @@ sm100_get_gmem_load_op() {
|
||||
template <class Schedule, class LayoutTag>
|
||||
constexpr auto
|
||||
sm100_get_gmem_store_op() {
|
||||
|
||||
if constexpr (detail::is_im2col_mode<LayoutTag>) {
|
||||
return SM90_TMA_STORE_IM2COL{};
|
||||
}
|
||||
|
||||
@@ -208,7 +208,6 @@ struct IsThreadEpilogueOpWithElementwiseArguments<
|
||||
ThreadEpilogueOp,
|
||||
cute::void_t<typename ThreadEpilogueOp::ElementwiseOp::Arguments>> : cute::true_type {};
|
||||
|
||||
|
||||
// Check if ActivationFn has 'Arguments' type defined
|
||||
template <class ActivationFn, class = void>
|
||||
struct sm100_act_has_arguments : cute::false_type {};
|
||||
@@ -499,7 +498,6 @@ public:
|
||||
using TensorMapStorage = typename EpilogueOp::SharedStorage;
|
||||
using PipelineStorage = typename LoadPipeline::SharedStorage;
|
||||
|
||||
// Planar complex kernels have two accumulator copies for the real and imaginary tensors.
|
||||
static constexpr int NumAccumulatorMtxs = Sm100EpilogueOpNumAccumulatorMtxs<EpilogueOp>::value;
|
||||
|
||||
template<class CtaTileMNK>
|
||||
|
||||
@@ -986,6 +986,314 @@ public:
|
||||
return cute::make_tuple(load_pipe_consumer_state, store_pipe_producer_state, acc_pipe_consumer_state);
|
||||
}
|
||||
|
||||
// API with Global Accumulator in registers for FastFP32 (emulated MMA) kernels.
|
||||
// The accumulator in TMEM periodically loaded into the registers so that the MMA can clear out the TMEM accumulator
|
||||
// values for better accuracy. This epilogue accepts the accumulator in registers and take TiledCopy for the
|
||||
// TMEM->Reg as a parameter to be used in partitioning GMEM tensors C and D.
|
||||
template<
|
||||
class ProblemShapeMNKL,
|
||||
class CtaTileMNK,
|
||||
class CtaCoordMNKL,
|
||||
class MmaTileMNK,
|
||||
class TiledMma,
|
||||
class AccEngine,
|
||||
class AccLayout,
|
||||
class TiledCopyT2R,
|
||||
class TensorMapD
|
||||
>
|
||||
CUTLASS_DEVICE auto
|
||||
store(
|
||||
LoadPipeline load_pipeline,
|
||||
LoadPipelineState load_pipe_consumer_state,
|
||||
StorePipeline store_pipeline,
|
||||
StorePipelineState store_pipe_producer_state,
|
||||
ProblemShapeMNKL problem_shape_mnkl,
|
||||
CtaTileMNK cta_tile_mnk,
|
||||
CtaCoordMNKL cta_coord_mnkl,
|
||||
MmaTileMNK mma_tile_mnk,
|
||||
TiledMma tiled_mma,
|
||||
cute::Tensor<AccEngine, AccLayout>& tTR_rAcc, // (T2R,T2R_M,T2R_N,EPI_M,EPI_N)
|
||||
TensorStorage& shared_tensors,
|
||||
TensorMapD store_tensormap,
|
||||
TiledCopyT2R tiled_t2r
|
||||
) {
|
||||
using namespace cute;
|
||||
using ElementAccumulator = typename AccEngine::value_type;
|
||||
using ElementCompute_ = typename epilogue::fusion::FusionCallbacksTraits<FusionCallbacks>::ElementCompute;
|
||||
using ElementCompute = cute::conditional_t<cute::is_void_v<ElementCompute_>,ElementAccumulator,ElementCompute_>;
|
||||
|
||||
static_assert(is_rmem<AccEngine>::value, "Accumulator must be Register resident.");
|
||||
static_assert(rank(AccLayout{}) == 5, "Accumulators must be copy-partitioned: (T2R,T2R_M,T2R_N,EPI_M,EPI_N)");
|
||||
static_assert(rank(ProblemShapeMNKL{}) == 4, "ProblemShapeMNKL must be rank 4");
|
||||
static_assert(rank(CtaCoordMNKL{}) == 4, "CoordMNKL must be rank 4");
|
||||
|
||||
// Indexing variables
|
||||
auto [M, N, K, L] = problem_shape_mnkl;
|
||||
auto [m_coord, n_coord, k_coord, l_coord] = cta_coord_mnkl;
|
||||
int thread_idx = threadIdx.x % ThreadCount;
|
||||
int warp_idx = thread_idx / NumThreadsPerWarp;
|
||||
[[maybe_unused]] int lane_idx = thread_idx % NumThreadsPerWarp;
|
||||
|
||||
auto coord_shape = append<3>(make_shape(m_coord, n_coord),Int<0>{});
|
||||
|
||||
// Represent the full output tensor, slice to get the tile this CTA is responsible for
|
||||
Tensor mD_mn = params.tma_store_d.get_tma_tensor(append<3>(make_shape(M,N),Int<1>{})); // (M,N,L)
|
||||
Tensor mD = coalesce(mD_mn, take<0,2>(cta_tile_mnk));
|
||||
Tensor gD = local_tile(mD, take<0,2>(cta_tile_mnk), coord_shape); // (CTA_M,CTA_N)
|
||||
|
||||
// Apply epilogue subtiling
|
||||
Tensor gD_epi = flat_divide( gD, EpilogueTile{}); // (EPI_TILE_M,EPI_TILE_N,EPI_M,EPI_N)
|
||||
|
||||
// Construct the corresponding pipelined smem tensors
|
||||
auto ptr_sC = shared_tensors.collective.smem_C.begin();
|
||||
auto ptr_sD = shared_tensors.collective.smem_D.begin();
|
||||
Tensor sC_epi = cute::as_position_independent_swizzle_tensor(
|
||||
make_tensor(make_smem_ptr(ptr_sC), SmemLayoutC{})); // (EPI_TILE_M,EPI_TILE_N,PIPE_C)
|
||||
Tensor sD_epi = cute::as_position_independent_swizzle_tensor(
|
||||
make_tensor(make_smem_ptr(ptr_sD), SmemLayoutD{})); // (EPI_TILE_M,EPI_TILE_N,PIPE_D)
|
||||
|
||||
// (t)hread-partition for (t)mem to (r)egister copy (tTR_)
|
||||
ThrCopy thread_t2r = tiled_t2r.get_slice(thread_idx);
|
||||
Tensor tTR_sD = thread_t2r.partition_D(sD_epi(_,_,_0{})); // (T2R,T2R_M,T2R_N)
|
||||
|
||||
// Allocate D and accumulator registers
|
||||
Tensor tTR_rD = make_tensor<SmemElementD>(shape(tTR_sD)); // (T2R,T2R_M,T2R_N)
|
||||
|
||||
// Vectorized fragment view
|
||||
constexpr int FragmentSize = DispatchPolicy::FragmentSize;
|
||||
Tensor tTR_rD_frg = recast<Array<SmemElementD, FragmentSize>>(coalesce(tTR_rD)); // (EPI_V)
|
||||
|
||||
// (t)hread-partition for (s)mem to (r)egister copy (tSR_)
|
||||
TiledCopy tiled_s2r = make_tiled_copy_D(Copy_Atom<CopyOpS2R, SmemElementC>{}, tiled_t2r);
|
||||
ThrCopy thread_s2r = tiled_s2r.get_slice(thread_idx);
|
||||
Tensor tSR_sC = thread_s2r.partition_S(sC_epi); // (S2R,S2R_M,S2R_N,PIPE_C)
|
||||
Layout tSR_rC_layout = thread_s2r.retile_D(tTR_rD).layout(); // (S2R,S2R_M,S2R_N)
|
||||
|
||||
// Allocate C registers
|
||||
// If C smem load is a non-vectorized dst(i) = src(i) then we can allocate C registers directly in the compute type
|
||||
// to eliminate some redundant pack+unpack instruction sequences for sub-word types
|
||||
constexpr bool IsDirectS2R = cute::is_same_v<CopyOpS2R, AutoVectorizingCopyWithAssumedAlignment<128>>
|
||||
&& decltype(max_common_vector(tSR_rC_layout, tSR_sC.layout()))::value <= 1;
|
||||
using RegisterElementC = cute::conditional_t<IsDirectS2R, ElementCompute, SmemElementC>;
|
||||
Tensor tTR_rC = make_tensor<RegisterElementC>(shape(tTR_sD)); // (T2R,T2R_M,T2R_N)
|
||||
Tensor tSR_rC = thread_s2r.retile_D(tTR_rC); // (S2R,S2R_M,S2R_N)
|
||||
|
||||
// (t)hread-partition for (r)egister to (s)mem copy (tRS_)
|
||||
TiledCopy tiled_r2s = make_tiled_copy_D(Copy_Atom<CopyOpR2S,SmemElementD>{}, tiled_t2r);
|
||||
ThrCopy thread_r2s = tiled_r2s.get_slice(thread_idx);
|
||||
Tensor tRS_rD = thread_r2s.retile_S(tTR_rD); // (R2S,R2S_M,R2S_N)
|
||||
Tensor tRS_sD = thread_r2s.partition_D(sD_epi); // (R2S,R2S_M,R2S_N,PIPE_D)
|
||||
|
||||
// thread(b)lock-partition for (s)mem to (g)mem copy (bSG_)
|
||||
ThrCopy thrblk_s2g = params.tma_store_d.get_slice(Int<0>{});
|
||||
Tensor bSG_sD = thrblk_s2g.partition_S(sD_epi); // (S2G,S2G_M,S2G_N,PIPE_D)
|
||||
Tensor bSG_gD = thrblk_s2g.partition_D(gD_epi); // (S2G,S2G_M,S2G_N,EPI_M,EPI_N)
|
||||
|
||||
// OOB predication for tile quantization "residue"
|
||||
// Absolute coordinate tensors (dynamic)
|
||||
Tensor mD_crd = make_identity_tensor(make_shape(M,N)); // (M,N)
|
||||
Tensor cD_mn = local_tile(mD_crd, take<0,2>(cta_tile_mnk), make_coord(m_coord, n_coord)); // (CTA_M,CTA_N)
|
||||
Tensor tTR_cD_mn = thread_t2r.partition_D(flat_divide(cD_mn, EpilogueTile{})); // (T2R,T2R_M,T2R_N,EPI_M,EPI_N)
|
||||
// Relative coordinate tensors (static)
|
||||
Tensor cD = make_counting_tensor(cD_mn.layout()); // (CTA_M,CTA_N)
|
||||
Tensor tTR_cD = make_counting_tensor(tTR_cD_mn.layout()); // (T2R,T2R_M,T2R_N,EPI_M,EPI_N)
|
||||
// Subtract the global "bottom right" corner from the local "top left" corner to get the max relative coordinate
|
||||
auto residue_cD = make_coord(M,N) - cD_mn(_0{}); // (m,n)
|
||||
auto residue_tTR_cD = make_coord(M,N) - tTR_cD_mn(_0{}); // (m,n)
|
||||
|
||||
// Get the fusion callbacks for the consumer store warps
|
||||
constexpr bool RefSrc = false; // Register tensors reference T2R copy dst layout
|
||||
auto cst_args = cutlass::epilogue::fusion::detail::ConsumerStoreArgs{
|
||||
problem_shape_mnkl,
|
||||
cta_tile_mnk,
|
||||
cta_coord_mnkl,
|
||||
tiled_mma,
|
||||
EpilogueTile{},
|
||||
tiled_t2r,
|
||||
cD,
|
||||
residue_cD,
|
||||
tTR_cD,
|
||||
residue_tTR_cD,
|
||||
tTR_rC,
|
||||
thread_idx
|
||||
};
|
||||
|
||||
auto cst_callbacks = fusion_callbacks.template get_consumer_store_callbacks<RefSrc>(cst_args);
|
||||
bool is_producer_load_needed = fusion_callbacks.is_producer_load_needed();
|
||||
bool is_C_load_needed = is_source_supported && fusion_callbacks.is_C_load_needed();
|
||||
|
||||
// Thread synchronizer for previously issued waits or fences
|
||||
// to ensure visibility of smem reads/writes to threads or TMA unit
|
||||
auto synchronize = [] () { cutlass::arch::NamedBarrier::sync(ThreadCount, cutlass::arch::ReservedNamedBarriers::EpilogueBarrier); };
|
||||
|
||||
// Predication for TMA store (one warp issues TMA store)
|
||||
bool issue_tma_store = warp_idx == 0;
|
||||
|
||||
// In the reuse smem configuration we have StagesC smem buffers and at most StagesD committed TMA stores in flight.
|
||||
// The TMA store pipeline producer acquire returns when at most StagesD-1 committed stores are in-flight, so we can
|
||||
// only guarantee store completion after StagesD iterations, then we can begin issuing releases on the smem buffer locks.
|
||||
// store_pipe_producer_state tracks the acquire and load_pipe_consumer_state tracks the release, in circular buffer fashion.
|
||||
// If TMA store supported async transaction mbarriers we would not need this synchronous release behavior.
|
||||
LoadPipelineState load_wait_state = load_pipe_consumer_state;
|
||||
if constexpr (ReuseSmemC) {
|
||||
load_wait_state = store_pipe_producer_state;
|
||||
load_wait_state.phase_ ^= 1;
|
||||
}
|
||||
|
||||
// We can delay issue of TMA store by one iteration to achieve better interleaving of non-TMA instructions
|
||||
// Sync requirements of smem reuse may preclude this optimization
|
||||
// Delayed stores cause delayed stage releases which causes deadlock when StagesC == StagesD
|
||||
int epi_m_prev = 0, epi_n_prev = 0;
|
||||
static_assert(not (DelayTmaStore and ReuseSmemC and StagesC <= StagesD), "This TMA epilogue configuration will deadlock");
|
||||
|
||||
// The TMA store sequence for one subtile iteration
|
||||
auto tma_store_fn = [&] (int epi_m, int epi_n) {
|
||||
// Write the tile from smem to gmem with TMA
|
||||
cutlass::arch::fence_view_async_shared(); // ensure smem writes are visible to TMA
|
||||
synchronize(); // ensure all threads have issued their async fence
|
||||
if (issue_tma_store) {
|
||||
copy(params.tma_store_d.with(store_tensormap), bSG_sD(_,_,_,store_pipe_producer_state.index()), bSG_gD(_,_,_,epi_m,epi_n));
|
||||
}
|
||||
|
||||
// Post async fence, pre TMA commit callback entry point
|
||||
cst_callbacks.tma_store(epi_m, epi_n, store_pipe_producer_state.count(), issue_tma_store);
|
||||
|
||||
// Commit the TMA stores for this stage
|
||||
if (issue_tma_store) {
|
||||
store_pipeline.producer_commit(store_pipe_producer_state);
|
||||
}
|
||||
++store_pipe_producer_state;
|
||||
|
||||
// Wait for the next smem buffer to be available
|
||||
if (issue_tma_store) {
|
||||
store_pipeline.producer_acquire(store_pipe_producer_state);
|
||||
}
|
||||
synchronize();
|
||||
|
||||
if constexpr (ReuseSmemC) {
|
||||
// producer_acquire returns when at most StagesD-1 committed stores are pending
|
||||
bool store_finished = store_pipe_producer_state.count() > StorePipeline::UnacquiredStages;
|
||||
// Let dma warp know earliest smem buffer is consumed and empty after StagesD producer commits
|
||||
if (store_finished) {
|
||||
if (is_producer_load_needed) {
|
||||
load_pipeline.consumer_release(load_pipe_consumer_state);
|
||||
}
|
||||
++load_pipe_consumer_state;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// BEGIN EPILOGUE
|
||||
//
|
||||
|
||||
// Begin the wait for the producer load results
|
||||
ConsumerToken load_wait_token{BarrierStatus::WaitDone};
|
||||
if (is_producer_load_needed) {
|
||||
load_wait_token = load_pipeline.consumer_try_wait(load_wait_state);
|
||||
}
|
||||
|
||||
cst_callbacks.begin();
|
||||
if (cst_callbacks.begin_sync_needed()) {
|
||||
synchronize();
|
||||
}
|
||||
|
||||
// For each epilogue subtile within the CTA tile
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int iter_n = 0; iter_n < size<3>(gD_epi); ++iter_n) {
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int iter_m = 0; iter_m < size<2>(gD_epi); ++iter_m) {
|
||||
int epi_m = iter_m, epi_n = iter_n;
|
||||
bool is_first_iteration = iter_m == 0 && iter_n == 0;
|
||||
bool is_last_iteration = iter_m == size<2>(gD_epi)-1 && iter_n == size<3>(gD_epi)-1;
|
||||
|
||||
cst_callbacks.begin_loop(epi_m, epi_n);
|
||||
|
||||
if (is_producer_load_needed) {
|
||||
// Wait for the producer load to fill smem
|
||||
load_pipeline.consumer_wait(load_wait_state, load_wait_token);
|
||||
|
||||
if (is_C_load_needed) {
|
||||
// Copy source tile from smem to register
|
||||
copy(tiled_s2r, tSR_sC(_,_,_,load_wait_state.index()), tSR_rC);
|
||||
// Ensure smem loads are complete before reusing smem for mixed types/layouts
|
||||
if constexpr (ReuseSmemC && not (SmemLayoutC{} == SmemLayoutD{})) {
|
||||
synchronize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// First loop fusion callback entry point
|
||||
cst_callbacks.previsit(epi_m, epi_n, load_wait_state.count(), is_producer_load_needed);
|
||||
|
||||
if (is_producer_load_needed) {
|
||||
// Let producer load warp know smem buffers are consumed and empty
|
||||
if constexpr (not ReuseSmemC) {
|
||||
cutlass::arch::fence_view_async_shared();
|
||||
load_pipeline.consumer_release(load_pipe_consumer_state);
|
||||
++load_pipe_consumer_state;
|
||||
}
|
||||
++load_wait_state;
|
||||
}
|
||||
|
||||
bool issue_smem_store = true;
|
||||
Tensor tTR_rAcc_epi_tile = tTR_rAcc(_,_,_,epi_m,epi_n);
|
||||
Tensor tTR_rAcc_frg = recast<Array<ElementAccumulator, FragmentSize>>(coalesce(tTR_rAcc_epi_tile)); // (EPI_V)
|
||||
|
||||
// Vectorized fragment loop with visitor callback entry point
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int epi_v = 0; epi_v < size(tTR_rD_frg); ++epi_v) {
|
||||
tTR_rD_frg(epi_v) = cst_callbacks.visit(tTR_rAcc_frg(epi_v), epi_v, epi_m, epi_n);
|
||||
}
|
||||
|
||||
// The latest we can delay the TMA store is right before the smem store of the next iteration
|
||||
// since the current TMA store needs to be committed before we can acquire the next smem buffer
|
||||
if constexpr (DelayTmaStore) {
|
||||
// Issue TMA stores for the previous subtile
|
||||
if (not is_first_iteration) {
|
||||
tma_store_fn(epi_m_prev, epi_n_prev);
|
||||
}
|
||||
epi_m_prev = epi_m;
|
||||
epi_n_prev = epi_n;
|
||||
}
|
||||
|
||||
// Smem reduction callback entry point using current store buffer for workspace
|
||||
Tensor reduction_buffer = make_tensor(raw_pointer_cast(sD_epi(_,_,store_pipe_producer_state.index()).data()),
|
||||
make_layout(stride<2>(get_nonswizzle_portion(SmemLayoutD{})), _1{}));
|
||||
cst_callbacks.reduce(reduction_buffer, synchronize, epi_m, epi_n, is_last_iteration, tTR_rD_frg);
|
||||
|
||||
// Copy output tile from register to smem
|
||||
if (issue_smem_store) {
|
||||
copy(tiled_r2s, tRS_rD, tRS_sD(_,_,_,store_pipe_producer_state.index()));
|
||||
}
|
||||
|
||||
// Post reduction, pre TMA store callback entry point
|
||||
cst_callbacks.postreduce(epi_m, epi_n, store_pipe_producer_state.count(), issue_smem_store);
|
||||
|
||||
if constexpr (not DelayTmaStore) {
|
||||
// Issue TMA stores for this subtile
|
||||
tma_store_fn(epi_m, epi_n);
|
||||
}
|
||||
|
||||
cst_callbacks.end_loop(epi_m, epi_n);
|
||||
|
||||
if (is_producer_load_needed) {
|
||||
// Begin the wait for the next subtile producer load
|
||||
load_wait_token = load_pipeline.consumer_try_wait(load_wait_state, is_last_iteration);
|
||||
}
|
||||
} // for epi_m
|
||||
} // for epi_n
|
||||
|
||||
if constexpr (DelayTmaStore) {
|
||||
// Issue TMA stores for the last subtile
|
||||
tma_store_fn(epi_m_prev, epi_n_prev);
|
||||
}
|
||||
|
||||
cst_callbacks.end();
|
||||
|
||||
return cute::make_tuple(load_pipe_consumer_state, store_pipe_producer_state);
|
||||
}
|
||||
|
||||
template <class CtaTileMNK>
|
||||
CUTLASS_DEVICE void
|
||||
store_tail(
|
||||
|
||||
@@ -82,11 +82,9 @@ struct FusionOperation {
|
||||
using ElementAmax = void;
|
||||
static constexpr bool IsAbsMaxSupported = false;
|
||||
|
||||
|
||||
using ElementBlockScaleFactor = void;
|
||||
static constexpr int SFVecSize = 0;
|
||||
static constexpr bool IsBlockScaleSupported = false; // Umbrella variable to check BlockScaling support in the epilogues
|
||||
|
||||
using GmemLayoutTagScalefactor = void;
|
||||
};
|
||||
|
||||
@@ -484,7 +482,6 @@ struct LinCombDeEltActDePerRowBias
|
||||
static constexpr bool IsDePerRowBiasSupported = true;
|
||||
};
|
||||
|
||||
|
||||
template<
|
||||
int SFVecSize_,
|
||||
class ElementOutput_,
|
||||
|
||||
@@ -417,7 +417,6 @@ struct FusionCallbacks<
|
||||
using Impl::Impl;
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// D = alpha * acc + beta * C + per-row bias
|
||||
|
||||
@@ -747,7 +747,6 @@ private:
|
||||
src_sign_bit, dst_exponent, dst_mantissa);
|
||||
#endif
|
||||
|
||||
// TODO potential narrowing here
|
||||
if (dst_encoding.significand_hidden_bits(dst_mantissa) > 0b1) {
|
||||
|
||||
// Significant became larger than 01.X...X. Divide significand by 2 and multiply exp by 2
|
||||
@@ -848,16 +847,13 @@ CUTLASS_CONSTEXPR_IF_CXX17 auto fp_encoding_selector() {
|
||||
return cutlass::detail::FpBitRepresentation<uint32_t, 32, 8, 23, cutlass::detail::NanInfEncoding::IEEE_754>{};
|
||||
}
|
||||
else if CUTLASS_CONSTEXPR_IF_CXX17 (FpExMyCode == FpEncoding::E5M2) { // FP8
|
||||
// TODO: Not tested. Will be done in another MR
|
||||
return cutlass::detail::FpBitRepresentation<uint8_t, 8, 5, 2, cutlass::detail::NanInfEncoding::IEEE_754>{};
|
||||
}
|
||||
else if CUTLASS_CONSTEXPR_IF_CXX17 (FpExMyCode == FpEncoding::E4M3) { // FP8
|
||||
// TODO: Not tested. Will be done in another MR
|
||||
return cutlass::detail::FpBitRepresentation<uint8_t, 8, 4, 3, cutlass::detail::NanInfEncoding::CANONICAL_ONLY>{};
|
||||
}
|
||||
|
||||
else if CUTLASS_CONSTEXPR_IF_CXX17 (FpExMyCode == FpEncoding::UE4M3) { // FP8
|
||||
// TODO: Not tested. Will be done in another MR
|
||||
return cutlass::detail::FpBitRepresentation<uint8_t, 8, 4, 3, cutlass::detail::NanInfEncoding::CANONICAL_ONLY, false>{};
|
||||
}
|
||||
|
||||
@@ -993,20 +989,16 @@ struct float_exmy_base
|
||||
return f;
|
||||
}
|
||||
|
||||
// TODO: Add rounding parameter with a reasonable default
|
||||
CUTLASS_HOST_DEVICE
|
||||
float_exmy_base convert_from_float(float const &flt) const {
|
||||
// TODO: If we have a cvt instruction specialize in the children structs
|
||||
FP32BitRepresentation::Storage fp32_bits = FP32BitRepresentation::to_bits(flt);
|
||||
float_exmy_base float_exmy;
|
||||
float_exmy.storage = BitRepresentation::convert_from(fp32_bits, FP32BitRepresentation{});
|
||||
return float_exmy;
|
||||
}
|
||||
|
||||
// TODO: Add rounding parameter with a reasonable default
|
||||
CUTLASS_HOST_DEVICE
|
||||
float convert_to_float(float_exmy_base<T, Derived> const &x) const {
|
||||
// TODO: If we have a cvt instruction specialize in the children structs
|
||||
FP32BitRepresentation::Storage fp32_bits;
|
||||
fp32_bits = BitRepresentation::convert_to(x.storage, FP32BitRepresentation{});
|
||||
return detail::copy_bits<FP32BitRepresentation::Storage, float>(fp32_bits);
|
||||
|
||||
@@ -39,8 +39,13 @@
|
||||
#include <type_traits>
|
||||
#endif
|
||||
#if !defined(__QNX__)
|
||||
#include <cuda/std/version>
|
||||
#if defined(_MSC_VER) && defined(CCCL_VERSION) && CCCL_VERSION >= 2008000
|
||||
#include <cuda/std/__utility/swap.h>
|
||||
#else
|
||||
#include <cuda/std/utility>
|
||||
#endif
|
||||
#endif
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/array.h"
|
||||
#include "cutlass/uint128.h"
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
#pragma once
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
#include "cutlass/gemm/collective/builders/sm100_common.inl"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cutlass::gemm::collective {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<
|
||||
int CapacityBytes,
|
||||
class CtaTileShape_MNK,
|
||||
class TiledMma,
|
||||
class KernelScheduleType,
|
||||
UMMA::Major UmmaMajorA,
|
||||
int ComplexComponent = 1,
|
||||
int NumComputeMtxs = 3,
|
||||
int carveout_bytes
|
||||
>
|
||||
constexpr cute::tuple<int, int, int>
|
||||
sm100_compute_stage_count_or_override_fast_fp32(StageCountAutoCarveout<carveout_bytes> stage_count) {
|
||||
constexpr int CtaM = get<0>(CtaTileShape_MNK{});
|
||||
constexpr int CtaN = get<1>(CtaTileShape_MNK{});
|
||||
static_assert(CtaN <= 128, "Can't support CtaN>128 tiles");
|
||||
constexpr int CtaK = get<2>(CtaTileShape_MNK{});
|
||||
using AtomThrID = typename TiledMma::AtomThrID;
|
||||
// Detect 2x2 TMEM layout
|
||||
constexpr int TmemAccWordsPerDP = (CtaM == 64 && size(AtomThrID{}) == 2) ? CtaN/2 : CtaN;
|
||||
constexpr int TmemAWordsPerDP = ComplexComponent * NumComputeMtxs * CtaK / 2;
|
||||
constexpr bool IsAComputeinTmem = UmmaMajorA == cute::UMMA::Major::K && !cute::is_base_of_v<KernelTmaWarpSpecializedFastFP32SmemSm100, KernelScheduleType>;
|
||||
constexpr bool IsAComputeinSmem = !IsAComputeinTmem;
|
||||
constexpr int AccumulatorStageCount = (IsAComputeinTmem) ? (((TmemAccWordsPerDP * ComplexComponent == 128) ? 2 : 3) * ComplexComponent) : (512 / TmemAccWordsPerDP);
|
||||
|
||||
constexpr int SmemCapacityAfterMma2AccumCarveout = CapacityBytes - (carveout_bytes + AccumulatorStageCount * 32);
|
||||
|
||||
constexpr int TmemInAStageCount_Potential = (IsAComputeinTmem) ? (512 - AccumulatorStageCount * TmemAccWordsPerDP) / TmemAWordsPerDP : 10000;
|
||||
|
||||
constexpr auto load2transform_pipeline_bytes = sizeof(typename cutlass::PipelineTmaTransformAsync<1>::SharedStorage);
|
||||
constexpr auto a_bits = cute::sizeof_bits_v<float> * ComplexComponent;
|
||||
constexpr auto b_bits = cute::sizeof_bits_v<float> * ComplexComponent;
|
||||
constexpr int ab_stage_bytes =
|
||||
cutlass::bits_to_bytes(a_bits * size<0>(CtaTileShape_MNK{}) * size<2>(CtaTileShape_MNK{})) +
|
||||
cutlass::bits_to_bytes(b_bits * size<1>(CtaTileShape_MNK{}) / size(AtomThrID{}) * size<2>(CtaTileShape_MNK{})) +
|
||||
static_cast<int>(load2transform_pipeline_bytes);
|
||||
|
||||
constexpr auto transform2mma_pipeline_bytes = sizeof(typename cutlass::PipelineUmmaConsumerAsync<1>::SharedStorage);
|
||||
constexpr auto a_compute_bits = cute::sizeof_bits_v<cutlass::bfloat16_t> * ComplexComponent;
|
||||
constexpr auto b_compute_bits = cute::sizeof_bits_v<cutlass::bfloat16_t> * ComplexComponent * ComplexComponent;
|
||||
constexpr int ab_compute_stage_bytes =
|
||||
cutlass::bits_to_bytes(NumComputeMtxs * a_compute_bits * int(IsAComputeinSmem) * size<0>(CtaTileShape_MNK{}) * size<2>(CtaTileShape_MNK{})) + // If ACompute is in TMEM, Acompute buffer has 0 bytes.
|
||||
cutlass::bits_to_bytes(NumComputeMtxs * b_compute_bits * size<1>(CtaTileShape_MNK{}) / size(AtomThrID{}) * size<2>(CtaTileShape_MNK{})) +
|
||||
static_cast<int>(transform2mma_pipeline_bytes);
|
||||
|
||||
constexpr int ABComputeStageCount_Potential = SmemCapacityAfterMma2AccumCarveout / (ab_stage_bytes + ab_compute_stage_bytes);
|
||||
// The number of SMEM buffers for A, B. ACompute (if in SMEM), BCompute should be at least Transform2MmaStageCount
|
||||
constexpr int Transform2MmaStageCount = std::min(TmemInAStageCount_Potential, ABComputeStageCount_Potential);
|
||||
|
||||
constexpr int SmemCapacityAfterABComputeCarveout = SmemCapacityAfterMma2AccumCarveout - (Transform2MmaStageCount * ab_compute_stage_bytes);
|
||||
// Can we boost the number of buffers for A and B?
|
||||
constexpr int Load2TransformStageCount = SmemCapacityAfterABComputeCarveout / ab_stage_bytes;
|
||||
|
||||
static_assert(Load2TransformStageCount >= 2 && Transform2MmaStageCount >= 2 && AccumulatorStageCount >= 2, "Not enough SMEM or TMEM capacity for selected tile size");
|
||||
return cute::make_tuple(Load2TransformStageCount, Transform2MmaStageCount, AccumulatorStageCount);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
// FastFP (9xBF16) MMA kernels builder
|
||||
template <
|
||||
class GmemLayoutATag,
|
||||
int AlignmentA,
|
||||
class GmemLayoutBTag,
|
||||
int AlignmentB,
|
||||
class ElementAccumulator,
|
||||
class TileShape_MNK, // The Cluster-level TileShape
|
||||
class ClusterShape_MNK,
|
||||
class StageCountType,
|
||||
class KernelScheduleType
|
||||
>
|
||||
struct CollectiveBuilder<
|
||||
arch::Sm100,
|
||||
arch::OpClassTensorOp,
|
||||
float, // ElementA
|
||||
GmemLayoutATag, // LayoutA
|
||||
AlignmentA,
|
||||
float, // ElementB
|
||||
GmemLayoutBTag, // LayoutB
|
||||
AlignmentB,
|
||||
ElementAccumulator,
|
||||
TileShape_MNK, // (MmaAtomShapeM, MmaAtomShapeN, TileK)
|
||||
ClusterShape_MNK, // Static cluster shape or dynamic (int, int, int)
|
||||
StageCountType,
|
||||
KernelScheduleType,
|
||||
cute::enable_if_t<
|
||||
(not cute::is_tuple<GmemLayoutATag>::value && not cute::is_tuple<GmemLayoutBTag>::value) &&
|
||||
(cute::is_base_of_v<KernelScheduleSm100FastFP32Gemm, KernelScheduleType>) &&
|
||||
((sizeof(float) * AlignmentA) % detail::tma_alignment_bytes == 0) &&
|
||||
((sizeof(float) * AlignmentB) % detail::tma_alignment_bytes == 0)>>
|
||||
{
|
||||
static constexpr cute::UMMA::Major UmmaMajorA = cutlass::gemm::collective::detail::tag_to_umma_major_A<GmemLayoutATag>();
|
||||
static constexpr cute::UMMA::Major UmmaMajorB = cutlass::gemm::collective::detail::tag_to_umma_major_B<GmemLayoutBTag>();
|
||||
|
||||
using ElementA = float;
|
||||
using ElementB = float;
|
||||
using ElementAMma = cutlass::bfloat16_t;
|
||||
using ElementBMma = cutlass::bfloat16_t;
|
||||
static constexpr int ScalingFactor = 8;
|
||||
|
||||
using TiledMma = decltype(detail::sm100_make_trivial_fastFP32_tiled_mma<ElementAMma, ElementBMma, ElementAccumulator, TileShape_MNK, ClusterShape_MNK, UmmaMajorA, UmmaMajorB, ScalingFactor, KernelScheduleType>());
|
||||
using AtomThrID = typename TiledMma::AtomThrID;
|
||||
using AtomThrShapeMNK = Shape<decltype(shape<0>(typename TiledMma::ThrLayoutVMNK{})), _1, _1>;
|
||||
using CtaTileShape_MNK = decltype(shape_div(TileShape_MNK{}, AtomThrShapeMNK{}));
|
||||
|
||||
// ((MMA_TILE_M,MMA_TILE_K), MMA_M, MMA_K)
|
||||
using MmaShapeA_MK = decltype(partition_shape_A(TiledMma{}, make_shape(cute::size<0>(TileShape_MNK{}),
|
||||
cute::size<2>(TileShape_MNK{}))));
|
||||
// ((MMA_TILE_N,MMA_TILE_K), MMA_N, MMA_K)
|
||||
using MmaShapeB_NK = decltype(partition_shape_B(TiledMma{}, make_shape(cute::size<1>(TileShape_MNK{}),
|
||||
cute::size<2>(TileShape_MNK{}))));
|
||||
|
||||
using BlockTileA_M = decltype(cute::size<0,0>(MmaShapeA_MK{}) * cute::size<1>(MmaShapeA_MK{}));
|
||||
using BlockTileA_K = decltype(cute::size<0,1>(MmaShapeA_MK{}) * cute::size<2>(MmaShapeA_MK{}));
|
||||
|
||||
using SmemLayoutAtomA = decltype(cutlass::gemm::collective::detail::sm100_smem_selector<UmmaMajorA, ElementA,
|
||||
BlockTileA_M, BlockTileA_K>());
|
||||
// Take 3 compute buffers into account for swizzle selection
|
||||
using SmemLayoutAtomACompute = decltype(cutlass::gemm::collective::detail::sm100_smem_selector<UmmaMajorA, ElementAMma,
|
||||
BlockTileA_M, BlockTileA_K>());
|
||||
|
||||
// Input transform kernel can not use TMA 2SM instructions.
|
||||
using GmemTiledCopyA = decltype(detail::sm90_cluster_shape_to_tma_atom(cute::size<1>(ClusterShape_MNK{})));
|
||||
using SmemLayoutAtomPairA = cutlass::gemm::collective::detail::CollectiveMmaEmulatedLayoutAtomType<
|
||||
SmemLayoutAtomA, SmemLayoutAtomACompute>;
|
||||
|
||||
static constexpr int MMA_M = cute::size<0,0>(MmaShapeA_MK{});
|
||||
using CopyAtomPairA = cutlass::gemm::collective::detail::CollectiveMmaEmulatedCopyType<
|
||||
Copy_Atom<AutoVectorizingCopyWithAssumedAlignment<128>, ElementA>,
|
||||
cute::conditional_t<(UmmaMajorA == cute::UMMA::Major::K && !cute::is_base_of_v<KernelTmaWarpSpecializedFastFP32SmemSm100, KernelScheduleType>),
|
||||
cute::conditional_t<(MMA_M == 64 && size(AtomThrID{}) == 1), SM100_TMEM_STORE_16dp256b1x, SM100_TMEM_STORE_32dp32b8x>, // TS Implementation
|
||||
Copy_Atom<AutoVectorizingCopyWithAssumedAlignment<128>, ElementA>> // SS Implementation
|
||||
>;
|
||||
|
||||
using BlockTileB_N = decltype(cute::size<0,0>(MmaShapeB_NK{}) * cute::size<1>(MmaShapeB_NK{}));
|
||||
using BlockTileB_K = decltype(cute::size<0,1>(MmaShapeB_NK{}) * cute::size<2>(MmaShapeB_NK{}));
|
||||
|
||||
// Input transform kernel can not use TMA 2SM instructions.
|
||||
using GmemTiledCopyB = decltype(detail::sm90_cluster_shape_to_tma_atom(cute::size<0>(ClusterShape_MNK{})));
|
||||
|
||||
using SmemLayoutAtomB = decltype(cutlass::gemm::collective::detail::sm100_smem_selector<UmmaMajorB, ElementB,
|
||||
BlockTileB_N, BlockTileB_K>());
|
||||
// Take 3 compute buffers into account for swizzle selection
|
||||
using SmemLayoutAtomBCompute = decltype(cutlass::gemm::collective::detail::sm100_smem_selector<UmmaMajorB, ElementBMma,
|
||||
BlockTileB_N, BlockTileB_K>());
|
||||
|
||||
using SmemLayoutAtomPairB = cutlass::gemm::collective::detail::CollectiveMmaEmulatedLayoutAtomType<
|
||||
SmemLayoutAtomB, SmemLayoutAtomBCompute>;
|
||||
using CopyAtomPairB = cutlass::gemm::collective::detail::CollectiveMmaEmulatedCopyType<
|
||||
Copy_Atom<AutoVectorizingCopyWithAssumedAlignment<128>, ElementB>,
|
||||
Copy_Atom<AutoVectorizingCopyWithAssumedAlignment<128>, ElementBMma>
|
||||
>;
|
||||
|
||||
// SmemCarveout
|
||||
static constexpr int NumBandsToCompute = 5;
|
||||
static constexpr int AccPromotionInterval = 1;
|
||||
static constexpr int SchedulerPipelineStageCount = 3;
|
||||
static constexpr bool IsArrayOfPointersGemm = (cute::is_base_of_v<KernelScheduleSm100PtrArrayFastFP32Gemm, KernelScheduleType>);
|
||||
|
||||
// CLCPipeline = PipelineCLCFetchAsync
|
||||
static constexpr auto CLCPipelineStorage = sizeof(typename cutlass::PipelineCLCFetchAsync<SchedulerPipelineStageCount, ClusterShape_MNK>::SharedStorage);
|
||||
// CLC (scheduler) response
|
||||
static constexpr auto CLCResponseStorage = SchedulerPipelineStageCount * detail::CLCResponseSize;
|
||||
// CLC Throttle pipeline storage
|
||||
static constexpr auto CLCThrottlePipelineStorage = sizeof(typename cutlass::PipelineAsync<SchedulerPipelineStageCount>::SharedStorage);
|
||||
// Tmem dealloc
|
||||
static constexpr auto TmemDeallocStorage = sizeof(cutlass::arch::ClusterBarrier);
|
||||
// Tmem ptr storage
|
||||
static constexpr auto TmemBasePtrsStorage = sizeof(uint32_t);
|
||||
// Tensormap Storage
|
||||
static constexpr size_t TensorMapStorage = IsArrayOfPointersGemm ? sizeof(cute::TmaDescriptor) * 2 /* for A and B */ : 0;
|
||||
|
||||
// Smem usage that's not part of CollectiveEpilogue::SharedStorage & CollectiveMainloop::SharedStorage
|
||||
static constexpr auto KernelSmemCarveout = static_cast<int>( CLCPipelineStorage +
|
||||
CLCResponseStorage +
|
||||
CLCThrottlePipelineStorage +
|
||||
TmemDeallocStorage +
|
||||
TmemBasePtrsStorage +
|
||||
TensorMapStorage);
|
||||
|
||||
// Reduce SMEM capacity available for buffers considering extra B smem and barrier smem allocations
|
||||
static constexpr int Sm100ReducedSmemCapacityBytes = detail::sm100_smem_capacity_bytes - KernelSmemCarveout;
|
||||
static constexpr auto stage_info = cutlass::gemm::collective::detail::sm100_compute_stage_count_or_override_fast_fp32<
|
||||
Sm100ReducedSmemCapacityBytes, CtaTileShape_MNK, TiledMma, KernelScheduleType, UmmaMajorA>(StageCountType{});
|
||||
|
||||
static constexpr int Load2TransformPipelineStageCount = get<0>(stage_info);
|
||||
static constexpr int Transform2MmaPipelineStageCount = get<1>(stage_info);
|
||||
static constexpr int AccumulatorPipelineStageCount = get<2>(stage_info);
|
||||
|
||||
using AccumulatorCopyAtom = cute::SM100_TMEM_LOAD_32dp32b32x;
|
||||
|
||||
using DispatchPolicy = cute::conditional_t<IsArrayOfPointersGemm,
|
||||
cutlass::gemm::MainloopSm100ArrayTmaUmmaWarpSpecializedFastF32<
|
||||
Load2TransformPipelineStageCount,
|
||||
Transform2MmaPipelineStageCount,
|
||||
SchedulerPipelineStageCount,
|
||||
AccumulatorPipelineStageCount,
|
||||
NumBandsToCompute,
|
||||
ScalingFactor,
|
||||
AccPromotionInterval,
|
||||
ClusterShape_MNK,
|
||||
AccumulatorCopyAtom>,
|
||||
cutlass::gemm::MainloopSm100TmaUmmaWarpSpecializedFastF32<
|
||||
Load2TransformPipelineStageCount,
|
||||
Transform2MmaPipelineStageCount,
|
||||
SchedulerPipelineStageCount,
|
||||
AccumulatorPipelineStageCount,
|
||||
NumBandsToCompute,
|
||||
ScalingFactor,
|
||||
AccPromotionInterval,
|
||||
ClusterShape_MNK,
|
||||
AccumulatorCopyAtom>
|
||||
>;
|
||||
using CollectiveOp = cutlass::gemm::collective::CollectiveMma<
|
||||
DispatchPolicy,
|
||||
TileShape_MNK,
|
||||
ElementA,
|
||||
cutlass::gemm::TagToStrideA_t<GmemLayoutATag>,
|
||||
ElementB,
|
||||
cutlass::gemm::TagToStrideB_t<GmemLayoutBTag>,
|
||||
TiledMma,
|
||||
GmemTiledCopyA,
|
||||
SmemLayoutAtomPairA,
|
||||
CopyAtomPairA,
|
||||
cute::identity,
|
||||
GmemTiledCopyB,
|
||||
SmemLayoutAtomPairB,
|
||||
CopyAtomPairB,
|
||||
cute::identity
|
||||
>;
|
||||
};
|
||||
|
||||
} // namespace cutlass::gemm::collective
|
||||
@@ -71,7 +71,7 @@ template <
|
||||
>
|
||||
constexpr int
|
||||
sm100_compute_stage_count_or_override_blockscaled(StageCountAutoCarveout<carveout_bytes> stage_count) {
|
||||
// For Mxf8f6f4 sub-bytes, ElementA/B will be passed in as uint8_t
|
||||
// For MXF8F6F4 MMA, ElementA/B will be passed in as uint8_t
|
||||
// Each stage include (CollectiveMma::SharedStorage)
|
||||
// 1. smem for A and smem for B (CollectiveMma::SharedStorage::TensorStorage)
|
||||
// 2. one MainloopPipeline = PipelineTmaUmmaAsync (CollectiveMma::SharedStorage::SharedStorage)
|
||||
@@ -386,7 +386,7 @@ select_instr() {
|
||||
}
|
||||
else if constexpr (( sizeof_bits_v<ElementA> == 4 && (sizeof_bits_v<ElementB> == 6 || sizeof_bits_v<ElementB> == 8)) ||
|
||||
((sizeof_bits_v<ElementA> == 6 || sizeof_bits_v<ElementA> == 8) && sizeof_bits_v<ElementB> == 4)) {
|
||||
// Fp4 can be mixed with FP6, Fp8 with Mxf8f6f4 only
|
||||
// Fp4 can be mixed with FP6, Fp8 with MMA.MXF8F6F4 only
|
||||
return detail::blockscaled::BlockScaledInstr::MXF4F6F8;
|
||||
}
|
||||
else if constexpr (sizeof_bits_v<ElementA> == 4 && sizeof_bits_v<ElementB> == 4) {
|
||||
@@ -400,7 +400,7 @@ select_instr() {
|
||||
static_assert( cute::is_same_v<ElementSF, cutlass::float_ue8m0_t> &&
|
||||
(cute::is_same_v<ElementA, cutlass::float_e2m1_t> && cute::is_same_v<ElementB, cutlass::float_e2m1_t> ||
|
||||
cute::is_same_v<ElementA, cutlass::type_erased_dynamic_float4_t> && cute::is_same_v<ElementB, cutlass::type_erased_dynamic_float4_t>),
|
||||
"Only MXF4 support with non-TN and Mxf8f6f4");
|
||||
"Only MXF4 support with non-TN and MMA.MXF8F6F4.");
|
||||
return detail::blockscaled::BlockScaledInstr::MXF4F6F8;
|
||||
}
|
||||
}
|
||||
@@ -636,7 +636,7 @@ struct CollectiveBuilder<
|
||||
|
||||
static constexpr bool UseMxf8f6f4 = Instr == detail::blockscaled::BlockScaledInstr::MXF4F6F8;
|
||||
|
||||
static_assert(UseMxf8f6f4 || (cutlass::gemm::detail::is_k_major_A<GmemLayoutATag>() && cutlass::gemm::detail::is_k_major_B<GmemLayoutBTag>()), "Only Mxf8f6f4 supports non-K major inputs");
|
||||
static_assert(UseMxf8f6f4 || (cutlass::gemm::detail::is_k_major_A<GmemLayoutATag>() && cutlass::gemm::detail::is_k_major_B<GmemLayoutBTag>()), "Only MMA.MXF8F6F4 supports non-K major inputs");
|
||||
|
||||
// Data type used by MMA instruction
|
||||
using ElementAMma = decltype(cutlass::gemm::collective::detail::sm100_kernel_input_element_to_mma_input_element<ElementA, UseMxf8f6f4>());
|
||||
|
||||
@@ -477,6 +477,94 @@ sm100_make_trivial_tiled_mma() {
|
||||
}
|
||||
}
|
||||
|
||||
template<
|
||||
class ElementAMma,
|
||||
class ElementBMma,
|
||||
class ElementAccumulator,
|
||||
class TileShape_MNK,
|
||||
class ClusterShape_MNK,
|
||||
UMMA::Major UmmaMajorA,
|
||||
UMMA::Major UmmaMajorB,
|
||||
int Scale,
|
||||
class KernelScheduleType
|
||||
>
|
||||
constexpr auto
|
||||
sm100_make_trivial_fastFP32_tiled_mma() {
|
||||
// MMA_2SM requested
|
||||
if constexpr (cute::is_base_of_v<KernelSchedule2Sm, KernelScheduleType> ) {
|
||||
using AtomLayout_MNK = decltype(make_layout(shape_div(ClusterShape_MNK{}, Shape<_2,_1,_1>{})));
|
||||
constexpr int M = cute::size<0>(TileShape_MNK{});
|
||||
constexpr int N = cute::size<1>(TileShape_MNK{});
|
||||
if constexpr (UmmaMajorA == cute::UMMA::Major::K && !cute::is_base_of_v<KernelTmaWarpSpecializedFastFP32SmemSm100, KernelScheduleType>) {
|
||||
return make_tiled_mma(cute::SM100_MMA_F16BF16_2x1SM_TS_SCALED<ElementAMma, ElementBMma, ElementAccumulator,
|
||||
M, N, UmmaMajorA, UmmaMajorB, Scale>{});
|
||||
}
|
||||
else { // If A needs to be transposed by MMA, fall back to SMEM from A MMA instructions
|
||||
return make_tiled_mma(cute::SM100_MMA_F16BF16_2x1SM_SS_SCALED<ElementAMma, ElementBMma, ElementAccumulator,
|
||||
M, N, UmmaMajorA, UmmaMajorB, Scale>{});
|
||||
}
|
||||
}
|
||||
// MMA_1SM requested
|
||||
else if constexpr (cute::is_base_of_v<KernelSchedule1Sm, KernelScheduleType> ) {
|
||||
// using AtomLayout_MNK = Layout<ClusterShape_MNK>;
|
||||
constexpr int M = cute::size<0>(TileShape_MNK{});
|
||||
constexpr int N = cute::size<1>(TileShape_MNK{});
|
||||
if constexpr (UmmaMajorA == cute::UMMA::Major::K && !cute::is_base_of_v<KernelTmaWarpSpecializedFastFP32SmemSm100, KernelScheduleType>) {
|
||||
return make_tiled_mma(cute::SM100_MMA_F16BF16_TS_SCALED<ElementAMma, ElementBMma, ElementAccumulator,
|
||||
M, N, UmmaMajorA, UmmaMajorB, Scale>{});
|
||||
}
|
||||
else { // If A needs to be transposed by MMA, fall back to SMEM from A MMA instructions
|
||||
return make_tiled_mma(cute::SM100_MMA_F16BF16_SS_SCALED<ElementAMma, ElementBMma, ElementAccumulator,
|
||||
M, N, UmmaMajorA, UmmaMajorB, Scale>{});
|
||||
}
|
||||
}
|
||||
else if constexpr (cute::is_same_v<KernelScheduleType, KernelScheduleSm100FastFP32Gemm> ||
|
||||
cute::is_same_v<KernelScheduleType, KernelTmaWarpSpecializedFastFP32SmemSm100> ||
|
||||
cute::is_same_v<KernelScheduleType, KernelScheduleSm100PtrArrayFastFP32Gemm> ||
|
||||
cute::is_same_v<KernelScheduleType, KernelTmaWarpSpecializedPtrArrayFastFP32SmemSm100>) {
|
||||
// Static cluster
|
||||
if constexpr (cute::is_static_v<ClusterShape_MNK>) {
|
||||
// For MMA_2SM we need a cluster shape that is multiple of 2x1
|
||||
// and only M=128 and M=256 are supported, otherwise, fall back to MMA_1SM
|
||||
if constexpr (cute::get<0>(ClusterShape_MNK{}) % 2 == 0 &&
|
||||
(cute::get<0>(TileShape_MNK{}) / cute::get<0>(ClusterShape_MNK{})) % 64 == 0) {
|
||||
if constexpr (!cute::is_base_of_v<KernelTmaWarpSpecializedFastFP32SmemSm100, KernelScheduleType>) {
|
||||
return sm100_make_trivial_fastFP32_tiled_mma<ElementAMma, ElementBMma, ElementAccumulator, TileShape_MNK,
|
||||
ClusterShape_MNK, UmmaMajorA, UmmaMajorB, Scale, KernelTmaWarpSpecialized2SmFastFP32Sm100>();
|
||||
}
|
||||
else {
|
||||
return sm100_make_trivial_fastFP32_tiled_mma<ElementAMma, ElementBMma, ElementAccumulator, TileShape_MNK,
|
||||
ClusterShape_MNK, UmmaMajorA, UmmaMajorB, Scale, KernelTmaWarpSpecialized2SmFastFP32SmemSm100>();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if constexpr (!cute::is_base_of_v<KernelTmaWarpSpecializedFastFP32SmemSm100, KernelScheduleType>) {
|
||||
return sm100_make_trivial_fastFP32_tiled_mma<ElementAMma, ElementBMma, ElementAccumulator, TileShape_MNK,
|
||||
ClusterShape_MNK, UmmaMajorA, UmmaMajorB, Scale, KernelTmaWarpSpecialized1SmFastFP32Sm100>();
|
||||
}
|
||||
else {
|
||||
return sm100_make_trivial_fastFP32_tiled_mma<ElementAMma, ElementBMma, ElementAccumulator, TileShape_MNK,
|
||||
ClusterShape_MNK, UmmaMajorA, UmmaMajorB, Scale, KernelTmaWarpSpecialized1SmFastFP32SmemSm100>();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Dynamic cluster shape means we cannot assume we can use 2SM MMA
|
||||
else {
|
||||
if constexpr (!cute::is_base_of_v<KernelTmaWarpSpecializedFastFP32SmemSm100, KernelScheduleType>) {
|
||||
return sm100_make_trivial_fastFP32_tiled_mma<ElementAMma, ElementBMma, ElementAccumulator, TileShape_MNK,
|
||||
ClusterShape_MNK, UmmaMajorA, UmmaMajorB, Scale, KernelTmaWarpSpecialized1SmFastFP32Sm100>();
|
||||
}
|
||||
else {
|
||||
return sm100_make_trivial_fastFP32_tiled_mma<ElementAMma, ElementBMma, ElementAccumulator, TileShape_MNK,
|
||||
ClusterShape_MNK, UmmaMajorA, UmmaMajorB, Scale, KernelTmaWarpSpecialized1SmFastFP32SmemSm100>();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
static_assert(cutlass::detail::dependent_false<TileShape_MNK> == 0,
|
||||
"Unsupported policy for SM100 collective builder.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check for U4_UNPACK_U8, U6_UNPACK_U8 alignment requirement
|
||||
@@ -547,22 +635,22 @@ template <class ElementA, int AlignmentA, class ElementB, int AlignmentB, class
|
||||
constexpr bool
|
||||
sm1xx_gemm_is_aligned() {
|
||||
// Only support dense gemm alignment check
|
||||
constexpr bool is_f6f4_subbytes = cute::sizeof_bits_v<ElementA> < 8 || cute::sizeof_bits_v<ElementB> < 8;
|
||||
constexpr bool is_f8f6f4_subbytes = cute::sizeof_bits_v<ElementA> < 8 || cute::sizeof_bits_v<ElementB> < 8;
|
||||
|
||||
return ((cute::sizeof_bits_v<ElementA> * AlignmentA) % cutlass::detail::get_input_alignment_bits<ElementA, is_f6f4_subbytes>() == 0) &&
|
||||
((cute::sizeof_bits_v<ElementB> * AlignmentB) % cutlass::detail::get_input_alignment_bits<ElementB, is_f6f4_subbytes>() == 0);
|
||||
return ((cute::sizeof_bits_v<ElementA> * AlignmentA) % cutlass::detail::get_input_alignment_bits<ElementA, is_f8f6f4_subbytes>() == 0) &&
|
||||
((cute::sizeof_bits_v<ElementB> * AlignmentB) % cutlass::detail::get_input_alignment_bits<ElementB, is_f8f6f4_subbytes>() == 0);
|
||||
}
|
||||
|
||||
template <class ElementA, int AlignmentA, class ElementB, int AlignmentB, class KernelScheduleType>
|
||||
constexpr bool
|
||||
sm1xx_blockscaled_gemm_is_aligned() {
|
||||
// Only support blocksscaled gemm alignment check
|
||||
constexpr bool is_f6f4_subbytes = (cute::sizeof_bits_v<ElementA> < 8 || cute::sizeof_bits_v<ElementB> < 8) &&
|
||||
constexpr bool is_mxf8f6f4_subbytes = (cute::sizeof_bits_v<ElementA> < 8 || cute::sizeof_bits_v<ElementB> < 8) &&
|
||||
(cute::is_base_of_v<KernelScheduleMxf8f6f4Sm100, KernelScheduleType>
|
||||
);
|
||||
|
||||
return ((cute::sizeof_bits_v<ElementA> * AlignmentA) % cutlass::detail::get_input_alignment_bits<ElementA, is_f6f4_subbytes>() == 0) &&
|
||||
((cute::sizeof_bits_v<ElementB> * AlignmentB) % cutlass::detail::get_input_alignment_bits<ElementB, is_f6f4_subbytes>() == 0);
|
||||
return ((cute::sizeof_bits_v<ElementA> * AlignmentA) % cutlass::detail::get_input_alignment_bits<ElementA, is_mxf8f6f4_subbytes>() == 0) &&
|
||||
((cute::sizeof_bits_v<ElementB> * AlignmentB) % cutlass::detail::get_input_alignment_bits<ElementB, is_mxf8f6f4_subbytes>() == 0);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
@@ -82,7 +82,7 @@ template<
|
||||
int carveout_bytes>
|
||||
constexpr int
|
||||
sm100_compute_stage_count_or_override(StageCountAutoCarveout<carveout_bytes> stage_count) {
|
||||
// For F8F6F4 sub-bytes, ElementA/B will be passed in as uint8_t
|
||||
// For F8/F6/F4 sub-bytes, ElementA/B will be passed in as uint8_t
|
||||
// For Planar Complex, ElementA/B will be passed in as cutlass::complex<ElementARaw>
|
||||
// Each stage include (CollectiveMma::SharedStorage)
|
||||
// 1. smem for A and smem for B (CollectiveMma::SharedStorage::TensorStorage)
|
||||
@@ -253,7 +253,9 @@ struct CollectiveBuilder<
|
||||
static constexpr uint32_t TotalTmemRows = 128;
|
||||
static constexpr uint32_t Sm100TmemCapacityColumns = 512;
|
||||
static constexpr uint32_t TotalTmem = TotalTmemRows * Sm100TmemCapacityColumns;
|
||||
static constexpr uint32_t AccumulatorPipelineStageCount = TotalTmem / (cute::size<0>(CtaTileShape_MNK{}) * cute::size<1>(CtaTileShape_MNK{}));
|
||||
static constexpr uint32_t AccumulatorPipelineStageCount = (is_2sm || (!is_2sm && size(shape<0,0>(MmaShapeA_MK{}) > 64))) ?
|
||||
TotalTmem / (cute::size<0>(CtaTileShape_MNK{}) * cute::size<1>(CtaTileShape_MNK{}))
|
||||
: (Sm100TmemCapacityColumns / cute::size<1>(CtaTileShape_MNK{})) * 2; // 1SM MMA_M = 64 case
|
||||
static_assert(AccumulatorPipelineStageCount > 0, "Accumulator pipeline stage count must be positive. This error probably means that TileShape_MNK and/or TiledMma::ThrLayoutVMNK are wrong.");
|
||||
|
||||
// Calculate scheduler pipeline stages. Having one more stage than the accumulator allows more latency hiding.
|
||||
|
||||
@@ -261,8 +261,9 @@ struct CollectiveBuilder<
|
||||
using SmemLayoutAtomB = decltype(detail::ss_smem_selector<
|
||||
GmmaMajorB, ElementBMma, decltype(cute::get<1>(TileShape_MNK{})), decltype(cute::get<2>(TileShape_MNK{}))>());
|
||||
|
||||
static constexpr int Sm90ReducedSmemCapacityBytes =
|
||||
detail::sm90_smem_capacity_bytes;
|
||||
static constexpr size_t TensorMapStorage = IsArrayOfPointersGemm ? sizeof(cute::TmaDescriptor) * 2 /* for A and B */ : 0;
|
||||
static constexpr int KernelSmemCarveout = static_cast<int>(TensorMapStorage);
|
||||
static constexpr int Sm90ReducedSmemCapacityBytes = detail::sm90_smem_capacity_bytes - KernelSmemCarveout;
|
||||
|
||||
static constexpr int PipelineStages = detail::compute_stage_count_or_override<Sm90ReducedSmemCapacityBytes,
|
||||
ElementAMma, ElementBMma, TileShape_MNK>(StageCountType{});
|
||||
@@ -368,7 +369,12 @@ public:
|
||||
return t;
|
||||
}
|
||||
else {
|
||||
if constexpr (cute::is_pointer_v<T>) {
|
||||
return &cute::stride(*t);
|
||||
}
|
||||
else {
|
||||
return cute::stride(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,14 +447,20 @@ public:
|
||||
static constexpr int Sm90ReducedSmemCapacityBytes = detail::sm90_smem_capacity_bytes - KernelSmemCarveout;
|
||||
|
||||
static constexpr int PipelineStages = IsMixedInput ?
|
||||
( IsArrayOfPointersGemm ?
|
||||
detail::compute_stage_count_or_override_single_affine_transformed_input<Sm90ReducedSmemCapacityBytes,
|
||||
RealElementA, RealElementB, ElementScale, ElementZero, TileShape_MNK, StageCountType::bytes, SmemAlignment>(StageCountType{}) :
|
||||
detail::compute_stage_count_or_override_single_affine_transformed_input<detail::sm90_smem_capacity_bytes,
|
||||
RealElementA, RealElementB, ElementScale, ElementZero, TileShape_MNK, StageCountType::bytes, SmemAlignment>(StageCountType{})
|
||||
)
|
||||
: detail::compute_stage_count_or_override<detail::sm90_smem_capacity_bytes,
|
||||
ElementAMma, ElementBMma, TileShape_MNK, StageCountType::bytes, SmemAlignment>(StageCountType{});
|
||||
|
||||
using DispatchPolicy = cute::conditional_t<IsMixedInput,
|
||||
MainloopSm90TmaGmmaRmemAWarpSpecializedMixedInput<PipelineStages, ClusterShape_MNK, KernelScheduleType>
|
||||
, MainloopSm90TmaGmmaRmemAWarpSpecialized<PipelineStages, ClusterShape_MNK, KernelScheduleType>>;
|
||||
cute::conditional_t<IsArrayOfPointersGemm,
|
||||
MainloopSm90ArrayTmaGmmaWarpSpecializedMixedInput<PipelineStages, ClusterShape_MNK, KernelScheduleType>,
|
||||
MainloopSm90TmaGmmaRmemAWarpSpecializedMixedInput<PipelineStages, ClusterShape_MNK, KernelScheduleType>>,
|
||||
MainloopSm90TmaGmmaRmemAWarpSpecialized<PipelineStages, ClusterShape_MNK, KernelScheduleType>>;
|
||||
|
||||
using SmemCopyAtomA = cute::conditional_t<SwapAB, void, Copy_Atom<cute::AutoVectorizingCopy, ElementA>>;
|
||||
using SmemCopyAtomB = cute::conditional_t<SwapAB, Copy_Atom<cute::AutoVectorizingCopy, ElementB>, void>;
|
||||
|
||||
@@ -71,15 +71,15 @@ struct Sm90GemmSparseConfig {
|
||||
using ElementEMmaSparsity = Int<ElementEMma::sparsity>;
|
||||
|
||||
// MMA type
|
||||
static constexpr bool IsQmma = cute::is_same_v<ElementAMmaRaw, float_e4m3_t> && ElementAMmaSparsity{} == _2{} ||
|
||||
static constexpr bool IsF8 = cute::is_same_v<ElementAMmaRaw, float_e4m3_t> && ElementAMmaSparsity{} == _2{} ||
|
||||
cute::is_same_v<ElementAMmaRaw, float_e5m2_t> && ElementAMmaSparsity{} == _2{};
|
||||
static constexpr bool IsImma = cute::is_same_v<ElementAMmaRaw, int8_t> && ElementAMmaSparsity{} == _2{} ||
|
||||
static constexpr bool IsI8 = cute::is_same_v<ElementAMmaRaw, int8_t> && ElementAMmaSparsity{} == _2{} ||
|
||||
cute::is_same_v<ElementAMmaRaw, uint8_t> && ElementAMmaSparsity{} == _2{};
|
||||
static constexpr bool IsHmma = cute::is_same_v<ElementAMmaRaw, half_t> && ElementAMmaSparsity{} == _2{} ||
|
||||
static constexpr bool IsF16BF16 = cute::is_same_v<ElementAMmaRaw, half_t> && ElementAMmaSparsity{} == _2{} ||
|
||||
cute::is_same_v<ElementAMmaRaw, bfloat16_t> && ElementAMmaSparsity{} == _2{};
|
||||
static constexpr bool IsTfmma = cute::is_same_v<ElementAMmaRaw, tfloat32_t> && ElementAMmaSparsity{} == _2{} ||
|
||||
static constexpr bool IsTF32 = cute::is_same_v<ElementAMmaRaw, tfloat32_t> && ElementAMmaSparsity{} == _2{} ||
|
||||
cute::is_same_v<ElementAMmaRaw, float> && ElementAMmaSparsity{} == _2{};
|
||||
static_assert(int(IsQmma) + int(IsImma) + int(IsHmma) + int(IsTfmma) == 1, "Ambigious Input Type Config (failed to choose MMA type)");
|
||||
static_assert(int(IsF8) + int(IsI8) + int(IsF16BF16) + int(IsTF32) == 1, "Ambigious Input Type Config (failed to choose MMA type)");
|
||||
|
||||
// Number of ElementARaw stored in ElementAMmaRaw. For Hopper this is always 1.
|
||||
using ElemsARawPerElementAMmaRaw = _1;
|
||||
@@ -89,12 +89,12 @@ struct Sm90GemmSparseConfig {
|
||||
static_assert(ElementASparsity{} == _2{}, "ElementASparsity must be 2 for Hopper Sparse Gemm");
|
||||
|
||||
// Logical/Physical ElementA per Chunk
|
||||
using LogicalElemsAPerChunk = conditional_t<IsTfmma, _2, _4>;
|
||||
using LogicalElemsAPerChunk = conditional_t<IsTF32, _2, _4>;
|
||||
using PhysicalElemsAPerChunk = Int<LogicalElemsAPerChunk{} / ElementASparsity{}>;
|
||||
|
||||
// Metadata Bits
|
||||
using ElementEBitsPerChunk = _4;
|
||||
using ElementEBitsPerElementAMma = cute::conditional_t<IsTfmma, _4, _2>;
|
||||
using ElementEBitsPerElementAMma = cute::conditional_t<IsTF32, _4, _2>;
|
||||
|
||||
// Metadata Layout. Unit in corresbonding logical elements.
|
||||
// Basic metadata block is (16,64) for 8-bit, (16,32) for 16-bit, (16,16) for 32-bit data types.
|
||||
@@ -114,8 +114,8 @@ struct Sm90GemmSparseConfig {
|
||||
using TensorEAtom_8bit = decltype(make_ordered_layout(Shape<_64,MinTileShapeK>{},
|
||||
Step < _1, _0>{}));
|
||||
|
||||
using TensorEAtom = cute::conditional_t<(IsQmma || IsImma), TensorEAtom_8bit,
|
||||
cute::conditional_t<IsTfmma, TensorEAtom_32bit,
|
||||
using TensorEAtom = cute::conditional_t<(IsF8 || IsI8), TensorEAtom_8bit,
|
||||
cute::conditional_t<IsTF32, TensorEAtom_32bit,
|
||||
TensorEAtom_16bit>>;
|
||||
|
||||
// Logical elems that construct the atomK for tensorE/A.
|
||||
|
||||
@@ -40,8 +40,9 @@
|
||||
#include "cutlass/gemm/collective/builders/sm90_gmma_builder.inl"
|
||||
#include "cutlass/gemm/collective/builders/sm90_sparse_gmma_builder.inl"
|
||||
#if !defined(__CUDACC_RTC__)
|
||||
#include "cutlass/gemm/collective/builders/sm100_umma_builder.inl"
|
||||
#include "cutlass/gemm/collective/builders/sm100_blockscaled_umma_builder.inl"
|
||||
#include "cutlass/gemm/collective/builders/sm100_umma_builder.inl"
|
||||
#include "cutlass/gemm/collective/builders/sm100_9xBF16_umma_builder.inl"
|
||||
#include "cutlass/gemm/collective/builders/sm100_blockscaled_umma_builder.inl"
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -46,11 +46,16 @@
|
||||
#include "cutlass/gemm/collective/sm90_sparse_mma_tma_gmma_ss_warpspecialized.hpp"
|
||||
#include "cutlass/gemm/collective/sm90_sparse_mma_tma_gmma_ss_warpspecialized_fp8.hpp"
|
||||
#include "cutlass/gemm/collective/sm90_mma_array_tma_gmma_ss_warpspecialized.hpp"
|
||||
#include "cutlass/gemm/collective/sm90_mma_array_tma_gmma_rs_warpspecialized_mixed_input.hpp"
|
||||
#include "cutlass/gemm/collective/sm90_mma_tma_gmma_ss_warpspecialized_fp8.hpp"
|
||||
#include "cutlass/gemm/collective/sm90_mma_tma_gmma_ss_warpspecialized_fp8_blockwise_scaling.hpp"
|
||||
#if !defined(__CUDACC_RTC__)
|
||||
#include "cutlass/gemm/collective/sm100_mma_warpspecialized.hpp"
|
||||
#include "cutlass/gemm/collective/sm100_mma_array_warpspecialized.hpp"
|
||||
|
||||
#if !defined(__CUDACC_RTC__)
|
||||
#include "cutlass/gemm/collective/sm100_mma_warpspecialized.hpp"
|
||||
#include "cutlass/gemm/collective/sm100_mma_array_warpspecialized.hpp"
|
||||
#include "cutlass/gemm/collective/sm100_mma_warpspecialized_emulated.hpp"
|
||||
#include "cutlass/gemm/collective/sm100_mma_array_warpspecialized_emulated.hpp"
|
||||
|
||||
#include "cutlass/gemm/collective/sm100_blockscaled_mma_warpspecialized.hpp"
|
||||
#include "cutlass/gemm/collective/sm100_blockscaled_mma_array_warpspecialized.hpp"
|
||||
#endif // !defined(__CUDACC_RTC__)
|
||||
|
||||
@@ -682,11 +682,11 @@ struct CollectiveMma<
|
||||
auto mSFB_nkl = [=](){
|
||||
if constexpr (IsCtaN192) {
|
||||
Tensor mSFB_tmp = observed_tma_load_sfb_->get_tma_tensor(shape(layout_SFB));
|
||||
auto x = stride<0,2>(mSFB_tmp);
|
||||
auto y = ceil_div(shape<0,2>(mSFB_tmp), 4);
|
||||
auto new_shape = make_shape (make_shape( shape<0,0>(mSFB_tmp), shape<0,1>(mSFB_tmp),
|
||||
auto x = stride<0,1>(mSFB_tmp);
|
||||
auto y = ceil_div(shape<0,1>(mSFB_tmp), 4);
|
||||
auto new_shape = make_shape (make_shape( shape<0,0>(mSFB_tmp),
|
||||
make_shape( make_shape(_2{}, _2{}), y)), shape<1>(mSFB_tmp), shape<2>(mSFB_tmp));
|
||||
auto new_stride = make_stride(make_stride(stride<0,0>(mSFB_tmp), stride<0,1>(mSFB_tmp),
|
||||
auto new_stride = make_stride(make_stride(stride<0,0>(mSFB_tmp),
|
||||
make_stride(make_stride( x, x), x*3)), stride<1>(mSFB_tmp), stride<2>(mSFB_tmp));
|
||||
return make_tensor(mSFB_tmp.data(), make_layout(new_shape, new_stride));
|
||||
}
|
||||
|
||||
@@ -717,11 +717,11 @@ struct CollectiveMma<
|
||||
auto mSFB_nkl = [=](){
|
||||
if constexpr (IsCtaN192) {
|
||||
Tensor mSFB_tmp = observed_tma_load_sfb_->get_tma_tensor(shape(layout_SFB_));
|
||||
auto x = stride<0,2>(mSFB_tmp);
|
||||
auto y = ceil_div(shape<0,2>(mSFB_tmp), 4);
|
||||
auto new_shape = make_shape (make_shape( shape<0,0>(mSFB_tmp), shape<0,1>(mSFB_tmp),
|
||||
auto x = stride<0,1>(mSFB_tmp);
|
||||
auto y = ceil_div(shape<0,1>(mSFB_tmp), 4);
|
||||
auto new_shape = make_shape (make_shape( shape<0,0>(mSFB_tmp),
|
||||
make_shape( make_shape(_2{}, _2{}), y)), shape<1>(mSFB_tmp), shape<2>(mSFB_tmp));
|
||||
auto new_stride = make_stride(make_stride(stride<0,0>(mSFB_tmp), stride<0,1>(mSFB_tmp),
|
||||
auto new_stride = make_stride(make_stride(stride<0,0>(mSFB_tmp),
|
||||
make_stride(make_stride( x, x), x*3)), stride<1>(mSFB_tmp), stride<2>(mSFB_tmp));
|
||||
return make_tensor(mSFB_tmp.data(), make_layout(new_shape, new_stride));
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
-7
@@ -240,7 +240,6 @@ public:
|
||||
|
||||
// To relax them, we need to handle loading more than 1 row of scales for every main loop iteration.
|
||||
// We must also handle updating the pipeline transaction bytes on the fly.
|
||||
// NOTE: Deleting this assertion without required changes will cause the code to hang.
|
||||
static_assert(size<1>(SmemLayoutAtomScale{}) == 1, "size<1>(SmemLayoutAtomScale) must be 1.");
|
||||
|
||||
private:
|
||||
@@ -490,8 +489,6 @@ public:
|
||||
: args_setup(args.ptr_A, args.ptr_B);
|
||||
}
|
||||
else if constexpr (ModeHasScales) {
|
||||
// NOTE: fix chunk wise scaling
|
||||
//auto scale_k = (K + args.chunk_size - 1) / args.chunk_size;
|
||||
auto scale_k = 1;
|
||||
ElementScale const* ptr_S = reinterpret_cast<ElementScale const*>(args.ptr_S);
|
||||
StrideScale dS{};
|
||||
@@ -998,7 +995,6 @@ public:
|
||||
Utils::copy_tensors_MK(smem_tiled_copy_A, tCsA, tCrA_copy_view,
|
||||
partitioned_extra_info, copy_partitions_extra_info, 0, smem_pipe_read.index());
|
||||
|
||||
// NOTE: Check this when applying swizzling PR on top of GGMD
|
||||
Utils::copy_tensors_MK(smem_tiled_copy_A, tCsA, tCrA_copy_view,
|
||||
partitioned_extra_info, copy_partitions_extra_info, 1, smem_pipe_read.index());
|
||||
|
||||
@@ -1049,7 +1045,6 @@ public:
|
||||
Utils::copy_tensors_MK(smem_tiled_copy_A, tCsA, tCrA_copy_view,
|
||||
partitioned_extra_info, copy_partitions_extra_info, 0, smem_pipe_read.index());
|
||||
|
||||
// NOTE: Check this when applying swizzling PR on top of GGMD
|
||||
Utils::copy_tensors_MK(smem_tiled_copy_A, tCsA, tCrA_copy_view,
|
||||
partitioned_extra_info, copy_partitions_extra_info, 1, smem_pipe_read.index());
|
||||
Utils::dequantize_A_kblock(tCrA_load, tCrA_mma, partitioned_extra_info, 0);
|
||||
@@ -1248,7 +1243,6 @@ public:
|
||||
|
||||
if constexpr (KernelConversionMode == ConversionMode::ConvertAndScale) {
|
||||
NonVoidElementScale const* ptr_S = nullptr;
|
||||
// NOTE: figure out chunk wise scaling. auto scale_k = (K + mainloop_params.chunk_size - 1) / mainloop_params.chunk_size;
|
||||
auto scale_k = 1;
|
||||
Tensor tensor_scale = make_tensor(detail::get_logical_ptr(ptr_S), make_shape(M,scale_k,Int<1>{}), mainloop_params.dS[next_group]);
|
||||
cute::detail::fill_tma_gmem_shape_stride(mainloop_params.tma_load_scale, tensor_scale,
|
||||
@@ -1256,7 +1250,6 @@ public:
|
||||
}
|
||||
else if constexpr (KernelConversionMode == ConversionMode::ConvertAndScaleWithZero) {
|
||||
ElementZero const* ptr_Z = nullptr;
|
||||
// NOTE: figure out chunk wise scaling. auto scale_k = (K + mainloop_params.chunk_size - 1) / mainloop_params.chunk_size;
|
||||
auto scale_k = 1;
|
||||
Tensor tensor_zero = make_tensor(detail::get_logical_ptr(ptr_Z), make_shape(M,scale_k,Int<1>{}), mainloop_params.dS[next_group]);
|
||||
cute::detail::fill_tma_gmem_shape_stride(mainloop_params.tma_load_zero, tensor_zero,
|
||||
|
||||
+1
-2
@@ -531,7 +531,7 @@ struct CollectiveMma<
|
||||
TiledMma tiled_mma;
|
||||
auto thread_mma = tiled_mma.get_slice(warp_group_thread_layout(warp_group_idx));
|
||||
|
||||
Tensor tCsScaleAViewAsC = tiled_mma.get_slice(thread_idx).partition_C(sScaleAViewAsC); // (MMA,MMA_M,MMA_N,PIPE), `thread_mma` above is correct when partitioning A and B, but it is not correct when partitioning C.
|
||||
Tensor tCsScaleAViewAsC = tiled_mma.get_slice(thread_idx).partition_C(sScaleAViewAsC); // (MMA,MMA_M,MMA_N,PIPE), `thread_mma` above is correct when partitioning A and B, but it is not correct when partitioning C.
|
||||
|
||||
Tensor tCsA = thread_mma.partition_A(sA); // (MMA,MMA_M,MMA_K,PIPE)
|
||||
Tensor tCsB = thread_mma.partition_B(sB); // (MMA,MMA_N,MMA_K,PIPE)
|
||||
@@ -557,7 +557,6 @@ struct CollectiveMma<
|
||||
PipelineState smem_pipe_release = smem_pipe_read;
|
||||
|
||||
// Per block scale values for operand A and B
|
||||
|
||||
using RegLayoutScaleAViewAsC = decltype(make_layout_like(tCsScaleAViewAsC(_, _, _, 0).layout())); // `make_layout_like` makes a compact layout.
|
||||
using RegLayoutScaleAEssential = decltype(filter_zeros(RegLayoutScaleAViewAsC{}.stride(), RegLayoutScaleAViewAsC{}.shape())); // an interface to traverse the underlying storage for the compact layout mentioned above
|
||||
|
||||
|
||||
@@ -351,6 +351,23 @@ struct MainloopSm90TmaGmmaWarpSpecializedSparseFP8
|
||||
: MainloopSm90TmaGmmaWarpSpecializedSparse<Stages, ClusterShape, KernelSchedule> {
|
||||
};
|
||||
|
||||
// Mixed precision version n-buffer in rmem (Hopper TMA), pipelined with Hopper GMMA and TMA, Warp specialized dynamic schedule for Ptr-Array and Grouped Gemm
|
||||
template<
|
||||
int Stages_,
|
||||
class ClusterShape_ = Shape<_1,_1,_1>,
|
||||
class KernelSchedule = KernelPtrArrayTmaWarpSpecializedCooperative
|
||||
>
|
||||
struct MainloopSm90ArrayTmaGmmaWarpSpecializedMixedInput {
|
||||
constexpr static int Stages = Stages_;
|
||||
using ClusterShape = ClusterShape_;
|
||||
using ArchTag = arch::Sm90;
|
||||
using Schedule = KernelSchedule;
|
||||
static_assert(
|
||||
cute::is_same_v<Schedule, KernelPtrArrayTmaWarpSpecializedCooperative> ||
|
||||
cute::is_same_v<Schedule, KernelPtrArrayTmaWarpSpecializedPingpong>,
|
||||
"KernelSchedule must be one of the Ptr-Array or Grouped Gemm TMA Warp Specialized Cooperative policies");
|
||||
};
|
||||
|
||||
|
||||
template<
|
||||
int SchedulerPipelineStageCount_,
|
||||
@@ -373,6 +390,16 @@ struct KernelTmaWarpSpecializedBlockScaledSm100 final {
|
||||
|
||||
|
||||
|
||||
// InputTransform GEMM
|
||||
template<
|
||||
int SchedulerPipelineStageCount_,
|
||||
int AccumulatorPipelineStageCount_
|
||||
>
|
||||
struct KernelTmaWarpSpecializedInputTransformSm100 final {
|
||||
static constexpr int SchedulerPipelineStageCount = SchedulerPipelineStageCount_;
|
||||
static constexpr int AccumulatorPipelineStageCount = AccumulatorPipelineStageCount_;
|
||||
};
|
||||
|
||||
// Ptr-Array Dense GEMM: SM100 tensor op policy that applies to both 1SM and 2SM MMA atoms
|
||||
template<
|
||||
int SchedulerPipelineStageCount_,
|
||||
@@ -393,6 +420,15 @@ struct KernelPtrArrayTmaWarpSpecializedBlockScaledSm100 final {
|
||||
static constexpr int AccumulatorPipelineStageCount = AccumulatorPipelineStageCount_;
|
||||
};
|
||||
|
||||
// Ptr-Array InputTransform GEMM
|
||||
template<
|
||||
int SchedulerPipelineStageCount_,
|
||||
int AccumulatorPipelineStageCount_
|
||||
>
|
||||
struct KernelPtrArrayTmaWarpSpecializedInputTransformSm100 final {
|
||||
static constexpr int SchedulerPipelineStageCount = SchedulerPipelineStageCount_;
|
||||
static constexpr int AccumulatorPipelineStageCount = AccumulatorPipelineStageCount_;
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@@ -401,32 +437,67 @@ struct KernelPtrArrayTmaWarpSpecializedBlockScaledSm100 final {
|
||||
// Collective Builder Tag Property
|
||||
//
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// SM100 Dispatch Policies
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Base Dispatch Policies
|
||||
struct KernelSchedule1Sm {};
|
||||
struct KernelSchedule2Sm {};
|
||||
struct KernelScheduleSm100 {};
|
||||
struct KernelScheduleSm100DenseGemm : KernelScheduleSm100 {};
|
||||
|
||||
struct KernelScheduleBlockScaledGemmSm100 : KernelScheduleSm100 {};
|
||||
struct KernelScheduleMxNvf4Sm100 : KernelScheduleBlockScaledGemmSm100 {};
|
||||
struct KernelScheduleMxf8f6f4Sm100 : KernelScheduleBlockScaledGemmSm100 {};
|
||||
|
||||
struct KernelScheduleSm100PtrArrayDenseGemm : KernelScheduleSm100DenseGemm {};
|
||||
struct KernelSchedulePtrArrayBlockScaledGemmSm100 : KernelScheduleBlockScaledGemmSm100 {};
|
||||
struct KernelSchedulePtrArrayMxNvf4Sm100 : KernelSchedulePtrArrayBlockScaledGemmSm100 {};
|
||||
struct KernelSchedulePtrArrayMxf8f6f4Sm100 : KernelSchedulePtrArrayBlockScaledGemmSm100 {};
|
||||
|
||||
|
||||
//
|
||||
// Collective Builder Tag
|
||||
// Only used in CollectiveBuilder
|
||||
//
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SM100 Dense GEMM Dispatch Policies
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
struct KernelScheduleSm100DenseGemm : KernelScheduleSm100 {}; // Base policy
|
||||
// Dense GEMM: Specialize for 1SM vs 2SM
|
||||
struct KernelTmaWarpSpecialized1SmSm100 final : KernelSchedule1Sm, KernelScheduleSm100DenseGemm {};
|
||||
struct KernelTmaWarpSpecialized2SmSm100 final : KernelSchedule2Sm, KernelScheduleSm100DenseGemm {};
|
||||
struct KernelTmaWarpSpecialized1SmSm100 final : KernelSchedule1Sm, KernelScheduleSm100DenseGemm {}; // Use for 1SM Dense GEMM Kernels for Collective Mainloop Builder
|
||||
struct KernelTmaWarpSpecialized2SmSm100 final : KernelSchedule2Sm, KernelScheduleSm100DenseGemm {}; // Use for 2SM Dense GEMM Kernels for Collective Mainloop Builder
|
||||
// Dense GEMM + (Ptr Array or Group GEMM)
|
||||
struct KernelScheduleSm100PtrArrayDenseGemm : KernelScheduleSm100DenseGemm {};
|
||||
// Ptr-Array Dense GEMM: Specialize for 1SM vs 2SM
|
||||
struct KernelPtrArrayTmaWarpSpecialized1SmSm100 final : KernelSchedule1Sm, KernelScheduleSm100PtrArrayDenseGemm {};
|
||||
struct KernelPtrArrayTmaWarpSpecialized2SmSm100 final : KernelSchedule2Sm, KernelScheduleSm100PtrArrayDenseGemm {};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SM100 Planar Complex GEMM Dispatch Policies
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
struct KernelScheduleSm100PlanarComplexGemm : KernelScheduleSm100{};
|
||||
// Planar Complex GEMM: Specialize for 1SM vs 2SM
|
||||
struct KernelTmaWarpSpecialized1SmPlanarComplexSm100 final : KernelSchedule1Sm, KernelScheduleSm100PlanarComplexGemm { };
|
||||
struct KernelTmaWarpSpecialized2SmPlanarComplexSm100 final : KernelSchedule2Sm, KernelScheduleSm100PlanarComplexGemm { };
|
||||
// Planar Complex GEMM + (Ptr Array or Group GEMM)
|
||||
struct KernelScheduleSm100PtrArrayPlanarComplexGemm : KernelScheduleSm100PlanarComplexGemm {};
|
||||
struct KernelPtrArrayTmaWarpSpecialized1SmPlanarComplexSm100 final : KernelSchedule1Sm, KernelScheduleSm100PtrArrayPlanarComplexGemm {};
|
||||
struct KernelPtrArrayTmaWarpSpecialized2SmPlanarComplexSm100 final : KernelSchedule2Sm, KernelScheduleSm100PtrArrayPlanarComplexGemm {};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SM100 FastF32 (9xBF16) GEMM Dispatch Policies
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
struct KernelScheduleSm100FastFP32Gemm : KernelScheduleSm100 {};
|
||||
struct KernelTmaWarpSpecializedFastFP32SmemSm100 : KernelScheduleSm100FastFP32Gemm { };
|
||||
// Dispatch policies without smem load the A operand from tmem
|
||||
struct KernelTmaWarpSpecialized1SmFastFP32Sm100 final : KernelSchedule1Sm, KernelScheduleSm100FastFP32Gemm { };
|
||||
struct KernelTmaWarpSpecialized2SmFastFP32Sm100 final : KernelSchedule2Sm, KernelScheduleSm100FastFP32Gemm { };
|
||||
// Dispatch policies with smem load the A operand from smem
|
||||
struct KernelTmaWarpSpecialized1SmFastFP32SmemSm100 final : KernelSchedule1Sm, KernelTmaWarpSpecializedFastFP32SmemSm100 { };
|
||||
struct KernelTmaWarpSpecialized2SmFastFP32SmemSm100 final : KernelSchedule2Sm, KernelTmaWarpSpecializedFastFP32SmemSm100 { };
|
||||
// Ptr-Array Transform GEMM: Specialize for 1SM vs 2SM FastF32 GEMM
|
||||
struct KernelScheduleSm100PtrArrayFastFP32Gemm : KernelScheduleSm100FastFP32Gemm {};
|
||||
struct KernelTmaWarpSpecializedPtrArrayFastFP32SmemSm100 : KernelScheduleSm100PtrArrayFastFP32Gemm { };
|
||||
struct KernelPtrArrayTmaWarpSpecialized1SmFastFP32Sm100 final : KernelSchedule1Sm, KernelScheduleSm100PtrArrayFastFP32Gemm { };
|
||||
struct KernelPtrArrayTmaWarpSpecialized2SmFastFP32Sm100 final : KernelSchedule2Sm, KernelScheduleSm100PtrArrayFastFP32Gemm { };
|
||||
struct KernelPtrArrayTmaWarpSpecialized1SmFastFP32SmemSm100 final : KernelSchedule1Sm, KernelTmaWarpSpecializedPtrArrayFastFP32SmemSm100 { };
|
||||
struct KernelPtrArrayTmaWarpSpecialized2SmFastFP32SmemSm100 final : KernelSchedule2Sm, KernelTmaWarpSpecializedPtrArrayFastFP32SmemSm100 { };
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SM100 BlockScaled Dense GEMM Dispatch Policies
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
struct KernelScheduleBlockScaledGemmSm100 : KernelScheduleSm100 {};
|
||||
struct KernelScheduleMxNvf4Sm100 : KernelScheduleBlockScaledGemmSm100 {};
|
||||
struct KernelScheduleMxf8f6f4Sm100 : KernelScheduleBlockScaledGemmSm100 {};
|
||||
// Block Scaled Dense GEMM: Specialize for instruction type, scale factor vector size, and 1SM vs. 2SM
|
||||
struct KernelTmaWarpSpecialized1SmBlockScaledSm100 final : KernelSchedule1Sm, KernelScheduleBlockScaledGemmSm100 { };
|
||||
struct KernelTmaWarpSpecialized2SmBlockScaledSm100 final : KernelSchedule2Sm, KernelScheduleBlockScaledGemmSm100 { };
|
||||
@@ -436,13 +507,10 @@ struct KernelTmaWarpSpecialized1SmMxf4Sm100 final : KernelSchedule1Sm, KernelSch
|
||||
struct KernelTmaWarpSpecialized2SmMxf4Sm100 final : KernelSchedule2Sm, KernelScheduleMxNvf4Sm100 { };
|
||||
struct KernelTmaWarpSpecialized1SmMxf8f6f4Sm100 final : KernelSchedule1Sm, KernelScheduleMxf8f6f4Sm100 { };
|
||||
struct KernelTmaWarpSpecialized2SmMxf8f6f4Sm100 final : KernelSchedule2Sm, KernelScheduleMxf8f6f4Sm100 { };
|
||||
|
||||
|
||||
// Ptr-Array Dense GEMM: Specialize for 1SM vs 2SM
|
||||
struct KernelPtrArrayTmaWarpSpecialized1SmSm100 final : KernelSchedule1Sm, KernelScheduleSm100PtrArrayDenseGemm {};
|
||||
struct KernelPtrArrayTmaWarpSpecialized2SmSm100 final : KernelSchedule2Sm, KernelScheduleSm100PtrArrayDenseGemm {};
|
||||
|
||||
|
||||
// BlockScaled Dense GEMM + (Ptr Array or Group GEMM)
|
||||
struct KernelSchedulePtrArrayBlockScaledGemmSm100 : KernelScheduleBlockScaledGemmSm100 {};
|
||||
struct KernelSchedulePtrArrayMxNvf4Sm100 : KernelSchedulePtrArrayBlockScaledGemmSm100 {};
|
||||
struct KernelSchedulePtrArrayMxf8f6f4Sm100 : KernelSchedulePtrArrayBlockScaledGemmSm100 {};
|
||||
// Ptr-Array Block Scaled Dense GEMM: Specialize for instruction type, scale factor vector size, and 1SM vs. 2SM
|
||||
struct KernelPtrArrayTmaWarpSpecialized1SmBlockScaledSm100 final : KernelSchedule1Sm, KernelSchedulePtrArrayBlockScaledGemmSm100 { };
|
||||
struct KernelPtrArrayTmaWarpSpecialized2SmBlockScaledSm100 final : KernelSchedule2Sm, KernelSchedulePtrArrayBlockScaledGemmSm100 { };
|
||||
@@ -454,6 +522,7 @@ struct KernelPtrArrayTmaWarpSpecialized1SmMxf8f6f4Sm100 final : KernelSchedule1S
|
||||
struct KernelPtrArrayTmaWarpSpecialized2SmMxf8f6f4Sm100 final : KernelSchedule2Sm, KernelSchedulePtrArrayMxf8f6f4Sm100 { };
|
||||
|
||||
|
||||
|
||||
// n-buffer in smem, pipelined with Blackwell UMMA and TMA, Warp specialized dynamic schedule
|
||||
template<
|
||||
int Stages_,
|
||||
@@ -488,6 +557,55 @@ struct MainloopSm100TmaUmmaWarpSpecializedBlockScaled {
|
||||
|
||||
|
||||
|
||||
// n-buffer in smem, pipelined with Blackwell Fast FP32 kernel with UMMA (HwScaled) and TMA,
|
||||
// Warp specialized dynamic schedule
|
||||
template<
|
||||
// Number of Pipeline stages for
|
||||
// MainloopLoad <-> Conversion <-> MainLoad
|
||||
int Load2TransformPipelineStageCount_,
|
||||
// Number of Pipeline stages for
|
||||
// MainloopLoad <-> Conversion <-> MainLoad
|
||||
int Transform2MmaPipelineStageCount_,
|
||||
// TileScheduler pipeline depth
|
||||
int SchedulerPipelineStageCount_,
|
||||
// Accmulator pipeline depth
|
||||
int AccumulatorPipelineStageCount_,
|
||||
// Number of MMA Bands to be computed in a single FastF32 MMA operation.
|
||||
// For BF16 emulation, we have 3 compute matrices, with 9 MMAs forming 5 bands.
|
||||
// We can eliminate bands 4 and/or 5 (up to last 3 MMA operations).
|
||||
// Valid values are 3, 4, 5
|
||||
int NumBandsToCompute_,
|
||||
// Scaling factor for decomposed matrices (2^ScalingFactor)
|
||||
// 8 for BF16, 11 for TF32
|
||||
int ScalingFactor_,
|
||||
// Number of UMMA instructions emulated a single stage
|
||||
// Ex: Staged16 has 1 FastF32 MMA per stage
|
||||
// Should be smaller than K-mode of a single ClusterTile
|
||||
int AccPromotionInterval_,
|
||||
// ClusterShape for the kernel
|
||||
class ClusterShape_ = Shape<_1,_1,_1>,
|
||||
// The TMEM_LOAD atom to be used for loading local accumulator
|
||||
// from TMEM to registers
|
||||
class AccumulatorCopyAtom_ = cute::SM100_TMEM_LOAD_32dp32b32x
|
||||
>
|
||||
struct MainloopSm100TmaUmmaWarpSpecializedFastF32 {
|
||||
constexpr static int Load2TransformPipelineStageCount = Load2TransformPipelineStageCount_;
|
||||
constexpr static int Transform2MmaPipelineStageCount = Transform2MmaPipelineStageCount_;
|
||||
constexpr static int NumBandsToCompute = NumBandsToCompute_;
|
||||
constexpr static int ScalingFactor = ScalingFactor_;
|
||||
constexpr static int AccPromotionInterval = AccPromotionInterval_;
|
||||
constexpr static detail::KernelInputTransformType InputTransformType = detail::KernelInputTransformType::FastF32;
|
||||
using ClusterShape = ClusterShape_;
|
||||
using AccumulatorCopyAtom = AccumulatorCopyAtom_;
|
||||
using ArchTag = arch::Sm100;
|
||||
using Schedule = KernelTmaWarpSpecializedInputTransformSm100<SchedulerPipelineStageCount_, AccumulatorPipelineStageCount_>;
|
||||
|
||||
// For backwards compatibility with GemmUniversalAdapter.
|
||||
constexpr static int Stages = Load2TransformPipelineStageCount;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// n-buffer in smem, pipelined with Blackwell UMMA and TMA, Warp specialized dynamic schedule
|
||||
template<
|
||||
int Stages_,
|
||||
@@ -520,6 +638,55 @@ struct MainloopSm100ArrayTmaUmmaWarpSpecializedBlockScaled {
|
||||
|
||||
|
||||
|
||||
// n-buffer in smem, pipelined with Blackwell Fast FP32 kernel with UMMA (HwScaled) and TMA,
|
||||
// Warp specialized dynamic schedule
|
||||
template<
|
||||
// Number of Pipeline stages for
|
||||
// MainloopLoad <-> Conversion <-> MainLoad
|
||||
int Load2TransformPipelineStageCount_,
|
||||
// Number of Pipeline stages for
|
||||
// MainloopLoad <-> Conversion <-> MainLoad
|
||||
int Transform2MmaPipelineStageCount_,
|
||||
// TileScheduler pipeline depth
|
||||
int SchedulerPipelineStageCount_,
|
||||
// Accmulator pipeline depth
|
||||
int AccumulatorPipelineStageCount_,
|
||||
// Number of MMA Bands to be computed in a single FastF32 MMA operation.
|
||||
// For BF16 emulation, we have 3 compute matrices, with 9 MMAs forming 5 bands.
|
||||
// We can eliminate bands 4 and/or 5 (up to last 3 MMA operations).
|
||||
// Valid values are 3, 4, 5
|
||||
int NumBandsToCompute_,
|
||||
// Scaling factor for decomposed matrices (2^ScalingFactor)
|
||||
// 8 for BF16, 11 for TF32
|
||||
int ScalingFactor_,
|
||||
// Number of UMMA instructions emulated a single stage
|
||||
// Ex: Staged16 has 1 FastF32 MMA per stage
|
||||
// Should be smaller than K-mode of a single ClusterTile
|
||||
int AccPromotionInterval_,
|
||||
// ClusterShape for the kernel
|
||||
class ClusterShape_ = Shape<_1,_1,_1>,
|
||||
// The TMEM_LOAD atom to be used for loading local accumulator
|
||||
// from TMEM to registers
|
||||
class AccumulatorCopyAtom_ = cute::SM100_TMEM_LOAD_32dp32b32x
|
||||
>
|
||||
struct MainloopSm100ArrayTmaUmmaWarpSpecializedFastF32 {
|
||||
constexpr static int Load2TransformPipelineStageCount = Load2TransformPipelineStageCount_;
|
||||
constexpr static int Transform2MmaPipelineStageCount = Transform2MmaPipelineStageCount_;
|
||||
constexpr static int NumBandsToCompute = NumBandsToCompute_;
|
||||
constexpr static int ScalingFactor = ScalingFactor_;
|
||||
constexpr static int AccPromotionInterval = AccPromotionInterval_;
|
||||
constexpr static detail::KernelInputTransformType InputTransformType = detail::KernelInputTransformType::FastF32;
|
||||
using ClusterShape = ClusterShape_;
|
||||
using AccumulatorCopyAtom = AccumulatorCopyAtom_;
|
||||
using ArchTag = arch::Sm100;
|
||||
using Schedule = KernelPtrArrayTmaWarpSpecializedInputTransformSm100<SchedulerPipelineStageCount_, AccumulatorPipelineStageCount_>;
|
||||
|
||||
// For backwards compatibility with GemmUniversalAdapter.
|
||||
constexpr static int Stages = Load2TransformPipelineStageCount;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace cutlass::gemm
|
||||
|
||||
@@ -63,6 +63,10 @@ struct IsCutlass3ArrayKernel<ProblemShape, cute::void_t<typename ProblemShape::U
|
||||
#include "cutlass/gemm/kernel/sm90_gemm_tma_warpspecialized_cooperative.hpp"
|
||||
#include "cutlass/gemm/kernel/sm90_gemm_array_tma_warpspecialized_pingpong.hpp"
|
||||
#include "cutlass/gemm/kernel/sm90_gemm_array_tma_warpspecialized_cooperative.hpp"
|
||||
#include "cutlass/gemm/kernel/sm100_gemm_tma_warpspecialized.hpp"
|
||||
#include "cutlass/gemm/kernel/sm100_gemm_array_tma_warpspecialized.hpp"
|
||||
|
||||
#include "cutlass/gemm/kernel/sm100_gemm_tma_warpspecialized.hpp"
|
||||
#include "cutlass/gemm/kernel/sm100_gemm_array_tma_warpspecialized.hpp"
|
||||
#include "cutlass/gemm/kernel/sm100_gemm_tma_warpspecialized_input_transform.hpp"
|
||||
#include "cutlass/gemm/kernel/sm100_gemm_array_tma_warpspecialized_input_transform.hpp"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
+1139
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -120,7 +120,7 @@ public:
|
||||
typename detail::TileSchedulerSelector<
|
||||
GroupScheduler, ArchTag,
|
||||
TileShape, ClusterShape,
|
||||
2, // Default unused parameter - SchedulerPipelineStageCoun
|
||||
2, // Default unused parameter - SchedulerPipelineStageCount
|
||||
ProblemShape>::Scheduler,
|
||||
typename detail::TileSchedulerSelector<
|
||||
void, ArchTag, TileShape, ClusterShape>::Scheduler>;
|
||||
|
||||
@@ -120,7 +120,7 @@ public:
|
||||
typename detail::TileSchedulerSelector<
|
||||
GroupScheduler, ArchTag,
|
||||
TileShape, ClusterShape,
|
||||
2, // Default unused parameter - SchedulerPipelineStageCoun
|
||||
2, // Default unused parameter - SchedulerPipelineStageCount
|
||||
ProblemShape>::Scheduler,
|
||||
typename detail::TileSchedulerSelector<
|
||||
void, ArchTag, TileShape, ClusterShape>::Scheduler>;
|
||||
|
||||
@@ -1095,6 +1095,34 @@ struct NumericArrayConverter<cutlass::bfloat16_t, float, 2, FloatRoundStyle::rou
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// Partial specialization for Array<cutlass::bfloat16_t, 2> <= Array<float, 2>, round to nearest with min/max saturation
|
||||
template <>
|
||||
struct NumericArrayConverter<cutlass::bfloat16_t, float, 2, FloatRoundStyle::round_to_nearest_satfinite> {
|
||||
|
||||
using result_type = Array<cutlass::bfloat16_t, 2>;
|
||||
using source_type = Array<float, 2>;
|
||||
static FloatRoundStyle const round_style = FloatRoundStyle::round_to_nearest_satfinite;
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
static result_type convert(source_type const & source) {
|
||||
|
||||
unsigned d;
|
||||
|
||||
asm("cvt.rn.satfinite.bf16x2.f32 %0, %1, %2;\n" : "=r"(d) : "f"(source[1]), "f"(source[0]) );
|
||||
|
||||
return reinterpret_cast<result_type const &>(d);
|
||||
}
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
result_type operator()(source_type const &s) const {
|
||||
return convert(s);
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/// Partial specialization for Array<cutlass::bfloat16_t> <= Array<float>
|
||||
template <
|
||||
int N,
|
||||
@@ -2382,7 +2410,6 @@ struct NumericArrayConverterPacked4Element<float_ue8m0_t, float, Round> {
|
||||
};
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Partial specializations for Array<float, N> <=> Array<float_e2m3_unpack8bits_t, N>
|
||||
@@ -3579,7 +3606,6 @@ template <
|
||||
>
|
||||
struct NumericArrayConverter<float_ue8m0_t, S, N, Round> :
|
||||
public PackedNumericArrayConverter<float_ue8m0_t, S, N, Round> {};
|
||||
|
||||
/// Partial specialization for Array<T, N> <= Array<float_ue4m3_t, N>
|
||||
template <
|
||||
typename T,
|
||||
|
||||
@@ -275,6 +275,189 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TMA (producer) Transform (consumer) Async Pipeline
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
template <
|
||||
int Stages_,
|
||||
class AtomThrShape_MNK_ = Shape<_1,_1,_1>
|
||||
>
|
||||
class PipelineTmaTransformAsync {
|
||||
public:
|
||||
static constexpr uint32_t Stages = Stages_;
|
||||
using AtomThrShape_MNK = AtomThrShape_MNK_;
|
||||
private:
|
||||
using Impl = PipelineTmaAsync<Stages>;
|
||||
public:
|
||||
using FullBarrier = typename Impl::FullBarrier;
|
||||
using EmptyBarrier = typename Impl::EmptyBarrier;
|
||||
using ProducerBarrierType = typename Impl::ProducerBarrierType;
|
||||
using ConsumerBarrierType = typename Impl::ConsumerBarrierType;
|
||||
using PipelineState = typename Impl::PipelineState;
|
||||
using SharedStorage = typename Impl::SharedStorage;
|
||||
using ThreadCategory = typename Impl::ThreadCategory;
|
||||
using Params = typename Impl::Params;
|
||||
|
||||
// Constructor
|
||||
template <class ClusterShape, class InitBarriers = cute::true_type, class InitMasks = cute::true_type>
|
||||
CUTLASS_DEVICE
|
||||
PipelineTmaTransformAsync(SharedStorage& storage, Params params, ClusterShape cluster_shape, InitBarriers = {}, InitMasks = {})
|
||||
: impl_(storage, params, cluster_shape, cute::false_type{}, cute::false_type{})
|
||||
, params_(params)
|
||||
, full_barrier_ptr_(&storage.full_barrier_[0])
|
||||
, empty_barrier_ptr_(&storage.empty_barrier_[0]) {
|
||||
|
||||
static_assert(cute::is_same_v<InitBarriers, cute::true_type> || cute::is_same_v<InitBarriers, cute::false_type>);
|
||||
if constexpr (cute::is_same_v<InitBarriers, cute::true_type>) {
|
||||
init_barriers(storage, params_, cluster_shape);
|
||||
}
|
||||
|
||||
static_assert(cute::is_same_v<InitMasks, cute::true_type> || cute::is_same_v<InitMasks, cute::false_type>);
|
||||
if constexpr (cute::is_same_v<InitMasks, cute::true_type>) {
|
||||
init_masks(cluster_shape);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to initialize barriers
|
||||
template <class ClusterShape>
|
||||
static
|
||||
CUTLASS_DEVICE
|
||||
void
|
||||
init_barriers(SharedStorage& storage, Params params, ClusterShape cluster_shape) {
|
||||
int warp_idx = canonical_warp_idx_sync();
|
||||
if (warp_idx == params.initializing_warp) {
|
||||
// Barrier FULL and EMPTY init
|
||||
constexpr int producer_arv_cnt = 1;
|
||||
auto atom_thr_shape = AtomThrShape_MNK{};
|
||||
static constexpr bool IsDynamicCluster = not cute::is_static_v<ClusterShape>;
|
||||
static_assert(IsDynamicCluster or ((cute::size<0>(cluster_shape) % cute::size<0>(atom_thr_shape) == 0) &&
|
||||
(cute::size<1>(cluster_shape) % cute::size<1>(atom_thr_shape) == 0)));
|
||||
uint32_t const multicast_consumer_arrival_count = (cute::size<0>(cluster_shape) / cute::size<0>(atom_thr_shape)) +
|
||||
(cute::size<1>(cluster_shape) / cute::size<1>(atom_thr_shape)) - 1;
|
||||
|
||||
cutlass::arch::detail::initialize_barrier_array_pair_aligned<decltype(storage.full_barrier_), decltype(storage.empty_barrier_), Stages>(
|
||||
storage.full_barrier_, storage.empty_barrier_, producer_arv_cnt, multicast_consumer_arrival_count);
|
||||
}
|
||||
cutlass::arch::fence_barrier_init();
|
||||
}
|
||||
|
||||
template <class ClusterShape>
|
||||
CUTLASS_DEVICE
|
||||
void init_masks(ClusterShape cluster_shape, dim3 block_id_in_cluster = cute::block_id_in_cluster()) {
|
||||
// Calculate consumer mask
|
||||
if (params_.role == ThreadCategory::Consumer) {
|
||||
// Logic to optimally schedule Empty Arrives
|
||||
// Goal : To divide SYNCS Empty Arrival duty equally amongst the Warp-Group (128 threads)
|
||||
int warp_idx = canonical_warp_idx_sync();
|
||||
int thread_idx = threadIdx.x;
|
||||
auto cluster_size = cute::size(cluster_shape);
|
||||
|
||||
// STEP 1 : Use Cute Layout function to generate an optimal dst block-id (0-15)
|
||||
if (params_.num_consumers % NumThreadsPerWarpGroup == 0) {
|
||||
auto [is_signaling_thread, dst_blockid] = detail::spread_arrivals_to_warpgroup(thread_idx % NumThreadsPerWarpGroup, warp_idx);
|
||||
is_signaling_thread_ = is_signaling_thread;
|
||||
dst_blockid_ = dst_blockid;
|
||||
}
|
||||
else if (params_.num_consumers == 32) {
|
||||
auto [is_signaling_thread, dst_blockid] = detail::spread_arrivals_to_warp(thread_idx % 32);
|
||||
is_signaling_thread_ = is_signaling_thread;
|
||||
dst_blockid_ = dst_blockid;
|
||||
}
|
||||
else {
|
||||
is_signaling_thread_ = 0;
|
||||
#ifndef NDEBUG
|
||||
asm volatile ("brkpt;\n" ::);
|
||||
#endif
|
||||
}
|
||||
|
||||
// STEP 2: Find if this dst block-id needs an arrival for this problem
|
||||
is_signaling_thread_ &= dst_blockid_ < cluster_size;
|
||||
is_signaling_thread_ &= is_same_row_or_col(dst_blockid_, block_id_in_cluster, cluster_shape);
|
||||
}
|
||||
}
|
||||
|
||||
template <class ClusterShape>
|
||||
CUTLASS_DEVICE
|
||||
bool is_same_row_or_col(int dst_block_id, dim3 block_id, ClusterShape cluster_shape) {
|
||||
return (((dst_block_id % cute::size<0>(cluster_shape)) == block_id.x) ||
|
||||
(
|
||||
((dst_block_id / cute::size<0>(cluster_shape)) == block_id.y)
|
||||
// If we are in the same cluster column and using 2CTA MMA, only odd or only even CTAs sync with each other
|
||||
&& ((dst_block_id % cute::size<0>(cluster_shape)) % cute::size<0>(AtomThrShape_MNK{}) ==
|
||||
block_id.x % cute::size<0>(AtomThrShape_MNK{}))
|
||||
));
|
||||
}
|
||||
|
||||
////////////////////
|
||||
// Producer APIs
|
||||
////////////////////
|
||||
CUTLASS_DEVICE
|
||||
ProducerToken producer_try_acquire(PipelineState state, uint32_t skip_wait = false) {
|
||||
return impl_.producer_try_acquire(state, skip_wait);
|
||||
}
|
||||
|
||||
CUTLASS_DEVICE
|
||||
void producer_acquire(PipelineState state, ProducerToken barrier_token = {BarrierStatus::WaitAgain}) {
|
||||
impl_.producer_acquire(state, barrier_token);
|
||||
}
|
||||
|
||||
CUTLASS_DEVICE
|
||||
void producer_commit(PipelineState state, uint32_t bytes) {
|
||||
impl_.producer_commit(state, bytes);
|
||||
}
|
||||
|
||||
// Prevents early exit of producer blocks in Cluster.
|
||||
// This should be called once before kernel exits.
|
||||
CUTLASS_DEVICE
|
||||
void producer_tail(PipelineState state) {
|
||||
impl_.producer_tail(state);
|
||||
}
|
||||
|
||||
CUTLASS_DEVICE
|
||||
ProducerBarrierType* producer_get_barrier(PipelineState state) {
|
||||
return impl_.producer_get_barrier(state);
|
||||
}
|
||||
|
||||
////////////////////
|
||||
// Consumer APIs
|
||||
////////////////////
|
||||
CUTLASS_DEVICE
|
||||
ConsumerToken consumer_try_wait(PipelineState state, uint32_t skip_wait = false) {
|
||||
return impl_.consumer_try_wait(state, skip_wait);
|
||||
}
|
||||
|
||||
CUTLASS_DEVICE
|
||||
ConsumerToken consumer_test_wait(PipelineState state, uint32_t skip_wait = false) {
|
||||
return impl_.consumer_test_wait(state, skip_wait);
|
||||
}
|
||||
|
||||
CUTLASS_DEVICE
|
||||
void consumer_wait(PipelineState state) {
|
||||
impl_.consumer_wait(state);
|
||||
}
|
||||
|
||||
CUTLASS_DEVICE
|
||||
void consumer_wait(PipelineState state, ConsumerToken barrier_token) {
|
||||
impl_.consumer_wait(state, barrier_token);
|
||||
}
|
||||
|
||||
CUTLASS_DEVICE
|
||||
void consumer_release(PipelineState state, uint32_t skip = false) {
|
||||
detail::pipeline_check_is_consumer(params_.role);
|
||||
empty_barrier_ptr_[state.index()].arrive(dst_blockid_, is_signaling_thread_ & (!skip));
|
||||
}
|
||||
|
||||
private:
|
||||
Impl impl_;
|
||||
uint32_t dst_blockid_ = 0;
|
||||
uint32_t is_signaling_thread_ = 0;
|
||||
FullBarrier *full_barrier_ptr_ = nullptr;
|
||||
EmptyBarrier *empty_barrier_ptr_ = nullptr;
|
||||
Params params_;
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -391,7 +574,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// !!!!!! I DONT LIKE THIS MCAST BASED CONSTRUCTOR SPECIALIZATION. THIS VARIABLE NEVER CHANGES AT RUNTIME.
|
||||
template<typename InitBarriers = cute::true_type, typename InitMasks = cute::true_type>
|
||||
CUTLASS_DEVICE
|
||||
PipelineTmaUmmaAsync(SharedStorage& storage, Params params, ClusterShape cluster_shape, McastDirection mcast_direction, InitBarriers = {}, InitMasks = {})
|
||||
|
||||
@@ -71,7 +71,7 @@ bool relatively_equal_float(T a, T b, T epsilon, T nonzero_floor) {
|
||||
if (a == b) {
|
||||
return true;
|
||||
}
|
||||
else if (a == zero || b == zero || diff < nonzero_floor) {
|
||||
else if (a == zero || b == zero || (abs_A + abs_B) < nonzero_floor) {
|
||||
return diff < epsilon * nonzero_floor;
|
||||
}
|
||||
|
||||
|
||||
@@ -285,7 +285,7 @@ private:
|
||||
uint8_t storage_ = 0b0000;
|
||||
};
|
||||
|
||||
using MetadataOneChunk = cute::conditional_t<SparseConfig::IsTfmma,
|
||||
using MetadataOneChunk = cute::conditional_t<SparseConfig::IsTF32,
|
||||
MetadataOneChunk1to2,
|
||||
MetadataOneChunk2to4>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user