co-authored by
Aniket Shivam
parent
ca23ff7924
commit
b72cbf957d
@@ -65,6 +65,8 @@
|
||||
#include "cutlass/epilogue/threadblock/shared_load_iterator.h"
|
||||
#include "cutlass/epilogue/threadblock/epilogue.h"
|
||||
|
||||
#include "cutlass/layout/permute.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cutlass {
|
||||
@@ -79,7 +81,8 @@ template <
|
||||
typename WarpMmaSimt_,
|
||||
typename OutputOp_,
|
||||
int ElementsPerAccess,
|
||||
bool ScatterD = false
|
||||
bool ScatterD = false,
|
||||
typename PermuteDLayout = layout::NoPermute
|
||||
>
|
||||
struct DefaultEpilogueSimt {
|
||||
|
||||
@@ -109,7 +112,8 @@ struct DefaultEpilogueSimt {
|
||||
using OutputTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<
|
||||
OutputTileThreadMap,
|
||||
ElementOutput,
|
||||
ScatterD
|
||||
ScatterD,
|
||||
PermuteDLayout
|
||||
>;
|
||||
|
||||
using AccumulatorFragmentIterator = cutlass::epilogue::warp::FragmentIteratorSimt<
|
||||
@@ -310,7 +314,6 @@ struct DefaultEpilogueSimtAffineRankN {
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace threadblock
|
||||
} // namespace epilogue
|
||||
} // namespace cutlass
|
||||
|
||||
@@ -74,6 +74,8 @@
|
||||
#include "cutlass/epilogue/threadblock/epilogue.h"
|
||||
#include "cutlass/epilogue/threadblock/interleaved_epilogue.h"
|
||||
|
||||
#include "cutlass/layout/permute.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cutlass {
|
||||
@@ -166,7 +168,7 @@ template <
|
||||
typename ThreadMap
|
||||
>
|
||||
struct DefaultIteratorsTensorOp<float, int32_t, 4, ThreadblockShape, WarpShape, InstructionShape, ThreadMap> {
|
||||
|
||||
|
||||
using WarpTileIterator = cutlass::epilogue::warp::TileIteratorTensorOp<
|
||||
WarpShape,
|
||||
InstructionShape,
|
||||
@@ -265,7 +267,7 @@ struct DefaultIteratorsTensorOp<
|
||||
layout::RowMajor
|
||||
>;
|
||||
|
||||
using WarpTileIterator = typename cutlass::platform::conditional<
|
||||
using WarpTileIterator = typename platform::conditional<
|
||||
(ThreadblockShape::kN == 256),
|
||||
WarpTileIteratorNotMixed,
|
||||
WarpTileIteratorMixed>::type;
|
||||
@@ -284,7 +286,7 @@ struct DefaultIteratorsTensorOp<
|
||||
int32_t
|
||||
>;
|
||||
|
||||
using SharedLoadIterator = typename cutlass::platform::conditional<
|
||||
using SharedLoadIterator = typename platform::conditional<
|
||||
(ThreadblockShape::kN == 256),
|
||||
SharedLoadIteratorNotMixed,
|
||||
SharedLoadIteratorMixed>::type;
|
||||
@@ -302,7 +304,8 @@ template <
|
||||
int PartitionsK,
|
||||
typename OutputOp_,
|
||||
int ElementsPerAccess,
|
||||
bool ScatterD = false
|
||||
bool ScatterD = false,
|
||||
typename PermuteDLayout = layout::NoPermute
|
||||
>
|
||||
struct DefaultEpilogueTensorOp {
|
||||
|
||||
@@ -334,6 +337,7 @@ struct DefaultEpilogueTensorOp {
|
||||
OutputTileThreadMap,
|
||||
ElementOutput,
|
||||
ScatterD,
|
||||
PermuteDLayout,
|
||||
UseCUDAStore
|
||||
>;
|
||||
|
||||
@@ -570,7 +574,6 @@ struct DefaultEpilogueTensorOpAffineRankN {
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Defines sensible defaults for epilogues for TensorOps which uses
|
||||
/// intereleaved output layout. For this case, shared memory is not needed.
|
||||
template <typename Shape_, typename WarpMmaTensorOp_, int PartitionsK,
|
||||
|
||||
@@ -66,6 +66,8 @@
|
||||
|
||||
#include "cutlass/epilogue/threadblock/epilogue.h"
|
||||
|
||||
#include "cutlass/layout/permute.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cutlass {
|
||||
@@ -81,7 +83,8 @@ template <
|
||||
int PartitionsK,
|
||||
typename OutputOp_,
|
||||
int ElementsPerAccess,
|
||||
bool ScatterD = false
|
||||
bool ScatterD = false,
|
||||
typename PermuteDLayout = layout::NoPermute
|
||||
>
|
||||
struct DefaultEpilogueVoltaTensorOp {
|
||||
|
||||
@@ -111,7 +114,8 @@ struct DefaultEpilogueVoltaTensorOp {
|
||||
using OutputTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<
|
||||
OutputTileThreadMap,
|
||||
ElementOutput,
|
||||
ScatterD
|
||||
ScatterD,
|
||||
PermuteDLayout
|
||||
>;
|
||||
|
||||
using AccumulatorFragmentIterator = cutlass::epilogue::warp::FragmentIteratorVoltaTensorOp<
|
||||
@@ -326,7 +330,6 @@ struct DefaultEpilogueVoltaTensorOpAffineRankN {
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace threadblock
|
||||
} // namespace epilogue
|
||||
} // namespace cutlass
|
||||
|
||||
@@ -49,6 +49,8 @@
|
||||
#include "cutlass/epilogue/threadblock/epilogue.h"
|
||||
#include "cutlass/epilogue/threadblock/epilogue_with_broadcast.h"
|
||||
|
||||
#include "cutlass/layout/permute.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cutlass {
|
||||
@@ -67,7 +69,8 @@ template <
|
||||
typename ElementVector,
|
||||
typename OutputOp,
|
||||
int ElementsPerAccess,
|
||||
bool ScatterD = false
|
||||
bool ScatterD = false,
|
||||
typename PermuteDLayout = layout::NoPermute
|
||||
>
|
||||
struct DefaultEpilogueWithBroadcastTensorOp {
|
||||
|
||||
@@ -86,7 +89,8 @@ struct DefaultEpilogueWithBroadcastTensorOp {
|
||||
using OutputTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<
|
||||
typename Base::OutputTileThreadMap,
|
||||
ElementOutput,
|
||||
ScatterD
|
||||
ScatterD,
|
||||
PermuteDLayout
|
||||
>;
|
||||
|
||||
//
|
||||
|
||||
@@ -50,6 +50,8 @@
|
||||
#include "cutlass/epilogue/threadblock/epilogue.h"
|
||||
#include "cutlass/epilogue/threadblock/epilogue_with_reduction.h"
|
||||
|
||||
#include "cutlass/layout/permute.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cutlass {
|
||||
@@ -67,7 +69,8 @@ template <
|
||||
typename OutputOp,
|
||||
typename ReductionOp,
|
||||
int ElementsPerAccess,
|
||||
bool ScatterD = false
|
||||
bool ScatterD = false,
|
||||
typename PermuteDLayout = layout::NoPermute
|
||||
>
|
||||
struct DefaultEpilogueWithReductionTensorOp {
|
||||
|
||||
@@ -89,7 +92,8 @@ struct DefaultEpilogueWithReductionTensorOp {
|
||||
using OutputTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<
|
||||
typename Base::OutputTileThreadMap,
|
||||
ElementOutput,
|
||||
ScatterD
|
||||
ScatterD,
|
||||
PermuteDLayout
|
||||
>;
|
||||
|
||||
/// Define the epilogue
|
||||
@@ -120,7 +124,8 @@ template <
|
||||
typename OutputOp,
|
||||
typename ReductionOp,
|
||||
int ElementsPerAccess,
|
||||
bool ScatterD = false
|
||||
bool ScatterD = false,
|
||||
typename PermuteDLayout = layout::NoPermute
|
||||
>
|
||||
struct DefaultEpilogueWithReductionVoltaTensorOp {
|
||||
|
||||
@@ -142,7 +147,8 @@ struct DefaultEpilogueWithReductionVoltaTensorOp {
|
||||
using OutputTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<
|
||||
typename Base::OutputTileThreadMap,
|
||||
ElementOutput,
|
||||
ScatterD
|
||||
ScatterD,
|
||||
PermuteDLayout
|
||||
>;
|
||||
|
||||
/// Define the epilogue
|
||||
|
||||
@@ -64,6 +64,8 @@
|
||||
|
||||
#include "cutlass/epilogue/threadblock/epilogue.h"
|
||||
|
||||
#include "cutlass/layout/permute.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cutlass {
|
||||
@@ -79,7 +81,8 @@ template <
|
||||
int PartitionsK,
|
||||
typename OutputOp_,
|
||||
int ElementsPerAccess,
|
||||
bool ScatterD = false
|
||||
bool ScatterD = false,
|
||||
typename PermuteDLayout = layout::NoPermute
|
||||
>
|
||||
struct DefaultEpilogueWmmaTensorOp {
|
||||
|
||||
@@ -109,7 +112,8 @@ struct DefaultEpilogueWmmaTensorOp {
|
||||
using OutputTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<
|
||||
OutputTileThreadMap,
|
||||
ElementOutput,
|
||||
ScatterD
|
||||
ScatterD,
|
||||
PermuteDLayout
|
||||
>;
|
||||
|
||||
using AccumulatorFragmentIterator = cutlass::epilogue::warp::FragmentIteratorWmmaTensorOp<
|
||||
|
||||
@@ -0,0 +1,513 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017 - 2022 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.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/*! \file
|
||||
\brief Epilogue visitor for threadblock scoped GEMMs that process softmax computations in epilogue.
|
||||
|
||||
The epilogue finds max values in each row of the row-major output matrix and stores them.
|
||||
The max values are also used for a further round of threadblock scoped reduction operation, where
|
||||
the partial reduction results are stored in a pre-allocated array and used for further full reduction.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/arch/memory.h"
|
||||
#include "cutlass/arch/memory_sm75.h"
|
||||
#include "cutlass/numeric_conversion.h"
|
||||
#include "cutlass/fast_math.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace epilogue {
|
||||
namespace threadblock {
|
||||
|
||||
template <
|
||||
typename ThreadblockShape_,
|
||||
int ThreadCount,
|
||||
typename OutputTileIterator_,
|
||||
typename ElementAccumulator_,
|
||||
typename ElementNorm_,
|
||||
typename ElementSum_,
|
||||
typename ElementSoftmaxCompute_,
|
||||
typename ElementwiseFunctor_,
|
||||
bool UseMasking_ = false
|
||||
>
|
||||
class EpilogueVisitorSoftmax {
|
||||
public:
|
||||
|
||||
using ThreadblockShape = ThreadblockShape_;
|
||||
static int const kThreadCount = ThreadCount;
|
||||
|
||||
using OutputTileIterator = OutputTileIterator_;
|
||||
using ElementwiseFunctor = ElementwiseFunctor_;
|
||||
|
||||
static int const kIterations = OutputTileIterator::kIterations;
|
||||
static int const kElementsPerAccess = OutputTileIterator::kElementsPerAccess;
|
||||
|
||||
using ElementOutput = typename OutputTileIterator::Element;
|
||||
using LayoutOutput = cutlass::layout::RowMajor;
|
||||
using ElementAccumulator = ElementAccumulator_;
|
||||
|
||||
using ElementNorm = ElementNorm_;
|
||||
using ElementSum = ElementSum_;
|
||||
using ElementSoftmaxCompute = ElementSoftmaxCompute_;
|
||||
|
||||
using AccumulatorFragment = Array<ElementAccumulator, kElementsPerAccess>;
|
||||
using SoftmaxFragment = Array<ElementSoftmaxCompute, kElementsPerAccess>;
|
||||
using OutputVector = Array<ElementOutput, kElementsPerAccess>;
|
||||
using TensorRefD = TensorRef<ElementOutput, LayoutOutput>;
|
||||
|
||||
static int const kThreadsPerRow = OutputTileIterator::ThreadMap::Detail::kAccessWidth;
|
||||
static bool const kHasMultiStepsInRow = (OutputTileIterator::ThreadMap::Iterations::kColumn > 1);
|
||||
static bool const kUseMasking = UseMasking_;
|
||||
|
||||
/// Argument structure
|
||||
struct Arguments {
|
||||
|
||||
typename ElementwiseFunctor::Params elementwise;
|
||||
int64_t batch_stride_C;
|
||||
int64_t batch_stride_D;
|
||||
int64_t batch_stride_Max;
|
||||
int64_t batch_stride_Sum;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
Arguments():
|
||||
batch_stride_C(0),
|
||||
batch_stride_D(0),
|
||||
batch_stride_Max(0),
|
||||
batch_stride_Sum(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Arguments(
|
||||
typename ElementwiseFunctor::Params elementwise_
|
||||
):
|
||||
elementwise(elementwise_),
|
||||
batch_stride_C(0),
|
||||
batch_stride_D(0),
|
||||
batch_stride_Max(0),
|
||||
batch_stride_Sum(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Arguments(
|
||||
typename ElementwiseFunctor::Params elementwise_,
|
||||
int64_t batch_stride_C_,
|
||||
int64_t batch_stride_D_,
|
||||
int64_t batch_stride_Max_,
|
||||
int64_t batch_stride_Sum_
|
||||
):
|
||||
elementwise(elementwise_),
|
||||
batch_stride_C(batch_stride_C_),
|
||||
batch_stride_D(batch_stride_D_),
|
||||
batch_stride_Max(batch_stride_Max_),
|
||||
batch_stride_Sum(batch_stride_Sum_)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct Params {
|
||||
|
||||
typename ElementwiseFunctor::Params elementwise;
|
||||
int64_t batch_stride_C;
|
||||
int64_t batch_stride_D;
|
||||
int64_t batch_stride_Max;
|
||||
int64_t batch_stride_Sum;
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
CUTLASS_HOST_DEVICE
|
||||
Params()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
Params(Arguments const &args):
|
||||
elementwise(args.elementwise),
|
||||
batch_stride_C(args.batch_stride_C),
|
||||
batch_stride_D(args.batch_stride_D),
|
||||
batch_stride_Max(args.batch_stride_Max),
|
||||
batch_stride_Sum(args.batch_stride_Sum)
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
/// Shared storage
|
||||
struct SharedStorage {
|
||||
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Params const & params_;
|
||||
SharedStorage & shared_storage_;
|
||||
MatrixCoord extent_;
|
||||
MatrixCoord extent_real_;
|
||||
ElementwiseFunctor elementwise_;
|
||||
|
||||
OutputTileIterator iterator_C_;
|
||||
OutputTileIterator iterator_D_;
|
||||
typename OutputTileIterator::Fragment fragment_C_;
|
||||
typename OutputTileIterator::Fragment fragment_D_;
|
||||
|
||||
ElementAccumulator alpha_;
|
||||
ElementAccumulator beta_;
|
||||
|
||||
ElementNorm *ptr_Max_;
|
||||
ElementSum *ptr_Sum_;
|
||||
|
||||
int column_offset_;
|
||||
|
||||
ElementSoftmaxCompute accum_max_;
|
||||
ElementSoftmaxCompute accum_sum_;
|
||||
|
||||
MatrixCoord thread_offset_;
|
||||
|
||||
float infinity_;
|
||||
|
||||
public:
|
||||
|
||||
CUTLASS_DEVICE
|
||||
EpilogueVisitorSoftmax(
|
||||
Params const ¶ms,
|
||||
SharedStorage &shared_storage,
|
||||
cutlass::MatrixCoord const &problem_size,
|
||||
int thread_idx,
|
||||
int warp_idx,
|
||||
int lane_idx,
|
||||
typename OutputTileIterator::Params params_C,
|
||||
typename OutputTileIterator::Params params_D,
|
||||
typename OutputTileIterator::Element *ptr_C,
|
||||
typename OutputTileIterator::Element *ptr_D,
|
||||
ElementNorm *ptr_Max = nullptr,
|
||||
ElementSum *ptr_Sum = nullptr,
|
||||
cutlass::MatrixCoord const &threadblock_offset = cutlass::MatrixCoord(0, 0),
|
||||
int column_offset = 0,
|
||||
cutlass::MatrixCoord const &problem_size_real = cutlass::MatrixCoord(0, 0),
|
||||
float infinity = 10000.0f
|
||||
):
|
||||
params_(params),
|
||||
shared_storage_(shared_storage),
|
||||
extent_(problem_size),
|
||||
elementwise_(params.elementwise),
|
||||
iterator_C_(params_C, ptr_C, problem_size, thread_idx, threadblock_offset),
|
||||
iterator_D_(params_D, ptr_D, problem_size, thread_idx, threadblock_offset),
|
||||
ptr_Max_(ptr_Max),
|
||||
ptr_Sum_(ptr_Sum),
|
||||
column_offset_(column_offset),
|
||||
extent_real_(problem_size_real),
|
||||
infinity_(infinity)
|
||||
{
|
||||
alpha_ = (params.elementwise.alpha_ptr ? *params.elementwise.alpha_ptr : params.elementwise.alpha);
|
||||
beta_ = (params.elementwise.beta_ptr ? *params.elementwise.beta_ptr : params.elementwise.beta);
|
||||
|
||||
if (beta_ == ElementAccumulator()) {
|
||||
iterator_C_.clear_mask();
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper to indicate split-K behavior
|
||||
CUTLASS_DEVICE
|
||||
void set_k_partition(
|
||||
int split_k_index, ///< Index of this threadblock within split-K partitioned scheme
|
||||
int split_k_slices) { ///< Total number of split-K slices
|
||||
|
||||
}
|
||||
|
||||
/// Called to set the batch index
|
||||
CUTLASS_DEVICE
|
||||
void set_batch_index(int batch_idx) {
|
||||
iterator_C_.add_pointer_offset(batch_idx * params_.batch_stride_C);
|
||||
iterator_D_.add_pointer_offset(batch_idx * params_.batch_stride_D);
|
||||
}
|
||||
|
||||
/// Called at the start of the epilogue just before iterating over accumulator slices
|
||||
CUTLASS_DEVICE
|
||||
void begin_epilogue() {
|
||||
|
||||
}
|
||||
|
||||
/// Called at the start of one step before starting accumulator exchange
|
||||
CUTLASS_DEVICE
|
||||
void begin_step(int step_idx) {
|
||||
fragment_D_.clear();
|
||||
fragment_C_.clear();
|
||||
|
||||
if (elementwise_.kScale != cutlass::epilogue::thread::ScaleType::OnlyAlphaScaling) {
|
||||
iterator_C_.load(fragment_C_);
|
||||
++iterator_C_;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Called at the start of a row
|
||||
CUTLASS_DEVICE
|
||||
void begin_row(int row_idx) {
|
||||
// Clear accumulators for max and sum when starting a whole row
|
||||
clear_accum_();
|
||||
|
||||
}
|
||||
|
||||
/// Called after accumulators have been exchanged for each accumulator vector
|
||||
CUTLASS_DEVICE
|
||||
void visit(
|
||||
int iter_idx,
|
||||
int row_idx,
|
||||
int column_idx,
|
||||
int frag_idx,
|
||||
AccumulatorFragment const &accum) {
|
||||
|
||||
using Mul = cutlass::multiplies<SoftmaxFragment>;
|
||||
using Minus = cutlass::minus<SoftmaxFragment>;
|
||||
using Exp = cutlass::fast_exp_op<SoftmaxFragment>;
|
||||
|
||||
Minus minus;
|
||||
Exp exponential;
|
||||
|
||||
SoftmaxFragment result;
|
||||
|
||||
NumericArrayConverter<ElementSoftmaxCompute, ElementOutput, kElementsPerAccess> source_converter;
|
||||
OutputVector &source_vector = reinterpret_cast<OutputVector *>(&fragment_C_)[frag_idx];
|
||||
|
||||
if (elementwise_.kScale == cutlass::epilogue::thread::ScaleType::OnlyAlphaScaling) {
|
||||
result = source_converter(elementwise_(accum));
|
||||
}else{
|
||||
result = source_converter(elementwise_(accum, source_vector));
|
||||
}
|
||||
|
||||
thread_offset_ =
|
||||
iterator_D_.thread_start() +
|
||||
OutputTileIterator::ThreadMap::iteration_offset(frag_idx);
|
||||
|
||||
bool column_guard = (thread_offset_.column() < extent_.column());
|
||||
|
||||
if (kUseMasking) {
|
||||
int elements_in_boundary = extent_real_.column() - thread_offset_.column();
|
||||
elements_in_boundary = (elements_in_boundary > kElementsPerAccess) ? kElementsPerAccess : elements_in_boundary;
|
||||
elementwise_padding_(result, elements_in_boundary);
|
||||
}
|
||||
|
||||
ElementSoftmaxCompute accum_max_prev = accum_max_;
|
||||
|
||||
// Compute the maximum within one row
|
||||
if (!column_idx) {
|
||||
// This is the first fragment in a new row
|
||||
if (column_guard) {
|
||||
accum_max_ = maximum_accumulator_(result);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// This is an additional fragment in the same row
|
||||
if (column_guard) {
|
||||
accum_max_ = maximum_accumulator_(result, accum_max_);
|
||||
}
|
||||
}
|
||||
|
||||
// proactively compute max in warps
|
||||
accum_max_ = warp_reduce_max_(accum_max_);
|
||||
|
||||
ElementSoftmaxCompute updater = fast_exp(accum_max_prev - accum_max_);
|
||||
|
||||
SoftmaxFragment intermediate = exponential(minus(result, accum_max_));
|
||||
|
||||
if (kHasMultiStepsInRow) {
|
||||
if (!column_idx) {
|
||||
accum_sum_ = (column_guard) ? \
|
||||
sum_accumulator_(intermediate) : ElementSoftmaxCompute(0);
|
||||
} else {
|
||||
// Algorithm in $3.1, https://arxiv.org/pdf/2205.14135v1.pdf
|
||||
// S* = S* x updater + sum_row(P'), where updater = exp(M* - M_row)
|
||||
accum_sum_ = (column_guard) ? \
|
||||
sum_accumulator_(intermediate, accum_sum_ * updater) : accum_sum_ * updater;
|
||||
}
|
||||
} else {
|
||||
accum_sum_ = (column_guard) ? sum_accumulator_(intermediate, accum_sum_) : ElementSoftmaxCompute(0);
|
||||
}
|
||||
|
||||
// Convert to the output
|
||||
NumericArrayConverter<ElementOutput, ElementSoftmaxCompute, kElementsPerAccess> output_converter;
|
||||
OutputVector &output = reinterpret_cast<OutputVector *>(&fragment_D_)[frag_idx];
|
||||
output = output_converter(result);
|
||||
}
|
||||
|
||||
/// Called at the end of a row
|
||||
CUTLASS_DEVICE
|
||||
void end_row(int row_idx) {
|
||||
|
||||
using ConvertSumOutput = cutlass::NumericConverter<ElementSum, ElementSoftmaxCompute>;
|
||||
using ConvertNormOutput = cutlass::NumericConverter<ElementNorm, ElementSoftmaxCompute>;
|
||||
|
||||
ConvertSumOutput convert_sum_output;
|
||||
ConvertNormOutput convert_norm_output;
|
||||
|
||||
// Compute accumulate sum only in the last step
|
||||
accum_sum_ = warp_reduce_sum_(accum_sum_);
|
||||
|
||||
bool is_first_thread_in_tile = ((threadIdx.x % kThreadsPerRow) == 0);
|
||||
bool row_guard = thread_offset_.row() < extent_.row();
|
||||
bool is_write_thread = row_guard && is_first_thread_in_tile;
|
||||
|
||||
int block_batch = blockIdx.z;
|
||||
|
||||
ElementNorm *curr_ptr_max = ptr_Max_ + thread_offset_.row() + column_offset_ + block_batch * params_.batch_stride_Max;
|
||||
ElementSum *curr_ptr_sum = ptr_Sum_ + thread_offset_.row() + column_offset_ + block_batch * params_.batch_stride_Sum;
|
||||
|
||||
arch::global_store<ElementNorm, sizeof(ElementNorm)>(
|
||||
convert_norm_output(accum_max_),
|
||||
(void *)curr_ptr_max,
|
||||
is_write_thread);
|
||||
|
||||
arch::global_store<ElementSum, sizeof(ElementSum)>(
|
||||
convert_sum_output(accum_sum_),
|
||||
(void *)curr_ptr_sum,
|
||||
is_write_thread);
|
||||
|
||||
// Clear accumulators for max and sum when finishing a whole row
|
||||
clear_accum_();
|
||||
|
||||
}
|
||||
|
||||
/// Called after all accumulator elements have been visited
|
||||
CUTLASS_DEVICE
|
||||
void end_step(int step_idx) {
|
||||
|
||||
iterator_D_.store(fragment_D_);
|
||||
++iterator_D_;
|
||||
}
|
||||
|
||||
/// Called after all steps have been completed
|
||||
CUTLASS_DEVICE
|
||||
void end_epilogue() {
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
CUTLASS_DEVICE
|
||||
void elementwise_padding_(SoftmaxFragment &result, int elements_in_boundary) {
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < SoftmaxFragment::kElements; ++i) {
|
||||
result[i] = (i < elements_in_boundary) ? result[i] : ElementSoftmaxCompute(-infinity_);
|
||||
}
|
||||
}
|
||||
|
||||
CUTLASS_DEVICE
|
||||
ElementSoftmaxCompute warp_reduce_sum_(ElementSoftmaxCompute sum_) {
|
||||
int half_thread_in_row = (kThreadsPerRow >> 1);
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = half_thread_in_row; i > 0; i >>= 1) {
|
||||
ElementSoftmaxCompute tmp = __shfl_xor_sync(0xFFFFFFFF, sum_, i);
|
||||
sum_ += tmp;
|
||||
}
|
||||
return sum_;
|
||||
}
|
||||
|
||||
CUTLASS_DEVICE
|
||||
ElementSoftmaxCompute warp_reduce_max_(ElementSoftmaxCompute max_) {
|
||||
int half_thread_in_row = (kThreadsPerRow >> 1);
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = half_thread_in_row; i > 0; i >>= 1) {
|
||||
ElementSoftmaxCompute tmp = __shfl_xor_sync(0xFFFFFFFF, max_, i);
|
||||
max_ = fast_max(max_, tmp);
|
||||
}
|
||||
return max_;
|
||||
}
|
||||
|
||||
CUTLASS_DEVICE
|
||||
void clear_accum_() {
|
||||
|
||||
uint32_t float_max_bits = 0xff7fffff; // -FLT_MAX
|
||||
float min_float = reinterpret_cast<float const &>(float_max_bits);
|
||||
accum_max_ = ElementSoftmaxCompute(min_float);
|
||||
accum_sum_ = ElementSoftmaxCompute(0);
|
||||
}
|
||||
|
||||
CUTLASS_DEVICE
|
||||
ElementSoftmaxCompute sum_accumulator_(SoftmaxFragment const &accum) {
|
||||
ElementSoftmaxCompute sum_ = ElementSoftmaxCompute(0);
|
||||
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < SoftmaxFragment::kElements; ++i) {
|
||||
sum_ += ElementSoftmaxCompute(accum[i]);
|
||||
}
|
||||
|
||||
return sum_;
|
||||
}
|
||||
|
||||
CUTLASS_DEVICE
|
||||
ElementSoftmaxCompute sum_accumulator_(SoftmaxFragment const &accum, ElementSoftmaxCompute sum_) {
|
||||
// ElementSoftmaxCompute sum_ = ElementSoftmaxCompute(0);
|
||||
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < SoftmaxFragment::kElements; ++i) {
|
||||
sum_ += ElementSoftmaxCompute(accum[i]);
|
||||
}
|
||||
|
||||
return sum_;
|
||||
}
|
||||
|
||||
CUTLASS_DEVICE
|
||||
ElementSoftmaxCompute maximum_accumulator_(SoftmaxFragment const &accum) {
|
||||
ElementSoftmaxCompute max_ = accum[0];
|
||||
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 1; i < SoftmaxFragment::kElements; ++i) {
|
||||
max_ = fast_max(max_, ElementSoftmaxCompute(accum[i]));
|
||||
}
|
||||
|
||||
return max_;
|
||||
}
|
||||
|
||||
CUTLASS_DEVICE
|
||||
ElementSoftmaxCompute maximum_accumulator_(SoftmaxFragment const &accum, ElementSoftmaxCompute max_) {
|
||||
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < SoftmaxFragment::kElements; ++i) {
|
||||
max_ = fast_max(max_, ElementSoftmaxCompute(accum[i]));
|
||||
}
|
||||
|
||||
return max_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace threadblock
|
||||
} // namespace epilogue
|
||||
} // namespace cutlass
|
||||
@@ -39,11 +39,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#if defined(__CUDACC_RTC__)
|
||||
#include <cuda/std/cassert>
|
||||
#include <cuda/std/utility>
|
||||
#else
|
||||
#include <assert.h>
|
||||
#include <utility>
|
||||
#endif
|
||||
|
||||
#include "cutlass/cutlass.h"
|
||||
|
||||
@@ -0,0 +1,409 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017 - 2022 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.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/*! \file
|
||||
\brief Generic epilogue for implementing certain kinds of fused epilogue behavior.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/fast_math.h"
|
||||
#include "cutlass/matrix_coord.h"
|
||||
#include "cutlass/semaphore.h"
|
||||
#include "cutlass/epilogue/threadblock/epilogue_base.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cutlass {
|
||||
namespace epilogue {
|
||||
namespace threadblock {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class EpilogueFusedVisitorConcept {
|
||||
public:
|
||||
|
||||
static int const kIterations = 1;
|
||||
static int const kElementsPerAccess = 4;
|
||||
using ElementOutput = float;
|
||||
using ElementAccumulator = float;
|
||||
using AccumulatorFragment = Array<ElementAccumulator, kElementsPerAccess>;
|
||||
|
||||
/// Arguments structure
|
||||
struct Arguments { };
|
||||
|
||||
/// Params structure
|
||||
struct Params {
|
||||
|
||||
Params() { }
|
||||
Params(Arguments const &args) { }
|
||||
};
|
||||
|
||||
/// Shared storage
|
||||
struct SharedStorage { };
|
||||
|
||||
public:
|
||||
|
||||
CUTLASS_DEVICE
|
||||
EpilogueFusedVisitorConcept(
|
||||
Params const ¶ms, ///< Parameters routed to the epilogue
|
||||
SharedStorage &shared_storage, ///< Shared storage needed by the functors here
|
||||
MatrixCoord const &problem_size, ///< Problem size of the output
|
||||
int thread_idx, ///< Thread index within the threadblock
|
||||
int warp_idx, ///< Warp index within the threadblock
|
||||
int lane_idx, ///< Lane index within the warp
|
||||
MatrixCoord const &threadblock_offset = MatrixCoord(0, 0)) { ///< Coordinate
|
||||
|
||||
}
|
||||
|
||||
/// Helper to indicate split-K behavior
|
||||
CUTLASS_DEVICE
|
||||
void set_k_partition(
|
||||
int split_k_index, ///< Index of this threadblock within split-K partitioned scheme
|
||||
int split_k_slices) { ///< Total number of split-K slices
|
||||
|
||||
}
|
||||
|
||||
/// Called to set the batch index
|
||||
CUTLASS_DEVICE
|
||||
void set_batch_index(int batch_idx) {
|
||||
|
||||
}
|
||||
|
||||
/// Called at the start of the epilogue just before iterating over accumulator slices
|
||||
CUTLASS_DEVICE
|
||||
void begin_epilogue() {
|
||||
|
||||
}
|
||||
|
||||
/// Called at the start of one step before starting accumulator exchange
|
||||
CUTLASS_DEVICE
|
||||
void begin_step(int step_idx) {
|
||||
|
||||
}
|
||||
|
||||
/// Called at the start of a row
|
||||
CUTLASS_DEVICE
|
||||
void begin_row(int row_idx) {
|
||||
|
||||
}
|
||||
|
||||
/// Called after accumulators have been exchanged for each accumulator vector
|
||||
CUTLASS_DEVICE
|
||||
void visit(
|
||||
int iter_idx,
|
||||
int row_idx,
|
||||
int column_idx,
|
||||
int frag_idx,
|
||||
AccumulatorFragment const &accum) {
|
||||
|
||||
}
|
||||
|
||||
/// Called at the end of a row
|
||||
CUTLASS_DEVICE
|
||||
void end_row(int row_idx) {
|
||||
|
||||
}
|
||||
|
||||
/// Called after all accumulator elements have been visited
|
||||
CUTLASS_DEVICE
|
||||
void end_step(int step_idx) {
|
||||
|
||||
}
|
||||
|
||||
/// Called after all steps have been completed
|
||||
CUTLASS_DEVICE
|
||||
void end_epilogue() {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Epilogue operator
|
||||
template <
|
||||
typename Visitor_, ///< Functor containing fused operations (satisfies EpilogueFusedVisitorConcept)
|
||||
typename Shape_, ///< Shape of threadblock tile (concept: GemmShape)
|
||||
typename WarpMmaOperator_, ///< Warp-level MMA operator (concept: gemm::warp::MmaTensorOp)
|
||||
int PartitionsK, ///< Number of partitions of the K dimension
|
||||
typename AccumulatorFragmentIterator_, ///< Fragment iterator selecting accumulators
|
||||
typename WarpTileIterator_, ///< Warp-scoped tile iterator writing accumulators to SMEM
|
||||
typename SharedLoadIterator_, ///< Threadblock-scoped tile iterator loading from SMEM
|
||||
typename Padding_, ///< Padding added to SMEM allocation to avoid bank conflicts (concept: MatrixShape)
|
||||
int FragmentsPerPartition = 1, ///< Used to coarsten the epilogue granularity
|
||||
int IterationsUnroll = ///< Used to reduce binary size when epilogue op is large
|
||||
(true || !IsEpilogueFunctorHeavy<Visitor_>::value)
|
||||
>
|
||||
class EpilogueWithVisitor :
|
||||
public EpilogueBase<
|
||||
Shape_,
|
||||
typename WarpMmaOperator_::Shape,
|
||||
PartitionsK,
|
||||
AccumulatorFragmentIterator_,
|
||||
WarpTileIterator_,
|
||||
Padding_,
|
||||
FragmentsPerPartition> {
|
||||
|
||||
public:
|
||||
|
||||
using Visitor = Visitor_;
|
||||
|
||||
using Base = EpilogueBase<
|
||||
Shape_,
|
||||
typename WarpMmaOperator_::Shape,
|
||||
PartitionsK,
|
||||
AccumulatorFragmentIterator_,
|
||||
WarpTileIterator_,
|
||||
Padding_,
|
||||
FragmentsPerPartition>;
|
||||
|
||||
using Shape = Shape_;
|
||||
using WarpMmaOperator = WarpMmaOperator_;
|
||||
static int const kPartitionsK = PartitionsK;
|
||||
|
||||
using AccumulatorFragmentIterator = AccumulatorFragmentIterator_;
|
||||
using WarpTileIterator = WarpTileIterator_;
|
||||
using SharedLoadIterator = SharedLoadIterator_;
|
||||
using Padding = Padding_;
|
||||
|
||||
using Layout = layout::RowMajor;
|
||||
using LongIndex = typename Layout::LongIndex;
|
||||
|
||||
/// The complete warp-level accumulator tile
|
||||
using AccumulatorTile = typename Base::AccumulatorTile;
|
||||
|
||||
/// Accumulator element
|
||||
using ElementAccumulator = typename WarpTileIterator::Element;
|
||||
|
||||
/// Output access size
|
||||
static int const kElementsPerAccess = Visitor::kElementsPerAccess;
|
||||
|
||||
/// Tensor reference to sync tensor
|
||||
using SyncTensorRef = typename cutlass::TensorRef<int, cutlass::layout::PackedVectorLayout>;
|
||||
|
||||
/// Array type used by output functor
|
||||
using AccumulatorAccessType = Array<
|
||||
typename WarpTileIterator::Element, kElementsPerAccess>;
|
||||
|
||||
/// Number of warps
|
||||
using WarpCount = typename Base::WarpCount;
|
||||
|
||||
static int constexpr kSmemTiles = Base::kFragmentsPerIteration > 1 ? Base::kFragmentsPerIteration : kPartitionsK;
|
||||
static int constexpr kSmemPointerOffset = Base::SharedStorage::StorageShape::kCount / kSmemTiles;
|
||||
|
||||
using SharedStorage = typename Base::SharedStorage;
|
||||
|
||||
private:
|
||||
|
||||
/// Loads fragment from shared memory aligned with output tensor
|
||||
SharedLoadIterator shared_load_iterator_;
|
||||
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
CUTLASS_DEVICE
|
||||
EpilogueWithVisitor(
|
||||
SharedStorage &shared_storage, ///< Shared storage object
|
||||
int thread_idx, ///< ID of a thread within the threadblock
|
||||
int warp_idx, ///< ID of warp within threadblock
|
||||
int lane_idx ///< Id of thread within warp
|
||||
):
|
||||
Base(shared_storage, thread_idx, warp_idx, lane_idx),
|
||||
shared_load_iterator_(shared_storage.reference(), thread_idx)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// Streams the result to global memory
|
||||
CUTLASS_DEVICE
|
||||
void operator()(
|
||||
Visitor & visitor,
|
||||
AccumulatorTile const &accumulators) { ///< Threadblock tile coordinate in GEMM (in units of threadblock tiles)
|
||||
|
||||
visitor.begin_epilogue();
|
||||
|
||||
//
|
||||
// Iterator over warp-level accumulator fragment
|
||||
//
|
||||
|
||||
AccumulatorFragmentIterator accum_fragment_iterator(accumulators);
|
||||
|
||||
//
|
||||
// Iterate over accumulator tile
|
||||
//
|
||||
|
||||
#pragma unroll(IterationsUnroll ? Visitor::kIterations : 1)
|
||||
for (int iter_idx = 0; iter_idx < Visitor::kIterations; ++iter_idx) {
|
||||
|
||||
//
|
||||
// Load the source
|
||||
//
|
||||
|
||||
visitor.begin_step(iter_idx);
|
||||
|
||||
//
|
||||
// Convert and store fragment
|
||||
//
|
||||
|
||||
__syncthreads();
|
||||
|
||||
acc2smem_source_needed<cutlass::make_index_sequence<Visitor::kIterations>>::push(
|
||||
iter_idx, accum_fragment_iterator, this->warp_tile_iterator_);
|
||||
|
||||
__syncthreads();
|
||||
|
||||
//
|
||||
// Load fragments from shared memory
|
||||
//
|
||||
|
||||
typename SharedLoadIterator::Fragment aligned_accum_fragment[kPartitionsK];
|
||||
|
||||
shared_load_iterator_.load(aligned_accum_fragment[0]);
|
||||
|
||||
// If the number of k-slices is > 1 - perform a reduction amongst the k-slices
|
||||
if (kPartitionsK > 1) {
|
||||
|
||||
plus <typename SharedLoadIterator::Fragment> add_fragments;
|
||||
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for ( int i = 1; i < kPartitionsK; ++i) {
|
||||
shared_load_iterator_.add_pointer_offset(kSmemPointerOffset);
|
||||
shared_load_iterator_.load(aligned_accum_fragment[i]);
|
||||
aligned_accum_fragment[0] = add_fragments(aligned_accum_fragment[0], aligned_accum_fragment[i]);
|
||||
}
|
||||
|
||||
shared_load_iterator_.add_pointer_offset((1 - kPartitionsK) * kSmemPointerOffset);
|
||||
}
|
||||
|
||||
//
|
||||
// Iterate over output fragments
|
||||
//
|
||||
|
||||
AccumulatorAccessType const *accum_frag_ptr =
|
||||
reinterpret_cast<AccumulatorAccessType const *>(&aligned_accum_fragment[0]);
|
||||
|
||||
int const kAccumulatorFragmentCount = AccumulatorTile::kElements / (Visitor::kIterations * AccumulatorAccessType::kElements);
|
||||
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int idx = 0; idx < kAccumulatorFragmentCount; ++idx) {
|
||||
|
||||
int row_idx = idx / SharedLoadIterator::ThreadMap::Iterations::kColumn;
|
||||
int col_idx = idx % SharedLoadIterator::ThreadMap::Iterations::kColumn;
|
||||
|
||||
// Start a new row of the output fragment
|
||||
if (!col_idx) {
|
||||
visitor.begin_row(row_idx);
|
||||
}
|
||||
|
||||
visitor.visit(
|
||||
iter_idx,
|
||||
row_idx,
|
||||
col_idx,
|
||||
idx,
|
||||
accum_frag_ptr[idx]
|
||||
);
|
||||
|
||||
// End the row of the output fragment
|
||||
if (col_idx + 1 == SharedLoadIterator::ThreadMap::Iterations::kColumn) {
|
||||
visitor.end_row(row_idx);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Conclude the step
|
||||
//
|
||||
|
||||
visitor.end_step(iter_idx);
|
||||
}
|
||||
|
||||
visitor.end_epilogue();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
|
||||
template<class Seq>
|
||||
struct acc2smem_source_needed;
|
||||
|
||||
template <size_t... Seq>
|
||||
struct acc2smem_source_needed<cutlass::index_sequence<Seq...>> {
|
||||
template<int Advance>
|
||||
CUTLASS_DEVICE
|
||||
static void helper(AccumulatorFragmentIterator accum_fragment_iterator,
|
||||
WarpTileIterator &warp_tile_iterator) {
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < Advance; i++) {
|
||||
++accum_fragment_iterator;
|
||||
}
|
||||
|
||||
typename AccumulatorFragmentIterator::Fragment accum_fragment;
|
||||
accum_fragment_iterator.load(accum_fragment);
|
||||
warp_tile_iterator.store(accum_fragment);
|
||||
}
|
||||
|
||||
CUTLASS_DEVICE
|
||||
static void push(size_t pos,
|
||||
AccumulatorFragmentIterator const &iterator_begin,
|
||||
WarpTileIterator &warp_tile_iterator) {
|
||||
int dummy[] = {(pos == Seq) && (helper<Seq>(iterator_begin, warp_tile_iterator), 0)...};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Helper to create an EpilogueWithVisitor from an existing epilogue
|
||||
template <typename Visitor_, typename Existing_, bool IterationsUnroll = true>
|
||||
struct EpilogueWithVisitorFromExistingEpilogue {
|
||||
|
||||
using Epilogue = EpilogueWithVisitor<
|
||||
Visitor_,
|
||||
typename Existing_::Shape,
|
||||
typename Existing_::WarpMmaOperator,
|
||||
Existing_::kPartitionsK,
|
||||
typename Existing_::AccumulatorFragmentIterator,
|
||||
typename Existing_::WarpTileIterator,
|
||||
typename Existing_::SharedLoadIterator,
|
||||
typename Existing_::Padding,
|
||||
Existing_::kFragmentsPerIteration,
|
||||
IterationsUnroll
|
||||
>;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace threadblock
|
||||
} // namespace epilogue
|
||||
} // namespace cutlass
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -391,10 +391,10 @@ struct OutputTileOptimalThreadMap {
|
||||
1>;
|
||||
|
||||
/// Initial offset function
|
||||
CUTLASS_HOST_DEVICE
|
||||
CUTLASS_DEVICE
|
||||
static MatrixCoord initial_offset(int thread_idx) {
|
||||
|
||||
int warp_idx = thread_idx / kWarpSize;
|
||||
int warp_idx = __shfl_sync(0xffffffff, thread_idx / kWarpSize, 0);
|
||||
int lane_idx = thread_idx % kWarpSize;
|
||||
|
||||
// Compute warp location
|
||||
@@ -419,7 +419,7 @@ struct OutputTileOptimalThreadMap {
|
||||
|
||||
return MatrixCoord(
|
||||
cluster_offset + group_offset + row_offset + lane_row_offset,
|
||||
(column_offset + lane_col_offset) * kElementsPerAccess
|
||||
column_offset + lane_col_offset * kElementsPerAccess
|
||||
);
|
||||
}
|
||||
|
||||
@@ -461,10 +461,10 @@ struct OutputTileOptimalThreadMap {
|
||||
static int const kThreads = Threads;
|
||||
|
||||
/// Function to compute each thread's initial offset
|
||||
CUTLASS_HOST_DEVICE
|
||||
CUTLASS_DEVICE
|
||||
static MatrixCoord initial_offset(int thread_idx) {
|
||||
|
||||
int warp_idx = thread_idx / kWarpSize;
|
||||
int warp_idx = __shfl_sync(0xffffffff, thread_idx / kWarpSize, 0);
|
||||
int lane_idx = thread_idx % kWarpSize;
|
||||
|
||||
// Compute warp location
|
||||
@@ -489,7 +489,7 @@ struct OutputTileOptimalThreadMap {
|
||||
|
||||
MatrixCoord coord(
|
||||
cluster_offset + group_offset + row_offset + lane_row_offset,
|
||||
(column_offset + lane_col_offset) * kElementsPerAccess
|
||||
column_offset + lane_col_offset * kElementsPerAccess
|
||||
);
|
||||
|
||||
return coord;
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "cutlass/array.h"
|
||||
#include "cutlass/layout/matrix.h"
|
||||
#include "cutlass/layout/tensor.h"
|
||||
#include "cutlass/layout/permute.h"
|
||||
#include "cutlass/matrix_shape.h"
|
||||
#include "cutlass/tensor_ref.h"
|
||||
#include "cutlass/transform/pitch_linear_thread_map.h"
|
||||
@@ -70,6 +71,7 @@ template <
|
||||
typename ThreadMap_, ///< Thread map (conept: OutputTileThreadMap)
|
||||
typename Element_, ///< Element data type
|
||||
bool ScatterD = false, ///< Scatter D operand or not
|
||||
typename PermuteDLayout = layout::NoPermute, ///< Permute D operand or not
|
||||
bool UseCUDAStore = false
|
||||
>
|
||||
class PredicatedTileIterator {
|
||||
@@ -173,9 +175,12 @@ private:
|
||||
/// Parameters structure containing reference and precomputed state.
|
||||
PredicatedTileIteratorParams params_;
|
||||
|
||||
/// Byte-level pointer
|
||||
/// Byte-level pointer. This pointer is usually for both load() and store(), unless PermuteD is performed. When having PermuteD, byte_pointer_ is only for load().
|
||||
uint8_t *byte_pointer_;
|
||||
|
||||
/// Byte-level pointer for store(). Due to PermuteD Op, store_byte_pointer_ may be with different address computation compared to byte_pointer_.
|
||||
uint8_t *store_byte_pointer_;
|
||||
|
||||
/// Array of boolean values to contain steady-state predicates
|
||||
Mask mask_;
|
||||
|
||||
@@ -196,6 +201,11 @@ private:
|
||||
|
||||
/// Scatter indices
|
||||
int const *indices_;
|
||||
|
||||
/// Whether to perform Permute Op
|
||||
bool PermuteD;
|
||||
/// PermuteDLayout
|
||||
mutable PermuteDLayout permute_layout_;
|
||||
|
||||
//
|
||||
// Static asserts about internal strides
|
||||
@@ -255,7 +265,7 @@ public:
|
||||
mask_.clear();
|
||||
}
|
||||
|
||||
// Initialize pointer
|
||||
// Initialize byte_pointer_
|
||||
byte_pointer_ = reinterpret_cast<uint8_t *>(pointer) +
|
||||
LongIndex(thread_offset.row()) * LongIndex(params_.stride) +
|
||||
LongIndex(thread_offset.column()) * sizeof(AccessType) / kElementsPerAccess;
|
||||
@@ -265,6 +275,19 @@ public:
|
||||
LongIndex(thread_offset.column()) * sizeof(AccessType) / kElementsPerAccess;
|
||||
}
|
||||
|
||||
// store_byte_pointer_ is set to be the same with byte_pointer_ unless PermuteD is used.
|
||||
store_byte_pointer_ = byte_pointer_;
|
||||
|
||||
// Initialize PermuteD. If PermuteD is true, store_byte_pointer_ is initialized accordingly.
|
||||
if (platform::is_same<PermuteDLayout, layout::NoPermute>::value) {
|
||||
PermuteD = false;
|
||||
}else{
|
||||
PermuteD = true;
|
||||
store_byte_pointer_ = reinterpret_cast<uint8_t *>(pointer);
|
||||
permute_layout_ = PermuteDLayout(extent,
|
||||
params_.stride * kElementsPerAccess / sizeof(AccessType));
|
||||
}
|
||||
|
||||
// Initialize internal state counter
|
||||
state_[0] = state_[1] = state_[2] = 0;
|
||||
}
|
||||
@@ -272,6 +295,7 @@ public:
|
||||
/// Adds a pointer offset in units of Element
|
||||
CUTLASS_HOST_DEVICE
|
||||
void add_pointer_offset(LongIndex pointer_offset) {
|
||||
store_byte_pointer_ += pointer_offset * sizeof_bits<Element>::value / 8;
|
||||
byte_pointer_ += pointer_offset * sizeof_bits<Element>::value / 8;
|
||||
}
|
||||
|
||||
@@ -353,7 +377,7 @@ public:
|
||||
/// Stores a fragment to memory
|
||||
CUTLASS_DEVICE
|
||||
void store_with_byte_offset(Fragment const &frag, int64_t byte_offset) const {
|
||||
uint8_t *byte_pointer = byte_pointer_;
|
||||
uint8_t *byte_pointer = store_byte_pointer_;
|
||||
AccessType const *frag_ptr = reinterpret_cast<AccessType const *>(&frag);
|
||||
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
@@ -388,21 +412,38 @@ public:
|
||||
|
||||
bool guard = row_guard && mask_.predicates[column];
|
||||
|
||||
int col_offset = column * ThreadMap::Delta::kColumn;
|
||||
|
||||
if (PermuteD) {
|
||||
int col = col_offset + thread_start_column_;
|
||||
int row = row_offset + thread_start_row_;
|
||||
|
||||
TensorCoord init_coord(row, col);
|
||||
|
||||
// Locate memory_pointer
|
||||
memory_pointer = reinterpret_cast<AccessType *>(byte_pointer + byte_offset
|
||||
+ permute_layout_(init_coord) * sizeof(AccessType) / kElementsPerAccess);
|
||||
}
|
||||
|
||||
if (UseCUDAStore) {
|
||||
if (guard) {
|
||||
memory_pointer[column * ThreadMap::Delta::kColumn / kElementsPerAccess] =
|
||||
memory_pointer[0] =
|
||||
frag_ptr[frag_row_idx * ThreadMap::Iterations::kColumn + column];
|
||||
}
|
||||
} else {
|
||||
cutlass::arch::global_store<AccessType, sizeof(AccessType)>(
|
||||
frag_ptr[frag_row_idx * ThreadMap::Iterations::kColumn + column],
|
||||
(void *)&memory_pointer[column * ThreadMap::Delta::kColumn / kElementsPerAccess],
|
||||
(void *)&memory_pointer[0],
|
||||
guard);
|
||||
}
|
||||
|
||||
if (!PermuteD) {
|
||||
memory_pointer += (ThreadMap::Delta::kColumn / kElementsPerAccess);
|
||||
}
|
||||
}
|
||||
|
||||
if (row + 1 < ThreadMap::Iterations::kRow) {
|
||||
if (!ScatterD) {
|
||||
if (!ScatterD && !PermuteD) {
|
||||
byte_pointer += params_.increment_row;
|
||||
}
|
||||
}
|
||||
@@ -605,6 +646,10 @@ public:
|
||||
|
||||
++state_[0];
|
||||
|
||||
if (!ScatterD && !PermuteD) {
|
||||
store_byte_pointer_ += params_.advance_row;
|
||||
}
|
||||
|
||||
if (!ScatterD) {
|
||||
byte_pointer_ += params_.advance_row;
|
||||
}
|
||||
@@ -616,6 +661,7 @@ public:
|
||||
state_[0] = 0;
|
||||
++state_[1];
|
||||
byte_pointer_ += params_.advance_group;
|
||||
store_byte_pointer_ += params_.advance_group;
|
||||
|
||||
thread_start_row_ += (ThreadMap::Shape::kGroup - 1) *
|
||||
ThreadMap::Shape::kRow * ThreadMap::Count::kRow;
|
||||
@@ -625,6 +671,7 @@ public:
|
||||
state_[1] = 0;
|
||||
++state_[2];
|
||||
byte_pointer_ += params_.advance_cluster;
|
||||
store_byte_pointer_ += params_.advance_group;
|
||||
|
||||
thread_start_row_ += ThreadMap::Count::kGroup *
|
||||
ThreadMap::Shape::kGroup * ThreadMap::Count::kRow * ThreadMap::Shape::kRow;
|
||||
@@ -632,6 +679,7 @@ public:
|
||||
if (state_[2] == ThreadMap::Count::kCluster) {
|
||||
state_[2] = 0;
|
||||
byte_pointer_ += params_.advance_tile;
|
||||
store_byte_pointer_ += params_.advance_group;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/layout/pitch_linear.h"
|
||||
#include "cutlass/layout/matrix.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
Reference in New Issue
Block a user