/*************************************************************************************************** * Copyright (c) 2017-2018, 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. * **************************************************************************************************/ /*! \file \brief Defines Fragment, a statically-sized array for storing parts of matrices within a thread's registers. */ #pragma once #include #include #include #include namespace cutlass { /////////////////////////////////////////////////////////////////////////////////////////////////// /*!@defgroup fragment_concept Fragment Concept @{ \ref fragment_concept is a statically sized array for storing parts of tiles held by individual CUDA threads. @par \ref fragment_concept Types satisfying \ref fragment_concept define the following members - Element - type of each access held within the fragment - kElements - number of elements stored by the fragment - clear() - overwrites the fragment storage with zeros - Element & operator[](int i) - by-reference access of the ith element - Element const & operator[](int i) const - const by-reference access of the ith element @} */ /////////////////////////////////////////////////////////////////////////////////////////////////// /*!@defgroup fragment_iterator_concept Fragment Iterator Concept @{ \ref fragment_iterator_concept provides structured access to the elements within a fragment with an optional bitcast to the desired access type @par \ref fragment_iterator_concept Types satisfying \ref fragment_iterator_concept define the following members - AccessType& operator[](int i) - provides access to the ith element of the fragment - AccessType& at(int d, int h, int w, int c) - applies \ref layout_concept to fragment and provides access to element at (d, h, w, c) @} */ //////////////////////////////////////////////////////////////////////////////////////////////////// template struct StorageType { typedef uint64_t Type; }; template <> struct StorageType<4> { typedef uint32_t Type; }; template <> struct StorageType<2> { typedef uint16_t Type; }; template <> struct StorageType<1> { typedef uint8_t Type; }; //////////////////////////////////////////////////////////////////////////////////////////////////// /** * @brief A template defining \ref fragment_concept * @concept{fragment_concept} */ template struct Fragment : public AlignedStruct { /// Make sure the alignment makes sense wrt the size of elements. static_assert(kAlignment_ == 16 || kAlignment_ >= sizeof(Element_), "Alignment is too small"); /// Alignment must be a power of two static_assert(is_pow2::value, "Alignment must be a power of two"); /// This class. typedef Fragment This_; /// The element. typedef Element_ Element; /// The number of elements. static int const kElements = kElements_; /// Clear a fragment. CUTLASS_DEVICE void clear() { // Avoid element-wise access for sub 32b element type if (kAlignment_ >= 8 && (kElements * sizeof(Element)) % 8 == 0) { uint64_t* ptr = reinterpret_cast(storage); for (int i = 0; i < (kElements * sizeof(Element)) / 8; ++i) { ptr[i] = uint64_t(0); } } else if (kAlignment_ >= 4 && (kElements * sizeof(Element)) % 4 == 0) { uint32_t* ptr = reinterpret_cast(storage); for (int i = 0; i < (kElements * sizeof(Element)) / 4; ++i) { ptr[i] = uint32_t(0); } } else if (kAlignment_ >= 2 && (kElements * sizeof(Element)) % 2 == 0) { uint16_t* ptr = reinterpret_cast(storage); for (int i = 0; i < (kElements * sizeof(Element)) / 2; ++i) { ptr[i] = uint16_t(0); } } else { for (int i = 0; i < kElements; ++i) { storage[i] = 0; } } } /// The accessor. CUTLASS_DEVICE Element& operator[](int i) { assert(i < kElements_); return reinterpret_cast(storage)[i]; } /// The accessor. CUTLASS_DEVICE Element const& operator[](int i) const { assert(i < kElements_); return reinterpret_cast(storage)[i]; } private: /// Storage type to use for Elements typedef typename StorageType::Type StorageType; /// Number of elements in the storage static int const kStorageCount = (sizeof(Element_) * kElements_ + sizeof(StorageType) - 1) / sizeof(StorageType); /// The storage. StorageType storage[kStorageCount]; /// Ensure that there's enough storage for all elements static_assert(sizeof(StorageType) <= kAlignment_, "StorageType is too big for given alignment"); }; //////////////////////////////////////////////////////////////////////////////////////////////////// /** * @brief A template defining \ref fragment_iterator_concept * @concept{fragment_iterator_concept} */ template struct FragmentIterator { /// This class. typedef FragmentIterator This_; /// The fragment. typedef Fragment_ Fragment; /// The number of iterations. typedef Iterations_ Iterations; /// The access type. typedef AccessType_ AccessType; /// The element. typedef typename Fragment::Element Element; /// The number of elements per access. static int const kElementsPerAccess = (int)(sizeof(AccessType) / sizeof(Element)); /// The shape of the the fragment. typedef typename ShapeMul >::Shape FragmentShape; /// The linear strides for iterations. typedef typename ShapeStrides::Shape Strides; /// Ctor. template CUTLASS_DEVICE FragmentIterator(OtherFragment_& fragment, int offset = 0) : pointer(reinterpret_cast(&fragment[offset])) { static_assert(OtherFragment_::kElements >= Fragment::kElements, ""); } /// The accessor. CUTLASS_DEVICE AccessType const& at(int d, int h, int w, int c = 0) const { int const imm = ComputeOffsetFromStrides::get(d, h, w, c); return reinterpret_cast(pointer[imm]); } /// The accessor. CUTLASS_DEVICE AccessType& at(int d, int h, int w, int c = 0) { int const imm = ComputeOffsetFromStrides::get(d, h, w, c); return reinterpret_cast(pointer[imm]); } /// The accessor. CUTLASS_DEVICE AccessType const& operator[](int i) const { return reinterpret_cast(pointer[i * kElementsPerAccess]); } /// The accessor. CUTLASS_DEVICE AccessType& operator[](int i) { return reinterpret_cast(pointer[i * kElementsPerAccess]); } /// Is the iterator valid? CUTLASS_DEVICE bool valid(int d, int h, int w, int c) const { return true; } /// The pointer. Element* pointer; }; //////////////////////////////////////////////////////////////////////////////////////////////////// template struct FragmentConstIterator { /// This class. typedef FragmentIterator This_; /// The fragment. typedef Fragment_ Fragment; /// The number of iterations. typedef Iterations_ Iterations; /// The access type. typedef AccessType_ AccessType; /// The element. typedef typename Fragment::Element Element; /// The number of elements per access. static int const kElementsPerAccess = (int)(sizeof(AccessType) / sizeof(Element)); /// The shape of the the fragment. typedef typename ShapeMul >::Shape FragmentShape; /// The linear strides for iterations. typedef typename ShapeStrides::Shape IterationsStrides; /// Ctor. template CUTLASS_DEVICE FragmentConstIterator(OtherFragment_& fragment, int offset = 0) : pointer(reinterpret_cast(&fragment[offset])) { static_assert(OtherFragment_::kElements >= Fragment::kElements, ""); } /// Create from non-constant FragmentIterator CUTLASS_DEVICE FragmentConstIterator( FragmentIterator const& rhs_) : pointer(reinterpret_cast(rhs_.offset)) {} /// The accessor. CUTLASS_DEVICE AccessType const& at(int d, int h, int w, int c = 0) const { int const imm = ComputeOffsetFromStrides::get(d, h, w, c); return reinterpret_cast(pointer[imm]); } /// The accessor. CUTLASS_DEVICE AccessType const& operator[](int i) const { return reinterpret_cast(pointer[i * kElementsPerAccess]); } /// Is the iterator valid? CUTLASS_DEVICE bool valid(int d, int h, int w, int c) const { return true; } /// The pointer. Element const* pointer; }; //////////////////////////////////////////////////////////////////////////////////////////////////// } // namespace cutlass