Updates for CUTLASS 3.4.1 (#1346)
* Updates for CUTLASS 3.4.1 * minor epi change
This commit is contained in:
@@ -62,8 +62,7 @@ class GemmUniversal<
|
||||
CollectiveMainloop_,
|
||||
CollectiveEpilogue_,
|
||||
TileScheduler_,
|
||||
cute::enable_if_t<cute::is_base_of_v<KernelArrayTmaWarpSpecializedCooperative, typename CollectiveMainloop_::DispatchPolicy::Schedule> ||
|
||||
cute::is_base_of_v<KernelGroupTmaWarpSpecializedCooperative, typename CollectiveMainloop_::DispatchPolicy::Schedule>>
|
||||
cute::enable_if_t<cute::is_base_of_v<KernelPtrArrayTmaWarpSpecializedCooperative, typename CollectiveMainloop_::DispatchPolicy::Schedule>>
|
||||
>
|
||||
{
|
||||
public:
|
||||
@@ -80,7 +79,9 @@ public:
|
||||
using ArchTag = typename CollectiveMainloop::ArchTag;
|
||||
using ElementA = typename CollectiveMainloop::ElementA;
|
||||
using StrideA = typename CollectiveMainloop::StrideA;
|
||||
using UnderlyingStrideA = typename CollectiveMainloop::UnderlyingStrideA;
|
||||
using ElementB = typename CollectiveMainloop::ElementB;
|
||||
using UnderlyingStrideB = typename CollectiveMainloop::UnderlyingStrideB;
|
||||
using StrideB = typename CollectiveMainloop::StrideB;
|
||||
using DispatchPolicy = typename CollectiveMainloop::DispatchPolicy;
|
||||
using Schedule = typename DispatchPolicy::Schedule;
|
||||
@@ -93,8 +94,10 @@ public:
|
||||
using CollectiveEpilogue = CollectiveEpilogue_;
|
||||
using ElementC = typename CollectiveEpilogue::ElementC;
|
||||
using StrideC = typename CollectiveEpilogue::StrideC;
|
||||
using UnderlyingStrideC = typename CollectiveEpilogue::UnderlyingStrideC;
|
||||
using ElementD = typename CollectiveEpilogue::ElementD;
|
||||
using StrideD = typename CollectiveEpilogue::StrideD;
|
||||
using UnderlyingStrideD = typename CollectiveEpilogue::UnderlyingStrideD;
|
||||
using EpilogueArguments = typename CollectiveEpilogue::Arguments;
|
||||
using EpilogueParams = typename CollectiveEpilogue::Params;
|
||||
|
||||
@@ -102,7 +105,7 @@ public:
|
||||
static_assert(cute::is_void_v<TileScheduler_>,
|
||||
"Ptr-Array Cooperative and Grouped Gemm Cooperative kernel only supports the default scheduler.");
|
||||
|
||||
static constexpr bool IsGroupedGemmKernel = cute::is_base_of_v<KernelGroupTmaWarpSpecializedCooperative, Schedule>;
|
||||
static constexpr bool IsGroupedGemmKernel = !cute::is_same_v<UnderlyingStrideA, StrideA>;
|
||||
|
||||
using TileScheduler = cute::conditional_t<IsGroupedGemmKernel,
|
||||
typename detail::TileSchedulerSelector<
|
||||
@@ -204,7 +207,7 @@ public:
|
||||
|
||||
void* scheduler_workspace = workspace_ptr;
|
||||
workspace_offset += TileScheduler::template get_workspace_size<typename ProblemShape::UnderlyingProblemShape, ElementAccumulator>(
|
||||
args.scheduler, problem_shapes.get_host_problem_shape(0), args.hw_info, NumMmaWarpGroups);
|
||||
args.scheduler, typename ProblemShape::UnderlyingProblemShape{}, args.hw_info, NumMmaWarpGroups);
|
||||
workspace_offset = round_nearest(workspace_offset, MinWorkspaceAlignment);
|
||||
|
||||
void* epilogue_workspace = workspace_ptr + workspace_offset;
|
||||
@@ -244,14 +247,11 @@ public:
|
||||
bool
|
||||
can_implement(Arguments const& args) {
|
||||
bool implementable = true;
|
||||
if constexpr (cute::is_base_of_v<KernelArrayTmaWarpSpecializedCooperative, Schedule>) {
|
||||
implementable &= (args.mode == GemmUniversalMode::kArray && rank(typename ProblemShape::UnderlyingProblemShape{}) == 4);
|
||||
} else if constexpr (IsGroupedGemmKernel) {
|
||||
if constexpr (IsGroupedGemmKernel) {
|
||||
// Group GEMM currently only supports rank-3 problem shapes
|
||||
implementable &= (args.mode == GemmUniversalMode::kGrouped && rank(typename ProblemShape::UnderlyingProblemShape{}) == 3);
|
||||
}
|
||||
else {
|
||||
implementable = false;
|
||||
} else {
|
||||
implementable &= (args.mode == GemmUniversalMode::kArray && rank(typename ProblemShape::UnderlyingProblemShape{}) == 4);
|
||||
}
|
||||
if (!implementable) {
|
||||
CUTLASS_TRACE_HOST(" CAN IMPLEMENT: Arguments or Problem Shape don't meet the requirements for Ptr Array Gemm or Grouped Gemm.\n");
|
||||
@@ -269,7 +269,7 @@ public:
|
||||
constexpr uint32_t NumEpilogueSubTiles = CollectiveEpilogue::get_store_pipe_increment(TileShape{});
|
||||
|
||||
workspace_size += TileScheduler::template get_workspace_size<typename ProblemShape::UnderlyingProblemShape, ElementAccumulator>(
|
||||
args.scheduler, args.problem_shape.get_host_problem_shape(0), args.hw_info, NumMmaWarpGroups, NumEpilogueSubTiles);
|
||||
args.scheduler, typename ProblemShape::UnderlyingProblemShape{}, args.hw_info, NumMmaWarpGroups, NumEpilogueSubTiles);
|
||||
workspace_size = round_nearest(workspace_size, MinWorkspaceAlignment);
|
||||
|
||||
workspace_size += CollectiveEpilogue::get_workspace_size(args.problem_shape, args.epilogue);
|
||||
@@ -297,9 +297,9 @@ public:
|
||||
constexpr uint32_t NumEpilogueSubTiles = CollectiveEpilogue::get_store_pipe_increment(TileShape{});
|
||||
|
||||
status = TileScheduler::template initialize_workspace<typename ProblemShape::UnderlyingProblemShape, ElementAccumulator>(
|
||||
args.scheduler, workspace_ptr + workspace_offset, stream, args.problem_shape.get_host_problem_shape(0), args.hw_info, NumMmaWarpGroups, NumEpilogueSubTiles);
|
||||
args.scheduler, workspace_ptr + workspace_offset, stream, typename ProblemShape::UnderlyingProblemShape{}, args.hw_info, NumMmaWarpGroups, NumEpilogueSubTiles);
|
||||
workspace_offset += TileScheduler::template get_workspace_size<typename ProblemShape::UnderlyingProblemShape, ElementAccumulator>(
|
||||
args.scheduler, args.problem_shape.get_host_problem_shape(0), args.hw_info, NumMmaWarpGroups, NumEpilogueSubTiles);
|
||||
args.scheduler, typename ProblemShape::UnderlyingProblemShape{}, args.hw_info, NumMmaWarpGroups, NumEpilogueSubTiles);
|
||||
workspace_offset = round_nearest(workspace_offset, MinWorkspaceAlignment);
|
||||
if (status != Status::kSuccess) {
|
||||
return status;
|
||||
@@ -350,23 +350,20 @@ public:
|
||||
using namespace cute;
|
||||
using X = Underscore;
|
||||
|
||||
// Any Tensor Op MMA Atom in the WGMMA ISA is arch conditional to sm90a.
|
||||
#if ! defined(__CUDA_ARCH_FEAT_SM90_ALL)
|
||||
if constexpr(size<0>(typename TiledMma::AtomShape_MNK{}) == 64) {
|
||||
printf("ERROR : Arch conditional MMA instruction used without targeting sm90a compute capability. Aborting.\n");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Any Tensor Op MMA Atom in the WGMMA ISA is arch conditional to sm90a.
|
||||
#if ! defined(__CUDA_ARCH_FEAT_SM90_ALL)
|
||||
printf("ERROR : Arch conditional MMA instruction used without targeting sm90a compute capability. Aborting.\n");
|
||||
#else
|
||||
|
||||
// Preconditions
|
||||
static_assert(size(TiledMma{}) == 256, "Cooperative kernel must have TiledMMA operating using 256 threads.");
|
||||
static_assert(size<0>(TileShape{}) >= 128,
|
||||
"Cooperative kernel requires Tile Size to be greater than or equal to 128 along the M-dimension.");
|
||||
|
||||
static_assert(cute::rank(StrideA{}) == 3, "StrideA must be rank-3: [M, K, L]. If batch mode is not needed, set L stride to Int<0>.");
|
||||
static_assert(cute::rank(StrideB{}) == 3, "StrideB must be rank-3: [N, K, L]. If batch mode is not needed, set L stride to Int<0>.");
|
||||
static_assert(cute::rank(StrideC{}) == 3, "StrideC must be rank-3: [M, N, L]. If batch mode is not needed, set L stride to Int<0>.");
|
||||
static_assert(cute::rank(StrideD{}) == 3, "StrideD must be rank-3: [M, N, L]. If batch mode is not needed, set L stride to Int<0>.");
|
||||
static_assert(cute::rank(UnderlyingStrideA{}) == 3, "StrideA must be rank-3: [M, K, L]. If batch mode is not needed, set L stride to Int<0>.");
|
||||
static_assert(cute::rank(UnderlyingStrideB{}) == 3, "StrideB must be rank-3: [N, K, L]. If batch mode is not needed, set L stride to Int<0>.");
|
||||
static_assert(cute::rank(UnderlyingStrideC{}) == 3, "StrideC must be rank-3: [M, N, L]. If batch mode is not needed, set L stride to Int<0>.");
|
||||
static_assert(cute::rank(UnderlyingStrideD{}) == 3, "StrideD must be rank-3: [M, N, L]. If batch mode is not needed, set L stride to Int<0>.");
|
||||
|
||||
/* In the Cooperative kernel, Consumer0 and Consumer1 collaborate on the same tile */
|
||||
enum class WarpGroupRole {
|
||||
@@ -441,8 +438,6 @@ public:
|
||||
// Epilogue store pipe is producer-only (consumer is TMA unit, waits via scoreboarding)
|
||||
typename CollectiveMainloop::PipelineState mainloop_pipe_consumer_state;
|
||||
typename CollectiveEpilogue::LoadPipelineState epi_load_pipe_consumer_state;
|
||||
// Purpose of maintaining this pipeline state is to make sure TMA loads have finished before doing descriptor updates
|
||||
typename CollectiveMainloop::PipelineState mainloop_pipe_tma_consumer_state;
|
||||
|
||||
// For the DMA Load (producer) we start with an opposite phase
|
||||
// i.e., we skip all waits since we know that the buffer is indeed empty
|
||||
@@ -554,7 +549,8 @@ public:
|
||||
shared_storage.tensors.mainloop
|
||||
);
|
||||
// Update starting pipeline state for the next tile
|
||||
mainloop_pipe_producer_state.advance(work_k_tile_count);
|
||||
// Wait for the last TMA stage to complete loading, before issuing tensormap updates
|
||||
mainloop_pipe_producer_state.advance(work_k_tile_count - 1);
|
||||
|
||||
// Signal for the epilogue load warp to begin
|
||||
if (do_load_order_arrive) {
|
||||
@@ -570,8 +566,10 @@ public:
|
||||
if constexpr (IsGroupedGemmKernel) {
|
||||
problem_shape_MNKL = append<4>(params.problem_shape.get_problem_shape(next_batch), Int<1>{});
|
||||
}
|
||||
// Wait for the last TMA stage to complete loading, before issuing tensormap updates
|
||||
mainloop_pipe_tma_consumer_state.advance(work_k_tile_count-1);
|
||||
// Purpose of this pipeline state is to make sure TMA loads have finished before doing descriptor updates
|
||||
// Since this state is waiting for loads to finish, it must start in the inverted phase.
|
||||
typename CollectiveMainloop::PipelineState mainloop_pipe_tma_consumer_state =
|
||||
{mainloop_pipe_producer_state.index(), !mainloop_pipe_producer_state.phase(), mainloop_pipe_producer_state.count()};
|
||||
mainloop_pipeline.consumer_wait(mainloop_pipe_tma_consumer_state);
|
||||
collective_mainloop.tensormaps_perform_update(
|
||||
shared_storage.tensormaps.mainloop,
|
||||
@@ -585,13 +583,9 @@ public:
|
||||
// Entire warp must do this (ie its aligned)
|
||||
collective_mainloop.tensormaps_cp_fence_release(shared_storage.tensormaps.mainloop, input_tensormaps);
|
||||
curr_batch = next_batch;
|
||||
// Advance the TMA consumer state for the last remaining stage that was being waited for above
|
||||
mainloop_pipe_tma_consumer_state.advance(1);
|
||||
}
|
||||
else if (work_tile_info.is_valid()) { // case where batch/group didn't change between tiles
|
||||
// Advance the TMA consumer state for all the stages to be in sync
|
||||
mainloop_pipe_tma_consumer_state.advance(work_k_tile_count);
|
||||
}
|
||||
// Advance the producer state for the last remaining stage that was being waited for above
|
||||
mainloop_pipe_producer_state.advance(1);
|
||||
} // Scheduler work fetch loop
|
||||
|
||||
// Make sure all Consumer Warp Groups have been waited upon
|
||||
@@ -720,6 +714,7 @@ public:
|
||||
);
|
||||
}
|
||||
} // Consumer Warp Groups End
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -211,13 +211,10 @@ public:
|
||||
using namespace cute;
|
||||
using X = Underscore;
|
||||
|
||||
// Any Tensor Op MMA Atom in the WGMMA ISA is arch conditional to sm90a.
|
||||
#if ! defined(__CUDA_ARCH_FEAT_SM90_ALL)
|
||||
if constexpr(size<0>(typename TiledMma::AtomShape_MNK{}) == 64) {
|
||||
printf("ERROR : Arch conditional MMA instruction used without targeting sm90a compute capability. Aborting.\n");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Any Tensor Op MMA Atom in the WGMMA ISA is arch conditional to sm90a.
|
||||
#if ! defined(__CUDA_ARCH_FEAT_SM90_ALL)
|
||||
printf("ERROR : Arch conditional MMA instruction used without targeting sm90a compute capability. Aborting.\n");
|
||||
#else
|
||||
|
||||
// Preconditions
|
||||
static_assert(cute::rank(StrideA{}) == 3, "StrideA must be rank-3: [M, K, L]. If batch mode is not needed, set L stride to Int<0>.");
|
||||
@@ -311,6 +308,7 @@ public:
|
||||
thread_idx,
|
||||
smem_buf
|
||||
);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -219,13 +219,10 @@ public:
|
||||
using namespace cute;
|
||||
using X = Underscore;
|
||||
|
||||
// Any Tensor Op MMA Atom in the WGMMA ISA is arch conditional to sm90a.
|
||||
#if ! defined(__CUDA_ARCH_FEAT_SM90_ALL)
|
||||
if constexpr(size<0>(typename TiledMma::AtomShape_MNK{}) == 64) {
|
||||
printf("ERROR : Arch conditional MMA instruction used without targeting sm90a compute capability. Aborting.\n");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Any Tensor Op MMA Atom in the WGMMA ISA is arch conditional to sm90a.
|
||||
#if ! defined(__CUDA_ARCH_FEAT_SM90_ALL)
|
||||
printf("ERROR : Arch conditional MMA instruction used without targeting sm90a compute capability. Aborting.\n");
|
||||
#else
|
||||
|
||||
enum class WarpGroupRole {
|
||||
Producer = 0,
|
||||
@@ -435,6 +432,7 @@ public:
|
||||
epi_store_pipe_producer_state_next
|
||||
);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -298,13 +298,10 @@ public:
|
||||
using namespace cute;
|
||||
using X = Underscore;
|
||||
|
||||
// Any Tensor Op MMA Atom in the WGMMA ISA is arch conditional to sm90a.
|
||||
#if ! defined(__CUDA_ARCH_FEAT_SM90_ALL)
|
||||
if constexpr(size<0>(typename TiledMma::AtomShape_MNK{}) == 64) {
|
||||
printf("ERROR : Arch conditional MMA instruction used without targeting sm90a compute capability. Aborting.\n");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Any Tensor Op MMA Atom in the WGMMA ISA is arch conditional to sm90a.
|
||||
#if ! defined(__CUDA_ARCH_FEAT_SM90_ALL)
|
||||
printf("ERROR : Arch conditional MMA instruction used without targeting sm90a compute capability. Aborting.\n");
|
||||
#else
|
||||
|
||||
// Preconditions
|
||||
static_assert(size(TiledMma{}) == 256, "Cooperative kernel must have TiledMMA operating using 256 threads.");
|
||||
@@ -610,6 +607,7 @@ public:
|
||||
);
|
||||
}
|
||||
} // Consumer Warp Groups End
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -296,13 +296,10 @@ public:
|
||||
using namespace cute;
|
||||
using X = Underscore;
|
||||
|
||||
// Any Tensor Op MMA Atom in the WGMMA ISA is arch conditional to sm90a.
|
||||
#if ! defined(__CUDA_ARCH_FEAT_SM90_ALL)
|
||||
if constexpr(size<0>(typename TiledMma::AtomShape_MNK{}) == 64) {
|
||||
printf("ERROR : Arch conditional MMA instruction used without targeting sm90a compute capability. Aborting.\n");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Any Tensor Op MMA Atom in the WGMMA ISA is arch conditional to sm90a.
|
||||
#if ! defined(__CUDA_ARCH_FEAT_SM90_ALL)
|
||||
printf("ERROR : Arch conditional MMA instruction used without targeting sm90a compute capability. Aborting.\n");
|
||||
#else
|
||||
|
||||
// Preconditions
|
||||
static_assert(cute::rank(StrideA{}) == 3, "StrideA must be rank-3: [M, K, L]. If batch mode is not needed, set L stride to Int<0>.");
|
||||
@@ -612,6 +609,7 @@ public:
|
||||
work_tile_info = scheduler.get_current_work();
|
||||
} // Scheduler work fetch loop
|
||||
} // Consumer Warp Groups End
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -223,13 +223,10 @@ public:
|
||||
using namespace cute;
|
||||
using X = Underscore;
|
||||
|
||||
// Any Tensor Op MMA Atom in the WGMMA ISA is arch conditional to sm90a.
|
||||
#if ! defined(__CUDA_ARCH_FEAT_SM90_ALL)
|
||||
if constexpr(size<0>(typename TiledMma::AtomShape_MNK{}) == 64) {
|
||||
printf("ERROR : Arch conditional MMA instruction used without targeting sm90a compute capability. Aborting.\n");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Any Tensor Op MMA Atom in the WGMMA ISA is arch conditional to sm90a.
|
||||
#if ! defined(__CUDA_ARCH_FEAT_SM90_ALL)
|
||||
printf("ERROR : Arch conditional MMA instruction used without targeting sm90a compute capability. Aborting.\n");
|
||||
#else
|
||||
|
||||
enum class WarpGroupRole {
|
||||
Producer = 0,
|
||||
@@ -409,6 +406,7 @@ public:
|
||||
shared_storage.tensors.epilogue
|
||||
);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -250,13 +250,10 @@ public:
|
||||
using namespace cute;
|
||||
using X = Underscore;
|
||||
|
||||
// Any Tensor Op MMA Atom in the WGMMA ISA is arch conditional to sm90a.
|
||||
#if ! defined(__CUDA_ARCH_FEAT_SM90_ALL)
|
||||
if constexpr(size<0>(typename TiledMma::AtomShape_MNK{}) == 64) {
|
||||
printf("ERROR : Arch conditional MMA instruction used without targeting sm90a compute capability. Aborting.\n");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Any Tensor Op MMA Atom in the WGMMA ISA is arch conditional to sm90a.
|
||||
#if ! defined(__CUDA_ARCH_FEAT_SM90_ALL)
|
||||
printf("ERROR : Arch conditional MMA instruction used without targeting sm90a compute capability. Aborting.\n");
|
||||
#else
|
||||
|
||||
static_assert(cute::rank(StrideA{}) == 3, "StrideA must be rank-3: [M, K, L]. If batch mode is not needed, set L stride to Int<0>.");
|
||||
static_assert(cute::rank(StrideB{}) == 3, "StrideB must be rank-3: [N, K, L]. If batch mode is not needed, set L stride to Int<0>.");
|
||||
@@ -493,6 +490,7 @@ public:
|
||||
);
|
||||
}
|
||||
} // Consumer Warp Groups End
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -257,13 +257,10 @@ public:
|
||||
using namespace cute;
|
||||
using X = Underscore;
|
||||
|
||||
// Any Tensor Op MMA Atom in the WGMMA ISA is arch conditional to sm90a.
|
||||
#if ! defined(__CUDA_ARCH_FEAT_SM90_ALL)
|
||||
if constexpr(size<0>(typename TiledMma::AtomShape_MNK{}) == 64) {
|
||||
printf("ERROR : Arch conditional MMA instruction used without targeting sm90a compute capability. Aborting.\n");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Any Tensor Op MMA Atom in the WGMMA ISA is arch conditional to sm90a.
|
||||
#if ! defined(__CUDA_ARCH_FEAT_SM90_ALL)
|
||||
printf("ERROR : Arch conditional MMA instruction used without targeting sm90a compute capability. Aborting.\n");
|
||||
#else
|
||||
|
||||
// Preconditions
|
||||
static_assert(cute::rank(StrideA{}) == 3, "StrideA must be rank-3: [M, K, L]. If batch mode is not needed, set L stride to Int<0>.");
|
||||
@@ -509,6 +506,7 @@ public:
|
||||
work_tile_info = scheduler.get_current_work();
|
||||
} // Scheduler work fetch loop
|
||||
} // Consumer Warp Groups End
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ private:
|
||||
|
||||
// Tracking current group, its starting linear idx and total tiles
|
||||
struct GroupInfo {
|
||||
uint64_t group = 0;
|
||||
int group_idx = 0;
|
||||
uint64_t start_linear_idx = 0;
|
||||
uint64_t total_tiles = 0;
|
||||
} current_group_info_;
|
||||
@@ -115,7 +115,7 @@ public:
|
||||
GroupProblemShape problem_shapes,
|
||||
TileShape tile_shape,
|
||||
ClusterShape cluster_shape,
|
||||
[[maybe_unused]] KernelHardwareInfo const& hw_info,
|
||||
KernelHardwareInfo const& hw_info,
|
||||
Arguments const& arguments,
|
||||
[[maybe_unused]] void* workspace=nullptr,
|
||||
[[maybe_unused]] const uint32_t epilogue_subtile = 1) {
|
||||
@@ -126,14 +126,16 @@ public:
|
||||
|
||||
dim3 problem_blocks = get_tiled_cta_shape_mnl(
|
||||
problem_shapes.groups(),
|
||||
reinterpret_cast<ProblemShape const*>(problem_shapes.host_problem_shapes),
|
||||
problem_shapes,
|
||||
hw_info,
|
||||
tile_shape, cluster_shape);
|
||||
|
||||
Params params;
|
||||
params.initialize(
|
||||
problem_blocks,
|
||||
problem_shapes.groups(),
|
||||
reinterpret_cast<ProblemShape*>(problem_shapes.problem_shapes),
|
||||
problem_shapes.problem_shapes,
|
||||
problem_shapes.host_problem_shapes,
|
||||
to_gemm_coord(tile_shape),
|
||||
to_gemm_coord(cluster_shape),
|
||||
hw_info,
|
||||
@@ -144,6 +146,64 @@ public:
|
||||
return params;
|
||||
}
|
||||
|
||||
// Given the inputs, computes the physical grid we should launch.
|
||||
template<class TileShape, class ClusterShape>
|
||||
CUTLASS_HOST_DEVICE static
|
||||
dim3
|
||||
get_grid_shape(
|
||||
GroupProblemShape problem_shapes,
|
||||
TileShape tile_shape,
|
||||
ClusterShape cluster_shape,
|
||||
KernelHardwareInfo hw_info,
|
||||
Arguments arguments,
|
||||
bool truncate_by_problem_size=true) {
|
||||
|
||||
dim3 problem_blocks = get_tiled_cta_shape_mnl(
|
||||
problem_shapes.groups(),
|
||||
problem_shapes,
|
||||
hw_info,
|
||||
tile_shape, cluster_shape);
|
||||
|
||||
return Params::get_grid_shape(
|
||||
problem_blocks,
|
||||
to_gemm_coord(cluster_shape),
|
||||
hw_info,
|
||||
arguments.max_swizzle_size,
|
||||
arguments.raster_order,
|
||||
/* truncate_by_problem_size = */true
|
||||
);
|
||||
}
|
||||
|
||||
// Given the inputs, computes the total number of output blocks this problem will compute over
|
||||
// Note that this is only the logical size of our grid, not the physical grid we will actually launch.
|
||||
template<class BlockShape, class ClusterShape>
|
||||
CUTLASS_HOST_DEVICE static
|
||||
dim3
|
||||
get_tiled_cta_shape_mnl(int groups, GroupProblemShape problem_shapes, KernelHardwareInfo hw_info, BlockShape cta_shape, ClusterShape cluster_shape) {
|
||||
uint32_t total_ctas = 0;
|
||||
uint32_t cta_in_N_dim = 1; // We linearize the blocks across all the problems here
|
||||
|
||||
// If host problem shapes are not provided.
|
||||
if (!problem_shapes.is_host_problem_shape_available()) {
|
||||
total_ctas = hw_info.sm_count;
|
||||
}
|
||||
// If host problem shapes are provided, make a better decision about possibility to launch smaller grid.
|
||||
else {
|
||||
for (int group = 0; group < groups; group++) {
|
||||
auto ctas_along_m = cute::size(cute::ceil_div(cute::shape<0>(problem_shapes.get_host_problem_shape(group)), cute::shape<0>(cta_shape)));
|
||||
auto ctas_along_n = cute::size(cute::ceil_div(cute::shape<1>(problem_shapes.get_host_problem_shape(group)), cute::shape<1>(cta_shape)));
|
||||
auto problem_blocks_m = round_up(ctas_along_m, cute::get<0>(cluster_shape));
|
||||
auto problem_blocks_n = round_up(ctas_along_n, cute::get<1>(cluster_shape));
|
||||
total_ctas += problem_blocks_m * problem_blocks_n;
|
||||
}
|
||||
}
|
||||
|
||||
return Params::get_tiled_cta_shape_mnl(
|
||||
to_gemm_coord(cluster_shape),
|
||||
total_ctas, cta_in_N_dim
|
||||
);
|
||||
}
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
static bool
|
||||
can_implement(Arguments const& args) {
|
||||
@@ -156,7 +216,7 @@ public:
|
||||
// MSVC requires protecting use of CUDA-specific nonstandard syntax,
|
||||
// like blockIdx and gridDim, with __CUDA_ARCH__.
|
||||
#if defined(__CUDA_ARCH__)
|
||||
if (params_.raster_order_ == RasterOrder::AlongN) {
|
||||
if (scheduler_params.raster_order_ == RasterOrder::AlongN) {
|
||||
current_work_linear_idx_ = uint64_t(blockIdx.x) + uint64_t(blockIdx.y) * uint64_t(gridDim.x);
|
||||
}
|
||||
else {
|
||||
@@ -165,9 +225,19 @@ public:
|
||||
|
||||
total_grid_size_ = uint64_t(gridDim.x) * uint64_t(gridDim.y) * uint64_t(gridDim.z);
|
||||
|
||||
auto cta_m = cute::size(cute::ceil_div(cute::shape<0>(params_.problem_shapes_[0]), params_.cta_shape_.m()));
|
||||
auto cta_n = cute::size(cute::ceil_div(cute::shape<1>(params_.problem_shapes_[0]), params_.cta_shape_.n()));
|
||||
current_group_info_.total_tiles = cta_m * cta_n;
|
||||
uint64_t ctas_along_m, ctas_along_n;
|
||||
if (is_tuple<decltype(cute::shape<0>(params_.problem_shapes_[0]))>::value ||
|
||||
is_tuple<decltype(cute::shape<1>(params_.problem_shapes_[0]))>::value) {
|
||||
ctas_along_m = cute::size(cute::ceil_div(cute::shape<0>(params_.problem_shapes_[0]), scheduler_params.cta_shape_.m()));
|
||||
ctas_along_n = cute::size(cute::ceil_div(cute::shape<1>(params_.problem_shapes_[0]), scheduler_params.cta_shape_.n()));
|
||||
}
|
||||
else {
|
||||
ctas_along_m = scheduler_params.divmod_cta_shape_m_.divide(cute::shape<0>(params_.problem_shapes_[0]) + scheduler_params.divmod_cta_shape_m_.divisor - 1);
|
||||
ctas_along_n = scheduler_params.divmod_cta_shape_n_.divide(cute::shape<1>(params_.problem_shapes_[0]) + scheduler_params.divmod_cta_shape_n_.divisor - 1);
|
||||
}
|
||||
auto problem_blocks_m = round_up(ctas_along_m, (1 << params_.log_swizzle_size_) * params_.cluster_shape_.m());
|
||||
auto problem_blocks_n = round_up(ctas_along_n, (1 << params_.log_swizzle_size_) * params_.cluster_shape_.n());
|
||||
current_group_info_.total_tiles = problem_blocks_m * problem_blocks_n;
|
||||
#else
|
||||
CUTLASS_ASSERT(false && "This line should never be reached");
|
||||
#endif
|
||||
@@ -182,24 +252,22 @@ public:
|
||||
CUTLASS_DEVICE
|
||||
WorkTileInfo
|
||||
get_current_work_for_linear_idx(uint64_t linear_idx) {
|
||||
if (linear_idx >= scheduler_params.blocks_per_problem_) {
|
||||
if (scheduler_params.pre_processed_problem_shapes && linear_idx >= scheduler_params.blocks_across_problem_) {
|
||||
return WorkTileInfo::invalid_work_tile();
|
||||
}
|
||||
|
||||
uint64_t blk_per_grid_dim = scheduler_params.divmod_cluster_shape_minor_.divide(linear_idx);
|
||||
|
||||
auto [work_idx_m, work_idx_n, new_group_info, valid_tile] = get_work_idx_m_and_n(blk_per_grid_dim,
|
||||
current_group_info_,
|
||||
scheduler_params.groups_,
|
||||
scheduler_params.problem_shapes_,
|
||||
scheduler_params.cta_shape_,
|
||||
scheduler_params.divmod_cluster_shape_major_,
|
||||
scheduler_params.divmod_cluster_shape_minor_,
|
||||
scheduler_params.log_swizzle_size_,
|
||||
scheduler_params.raster_order_);
|
||||
|
||||
current_group_info_ = new_group_info;
|
||||
return {work_idx_m, work_idx_n, static_cast<int>(current_group_info_.group), valid_tile};
|
||||
return get_work_idx_m_and_n(linear_idx,
|
||||
current_group_info_,
|
||||
scheduler_params.groups_,
|
||||
scheduler_params.problem_shapes_,
|
||||
scheduler_params.cta_shape_,
|
||||
scheduler_params.cluster_shape_,
|
||||
scheduler_params.divmod_cluster_shape_major_,
|
||||
scheduler_params.divmod_cluster_shape_minor_,
|
||||
scheduler_params.divmod_cta_shape_m_,
|
||||
scheduler_params.divmod_cta_shape_n_,
|
||||
scheduler_params.log_swizzle_size_,
|
||||
scheduler_params.raster_order_);
|
||||
}
|
||||
|
||||
CUTLASS_DEVICE
|
||||
@@ -208,34 +276,62 @@ public:
|
||||
current_work_linear_idx_ += total_grid_size_ * uint64_t(advance_count);
|
||||
}
|
||||
|
||||
// get work_idx_m, work_idx_n from blk_per_grid_dim while applying swizzle
|
||||
// get work_idx_m, work_idx_n from linear_idx while applying swizzle
|
||||
static CUTLASS_DEVICE
|
||||
cute::tuple<int32_t, int32_t, struct GroupInfo, bool>
|
||||
WorkTileInfo
|
||||
get_work_idx_m_and_n(
|
||||
uint64_t blk_per_grid_dim,
|
||||
struct GroupInfo group_info,
|
||||
uint64_t linear_idx,
|
||||
struct GroupInfo& group_info,
|
||||
int32_t total_problem_groups,
|
||||
ProblemShape* problem_shapes,
|
||||
GemmCoord cta_shape,
|
||||
GemmCoord cluster_shape,
|
||||
FastDivmodU64Pow2 const& divmod_cluster_shape_major,
|
||||
FastDivmodU64Pow2 const& divmod_cluster_shape_minor,
|
||||
FastDivmodU64 const& divmod_cta_shape_m,
|
||||
FastDivmodU64 const& divmod_cta_shape_n,
|
||||
int32_t log_swizzle_size,
|
||||
RasterOrder raster_order) {
|
||||
|
||||
bool valid_tile = true;
|
||||
int cta_m = cute::size(cute::ceil_div(cute::shape<0>(problem_shapes[group_info.group]), cta_shape.m()));
|
||||
int cta_n = cute::size(cute::ceil_div(cute::shape<1>(problem_shapes[group_info.group]), cta_shape.n()));
|
||||
uint64_t ctas_along_m, ctas_along_n;
|
||||
if (is_tuple<decltype(cute::shape<0>(problem_shapes[group_info.group_idx]))>::value ||
|
||||
is_tuple<decltype(cute::shape<1>(problem_shapes[group_info.group_idx]))>::value) {
|
||||
ctas_along_m = cute::size(cute::ceil_div(cute::shape<0>(problem_shapes[group_info.group_idx]), cta_shape.m()));
|
||||
ctas_along_n = cute::size(cute::ceil_div(cute::shape<1>(problem_shapes[group_info.group_idx]), cta_shape.n()));
|
||||
}
|
||||
else {
|
||||
ctas_along_m = divmod_cta_shape_m.divide(cute::shape<0>(problem_shapes[group_info.group_idx]) + divmod_cta_shape_m.divisor - 1);
|
||||
ctas_along_n = divmod_cta_shape_n.divide(cute::shape<1>(problem_shapes[group_info.group_idx]) + divmod_cta_shape_n.divisor - 1);
|
||||
}
|
||||
auto problem_blocks_m = round_up(ctas_along_m, (1 << log_swizzle_size) * cluster_shape.m());
|
||||
auto problem_blocks_n = round_up(ctas_along_n, (1 << log_swizzle_size) * cluster_shape.n());
|
||||
group_info.total_tiles = problem_blocks_m * problem_blocks_n;
|
||||
|
||||
while (group_info.start_linear_idx + group_info.total_tiles <= linear_idx) {
|
||||
group_info.group_idx++;
|
||||
|
||||
if (group_info.group_idx >= total_problem_groups)
|
||||
return WorkTileInfo::invalid_work_tile();
|
||||
|
||||
while (group_info.start_linear_idx + group_info.total_tiles <= blk_per_grid_dim) {
|
||||
group_info.group++;
|
||||
group_info.start_linear_idx += group_info.total_tiles;
|
||||
cta_m = cute::size(cute::ceil_div(cute::shape<0>(problem_shapes[group_info.group]), cta_shape.m()));
|
||||
cta_n = cute::size(cute::ceil_div(cute::shape<1>(problem_shapes[group_info.group]), cta_shape.n()));
|
||||
group_info.total_tiles = cta_m * cta_n;
|
||||
if (is_tuple<decltype(cute::shape<0>(problem_shapes[group_info.group_idx]))>::value ||
|
||||
is_tuple<decltype(cute::shape<1>(problem_shapes[group_info.group_idx]))>::value) {
|
||||
ctas_along_m = cute::size(cute::ceil_div(cute::shape<0>(problem_shapes[group_info.group_idx]), cta_shape.m()));
|
||||
ctas_along_n = cute::size(cute::ceil_div(cute::shape<1>(problem_shapes[group_info.group_idx]), cta_shape.n()));
|
||||
}
|
||||
else {
|
||||
ctas_along_m = divmod_cta_shape_m.divide(cute::shape<0>(problem_shapes[group_info.group_idx]) + divmod_cta_shape_m.divisor - 1);
|
||||
ctas_along_n = divmod_cta_shape_n.divide(cute::shape<1>(problem_shapes[group_info.group_idx]) + divmod_cta_shape_n.divisor - 1);
|
||||
}
|
||||
problem_blocks_m = round_up(ctas_along_m, (1 << log_swizzle_size) * cluster_shape.m());
|
||||
problem_blocks_n = round_up(ctas_along_n, (1 << log_swizzle_size) * cluster_shape.n());
|
||||
group_info.total_tiles = problem_blocks_m * problem_blocks_n;
|
||||
}
|
||||
|
||||
uint64_t cluster_id, cluster_major_offset = 0, cluster_minor_offset = 0;
|
||||
divmod_cluster_shape_major(cluster_id, cluster_major_offset, blk_per_grid_dim - group_info.start_linear_idx);
|
||||
uint64_t blk_per_grid_dim = divmod_cluster_shape_minor.divide(linear_idx - group_info.start_linear_idx);
|
||||
divmod_cluster_shape_major(cluster_id, cluster_major_offset, blk_per_grid_dim);
|
||||
|
||||
auto [cta_m_in_cluster, cta_n_in_cluster, _] = cute::block_id_in_cluster();
|
||||
if (raster_order == RasterOrder::AlongN) {
|
||||
@@ -252,8 +348,13 @@ public:
|
||||
offset = cluster_id & ((1 << log_swizzle_size) - 1);
|
||||
extra = cluster_id >> log_swizzle_size;
|
||||
|
||||
uint64_t curr_group_cluster_blk_major, remainder;
|
||||
divmod_cluster_shape_major(curr_group_cluster_blk_major, remainder, cta_m);
|
||||
uint64_t curr_group_cluster_blk_major;
|
||||
if (raster_order == RasterOrder::AlongN) {
|
||||
curr_group_cluster_blk_major = divmod_cluster_shape_major.divide(problem_blocks_n);
|
||||
}
|
||||
else {
|
||||
curr_group_cluster_blk_major = divmod_cluster_shape_major.divide(problem_blocks_m);
|
||||
}
|
||||
cluster_idx_minor_div_swizzle = extra / curr_group_cluster_blk_major;
|
||||
cluster_idx_major = extra % curr_group_cluster_blk_major;
|
||||
|
||||
@@ -265,61 +366,14 @@ public:
|
||||
cluster_major_offset);
|
||||
|
||||
if (raster_order == RasterOrder::AlongN) {
|
||||
return {minor_work_idx, major_work_idx, group_info, valid_tile};
|
||||
return {minor_work_idx, major_work_idx, group_info.group_idx, valid_tile};
|
||||
}
|
||||
else {
|
||||
return {major_work_idx, minor_work_idx, group_info, valid_tile};
|
||||
return {major_work_idx, minor_work_idx, group_info.group_idx, valid_tile};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Given the inputs, computes the total number of output blocks this problem will compute over
|
||||
// Note that this is only the logical size of our grid, not the physical grid we will actually launch.
|
||||
template<class BlockShape, class ClusterShape>
|
||||
CUTLASS_HOST_DEVICE static
|
||||
dim3
|
||||
get_tiled_cta_shape_mnl(int groups, ProblemShape const* problem_shapes, BlockShape cta_shape, ClusterShape cluster_shape) {
|
||||
uint32_t total_ctas = 0;
|
||||
uint32_t cta_in_N_dim = 1; // We linearize the blocks across all the problems here
|
||||
for (int group = 0; group < groups; group++) {
|
||||
auto cta_m = cute::size(cute::ceil_div(cute::shape<0>(problem_shapes[group]), cute::shape<0>(cta_shape)));
|
||||
auto cta_n = cute::size(cute::ceil_div(cute::shape<1>(problem_shapes[group]), cute::shape<1>(cta_shape)));
|
||||
total_ctas += cta_m * cta_n;
|
||||
}
|
||||
|
||||
return Params::get_tiled_cta_shape_mnl(
|
||||
to_gemm_coord(cluster_shape),
|
||||
total_ctas, cta_in_N_dim
|
||||
);
|
||||
}
|
||||
|
||||
// Given the inputs, computes the physical grid we should launch.
|
||||
template<class BlockShape, class ClusterShape>
|
||||
CUTLASS_HOST_DEVICE static
|
||||
dim3
|
||||
get_grid_shape(
|
||||
GroupProblemShape problem_shapes,
|
||||
BlockShape cta_shape,
|
||||
ClusterShape cluster_shape,
|
||||
KernelHardwareInfo hw_info,
|
||||
Arguments arguments,
|
||||
bool truncate_by_problem_size=true) {
|
||||
|
||||
dim3 problem_blocks = get_tiled_cta_shape_mnl(
|
||||
problem_shapes.groups(),
|
||||
reinterpret_cast<ProblemShape const*>(problem_shapes.host_problem_shapes),
|
||||
cta_shape, cluster_shape);
|
||||
|
||||
return Params::get_grid_shape(
|
||||
problem_blocks,
|
||||
to_gemm_coord(cluster_shape),
|
||||
hw_info,
|
||||
arguments.max_swizzle_size,
|
||||
arguments.raster_order,
|
||||
/* truncate_by_problem_size = */true
|
||||
);
|
||||
}
|
||||
|
||||
// Returns whether the block assigned this work should compute the epilogue for the corresponding
|
||||
// output tile. For the basic tile scheduler, this is always true.
|
||||
CUTLASS_HOST_DEVICE
|
||||
|
||||
@@ -1273,15 +1273,18 @@ struct PersistentTileSchedulerSm90GroupParams {
|
||||
|
||||
FastDivmodU64Pow2 divmod_cluster_shape_major_{};
|
||||
FastDivmodU64Pow2 divmod_cluster_shape_minor_{};
|
||||
FastDivmodU64 divmod_batch_{};
|
||||
FastDivmodU64 divmod_cta_shape_m_{};
|
||||
FastDivmodU64 divmod_cta_shape_n_{};
|
||||
|
||||
uint64_t blocks_per_problem_ = 0;
|
||||
uint64_t blocks_across_problem_ = 0;
|
||||
bool pre_processed_problem_shapes = true;
|
||||
int32_t log_swizzle_size_ = 0;
|
||||
RasterOrder raster_order_ = RasterOrder::AlongN;
|
||||
|
||||
int32_t groups_ = 0;
|
||||
ProblemShape* problem_shapes_ = nullptr;
|
||||
GemmCoord cta_shape_;
|
||||
GemmCoord cluster_shape_;
|
||||
|
||||
// Version of initialize that takes in as input the number of CTAs in the M and N and L dimensions.
|
||||
// This is useful for calculating the tiled shape when a mode of problem and/or CTA shape has rank > 1,
|
||||
@@ -1291,6 +1294,7 @@ struct PersistentTileSchedulerSm90GroupParams {
|
||||
dim3 problem_blocks,
|
||||
int32_t groups,
|
||||
ProblemShape* problem_shapes,
|
||||
ProblemShape const* host_problem_shapes,
|
||||
GemmCoord cta_shape,
|
||||
GemmCoord cluster_shape,
|
||||
KernelHardwareInfo const& hw_info,
|
||||
@@ -1317,11 +1321,12 @@ struct PersistentTileSchedulerSm90GroupParams {
|
||||
groups_ = groups;
|
||||
problem_shapes_ = problem_shapes;
|
||||
cta_shape_ = cta_shape;
|
||||
cluster_shape_ = cluster_shape;
|
||||
|
||||
blocks_per_problem_ = problem_blocks_m * problem_blocks_n * problem_blocks.z;
|
||||
blocks_across_problem_ = problem_blocks.x * problem_blocks.y * problem_blocks.z;
|
||||
pre_processed_problem_shapes = (host_problem_shapes == nullptr) ? false : true;
|
||||
log_swizzle_size_ = log_swizzle_size;
|
||||
raster_order_ = raster_order;
|
||||
divmod_batch_ = FastDivmodU64(problem_blocks_m * problem_blocks_n);
|
||||
|
||||
if (raster_order == RasterOrder::AlongN) {
|
||||
divmod_cluster_shape_major_ = FastDivmodU64Pow2(cluster_shape.n());
|
||||
@@ -1331,6 +1336,9 @@ struct PersistentTileSchedulerSm90GroupParams {
|
||||
divmod_cluster_shape_major_ = FastDivmodU64Pow2(cluster_shape.m());
|
||||
divmod_cluster_shape_minor_ = FastDivmodU64Pow2(cluster_shape.n());
|
||||
}
|
||||
|
||||
divmod_cta_shape_m_ = FastDivmodU64(cta_shape_.m());
|
||||
divmod_cta_shape_n_ = FastDivmodU64(cta_shape_.n());
|
||||
}
|
||||
|
||||
// Version of get_tiled_cta_shape_mnl that takes in as input the number of CTAs in the M and N dimensions.
|
||||
@@ -1344,8 +1352,8 @@ struct PersistentTileSchedulerSm90GroupParams {
|
||||
auto problem_blocks_n = ((cta_n + cluster_shape.n() - 1) / cluster_shape.n()) * cluster_shape.n();
|
||||
|
||||
return {
|
||||
static_cast<uint32_t>(problem_blocks_m),
|
||||
static_cast<uint32_t>(problem_blocks_n),
|
||||
static_cast<uint32_t>(cta_m),
|
||||
static_cast<uint32_t>(cta_n),
|
||||
static_cast<uint32_t>(1) // Only a single batch per group is currently supported
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user