/*************************************************************************************************** * Copyright (c) 2017-2021, NVIDIA CORPORATION. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted * provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 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. * * Neither the name of the NVIDIA CORPORATION 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 NVIDIA CORPORATION 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 TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * **************************************************************************************************/ /*************************************************************************************************** * Copyright (c) 2021 Iluvatar CoreX. All rights reserved. * Copyright Declaration: This software, including all of its code and documentation, * except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX * Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright * Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar * CoreX. No user of this software shall have any right, ownership or interest in this software and * any use of this software shall be in compliance with the terms and conditions of the End User * License Agreement. **************************************************************************************************/ /*! \file \brief Templates implementing warp-level matrix multiply-accumulate operations targeting Tensor Cores. */ #pragma once #include "cutlass/cutlass.h" #include "cutlass/array.h" #include "cutlass/platform/platform.h" #include "cutlass/numeric_conversion.h" #include "cutlass/numeric_types.h" #include "cutlass/matrix_shape.h" #include "cutlass/arch/mma.h" #include "cutlass/gemm/gemm.h" #include "cutlass/gemm/warp/mma.h" #include "cutlass/gemm/warp/mma_tensor_op_policy.h" #include "cutlass/gemm/warp/mma_tensor_op_tile_iterator.h" ///////////////////////////////////////////////////////////////////////////////////////////////// namespace cutlass { namespace gemm { namespace warp { ///////////////////////////////////////////////////////////////////////////////////////////////// namespace detail { template struct ConvertAndPack { using Converter = NumericArrayConverter; CUTLASS_HOST_DEVICE Array operator()(Array const &source) { Converter converter; return converter(source); } }; template struct ConvertAndPack { CUTLASS_HOST_DEVICE Array operator()(Array const &source) { return source; } }; template struct ConvertAndPack { using Converter = NumericArrayConverter; CUTLASS_HOST_DEVICE Array operator()(Array const &source) { Converter converter; Array tmp; CUTLASS_PRAGMA_UNROLL for (int i = 0; i < N; ++i) { int idx = (((i << 1) & 2) | ((i >> 1) & 1) | (i & 0xfffffffc)); tmp[i] = source[idx]; } return converter(tmp); } }; template struct ConvertAndPack { using Converter = NumericArrayConverter; CUTLASS_HOST_DEVICE Array operator()(Array const &source) { Converter converter; Array tmp; CUTLASS_PRAGMA_UNROLL for (int i = 0; i < N; ++i) { int idx = (((i << 1) & 2) | ((i >> 1) & 1) | (i & 0xfffffffc)); tmp[i] = source[idx]; } return converter(tmp); } }; ///////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////// } // namespace detail ///////////////////////////////////////////////////////////////////////////////////////////////// /// Structure to compute the matrix product targeting CUDA cores and SIMT math instructions. template < /// Size of the Gemm problem - concept: gemm::GemmShape<> typename Shape_, /// Data type of A elements typename ElementA_, /// Layout of A matrix (concept: MatrixLayout) typename LayoutA_, /// Data type of B elements typename ElementB_, /// Layout of B matrix (concept: MatrixLayout) typename LayoutB_, /// Element type of C matrix typename ElementC_, /// Layout of C matrix (concept: MatrixLayout) typename LayoutC_, /// Policy describing warp-level MmaTensorOp (concept: MmaTensorOp policy) typename Policy_, /// Number of partitions along K dimension int PartitionsK_ = 1, /// Store the accumulators in row major or column major. /// Iluvatar Tensor Core always stores accumulators in row major bool AccumulatorsInRowMajor = true, /// Used for partial specialization typename Enable = bool > class MmaTensorOp { public: /// Shape of warp-level matrix operation (concept: GemmShape) using Shape = Shape_; /// Data type of multiplicand A using ElementA = ElementA_; /// Layout of multiplicand A using LayoutA = LayoutA_; /// Data type of multiplicand B using ElementB = ElementB_; /// Layout of multiplicand B using LayoutB = LayoutB_; /// Data type of accumulator matrix C using ElementC = ElementC_; /// Layout of accumulator matrix C using LayoutC = LayoutC_; /// Shape of the warp in units of thread (concept: MmaLanePolicySimt) using Policy = Policy_; /// Underlying matrix multiply operator (concept: arch::Mma) using ArchMmaOperator = typename Policy::Operator; /// Architecture tag from underlying instruction using ArchTag = typename ArchMmaOperator::ArchTag; /// Indicates class of matrix operator using OperatorClass = arch::OpClassTensorOp; /// Shape of underlying instruction using InstructionShape = typename ArchMmaOperator::Shape; /// Complex transform on A operand static ComplexTransform const kTransformA = ComplexTransform::kNone; /// Complex transform on B operand static ComplexTransform const kTransformB = ComplexTransform::kNone; /// Number of threads participating in warp-level matrix product static int const kThreadCount = NUM_THREADS_PER_WARP; /// Number of partitions along K dimension static int const kPartitionsK = PartitionsK_; public: /// FIXME(Peter Han): workaround to adapt to simt epilogue, need to remove struct ThreadMma { using ElementC = ElementC; }; /// Iterates over the A operand in memory using IteratorA = MmaTensorOpMultiplicandTileIterator< MatrixShape, Operand::kA, ElementA, LayoutA, InstructionShape, kThreadCount, kPartitionsK>; /// Storage for A tile using FragmentA = typename IteratorA::Fragment; /// Storage for transformed A tile using TransformedFragmentA = Array; /// Iterates over the B operand in memory using IteratorB = MmaTensorOpMultiplicandTileIterator< MatrixShape, Operand::kB, ElementB, LayoutB, InstructionShape, kThreadCount, kPartitionsK>; /// Storage for B tile using FragmentB = typename IteratorB::Fragment; /// Storage for transformed B tile using TransformedFragmentB = Array; /// Iterates over the C operand in memory using IteratorC = MmaTensorOpAccumulatorTileIterator< MatrixShape, ElementC, LayoutC, InstructionShape>; /// Storage for C tile using FragmentC = typename IteratorC::Fragment; static_assert( !(Shape::kM % Policy::Operator::Shape::kM) && !(Shape::kN % Policy::Operator::Shape::kN) && !(Shape::kK % Policy::Operator::Shape::kK), "Shape of warp-level Mma must be divisible by operator shape."); using MmaIterations = gemm::GemmShape< (Shape::kM + ArchMmaOperator::Shape::kM - 1) / ArchMmaOperator::Shape::kM, (Shape::kN + ArchMmaOperator::Shape::kN - 1) / ArchMmaOperator::Shape::kN, InstructionShape::kK / Policy::Operator::Shape::kK >; public: /// Underlying matrix multiply operator (concept: arch::Mma) ArchMmaOperator mma; public: // // Methods // /// Ctor CUTLASS_DEVICE MmaTensorOp() {} /// Performs a warp-level matrix multiply-accumulate operation CUTLASS_DEVICE void operator()( FragmentC &D, TransformedFragmentA const &A, TransformedFragmentB const &B, FragmentC const &C ) const { using MmaOperandA = typename ArchMmaOperator::FragmentA; using MmaOperandB = typename ArchMmaOperator::FragmentB; using MmaOperandC = typename ArchMmaOperator::FragmentC; D = C; MmaOperandA const *ptr_A = reinterpret_cast(&A); MmaOperandB const *ptr_B = reinterpret_cast(&B); MmaOperandC *ptr_D = reinterpret_cast(&D); // Serpentine visitation order maximizing reuse of Rb CUTLASS_PRAGMA_UNROLL for (int k = 0; k < MmaIterations::kK; ++k) { CUTLASS_PRAGMA_UNROLL for (int m = 0; m < MmaIterations::kM; ++m) { CUTLASS_PRAGMA_UNROLL for (int n = 0; n < MmaIterations::kN; ++n) { int n_serpentine = ((m % 2) ? (MmaIterations::kN - 1 - n) : n); /// assume A is column-major in VRF, B is row-major in VRF if(AccumulatorsInRowMajor) { mma( ptr_D[n_serpentine + m * MmaIterations::kN], ptr_A[m + k * MmaIterations::kM], ptr_B[n_serpentine + k * MmaIterations::kN], ptr_D[n_serpentine + m * MmaIterations::kN]); } else { mma( ptr_D[m + n_serpentine * MmaIterations::kM], ptr_A[m + k * MmaIterations::kM], ptr_B[n_serpentine + k * MmaIterations::kN], ptr_D[m + n_serpentine * MmaIterations::kM]); } } } } } /// Transform the mma operands to the required types CUTLASS_DEVICE void transform(TransformedFragmentA &dst_A, TransformedFragmentB &dst_B, FragmentA const &A, FragmentB const &B) const { // // Define conversions from source type to instruction type // FloatRoundStyle const kRoundA = PreferredRoundingMode::kRound; FloatRoundStyle const kRoundB = PreferredRoundingMode::kRound; detail::ConvertAndPack convert_A; NumericArrayConverter convert_B; Array const *ptr_A = reinterpret_cast const *>(&A); Array * ptr_dst_A = reinterpret_cast *>(&dst_A); dst_B = convert_B(B); ptr_dst_A[0] = convert_A(ptr_A[0]); ptr_dst_A[1] = convert_A(ptr_A[1]); } }; ///////////////////////////////////////////////////////////////////////////////////////////////// } // namespace warp } // namespace gemm } // namespace cutlass /////////////////////////////////////////////////////////////////////////////////////////////////