co-authored by
Aniket Shivam
parent
9b8166e3f0
commit
d572cc1aab
@@ -0,0 +1,52 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2023 - 2023 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 Utilities for thread-level epilogues
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cutlass {
|
||||
namespace epilogue {
|
||||
namespace thread {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// Class used to identify cases in which no operation is performed
|
||||
template <typename T_>
|
||||
struct NoOp {};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace thread
|
||||
} // namespace epilogue
|
||||
} // namespace cutlass
|
||||
@@ -52,7 +52,7 @@ namespace thread {
|
||||
|
||||
/// Applies a linear combination operator to an array of elements.
|
||||
///
|
||||
/// D = alpha * accumulator + beta * source + uniform
|
||||
/// D = alpha * accumulator + beta * source
|
||||
///
|
||||
template <
|
||||
typename ElementOutput_, ///< Data type used to load and store tensors
|
||||
@@ -69,6 +69,7 @@ class LinearCombination {
|
||||
public:
|
||||
|
||||
using ElementOutput = ElementOutput_;
|
||||
using ElementSource = ElementSource_;
|
||||
using ElementAccumulator = ElementAccumulator_;
|
||||
using ElementCompute = ElementCompute_;
|
||||
using ElementC = ElementSource_;
|
||||
@@ -77,14 +78,15 @@ public:
|
||||
static int const kCount = Count;
|
||||
static const ScaleType::Kind kScale = Scale;
|
||||
using FragmentOutput = Array<ElementOutput, kCount>;
|
||||
using FragmentSource = Array<ElementSource, kCount>;
|
||||
using FragmentAccumulator = Array<ElementAccumulator, kCount>;
|
||||
using ComputeFragment = Array<ElementCompute, kCount>;
|
||||
using FragmentCompute = Array<ElementCompute, kCount>;
|
||||
|
||||
using ParamsBase = LinearCombinationParams;
|
||||
static FloatRoundStyle const kRound = Round;
|
||||
|
||||
/// Host-constructable parameters structure
|
||||
struct Params : ParamsBase{
|
||||
struct Params
|
||||
{
|
||||
ElementCompute alpha; ///< scales accumulators
|
||||
ElementCompute beta; ///< scales source tensor
|
||||
ElementCompute const *alpha_ptr; ///< pointer to accumulator scalar - if not null, loads it from memory
|
||||
@@ -92,10 +94,6 @@ public:
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
Params():
|
||||
ParamsBase(
|
||||
ElementCompute(1),
|
||||
ElementCompute(0)
|
||||
),
|
||||
alpha(ElementCompute(1)),
|
||||
beta(ElementCompute(0)),
|
||||
alpha_ptr(nullptr),
|
||||
@@ -106,14 +104,12 @@ public:
|
||||
ElementCompute alpha,
|
||||
ElementCompute beta
|
||||
):
|
||||
ParamsBase(alpha, beta),
|
||||
alpha(alpha), beta(beta), alpha_ptr(nullptr), beta_ptr(nullptr) { }
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
Params(
|
||||
ElementCompute alpha
|
||||
):
|
||||
ParamsBase(alpha, ElementCompute(0)),
|
||||
alpha(alpha), beta(0), alpha_ptr(nullptr), beta_ptr(nullptr) { }
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
@@ -121,28 +117,13 @@ public:
|
||||
ElementCompute const *alpha_ptr,
|
||||
ElementCompute const *beta_ptr
|
||||
):
|
||||
ParamsBase(*alpha_ptr, *beta_ptr),
|
||||
alpha(0), beta(0), alpha_ptr(alpha_ptr), beta_ptr(beta_ptr) { }
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
Params(
|
||||
ElementCompute const *alpha_ptr
|
||||
):
|
||||
ParamsBase(*alpha_ptr, ElementCompute(0)),
|
||||
alpha(0), beta(0), alpha_ptr(alpha_ptr), beta_ptr(nullptr) { }
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
Params(
|
||||
ParamsBase const& base
|
||||
): ParamsBase(base), alpha_ptr(nullptr), beta_ptr(nullptr) {
|
||||
#if defined(__CUDA_ARCH__)
|
||||
alpha = reinterpret_cast<ElementCompute const&>(base.alpha_data);
|
||||
beta = reinterpret_cast<ElementCompute const&>(base.beta_data);
|
||||
#else
|
||||
memcpy( alpha, base.alpha_data, sizeof(ElementCompute) );
|
||||
memcpy( beta, base.alpha_data, sizeof(ElementCompute) );
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -183,30 +164,73 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes linear scaling: D = alpha * accumulator + beta * source
|
||||
/// Computes intermediate: X = beta * source
|
||||
CUTLASS_HOST_DEVICE
|
||||
FragmentCompute compute_intermediate(
|
||||
FragmentSource const &source) const {
|
||||
|
||||
// Convert source to internal compute numeric type
|
||||
NumericArrayConverter<ElementCompute, ElementSource, kCount, Round> source_converter;
|
||||
FragmentCompute converted_source = source_converter(source);
|
||||
|
||||
if (Scale == ScaleType::NoBetaScaling) {
|
||||
return converted_source;
|
||||
}
|
||||
else {
|
||||
multiplies<FragmentCompute> mul_source;
|
||||
return mul_source(beta_, converted_source);
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes linear scaling with intermediate: D = alpha * accumulator + X
|
||||
CUTLASS_HOST_DEVICE
|
||||
FragmentOutput with_intermediate(
|
||||
FragmentAccumulator const& accumulator,
|
||||
FragmentCompute const& intermediate) const {
|
||||
|
||||
// Convert accumulator to internal compute numeric type
|
||||
NumericArrayConverter<ElementCompute, ElementAccumulator, kCount, Round> accumulator_converter;
|
||||
|
||||
// Convert to destination numeric type
|
||||
NumericArrayConverter<ElementOutput, ElementCompute, kCount, Round> destination_converter;
|
||||
|
||||
FragmentCompute converted_accumulator = accumulator_converter(accumulator);
|
||||
|
||||
if (Scale == ScaleType::Nothing) {
|
||||
return destination_converter(converted_accumulator);
|
||||
} else {
|
||||
// Perform binary operations
|
||||
multiply_add<FragmentCompute> mul_add_accumulator;
|
||||
FragmentCompute computed_output = mul_add_accumulator(alpha_, converted_accumulator, intermediate);
|
||||
|
||||
return destination_converter(computed_output);
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes linear scaling with source: D = alpha * accumulator + beta * source
|
||||
CUTLASS_HOST_DEVICE
|
||||
FragmentOutput operator()(
|
||||
FragmentAccumulator const &accumulator,
|
||||
FragmentOutput const &source) const {
|
||||
FragmentAccumulator const &accumulator,
|
||||
FragmentSource const &source) const {
|
||||
|
||||
// Convert source to interal compute numeric type
|
||||
NumericArrayConverter<ElementCompute, ElementOutput, kCount, Round> source_converter;
|
||||
// Convert source to internal compute numeric type
|
||||
NumericArrayConverter<ElementCompute, ElementSource, kCount, Round> source_converter;
|
||||
NumericArrayConverter<ElementCompute, ElementAccumulator, kCount, Round> accumulator_converter;
|
||||
|
||||
// Convert to destination numeric type
|
||||
NumericArrayConverter<ElementOutput, ElementCompute, kCount, Round> destination_converter;
|
||||
|
||||
ComputeFragment converted_source = source_converter(source);
|
||||
ComputeFragment converted_accumulator = accumulator_converter(accumulator);
|
||||
FragmentCompute converted_source = source_converter(source);
|
||||
FragmentCompute converted_accumulator = accumulator_converter(accumulator);
|
||||
|
||||
if (Scale == ScaleType::Nothing)
|
||||
return destination_converter(converted_accumulator);
|
||||
|
||||
// Perform binary operations
|
||||
ComputeFragment intermediate;
|
||||
FragmentCompute intermediate;
|
||||
|
||||
multiplies<ComputeFragment> mul_add_source;
|
||||
multiply_add<ComputeFragment> mul_add_accumulator;
|
||||
multiplies<FragmentCompute> mul_add_source;
|
||||
multiply_add<FragmentCompute> mul_add_accumulator;
|
||||
|
||||
if (Scale == ScaleType::NoBetaScaling)
|
||||
intermediate = converted_source;
|
||||
@@ -221,7 +245,7 @@ public:
|
||||
/// Computes linear scaling: D = alpha * accumulator
|
||||
CUTLASS_HOST_DEVICE
|
||||
FragmentOutput operator()(
|
||||
FragmentAccumulator const &accumulator) const {
|
||||
FragmentAccumulator const &accumulator) const {
|
||||
|
||||
// Convert source to interal compute numeric type
|
||||
NumericArrayConverter<ElementCompute, ElementAccumulator, kCount, Round> accumulator_converter;
|
||||
@@ -229,14 +253,14 @@ public:
|
||||
// Convert to destination numeric type
|
||||
NumericArrayConverter<ElementOutput, ElementCompute, kCount, Round> destination_converter;
|
||||
|
||||
ComputeFragment converted_accumulator = accumulator_converter(accumulator);
|
||||
FragmentCompute converted_accumulator = accumulator_converter(accumulator);
|
||||
|
||||
if (Scale == ScaleType::Nothing)
|
||||
return destination_converter(converted_accumulator);
|
||||
|
||||
// Perform binary operations
|
||||
ComputeFragment intermediate;
|
||||
multiplies<ComputeFragment> mul_accumulator;
|
||||
FragmentCompute intermediate;
|
||||
multiplies<FragmentCompute> mul_accumulator;
|
||||
|
||||
intermediate = mul_accumulator(alpha_, converted_accumulator); // D = alpha * Accum
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#include "cutlass/numeric_conversion.h"
|
||||
|
||||
#include "cutlass/epilogue/thread/activation.h"
|
||||
#include "cutlass/epilogue/thread/scale_type.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -90,7 +91,13 @@ public:
|
||||
using FragmentZ = Array<ElementZ, kElementsPerAccess>;
|
||||
using FragmentT = Array<ElementT, kElementsPerAccess>;
|
||||
|
||||
// Definitions needed for collective epilogue
|
||||
using FragmentSource = FragmentC;
|
||||
using FragmentOutput = FragmentZ;
|
||||
using ElementBias = ElementVector;
|
||||
using FragmentBias = FragmentCompute;
|
||||
using ActivationFunctor = ElementwiseOp;
|
||||
static const ScaleType::Kind kScale = ScaleType::Default;
|
||||
|
||||
static bool const kIsHeavy = ElementwiseOp::kIsHeavy;
|
||||
|
||||
@@ -196,8 +203,8 @@ public:
|
||||
/// Applies the operation when is_source_needed() is true
|
||||
CUTLASS_HOST_DEVICE
|
||||
void operator()(
|
||||
FragmentZ &frag_Z,
|
||||
FragmentT &frag_T,
|
||||
FragmentZ &frag_Z,
|
||||
FragmentT &frag_T,
|
||||
FragmentAccumulator const &AB,
|
||||
FragmentC const &frag_C,
|
||||
FragmentCompute const &V) const {
|
||||
@@ -227,8 +234,8 @@ public:
|
||||
/// Applies the operation when is_source_needed() is false
|
||||
CUTLASS_HOST_DEVICE
|
||||
void operator()(
|
||||
FragmentZ &frag_Z,
|
||||
FragmentT &frag_T,
|
||||
FragmentZ &frag_Z,
|
||||
FragmentT &frag_T,
|
||||
FragmentAccumulator const &AB,
|
||||
FragmentCompute const &V) const {
|
||||
|
||||
|
||||
@@ -87,6 +87,7 @@ public:
|
||||
using FragmentOutput = Array<ElementOutput, kCount>;
|
||||
using FragmentAccumulator = Array<ElementAccumulator, kCount>;
|
||||
using ComputeFragment = Array<ElementCompute, kCount>;
|
||||
using FragmentSource = Array<ElementOutput, kCount>;
|
||||
|
||||
static FloatRoundStyle const kRound = Round;
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cutlass/half.h>
|
||||
#include "cutlass/half.h"
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/numeric_types.h"
|
||||
#include "cutlass/array.h"
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cutlass/half.h>
|
||||
#include "cutlass/half.h"
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/numeric_types.h"
|
||||
#include "cutlass/array.h"
|
||||
|
||||
@@ -78,6 +78,7 @@ public:
|
||||
|
||||
using FragmentOutput = Array<ElementOutput, kCount>;
|
||||
using FragmentAccumulator = Array<ElementAccumulator, kCount>;
|
||||
using FragmentSource = Array<ElementOutput, kCount>;
|
||||
using FragmentCompute = Array<ElementCompute, kCount>;
|
||||
|
||||
static FloatRoundStyle const kRound = Round;
|
||||
|
||||
@@ -72,6 +72,7 @@ public:
|
||||
using FragmentOutput = Array<ElementOutput, kCount>;
|
||||
using FragmentAccumulator = Array<ElementAccumulator, kCount>;
|
||||
using ComputeFragment = Array<ElementCompute, kCount>;
|
||||
using FragmentSource = Array<ElementOutput, kCount>;
|
||||
|
||||
static FloatRoundStyle const kRound = Round;
|
||||
|
||||
|
||||
@@ -56,13 +56,13 @@ struct LinearCombinationParams {
|
||||
LinearCombinationParams(ElementCompute alpha, ElementCompute beta)
|
||||
: alpha_data {0lu, 0lu}, beta_data {0lu, 0lu}
|
||||
{
|
||||
#if defined(__CUDA_ARCH__)
|
||||
#if defined(__CUDA_ARCH__)
|
||||
reinterpret_cast<ElementCompute&>(alpha_data) = alpha;
|
||||
reinterpret_cast<ElementCompute&>(beta_data) = beta;
|
||||
#else
|
||||
#else
|
||||
memcpy( alpha_data, &alpha, sizeof(ElementCompute) );
|
||||
memcpy( beta_data, &beta, sizeof(ElementCompute) );
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cutlass/half.h>
|
||||
#include "cutlass/half.h"
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/numeric_types.h"
|
||||
#include "cutlass/array.h"
|
||||
@@ -90,6 +90,7 @@ public:
|
||||
using FragmentAccumulator = Array<ElementAccumulator, kCount>;
|
||||
using FragmentCompute = Array<ElementCompute, kCount>;
|
||||
using FragmentScaleBias = Array<ElementCompute, kCount>;
|
||||
using FragmentSource = Array<ElementOutput, kCount>;
|
||||
|
||||
static FloatRoundStyle const kRound = Round;
|
||||
|
||||
@@ -321,6 +322,7 @@ public:
|
||||
using FragmentAccumulator = Array<ElementAccumulator, kCount>;
|
||||
using FragmentCompute = Array<ElementCompute, kCount>;
|
||||
using FragmentScaleBias = Array<ElementCompute, kCount>;
|
||||
using FragmentSource = Array<ElementOutput, kCount>;
|
||||
|
||||
static FloatRoundStyle const kRound = Round;
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cutlass/half.h>
|
||||
#include "cutlass/half.h"
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/numeric_types.h"
|
||||
#include "cutlass/array.h"
|
||||
@@ -93,6 +93,7 @@ public:
|
||||
using FragmentAccumulator = Array<ElementAccumulator, kCount>;
|
||||
using FragmentCompute = Array<ElementCompute, kCount>;
|
||||
using FragmentScaleBias = Array<ElementCompute, kCount>;
|
||||
using FragmentSource = Array<ElementOutput, kCount>;
|
||||
|
||||
static FloatRoundStyle const kRound = Round;
|
||||
|
||||
@@ -308,6 +309,7 @@ public:
|
||||
using FragmentAccumulator = Array<ElementAccumulator, kCount>;
|
||||
using FragmentCompute = Array<ElementCompute, kCount>;
|
||||
using FragmentScaleBias = Array<ElementCompute, kCount>;
|
||||
using FragmentSource = Array<ElementOutput, kCount>;
|
||||
|
||||
static FloatRoundStyle const kRound = Round;
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "cutlass/array.h"
|
||||
#include "cutlass/functional.h"
|
||||
#include "cutlass/numeric_conversion.h"
|
||||
#include "cutlass/epilogue/thread/detail.hpp"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -45,14 +46,6 @@ namespace cutlass {
|
||||
namespace epilogue {
|
||||
namespace thread {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// Dummy class used to designate that the second binary operator in the epilogue is unsued
|
||||
template <typename T>
|
||||
class NoOp {};
|
||||
|
||||
}
|
||||
|
||||
/// Models a residual block of the form: UnaryOp(BinaryOp(BinaryOp(ActivationOp(TensorOp(X) + bias), residual1), residual2))
|
||||
template <typename ElementOutput_, typename ElementAccumulator_,
|
||||
typename ElementCompute_, typename ElementC_, int ElementsPerAccess,
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017 - 2023 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 Functor performing linear combination operation, bias addition, and tensor-tensor
|
||||
elementwise operations
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/array.h"
|
||||
#include "cutlass/functional.h"
|
||||
#include "cutlass/numeric_conversion.h"
|
||||
#include "cutlass/numeric_types.h"
|
||||
#include "cutlass/epilogue/thread/activation.h"
|
||||
#include "cutlass/epilogue/thread/detail.hpp"
|
||||
#include "cutlass/epilogue/thread/scale_type.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cutlass {
|
||||
namespace epilogue {
|
||||
namespace thread {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// Returns whether a source operand is needed for a combination of binary operation and scale
|
||||
/// type. Simple specialized checks are made for cases in which 0 is an identity element of
|
||||
/// the binary operation.
|
||||
template <class BinaryOp, class ElementCompute, ScaleType::Kind Scale>
|
||||
CUTLASS_HOST_DEVICE
|
||||
bool is_binary_op_source_needed(ElementCompute scale) {
|
||||
if constexpr (cute::is_same_v<BinaryOp, NoOp<ElementCompute>>) {
|
||||
return false;
|
||||
}
|
||||
else if constexpr (cute::is_same_v<BinaryOp, plus<ElementCompute>> || cute::is_same_v<BinaryOp, minus<ElementCompute>>) {
|
||||
// Cases for binary operators for which 0 is an identity element
|
||||
if constexpr (Scale == ScaleType::NoBetaScaling) return true;
|
||||
|
||||
if constexpr (Scale == ScaleType::OnlyAlphaScaling) return false;
|
||||
|
||||
if constexpr (Scale == ScaleType::Nothing) return false;
|
||||
|
||||
return scale != ElementCompute(0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** Compute a tensor-tensor broadcast epilogue.
|
||||
*
|
||||
* @param ElementOutput_ Data type used to load and store tensors
|
||||
* @param ElementAccumulator_ Accumulator data type
|
||||
* @param ElementCompute_ Data type used to compute linear combination
|
||||
* @param ElementBias_ Data type of Bias elements
|
||||
* @param ActivationFunctor_ Fused Activation
|
||||
* @param BinaryOp0_ Binary operation to perform on O0 and C0. detail::NoOp means no operation
|
||||
* @param BinaryOp1_ Binary operation to perform on O1 and C1. detail::NoOp means no operation
|
||||
* @param UnaryOp_ Unary operation to perform on final result
|
||||
* @param Scale Controls the type of Alpha and Beta scaling to perform
|
||||
* @param Round How values should be rounded in conversions
|
||||
* @param ElementSource_ Data type used for source operands
|
||||
*
|
||||
* Computes the following:
|
||||
* O0 = alpha * accumulator + bias
|
||||
* O1 = BinaryOp0(O0, beta * C0)
|
||||
* O2 = BinaryOp1(O1, beta * C1)
|
||||
* D = UnaryOp(O2)
|
||||
*/
|
||||
template <
|
||||
class ElementOutput_,
|
||||
class ElementAccumulator_ = ElementOutput_,
|
||||
class ElementCompute_ = ElementOutput_,
|
||||
class ElementBias_ = ElementCompute_,
|
||||
template <class T> class ActivationFunctor_ = Identity,
|
||||
template <class T> class BinaryOp0_ = plus,
|
||||
template <class T> class BinaryOp1_ = detail::NoOp,
|
||||
template <class T> class UnaryOp_ = Identity,
|
||||
ScaleType::Kind Scale = ScaleType::Default,
|
||||
FloatRoundStyle Round = FloatRoundStyle::round_to_nearest,
|
||||
class ElementSource_ = ElementOutput_
|
||||
>
|
||||
class LinearCombinationTensorBroadcast {
|
||||
public:
|
||||
|
||||
using ElementOutput = ElementOutput_;
|
||||
using ElementAccumulator = ElementAccumulator_;
|
||||
using ElementCompute = ElementCompute_;
|
||||
using ElementBias = ElementBias_;
|
||||
using ElementC = ElementSource_;
|
||||
using ElementD = ElementOutput_;
|
||||
using ElementScalingFactor = ElementAccumulator_;
|
||||
|
||||
using UnaryOp = UnaryOp_<ElementCompute>;
|
||||
using BinaryOp0 = BinaryOp0_<ElementCompute>;
|
||||
using BinaryOp1 = BinaryOp1_<ElementCompute>;
|
||||
using ActivationFunctor = ActivationFunctor_<ElementCompute>;
|
||||
|
||||
static constexpr int kCount = 1;
|
||||
|
||||
using FragmentOutput = Array<ElementOutput, kCount>;
|
||||
using FragmentAccumulator = Array<ElementAccumulator, kCount>;
|
||||
using ComputeFragment = Array<ElementCompute, kCount>;
|
||||
using FragmentBias = Array<ElementBias, kCount>;
|
||||
|
||||
static constexpr FloatRoundStyle kRound = Round;
|
||||
using NoOpType = detail::NoOp<ElementCompute>;
|
||||
static constexpr bool IsBinaryOp0Enabled = !cute::is_same_v<BinaryOp0, NoOpType>;
|
||||
static constexpr bool IsBinaryOp1Enabled = !cute::is_same_v<BinaryOp1, NoOpType>;
|
||||
static constexpr bool IsUnaryOpEnabled = !cute::is_same_v<UnaryOp, NoOpType> && !cute::is_same_v<UnaryOp, Identity<ElementCompute>>;
|
||||
|
||||
/// Host-constructable parameters structure
|
||||
struct Params {
|
||||
|
||||
ElementCompute alpha{}; ///< scales accumulators
|
||||
ElementCompute beta{}; ///< scales source tensor
|
||||
ElementCompute const* alpha_ptr = nullptr; ///< pointer to accumulator scalar - if not null, loads it from memory
|
||||
ElementCompute const* beta_ptr = nullptr; ///< pointer to source scalar - if not null, loads it from memory
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
Params() = default;
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
Params(ElementCompute const* alpha_ptr, ElementCompute const* beta_ptr)
|
||||
: alpha_ptr(alpha_ptr),
|
||||
beta_ptr(beta_ptr) {}
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
Params(ElementCompute const* alpha_ptr)
|
||||
: alpha_ptr(alpha_ptr) {}
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
Params(ElementCompute alpha,
|
||||
ElementCompute beta)
|
||||
: alpha(alpha),
|
||||
beta(beta) {}
|
||||
};
|
||||
|
||||
private:
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
ElementCompute alpha_;
|
||||
ElementCompute beta_;
|
||||
|
||||
public:
|
||||
|
||||
/// Constructs the function object, possibly loading from pointers in host memory
|
||||
CUTLASS_HOST_DEVICE
|
||||
LinearCombinationTensorBroadcast(Params const& params)
|
||||
: alpha_(params.alpha_ptr ? *params.alpha_ptr : params.alpha),
|
||||
beta_(params.beta_ptr ? *params.beta_ptr : params.beta) {}
|
||||
|
||||
/// Returns true if source 0 is needed
|
||||
CUTLASS_HOST_DEVICE
|
||||
bool is_source0_needed() const {
|
||||
return detail::is_binary_op_source_needed<BinaryOp0, ElementCompute, Scale>(beta_);
|
||||
}
|
||||
|
||||
/// Returns true if source 1 is needed
|
||||
CUTLASS_HOST_DEVICE
|
||||
bool is_source1_needed() const {
|
||||
return detail::is_binary_op_source_needed<BinaryOp1, ElementCompute, Scale>(beta_);
|
||||
}
|
||||
|
||||
//
|
||||
// Specialization for scalar
|
||||
//
|
||||
CUTLASS_HOST_DEVICE
|
||||
ElementD operator()(ElementAccumulator const accumulator, ElementC const source0, ElementC source1, ElementBias const bias) {
|
||||
// Convert everything to Compute type, do compute, and then store to output type
|
||||
NumericConverter<ElementCompute, ElementAccumulator, Round> accumulator_converter;
|
||||
NumericConverter<ElementCompute, ElementBias, Round> bias_converter;
|
||||
NumericConverter<ElementCompute, ElementC, Round> source_converter;
|
||||
NumericConverter<ElementD, ElementCompute, Round> destination_converter;
|
||||
|
||||
ActivationFunctor act;
|
||||
multiplies<ElementCompute> mul;
|
||||
multiply_add<ElementCompute> madd;
|
||||
|
||||
ElementCompute intermediate = accumulator_converter(accumulator);
|
||||
intermediate = madd(alpha_, intermediate, bias_converter(bias));
|
||||
intermediate = act(intermediate);
|
||||
|
||||
// Apply BinaryOp0, if needed
|
||||
if constexpr (IsBinaryOp0Enabled) {
|
||||
BinaryOp0 bin0;
|
||||
ElementCompute converted_source = source_converter(source0);
|
||||
intermediate = bin0(intermediate, mul(beta_, converted_source));
|
||||
}
|
||||
|
||||
// Apply BinaryOp1, if needed
|
||||
if constexpr (IsBinaryOp1Enabled) {
|
||||
BinaryOp1 bin1;
|
||||
ElementCompute converted_source = source_converter(source1);
|
||||
intermediate = bin1(intermediate, mul(beta_, converted_source));
|
||||
}
|
||||
|
||||
// Apply UnaryOp, if needed
|
||||
if constexpr (IsUnaryOpEnabled) {
|
||||
UnaryOp unary;
|
||||
intermediate = unary(intermediate);
|
||||
}
|
||||
|
||||
return destination_converter(intermediate);
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace thread
|
||||
} // namespace epilogue
|
||||
} // namespace cutlass
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cutlass/half.h>
|
||||
#include "cutlass/half.h"
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/numeric_types.h"
|
||||
#include "cutlass/array.h"
|
||||
|
||||
Reference in New Issue
Block a user