Extend DualGemm: support batched mode + decouple B0/B1 layouts (#790)

* Fix MHA kernel

Summary:

ATT

Test Plan:

Reviewers:

Subscribers:

Tasks:

Tags:

* Extend DualGemm to support batched mode (#5)

Following the GemmUniversalMode::kBatched implementation, batched mode is added to the DualGemm (under examples/45_dual_gemm). DualGemmMode::kBatched and SplitKSerial are not compatible: Status::kErrorInvalidProblem is returned if both are set.

* Decouple LayoutB0 and LayoutB1 in DualGemm

The DualGemm template assumed the same layout, LayoutB, for both right operand matrices B0 and B1. This is problematic if the layout of the two matrices is different. In particular, this may be the case when one of the matrices is row-major, while the other is a (column) vector that has to be broadcasted in column-major with zero stride (e.g., as {B1.device_data(), 0}) for the DualGemm implementation to be able to process B0 and B1 simultaneously.

In this commit, LayoutB0 and LayoutB1 are decoupled throughout the DualGemm code (device, kernel, and mma). Additionally, the batch strides of B0 and B1 are also decoupled to accommodate the column vector B1 case described above.

* Remove comment as no longer relevant

* Revert Fix MHA kernel

---------

Co-authored-by: mikeiovine <mikeiovine@fb.com>
This commit is contained in:
Adnan Akhundov
2023-02-13 15:27:13 -05:00
committed by GitHub
co-authored by mikeiovine
parent ce8597dc14
commit 3c995c7606
7 changed files with 793 additions and 305 deletions
@@ -58,7 +58,9 @@ template <
/// Size of the Gemm problem - concept: gemm::GemmShape<>
typename Shape_,
/// Policy describing tuning details (concept: MmaPolicy)
typename Policy_,
typename Policy0_,
/// B1-specific version of the policy (concept: MmaPolicy)
typename Policy1_,
/// Number of stages,
int Stages,
/// Used for partial specialization
@@ -69,18 +71,20 @@ class DualMmaBase {
using Shape = Shape_;
///< Policy describing tuning details
using Policy = Policy_;
using Policy0 = Policy0_;
using Policy1 = Policy1_;
//
// Dependent types
//
/// Warp-level Mma
using Operator = typename Policy::Operator;
using Operator0 = typename Policy0::Operator;
using Operator1 = typename Policy1::Operator;
/// Shape describing the overall GEMM computed from shared memory
/// by each warp.
using WarpGemm = typename Policy::Operator::Shape;
using WarpGemm = typename Policy0::Operator::Shape;
/// Shape describing the number of warps filling the CTA
using WarpCount = GemmShape<Shape::kM / WarpGemm::kM,
@@ -89,16 +93,17 @@ class DualMmaBase {
/// Number of warp-level GEMM oeprations
static int const kWarpGemmIterations =
(WarpGemm::kK / Operator::Policy::MmaShape::kK);
(WarpGemm::kK / Operator0::Policy::MmaShape::kK);
/// Number of stages
static int const kStages = Stages;
/// Tensor reference to the A operand
using TensorRefA = TensorRef<typename Operator::ElementA, typename Operator::LayoutA>;
using TensorRefA = TensorRef<typename Operator0::ElementA, typename Operator0::LayoutA>;
/// Tensor reference to the B operand
using TensorRefB = TensorRef<typename Operator::ElementB, typename Operator::LayoutB>;
using TensorRefB0 = TensorRef<typename Operator0::ElementB, typename Operator0::LayoutB>;
using TensorRefB1 = TensorRef<typename Operator1::ElementB, typename Operator1::LayoutB>;
static_assert(kWarpGemmIterations > 1,
"The pipelined structure requires at least two warp-level "
@@ -119,14 +124,17 @@ class DualMmaBase {
//
/// Shape of the A matrix operand in shared memory
using ShapeA = MatrixShape<Shape::kM + Policy::SmemPaddingA::kRow,
using ShapeA = MatrixShape<Shape::kM + Policy0::SmemPaddingA::kRow,
Shape::kK * kStages +
Policy::SmemPaddingA::kColumn>;
Policy0::SmemPaddingA::kColumn>;
/// Shape of the B matrix operand in shared memory
using ShapeB =
MatrixShape<Shape::kK * kStages + Policy::SmemPaddingB::kRow,
Shape::kN + Policy::SmemPaddingB::kColumn>;
using ShapeB0 =
MatrixShape<Shape::kK * kStages + Policy0::SmemPaddingB::kRow,
Shape::kN + Policy0::SmemPaddingB::kColumn>;
using ShapeB1 =
MatrixShape<Shape::kK * kStages + Policy1::SmemPaddingB::kRow,
Shape::kN + Policy1::SmemPaddingB::kColumn>;
public:
//
@@ -134,11 +142,11 @@ class DualMmaBase {
//
/// Buffer for A operand
AlignedBuffer<typename Operator::ElementA, ShapeA::kCount> operand_A;
AlignedBuffer<typename Operator0::ElementA, ShapeA::kCount> operand_A;
/// Buffer for B operand
AlignedBuffer<typename Operator::ElementB, ShapeB::kCount> operand_B0;
AlignedBuffer<typename Operator::ElementB, ShapeB::kCount> operand_B1;
AlignedBuffer<typename Operator0::ElementB, ShapeB0::kCount> operand_B0;
AlignedBuffer<typename Operator1::ElementB, ShapeB1::kCount> operand_B1;
public:
@@ -148,14 +156,20 @@ class DualMmaBase {
/// Returns a layout object for the A matrix
CUTLASS_DEVICE
static typename Operator::LayoutA LayoutA() {
return Operator::LayoutA::packed({ShapeA::kRow, ShapeA::kColumn});
static typename Operator0::LayoutA LayoutA() {
return Operator0::LayoutA::packed({ShapeA::kRow, ShapeA::kColumn});
}
/// Returns a layout object for the B matrix
CUTLASS_HOST_DEVICE
static typename Operator::LayoutB LayoutB() {
return Operator::LayoutB::packed({ShapeB::kRow, ShapeB::kColumn});
static typename Operator0::LayoutB LayoutB0() {
return Operator0::LayoutB::packed({ShapeB0::kRow, ShapeB0::kColumn});
}
/// Returns a layout object for the B matrix
CUTLASS_HOST_DEVICE
static typename Operator1::LayoutB LayoutB1() {
return Operator1::LayoutB::packed({ShapeB1::kRow, ShapeB1::kColumn});
}
/// Returns a TensorRef to the A operand
@@ -166,12 +180,12 @@ class DualMmaBase {
/// Returns a TensorRef to the B operand
CUTLASS_HOST_DEVICE
TensorRefB operand_B0_ref() {
return TensorRefB{operand_B0.data(), LayoutB()};
TensorRefB0 operand_B0_ref() {
return TensorRefB0{operand_B0.data(), LayoutB0()};
}
CUTLASS_HOST_DEVICE
TensorRefB operand_B1_ref() {
return TensorRefB{operand_B1.data(), LayoutB()};
TensorRefB1 operand_B1_ref() {
return TensorRefB1{operand_B1.data(), LayoutB1()};
}
};
@@ -182,11 +196,11 @@ class DualMmaBase {
//
/// Iterator to load a warp-scoped tile of A operand from shared memory
typename Operator::IteratorA warp_tile_iterator_A_;
typename Operator0::IteratorA warp_tile_iterator_A_;
/// Iterator to load a warp-scoped tile of B operand from shared memory
typename Operator::IteratorB warp_tile_iterator_B0_;
typename Operator::IteratorB warp_tile_iterator_B1_;
typename Operator0::IteratorB warp_tile_iterator_B0_;
typename Operator1::IteratorB warp_tile_iterator_B1_;
public:
@@ -67,21 +67,30 @@ template <
typename SmemIteratorA_,
/// Cache operation for operand A
cutlass::arch::CacheOperation::Kind CacheOpA,
/// Iterates over tiles of B operand in global memory
/// Iterates over tiles of B0 operand in global memory
// (concept: ReadableTileIterator | ForwardTileIterator |
// MaskedTileIterator)
typename IteratorB_,
/// Iterates over tiles of B operand in shared memory
typename IteratorB0_,
/// Iterates over tiles of B0 operand in shared memory
/// (concept: WriteableTileIterator | RandomAccessTileIterator)
typename SmemIteratorB_,
typename SmemIteratorB0_,
/// Cache operation for operand B
cutlass::arch::CacheOperation::Kind CacheOpB,
/// Iterates over tiles of B1 operand in global memory
// (concept: ReadableTileIterator | ForwardTileIterator |
// MaskedTileIterator)
typename IteratorB1_,
/// Iterates over tiles of B1 operand in shared memory
/// (concept: WriteableTileIterator | RandomAccessTileIterator)
typename SmemIteratorB1_,
/// Data type of accumulator matrix
typename ElementC_,
/// Data type of accumulator matrix
typename LayoutC_,
/// Policy describing tuning details (concept: MmaPolicy)
typename Policy_,
typename Policy0_,
/// B1-specific version of the policy (concept: MmaPolicy)
typename Policy1_,
/// Number of stages,
int Stages,
/// Use zfill or predicate for out-of-bound cp.async
@@ -89,25 +98,29 @@ template <
/// Used for partial specialization
typename Enable = bool>
class DualMmaMultistage :
public DualMmaBase<Shape_, Policy_, Stages> {
public DualMmaBase<Shape_, Policy0_, Policy1_, Stages> {
public:
///< Base class
using Base = DualMmaBase<Shape_, Policy_, Stages>;
using Base = DualMmaBase<Shape_, Policy0_, Policy1_, Stages>;
///< Size of the Gemm problem - concept: gemm::GemmShape<>
using Shape = Shape_;
///< Iterates over tiles of A operand in global memory
using IteratorA = IteratorA_;
///< Iterates over tiles of B operand in global memory
using IteratorB = IteratorB_;
///< Iterates over tiles of B0 operand in global memory
using IteratorB0 = IteratorB0_;
///< Iterates over tiles of B1 operand in global memory
using IteratorB1 = IteratorB1_;
///< Data type of accumulator matrix
using ElementC = ElementC_;
///< Layout of accumulator matrix
using LayoutC = LayoutC_;
///< Policy describing tuning details
using Policy = Policy_;
using Policy0 = Policy0_;
using Policy1 = Policy1_;
using SmemIteratorA = SmemIteratorA_;
using SmemIteratorB = SmemIteratorB_;
using SmemIteratorB0 = SmemIteratorB0_;
using SmemIteratorB1 = SmemIteratorB1_;
static cutlass::arch::CacheOperation::Kind const kCacheOpA = CacheOpA;
static cutlass::arch::CacheOperation::Kind const kCacheOpB = CacheOpB;
@@ -117,19 +130,21 @@ public:
//
/// Fragment of accumulator tile
using FragmentC = typename Policy::Operator::FragmentC;
using FragmentC = typename Policy0::Operator::FragmentC;
/// Warp-level Mma
using Operator = typename Policy::Operator;
using Operator0 = typename Policy0::Operator;
using Operator1 = typename Policy1::Operator;
/// Minimum architecture is Sm80 to support cp.async
using ArchTag = arch::Sm80;
/// Complex transform on A operand
static ComplexTransform const kTransformA = Operator::kTransformA;
static ComplexTransform const kTransformA = Operator0::kTransformA;
/// Complex transform on B operand
static ComplexTransform const kTransformB = Operator::kTransformB;
static ComplexTransform const kTransformB0 = Operator0::kTransformB;
static ComplexTransform const kTransformB1 = Operator1::kTransformB;
/// Internal structure exposed for introspection.
struct Detail {
@@ -140,7 +155,7 @@ public:
/// Number of cp.async instructions to load one stage of operand B
static int const AsyncCopyIterationsPerStageB =
IteratorB::ThreadMap::Iterations::kCount;
IteratorB0::ThreadMap::Iterations::kCount;
/// Number of stages
static int const kStages = Stages;
@@ -156,10 +171,12 @@ public:
private:
using WarpLoadedFragmentA = typename Operator::FragmentA;
using WarpLoadedFragmentB = typename Operator::FragmentB;
using WarpTransformedFragmentA = typename Operator::TransformedFragmentA;
using WarpTransformedFragmentB = typename Operator::TransformedFragmentB;
using WarpLoadedFragmentA = typename Operator0::FragmentA;
using WarpLoadedFragmentB0 = typename Operator0::FragmentB;
using WarpLoadedFragmentB1 = typename Operator1::FragmentB;
using WarpTransformedFragmentA = typename Operator0::TransformedFragmentA;
using WarpTransformedFragmentB0 = typename Operator0::TransformedFragmentB;
using WarpTransformedFragmentB1 = typename Operator1::TransformedFragmentB;
private:
@@ -171,8 +188,8 @@ public:
SmemIteratorA smem_iterator_A_;
/// Iterator to write threadblock-scoped tile of B operand to shared memory
SmemIteratorB smem_iterator_B0_;
SmemIteratorB smem_iterator_B1_;
SmemIteratorB0 smem_iterator_B0_;
SmemIteratorB1 smem_iterator_B1_;
public:
@@ -215,7 +232,7 @@ public:
}
CUTLASS_DEVICE
void copy_tiles_and_advance(IteratorA &iterator_A, IteratorB &iterator_B0, IteratorB &iterator_B1,
void copy_tiles_and_advance(IteratorA &iterator_A, IteratorB0 &iterator_B0, IteratorB1 &iterator_B1,
int group_start_A = 0, int group_start_B = 0) {
iterator_A.set_iteration_index(group_start_A *
IteratorA::kAccessesPerVector);
@@ -253,9 +270,9 @@ public:
}
iterator_B0.set_iteration_index(group_start_B *
IteratorB::kAccessesPerVector);
IteratorB0::kAccessesPerVector);
iterator_B1.set_iteration_index(group_start_B *
IteratorB::kAccessesPerVector);
IteratorB1::kAccessesPerVector);
this->smem_iterator_B0_.set_iteration_index(group_start_B);
this->smem_iterator_B1_.set_iteration_index(group_start_B);
@@ -263,16 +280,16 @@ public:
CUTLASS_PRAGMA_UNROLL
for (int j = 0; j < Detail::kAccessesPerGroupB; ++j) {
if (group_start_B + j < Detail::AsyncCopyIterationsPerStageB) {
typename IteratorB::AccessType *dst_ptr =
reinterpret_cast<typename IteratorB::AccessType *>(
typename IteratorB0::AccessType *dst_ptr =
reinterpret_cast<typename IteratorB0::AccessType *>(
this->smem_iterator_B0_.get());
int const kSrcBytes = sizeof_bits<typename IteratorB::Element>::value *
IteratorB::ThreadMap::kElementsPerAccess /
IteratorB::kAccessesPerVector / 8;
int const kSrcBytes = sizeof_bits<typename IteratorB0::Element>::value *
IteratorB0::ThreadMap::kElementsPerAccess /
IteratorB0::kAccessesPerVector / 8;
CUTLASS_PRAGMA_UNROLL
for (int v = 0; v < IteratorB::kAccessesPerVector; ++v) {
for (int v = 0; v < IteratorB0::kAccessesPerVector; ++v) {
auto gmem_ptr = iterator_B0.get();
if (SharedMemoryClear == SharedMemoryClearOption::kZfill) {
@@ -292,16 +309,16 @@ public:
CUTLASS_PRAGMA_UNROLL
for (int j = 0; j < Detail::kAccessesPerGroupB; ++j) {
if (group_start_B + j < Detail::AsyncCopyIterationsPerStageB) {
typename IteratorB::AccessType *dst_ptr =
reinterpret_cast<typename IteratorB::AccessType *>(
typename IteratorB1::AccessType *dst_ptr =
reinterpret_cast<typename IteratorB1::AccessType *>(
this->smem_iterator_B1_.get());
int const kSrcBytes = sizeof_bits<typename IteratorB::Element>::value *
IteratorB::ThreadMap::kElementsPerAccess /
IteratorB::kAccessesPerVector / 8;
int const kSrcBytes = sizeof_bits<typename IteratorB1::Element>::value *
IteratorB1::ThreadMap::kElementsPerAccess /
IteratorB1::kAccessesPerVector / 8;
CUTLASS_PRAGMA_UNROLL
for (int v = 0; v < IteratorB::kAccessesPerVector; ++v) {
for (int v = 0; v < IteratorB1::kAccessesPerVector; ++v) {
auto gmem_ptr = iterator_B1.get();
if (SharedMemoryClear == SharedMemoryClearOption::kZfill) {
@@ -330,8 +347,8 @@ public:
///< iterator over A operand in global memory
IteratorA iterator_A,
///< iterator over B operand in global memory
IteratorB iterator_B0,
IteratorB iterator_B1,
IteratorB0 iterator_B0,
IteratorB1 iterator_B1,
///< initial value of accumulator
FragmentC const &src_accum0,
FragmentC const &src_accum1
@@ -386,16 +403,16 @@ public:
// Async Copy for operand B0
CUTLASS_PRAGMA_UNROLL
for (int j = 0; j < Detail::AsyncCopyIterationsPerStageB; ++j) {
typename IteratorB::AccessType *dst_ptr =
reinterpret_cast<typename IteratorB::AccessType *>(
typename IteratorB0::AccessType *dst_ptr =
reinterpret_cast<typename IteratorB0::AccessType *>(
this->smem_iterator_B0_.get());
CUTLASS_PRAGMA_UNROLL
for (int v = 0; v < IteratorB::kAccessesPerVector; ++v) {
for (int v = 0; v < IteratorB0::kAccessesPerVector; ++v) {
int const kSrcBytes =
sizeof_bits<typename IteratorB::Element>::value *
IteratorB::ThreadMap::kElementsPerAccess /
IteratorB::kAccessesPerVector / 8;
sizeof_bits<typename IteratorB0::Element>::value *
IteratorB0::ThreadMap::kElementsPerAccess /
IteratorB0::kAccessesPerVector / 8;
cutlass::arch::cp_async_zfill<kSrcBytes, kCacheOpB>(
dst_ptr + v, iterator_B0.get(), iterator_B0.valid());
@@ -408,16 +425,16 @@ public:
// Async Copy for operand B1
CUTLASS_PRAGMA_UNROLL
for (int j = 0; j < Detail::AsyncCopyIterationsPerStageB; ++j) {
typename IteratorB::AccessType *dst_ptr =
reinterpret_cast<typename IteratorB::AccessType *>(
typename IteratorB1::AccessType *dst_ptr =
reinterpret_cast<typename IteratorB1::AccessType *>(
this->smem_iterator_B1_.get());
CUTLASS_PRAGMA_UNROLL
for (int v = 0; v < IteratorB::kAccessesPerVector; ++v) {
for (int v = 0; v < IteratorB1::kAccessesPerVector; ++v) {
int const kSrcBytes =
sizeof_bits<typename IteratorB::Element>::value *
IteratorB::ThreadMap::kElementsPerAccess /
IteratorB::kAccessesPerVector / 8;
sizeof_bits<typename IteratorB1::Element>::value *
IteratorB1::ThreadMap::kElementsPerAccess /
IteratorB1::kAccessesPerVector / 8;
cutlass::arch::cp_async_zfill<kSrcBytes, kCacheOpB>(
dst_ptr + v, iterator_B1.get(), iterator_B1.valid());
@@ -473,35 +490,35 @@ public:
++last_smem_iterator_A;
}
typename IteratorB::AccessType zero_B;
typename IteratorB0::AccessType zero_B;
zero_B.clear();
/// Iterator to write threadblock-scoped tile of B0 operand to shared memory
SmemIteratorB last_smem_iterator_B0(this->smem_iterator_B0_);
SmemIteratorB0 last_smem_iterator_B0(this->smem_iterator_B0_);
last_smem_iterator_B0.set_iteration_index(0);
// Async Copy for operand B
// Async Copy for operand B0
CUTLASS_PRAGMA_UNROLL
for (int j = 0; j < Detail::AsyncCopyIterationsPerStageB; ++j) {
typename IteratorB::AccessType *dst_ptr =
reinterpret_cast<typename IteratorB::AccessType *>(
typename IteratorB0::AccessType *dst_ptr =
reinterpret_cast<typename IteratorB0::AccessType *>(
last_smem_iterator_B0.get());
*dst_ptr = zero_B;
++last_smem_iterator_B0;
}
/// Iterator to write threadblock-scoped tile of B1 operand to shared memory
SmemIteratorB last_smem_iterator_B1(this->smem_iterator_B1_);
SmemIteratorB1 last_smem_iterator_B1(this->smem_iterator_B1_);
last_smem_iterator_B1.set_iteration_index(0);
// Async Copy for operand B
// Async Copy for operand B1
CUTLASS_PRAGMA_UNROLL
for (int j = 0; j < Detail::AsyncCopyIterationsPerStageB; ++j) {
typename IteratorB::AccessType *dst_ptr =
reinterpret_cast<typename IteratorB::AccessType *>(
typename IteratorB1::AccessType *dst_ptr =
reinterpret_cast<typename IteratorB1::AccessType *>(
last_smem_iterator_B1.get());
*dst_ptr = zero_B;
@@ -517,13 +534,14 @@ public:
// Pair of fragments used to overlap shared memory loads and math
// instructions
WarpLoadedFragmentA warp_loaded_frag_A[2];
WarpLoadedFragmentB warp_loaded_frag_B0[2];
WarpLoadedFragmentB warp_loaded_frag_B1[2];
WarpLoadedFragmentB0 warp_loaded_frag_B0[2];
WarpLoadedFragmentB1 warp_loaded_frag_B1[2];
WarpTransformedFragmentA warp_transformed_frag_A[2];
WarpTransformedFragmentB warp_transformed_frag_B0[2];
WarpTransformedFragmentB warp_transformed_frag_B1[2];
WarpTransformedFragmentB0 warp_transformed_frag_B0[2];
WarpTransformedFragmentB1 warp_transformed_frag_B1[2];
Operator warp_mma;
Operator0 warp_mma0;
Operator1 warp_mma1;
this->warp_tile_iterator_A_.set_kgroup_index(0);
this->warp_tile_iterator_B0_.set_kgroup_index(0);
@@ -544,10 +562,10 @@ public:
int smem_write_stage_idx = Base::kStages - 1;
int smem_read_stage_idx = 0;
warp_mma.transform(warp_transformed_frag_A[0], warp_transformed_frag_B0[0],
warp_loaded_frag_A[0], warp_loaded_frag_B0[0]);
warp_mma.transform(warp_transformed_frag_A[0], warp_transformed_frag_B1[0],
warp_loaded_frag_A[0], warp_loaded_frag_B1[0]);
warp_mma0.transform(warp_transformed_frag_A[0], warp_transformed_frag_B0[0],
warp_loaded_frag_A[0], warp_loaded_frag_B0[0]);
warp_mma1.transform(warp_transformed_frag_A[0], warp_transformed_frag_B1[0],
warp_loaded_frag_A[0], warp_loaded_frag_B1[0]);
// tf32x3 kernels use staging accumulation. warp_mma uses a temporary
// accumulator and this temporary accumulator is added to the final
@@ -556,9 +574,9 @@ public:
FragmentC tmp_accum0, tmp_accum1;
if (platform::is_same<typename Operator::MathOperator,
if (platform::is_same<typename Operator0::MathOperator,
arch::OpMultiplyAddFastF32>::value
|| platform::is_same<typename Operator::MathOperator,
|| platform::is_same<typename Operator0::MathOperator,
arch::OpMultiplyAddComplexFastF32>::value) {
tmp_accum0.clear();
@@ -597,28 +615,28 @@ public:
++this->warp_tile_iterator_B1_;
if (warp_mma_k > 0) {
warp_mma.transform(warp_transformed_frag_A[warp_mma_k % 2],
warp_transformed_frag_B0[warp_mma_k % 2],
warp_loaded_frag_A[warp_mma_k % 2],
warp_loaded_frag_B0[warp_mma_k % 2]);
warp_mma.transform(warp_transformed_frag_A[warp_mma_k % 2],
warp_transformed_frag_B1[warp_mma_k % 2],
warp_loaded_frag_A[warp_mma_k % 2],
warp_loaded_frag_B1[warp_mma_k % 2]);
warp_mma0.transform(warp_transformed_frag_A[warp_mma_k % 2],
warp_transformed_frag_B0[warp_mma_k % 2],
warp_loaded_frag_A[warp_mma_k % 2],
warp_loaded_frag_B0[warp_mma_k % 2]);
warp_mma1.transform(warp_transformed_frag_A[warp_mma_k % 2],
warp_transformed_frag_B1[warp_mma_k % 2],
warp_loaded_frag_A[warp_mma_k % 2],
warp_loaded_frag_B1[warp_mma_k % 2]);
}
if (platform::is_same<typename Operator::MathOperator,
if (platform::is_same<typename Operator0::MathOperator,
arch::OpMultiplyAddFastF32>::value
|| platform::is_same<typename Operator::MathOperator,
|| platform::is_same<typename Operator0::MathOperator,
arch::OpMultiplyAddComplexFastF32>::value) {
warp_mma(
warp_mma0(
tmp_accum0,
warp_transformed_frag_A[warp_mma_k % 2],
warp_transformed_frag_B0[warp_mma_k % 2],
tmp_accum0
);
warp_mma(
warp_mma1(
tmp_accum1,
warp_transformed_frag_A[warp_mma_k % 2],
warp_transformed_frag_B1[warp_mma_k % 2],
@@ -632,13 +650,13 @@ public:
tmp_accum1.clear();
}
} else {
warp_mma(
warp_mma0(
accum0,
warp_transformed_frag_A[warp_mma_k % 2],
warp_transformed_frag_B0[warp_mma_k % 2],
accum0
);
warp_mma(
warp_mma1(
accum1,
warp_transformed_frag_A[warp_mma_k % 2],
warp_transformed_frag_B1[warp_mma_k % 2],
@@ -696,14 +714,14 @@ public:
if (smem_read_stage_idx == (Base::kStages - 1)) {
this->warp_tile_iterator_A_.add_tile_offset(
{0, -Base::kStages * Policy::kPartitionsK *
{0, -Base::kStages * Policy0::kPartitionsK *
Base::kWarpGemmIterations});
this->warp_tile_iterator_B0_.add_tile_offset(
{-Base::kStages * Policy::kPartitionsK *
{-Base::kStages * Policy0::kPartitionsK *
Base::kWarpGemmIterations,
0});
this->warp_tile_iterator_B1_.add_tile_offset(
{-Base::kStages * Policy::kPartitionsK *
{-Base::kStages * Policy1::kPartitionsK *
Base::kWarpGemmIterations,
0});
smem_read_stage_idx = 0;
@@ -720,22 +738,22 @@ public:
// Do any conversions feeding the first stage at the end of the loop so
// we can start right away on mma instructions
if (warp_mma_k + 1 == Base::kWarpGemmIterations) {
warp_mma.transform(warp_transformed_frag_A[(warp_mma_k + 1) % 2],
warp_transformed_frag_B0[(warp_mma_k + 1) % 2],
warp_loaded_frag_A[(warp_mma_k + 1) % 2],
warp_loaded_frag_B0[(warp_mma_k + 1) % 2]);
warp_mma.transform(warp_transformed_frag_A[(warp_mma_k + 1) % 2],
warp_transformed_frag_B1[(warp_mma_k + 1) % 2],
warp_loaded_frag_A[(warp_mma_k + 1) % 2],
warp_loaded_frag_B1[(warp_mma_k + 1) % 2]);
warp_mma0.transform(warp_transformed_frag_A[(warp_mma_k + 1) % 2],
warp_transformed_frag_B0[(warp_mma_k + 1) % 2],
warp_loaded_frag_A[(warp_mma_k + 1) % 2],
warp_loaded_frag_B0[(warp_mma_k + 1) % 2]);
warp_mma1.transform(warp_transformed_frag_A[(warp_mma_k + 1) % 2],
warp_transformed_frag_B1[(warp_mma_k + 1) % 2],
warp_loaded_frag_A[(warp_mma_k + 1) % 2],
warp_loaded_frag_B1[(warp_mma_k + 1) % 2]);
}
}
}
if (platform::is_same<typename Operator::MathOperator,
if (platform::is_same<typename Operator0::MathOperator,
arch::OpMultiplyAddFastF32>::value
|| platform::is_same<typename Operator::MathOperator,
|| platform::is_same<typename Operator0::MathOperator,
arch::OpMultiplyAddComplexFastF32>::value) {
accum0 = plus_accum(accum0, tmp_accum0);
accum1 = plus_accum(accum1, tmp_accum1);