* Add more GPU architectures support * Update layout.py * Optimize performance, Add SM90 support, Add 1D2D SM100 support * Add fmtlib submodule at commit 553ec11 --------- Co-authored-by: fzyzcjy <5236035+fzyzcjy@users.noreply.github.com>
170 lines
7.6 KiB
Plaintext
170 lines
7.6 KiB
Plaintext
#pragma once
|
|
|
|
#include <cute/atom/mma_traits_sm100.hpp>
|
|
#include <cute/arch/mma_sm100_umma.hpp>
|
|
#include <cute/arch/tmem_allocator_sm100.hpp>
|
|
|
|
#include <deep_gemm/common/utils.cuh>
|
|
|
|
namespace deep_gemm::sm100 {
|
|
|
|
template <uint32_t BLOCK_INNER, uint32_t kSwizzleMode, typename dtype_t>
|
|
constexpr uint32_t get_inner_block_atom_size() {
|
|
return kSwizzleMode == 0 ? BLOCK_INNER : kSwizzleMode / sizeof(dtype_t);
|
|
}
|
|
|
|
template <uint32_t BLOCK_INNER, uint32_t BLOCK_OUTER,
|
|
uint32_t kSwizzleMode, uint32_t kNumMulticast,
|
|
typename dtype_t>
|
|
__device__ __forceinline__ void
|
|
tma_copy(void const* desc_ptr, cutlass::arch::ClusterTransactionBarrier* barrier_ptr,
|
|
dtype_t* smem_ptr, const uint32_t& inner_idx, const int32_t& outer_idx) {
|
|
DG_STATIC_ASSERT(1 <= kNumMulticast and kNumMulticast <= 2, "Invalid multicast config");
|
|
DG_STATIC_ASSERT(static_cast<uint64_t>(cute::TMA::CacheHintSm90::EVICT_NORMAL) ==
|
|
static_cast<uint64_t>(cute::TMA::CacheHintSm100::EVICT_NORMAL), "Invalid cache hint");
|
|
|
|
// 2-CTA function will send signals to the leader CTA only
|
|
const auto copy_func = kNumMulticast == 1 ? cute::SM90_TMA_LOAD_2D::copy : cute::SM100_TMA_2SM_LOAD_2D::copy;
|
|
|
|
// Issue multiple TMAs
|
|
constexpr uint32_t BLOCK_INNER_ATOM = get_inner_block_atom_size<BLOCK_INNER, kSwizzleMode, dtype_t>();
|
|
#pragma unroll
|
|
for (uint32_t i = 0; i < BLOCK_INNER / BLOCK_INNER_ATOM; ++ i) {
|
|
copy_func(desc_ptr, reinterpret_cast<uint64_t*>(barrier_ptr),
|
|
static_cast<uint64_t>(cute::TMA::CacheHintSm100::EVICT_NORMAL),
|
|
smem_ptr + i * BLOCK_OUTER * BLOCK_INNER_ATOM, inner_idx + i * BLOCK_INNER_ATOM, outer_idx);
|
|
}
|
|
}
|
|
|
|
__device__ __forceinline__
|
|
cute::UMMA::SmemDescriptor make_smem_desc(cute::UMMA::LayoutType layout, void* smem_ptr,
|
|
uint32_t stride_byte_offset, uint32_t leading_byte_offset) {
|
|
cute::UMMA::SmemDescriptor desc;
|
|
|
|
// Set the version for SM100
|
|
desc.version_ = 1;
|
|
|
|
// Legacy mode
|
|
desc.lbo_mode_ = 0;
|
|
|
|
// Layout
|
|
desc.layout_type_ = static_cast<uint8_t>(layout);
|
|
|
|
// Start address
|
|
const auto uint_ptr = cute::cast_smem_ptr_to_uint(smem_ptr);
|
|
desc.start_address_ = static_cast<uint16_t>(uint_ptr >> 4);
|
|
|
|
// Base offset
|
|
desc.base_offset_ = 0;
|
|
|
|
// SBO and LBO
|
|
desc.stride_byte_offset_ = stride_byte_offset >> 4;
|
|
desc.leading_byte_offset_ = leading_byte_offset >> 4;
|
|
|
|
return desc;
|
|
}
|
|
|
|
__device__ __forceinline__
|
|
cute::UMMA::SmemDescriptor make_sf_desc(void* smem_ptr) {
|
|
// NOTES: the UTCCP layout is K-major by default
|
|
// Atom size: 8 x 128 bits
|
|
// {SBO, LBO} means the byte stride between atoms on {MN, K}
|
|
// Since the UTCCP we used is 128b-wide (only 1 atom on K), so LBO can be zero
|
|
return make_smem_desc(cute::UMMA::LayoutType::SWIZZLE_NONE, smem_ptr, 8 * 16, 0);
|
|
}
|
|
|
|
__device__ __forceinline__
|
|
void replace_smem_desc_addr(cute::UMMA::SmemDescriptor& desc, const void* smem_ptr) {
|
|
const auto uint_ptr = cute::cast_smem_ptr_to_uint(smem_ptr);
|
|
desc.start_address_ = static_cast<uint16_t>(uint_ptr >> 4);
|
|
}
|
|
|
|
// ReSharper disable once CppNotAllPathsReturnValue
|
|
template <uint32_t kSwizzleMode>
|
|
constexpr static cute::UMMA::LayoutType to_umma_layout_type() {
|
|
DG_STATIC_ASSERT(kSwizzleMode == 0 or kSwizzleMode == 16 or
|
|
kSwizzleMode == 32 or kSwizzleMode == 64 or
|
|
kSwizzleMode == 128, "Invalid swizzling mode");
|
|
if constexpr (kSwizzleMode == 0) return cute::UMMA::LayoutType::SWIZZLE_NONE;
|
|
if constexpr (kSwizzleMode == 16) return cute::UMMA::LayoutType::SWIZZLE_NONE;
|
|
if constexpr (kSwizzleMode == 32) return cute::UMMA::LayoutType::SWIZZLE_32B;
|
|
if constexpr (kSwizzleMode == 64) return cute::UMMA::LayoutType::SWIZZLE_64B;
|
|
if constexpr (kSwizzleMode == 128) return cute::UMMA::LayoutType::SWIZZLE_128B;
|
|
}
|
|
|
|
template <cute::UMMA::Major kMajorMode, uint32_t BLOCK_MN, uint32_t kSwizzleMode, typename dtype_t>
|
|
__device__ __forceinline__
|
|
constexpr uint32_t get_umma_desc_stride_k() {
|
|
return kMajorMode == cute::UMMA::Major::K ? 1 : get_inner_block_atom_size<BLOCK_MN, kSwizzleMode, dtype_t>();
|
|
}
|
|
|
|
template <cute::UMMA::Major kMajorMode, uint32_t BLOCK_MN, uint32_t kSwizzleMode, typename dtype_t>
|
|
__device__ __forceinline__
|
|
uint32_t advance_umma_desc_lo(const uint32_t& base, const uint32_t& offset, const uint32_t& k_idx) {
|
|
return base + ((offset + k_idx * get_umma_desc_stride_k<kMajorMode, BLOCK_MN, kSwizzleMode, dtype_t>()) >> 4u);
|
|
}
|
|
|
|
template <cute::UMMA::Major kMajorMode, uint32_t BLOCK_MN, uint32_t BLOCK_K, uint32_t kSwizzleMode, typename dtype_t>
|
|
__device__ __forceinline__
|
|
cute::UMMA::SmemDescriptor make_umma_desc(dtype_t* base_smem_ptr, uint32_t mn_idx, uint32_t k_idx) {
|
|
const uint32_t stride_k = get_umma_desc_stride_k<kMajorMode, BLOCK_MN, kSwizzleMode, dtype_t>();
|
|
if constexpr (kMajorMode == cute::UMMA::Major::K) {
|
|
// NOTES: for K-major layout, the swizzle must be 128B (also, atom index must be 0), as `BLOCK_K` is always 128
|
|
DG_STATIC_ASSERT(kSwizzleMode == BLOCK_K * sizeof(dtype_t), "Unexpected value");
|
|
|
|
// Atom size: 8 x `kSwizzleMode` (in bytes, on K)
|
|
// {SBO, LBO} means the byte stride between atoms on {MN, K}
|
|
// NOTES: on K, there is only 1 atom as asserted previously, so LBO can be 0
|
|
const uint32_t stride_byte_offset = 8 * BLOCK_K * sizeof(dtype_t);
|
|
const uint32_t leading_byte_offset = 0;
|
|
return make_smem_desc(to_umma_layout_type<kSwizzleMode>(),
|
|
base_smem_ptr + mn_idx * BLOCK_K + k_idx * stride_k,
|
|
stride_byte_offset, leading_byte_offset);
|
|
} else {
|
|
constexpr uint32_t BLOCK_MN_ATOM = get_inner_block_atom_size<BLOCK_MN, kSwizzleMode, dtype_t>();
|
|
|
|
// Must have no in-atom MN-idx
|
|
// NOTES: no worries for the runtime assert, the `mn_idx` are constants at compilation time
|
|
DG_DEVICE_ASSERT(mn_idx % BLOCK_MN_ATOM == 0);
|
|
DG_STATIC_ASSERT(kSwizzleMode > 0, "Invalid swizzling");
|
|
|
|
// Atom size: `kSwizzleMode` (in bytes, on MN) x 8
|
|
// NOTES: `kSwizzleMode == 16` mean non-swizzling but interleaving
|
|
// {SBO, LBO} means the byte stride between atoms on {K, MN} for swizzling
|
|
// {SBO, LBO} means the byte stride between atoms on {MN, K} for non-swizzling
|
|
uint32_t stride_byte_offset = 8 * BLOCK_MN_ATOM * sizeof(dtype_t);
|
|
uint32_t leading_byte_offset = BLOCK_K * BLOCK_MN_ATOM * sizeof(dtype_t);
|
|
if constexpr (kSwizzleMode == 16)
|
|
swap(stride_byte_offset, leading_byte_offset);
|
|
return make_smem_desc(to_umma_layout_type<kSwizzleMode>(),
|
|
base_smem_ptr + mn_idx * BLOCK_K + k_idx * stride_k,
|
|
stride_byte_offset, leading_byte_offset);
|
|
}
|
|
}
|
|
|
|
__device__ __forceinline__
|
|
uint64_t make_runtime_instr_desc_with_sf_id(cute::UMMA::InstrDescriptorBlockScaled desc, const uint32_t& sf_id) {
|
|
desc.a_sf_id_ = sf_id, desc.b_sf_id_ = sf_id;
|
|
return static_cast<uint64_t>(static_cast<uint32_t>(desc)) << 32;
|
|
}
|
|
|
|
template <uint32_t kNumCols>
|
|
__device__ constexpr uint32_t get_num_aligned_tmem_cols() {
|
|
DG_STATIC_ASSERT(kNumCols <= 512, "Too many tensor memory columns");
|
|
if (kNumCols <= 32) return 32;
|
|
if (kNumCols <= 64) return 64;
|
|
if (kNumCols <= 128) return 128;
|
|
if (kNumCols <= 256) return 256;
|
|
return 512;
|
|
}
|
|
|
|
__device__ __forceinline__ void tcgen05_before_thread_sync() {
|
|
asm volatile("tcgen05.fence::before_thread_sync;");
|
|
}
|
|
|
|
__device__ __forceinline__ void tcgen05_after_thread_sync() {
|
|
asm volatile("tcgen05.fence::after_thread_sync;");
|
|
}
|
|
|
|
} // namespace `deep_gemm::sm100`
|