v4.1 release update v2. (#2481)
This commit is contained in:
+9
-14
@@ -2,23 +2,15 @@
|
|||||||
|
|
||||||
# CUTLASS 4.x
|
# CUTLASS 4.x
|
||||||
|
|
||||||
## [4.1.0](https://github.com/NVIDIA/cutlass/tree/main) (2025-06-30)
|
## [4.1.0](https://github.com/NVIDIA/cutlass/releases/tag/v4.1.0) (2025-07-16)
|
||||||
|
|
||||||
### CuTe DSL
|
### CuTe DSL
|
||||||
|
* Add aarch64 support, you can now pip install `nvidia-cutlass-dsl` on GB200 systems!
|
||||||
* More examples demonstrating how to use CuTe DSL to write peak-performance kernels
|
* More examples demonstrating how to use CuTe DSL to write peak-performance kernels
|
||||||
- [Blackwell Mamba2 SSD](https://github.com/NVIDIA/cutlass/tree/main/examples/python/CuTeDSL/blackwell/mamba2_ssd/mamba2_ssd.py)
|
- [Blackwell Mamba2 SSD](https://github.com/NVIDIA/cutlass/tree/main/examples/python/CuTeDSL/blackwell/mamba2_ssd/mamba2_ssd.py)
|
||||||
|
- [Blackwell SM100 persistent dense blockscaled GEMM with static scheduling](https://github.com/NVIDIA/cutlass/tree/main/examples/python/CuTeDSL/blackwell/dense_blockscaled_gemm_persistent.py)
|
||||||
* API updates
|
* API updates
|
||||||
- for loop
|
- Please refer to [FUNCTIONALITY.md](https://github.com/NVIDIA/cutlass/blob/main/FUNCTIONALITY.md) for details
|
||||||
- Python built-in ``range`` now always generates IR and executes at runtime
|
|
||||||
- ``cutlass.range`` is advanced ``range`` with IR level unrolling and pipelining control
|
|
||||||
- Deprecated ``cutlass.range_dynamic``, please replace with ``range`` or ``cutlass.range``
|
|
||||||
- **Experimental** Added ``pipelining`` control for compiler generated software pipeline code
|
|
||||||
- while/if
|
|
||||||
- ``while``/``if`` now by default generates IR and executes at runtime unless ``cutlass.const_expr`` is specified for the predicate
|
|
||||||
- Deprecated ``cutlass.dynamic_expr``, please remove it
|
|
||||||
- Rename mbarrier functions to reduce ambiguity
|
|
||||||
- Modify SyncObject API (`MbarrierArray`, `NamedBarrier`, `TmaStoreFence`) to match `std::barrier`
|
|
||||||
- Change pipeline `create` function to take only keyword arguments, and make `barrier_storage` optional.
|
|
||||||
|
|
||||||
### CUTLASS C++
|
### CUTLASS C++
|
||||||
* Further enhance Blackwell SM100 Attention kernels in [example 77](https://github.com/NVIDIA/cutlass/tree/main/examples/77_blackwell_fmha/).
|
* Further enhance Blackwell SM100 Attention kernels in [example 77](https://github.com/NVIDIA/cutlass/tree/main/examples/77_blackwell_fmha/).
|
||||||
@@ -31,7 +23,7 @@
|
|||||||
- Remove buggy and kludgy `get_layoutA|B|C_MN` and friends from Atoms/TiledX.
|
- Remove buggy and kludgy `get_layoutA|B|C_MN` and friends from Atoms/TiledX.
|
||||||
- Factor out `print_latex` and friends and rewrite.
|
- Factor out `print_latex` and friends and rewrite.
|
||||||
- Factor out `print_svg` and friends and rewrite.
|
- Factor out `print_svg` and friends and rewrite.
|
||||||
* Support Blackwell SM100 SIMT FFMA2 kernels.
|
* Support Blackwell SM100 SIMT packed fp32x2 kernels.
|
||||||
* Support residual add for implicit gemm kernels.
|
* Support residual add for implicit gemm kernels.
|
||||||
* Various fixes for CUTLASS C++ Python interface's EVT tracer:
|
* Various fixes for CUTLASS C++ Python interface's EVT tracer:
|
||||||
- Add verifier for sm90 to report the invalid input.
|
- Add verifier for sm90 to report the invalid input.
|
||||||
@@ -41,6 +33,9 @@
|
|||||||
* Fix profiler bugs in exhaustive perf search.
|
* Fix profiler bugs in exhaustive perf search.
|
||||||
- Fix incorrect cluster shape output issue when doing exhaustive search.
|
- Fix incorrect cluster shape output issue when doing exhaustive search.
|
||||||
- Fix a bug in profiler grouped GEMM for setting tile scheduler swizzles, cluster shapes, and raster orders.
|
- Fix a bug in profiler grouped GEMM for setting tile scheduler swizzles, cluster shapes, and raster orders.
|
||||||
|
* Fix some profiler issues.
|
||||||
|
- Complete the reference for Blackwell blockwise gemm kernels.
|
||||||
|
- Fix incorrect regex logic for L1 test.
|
||||||
|
|
||||||
## [4.0.0](https://github.com/NVIDIA/cutlass/releases/tag/v4.0.0) (2025-06-03)
|
## [4.0.0](https://github.com/NVIDIA/cutlass/releases/tag/v4.0.0) (2025-06-03)
|
||||||
|
|
||||||
@@ -61,7 +56,7 @@
|
|||||||
- [C-structure based customized interface between JIT function and user codes](https://github.com/NVIDIA/cutlass/tree/main/examples/python/CuTeDSL/cute/ffi/jit_argument.py)
|
- [C-structure based customized interface between JIT function and user codes](https://github.com/NVIDIA/cutlass/tree/main/examples/python/CuTeDSL/cute/ffi/jit_argument.py)
|
||||||
* [Educational notebooks for getting started with CuTe DSL](https://github.com/NVIDIA/cutlass/tree/main/examples/python/CuTeDSL/notebooks)
|
* [Educational notebooks for getting started with CuTe DSL](https://github.com/NVIDIA/cutlass/tree/main/examples/python/CuTeDSL/notebooks)
|
||||||
* API updates
|
* API updates
|
||||||
- Fixed API mismatch in class ``cute.runtime.Pointer``: change ``element_type`` to ``dtype`` to match ``typing.Pointer``
|
- Please refer to [FUNCTIONALITY.md](https://github.com/NVIDIA/cutlass/blob/main/FUNCTIONALITY.md) for details
|
||||||
|
|
||||||
### CUTLASS C++
|
### CUTLASS C++
|
||||||
* Support [Family Specific Architecture Features](https://developer.nvidia.com/blog/nvidia-blackwell-and-nvidia-cuda-12-9-introduce-family-specific-architecture-features/) which was introduced in CUDA 12.9
|
* Support [Family Specific Architecture Features](https://developer.nvidia.com/blog/nvidia-blackwell-and-nvidia-cuda-12-9-introduce-family-specific-architecture-features/) which was introduced in CUDA 12.9
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Changelog for CuTe DSL API changes
|
||||||
|
|
||||||
|
## [4.1.0](https://github.com/NVIDIA/cutlass/releases/tag/v4.1.0) (2025-07-16)
|
||||||
|
|
||||||
|
* for loop
|
||||||
|
- Python built-in ``range`` now always generates IR and executes at runtime
|
||||||
|
- ``cutlass.range`` is advanced ``range`` with IR level unrolling and pipelining control
|
||||||
|
- Deprecated ``cutlass.range_dynamic``, please replace with ``range`` or ``cutlass.range``
|
||||||
|
- **Experimental** Added ``pipelining`` control for compiler generated software pipeline code
|
||||||
|
* while/if
|
||||||
|
- ``while``/``if`` now by default generates IR and executes at runtime unless ``cutlass.const_expr`` is specified for the predicate
|
||||||
|
- Deprecated ``cutlass.dynamic_expr``, please remove it
|
||||||
|
* Rename mbarrier functions to reduce ambiguity
|
||||||
|
* Modify SyncObject API (`MbarrierArray`, `NamedBarrier`, `TmaStoreFence`) to match `std::barrier`
|
||||||
|
* Change pipeline `create` function to take only keyword arguments, and make `barrier_storage` optional.
|
||||||
|
* Introduce `cutlass.cute.arch.get_dyn_smem_size` api to get runtime dynamic shared memory size.
|
||||||
|
* Various API Support for SM100 BlockScaled Gemm
|
||||||
|
- Introduce BlockScaled MmaOps in [tcgen05/mma.py]([https://github.com/NVIDIA/cutlass/blob/main/python/CuTeDSL/cutlass/cute/nvgpu/tcgen05/mma.py]), and provide a `make_blockscaled_trivial_tiled_mma` function in [blackwell_helpers.py](https://github.com/NVIDIA/cutlass/blob/main/python/CuTeDSL/cutlass/utils/blackwell_helpers.py) to help construct a BlockScaled TiledMma.
|
||||||
|
- Introduce S2T CopyOps in [tcgen05/copy.py](https://github.com/NVIDIA/cutlass/blob/main/python/CuTeDSL/cutlass/cute/nvgpu/tcgen05/copy.py).
|
||||||
|
- Introduce BlockScaled layout utilities in [blockscaled_layout.py](https://github.com/NVIDIA/cutlass/blob/main/python/CuTeDSL/cutlass/utils/blockscaled_layout.py) for creating the required scale factor layouts in global memory, shared memory and tensor memory.
|
||||||
|
* `cutlass.cute.compile` now supports compilation options. Refer to [JIT compilation options](https://docs.nvidia.com/cutlass/media/docs/pythonDSL/cute_dsl_general/dsl_jit_compilation_options.html) for more details.
|
||||||
|
* `cutlass.cute.testing.assert_` now works for device JIT function. Specify `--enable-device-assertions` as compilation option to enable.
|
||||||
|
* `cutlass.cute.make_tiled_copy` is now deprecated. Please use `cutlass.cute.make_tiled_copy_tv` instead.
|
||||||
|
* Shared memory capacity query
|
||||||
|
- Introduce `cutlass.utils.get_smem_capacity_in_bytes` for querying the shared memory capacity.
|
||||||
|
- `<arch>_utils.SMEM_CAPACITY["<arch_str>"]` is now deprecated.
|
||||||
|
|
||||||
|
## [4.0.0](https://github.com/NVIDIA/cutlass/releases/tag/v4.0.0) (2025-06-03)
|
||||||
|
|
||||||
|
* Fixed API mismatch in class ``cute.runtime.Pointer``: change ``element_type`` to ``dtype`` to match ``typing.Pointer``
|
||||||
@@ -46,20 +46,12 @@ To get started quickly - please refer :
|
|||||||
# What's New in CUTLASS 4.1
|
# What's New in CUTLASS 4.1
|
||||||
|
|
||||||
## CuTe DSL
|
## CuTe DSL
|
||||||
|
* Add aarch64 support, you can now pip install `nvidia-cutlass-dsl` on GB200 systems!
|
||||||
* More examples demonstrating how to use CuTe DSL to write peak-performance kernels
|
* More examples demonstrating how to use CuTe DSL to write peak-performance kernels
|
||||||
- [Blackwell Mamba2 SSD](https://github.com/NVIDIA/cutlass/tree/main/examples/python/CuTeDSL/blackwell/mamba2_ssd/mamba2_ssd.py)
|
- [Blackwell Mamba2 SSD](https://github.com/NVIDIA/cutlass/tree/main/examples/python/CuTeDSL/blackwell/mamba2_ssd/mamba2_ssd.py)
|
||||||
|
- [Blackwell SM100 persistent dense blockscaled GEMM with static scheduling](https://github.com/NVIDIA/cutlass/tree/main/examples/python/CuTeDSL/blackwell/dense_blockscaled_gemm_persistent.py)
|
||||||
* API updates
|
* API updates
|
||||||
- for loop
|
- Please refer to [FUNCTIONALITY.md](https://github.com/NVIDIA/cutlass/blob/main/FUNCTIONALITY.md) for details
|
||||||
- Python built-in ``range`` now always generates IR and executes at runtime
|
|
||||||
- ``cutlass.range`` is advanced ``range`` with IR level unrolling and pipelining control
|
|
||||||
- Deprecated ``cutlass.range_dynamic``, please replace with ``range`` or ``cutlass.range``
|
|
||||||
- **Experimental** Added ``pipelining`` control for compiler generated software pipeline code
|
|
||||||
- while/if
|
|
||||||
- ``while``/``if`` now by default generates IR and executes at runtime unless ``cutlass.const_expr`` is specified for the predicate
|
|
||||||
- Deprecated ``cutlass.dynamic_expr``, please remove it
|
|
||||||
- Rename mbarrier functions to reduce ambiguity
|
|
||||||
- Modify SyncObject API (`MbarrierArray`, `NamedBarrier`, `TmaStoreFence`) to match `std::barrier`
|
|
||||||
- Change pipeline `create` function to take only keyword arguments, and make `barrier_storage` optional.
|
|
||||||
|
|
||||||
## CUTLASS C++
|
## CUTLASS C++
|
||||||
* Further enhance Blackwell SM100 Attention kernels in [example 77](https://github.com/NVIDIA/cutlass/tree/main/examples/77_blackwell_fmha/).
|
* Further enhance Blackwell SM100 Attention kernels in [example 77](https://github.com/NVIDIA/cutlass/tree/main/examples/77_blackwell_fmha/).
|
||||||
@@ -72,7 +64,7 @@ To get started quickly - please refer :
|
|||||||
- Remove buggy and kludgy `get_layoutA|B|C_MN` and friends from Atoms/TiledX.
|
- Remove buggy and kludgy `get_layoutA|B|C_MN` and friends from Atoms/TiledX.
|
||||||
- Factor out `print_latex` and friends and rewrite.
|
- Factor out `print_latex` and friends and rewrite.
|
||||||
- Factor out `print_svg` and friends and rewrite.
|
- Factor out `print_svg` and friends and rewrite.
|
||||||
* Support Blackwell SM100 SIMT FFMA2 kernels.
|
* Support Blackwell SM100 SIMT packed fp32x2 kernels.
|
||||||
* Support residual add for implicit gemm kernels.
|
* Support residual add for implicit gemm kernels.
|
||||||
* Various fixes for CUTLASS C++ Python interface's EVT tracer:
|
* Various fixes for CUTLASS C++ Python interface's EVT tracer:
|
||||||
- Add verifier for sm90 to report the invalid input.
|
- Add verifier for sm90 to report the invalid input.
|
||||||
@@ -82,6 +74,9 @@ To get started quickly - please refer :
|
|||||||
* Fix profiler bugs in exhaustive perf search.
|
* Fix profiler bugs in exhaustive perf search.
|
||||||
- Fix incorrect cluster shape output issue when doing exhaustive search.
|
- Fix incorrect cluster shape output issue when doing exhaustive search.
|
||||||
- Fix a bug in profiler grouped GEMM for setting tile scheduler swizzles, cluster shapes, and raster orders.
|
- Fix a bug in profiler grouped GEMM for setting tile scheduler swizzles, cluster shapes, and raster orders.
|
||||||
|
* Fix some profiler issues.
|
||||||
|
- Complete the reference for Blackwell blockwise gemm kernels.
|
||||||
|
- Fix incorrect regex logic for L1 test.
|
||||||
|
|
||||||
Note: CUTLASS 4.x builds are known to be down on Windows platforms for all CUDA toolkits.
|
Note: CUTLASS 4.x builds are known to be down on Windows platforms for all CUDA toolkits.
|
||||||
CUTLASS team is working on a fix.
|
CUTLASS team is working on a fix.
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ ElementAccumulator (float), ElementComputeEpilogue (float), ElementInputA (cutla
|
|||||||
ElementInputB (cutlass::half_t), ElementOutput (float). Communicating just the data type is not
|
ElementInputB (cutlass::half_t), ElementOutput (float). Communicating just the data type is not
|
||||||
enough. As the data is laid out linearly in memory, we have to convey the layout of matrices. We do
|
enough. As the data is laid out linearly in memory, we have to convey the layout of matrices. We do
|
||||||
that by initializing template variable LayoutInputA to column major cutlass variable, LayoutInputB
|
that by initializing template variable LayoutInputA to column major cutlass variable, LayoutInputB
|
||||||
to row major and LayoutOutput to row major. Next, we setup rules to comptue alpha * X + beta * C
|
to row major and LayoutOutput to row major. Next, we setup rules to compute alpha * X + beta * C
|
||||||
which is called epilogue of the kernel. We initialize template variable EpilogueOp, which takes the
|
which is called epilogue of the kernel. We initialize template variable EpilogueOp, which takes the
|
||||||
data type of output ElementOutput (int32_t), the number of elements per vector memory access (16),
|
data type of output ElementOutput (int32_t), the number of elements per vector memory access (16),
|
||||||
data type of accumulator (int32_t) and data type of computation of linear combination (alpha * X +
|
data type of accumulator (int32_t) and data type of computation of linear combination (alpha * X +
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ ElementComputeEpilogue (int32_t), ElementInputA (int8_t), ElementInputB (int8_t)
|
|||||||
(int32_t). Communicating just the data type is not enough. As the data is laid out linearly in
|
(int32_t). Communicating just the data type is not enough. As the data is laid out linearly in
|
||||||
memory, we have to convey the layout of matrices. We do that by initializing template variable
|
memory, we have to convey the layout of matrices. We do that by initializing template variable
|
||||||
LayoutInputA to column major cutlass variable, LayoutInputB to row major and LayoutOutput to row
|
LayoutInputA to column major cutlass variable, LayoutInputB to row major and LayoutOutput to row
|
||||||
major. Next, we setup rules to comptue alpha * X + beta * C which is called epilogue of the kernel.
|
major. Next, we setup rules to compute alpha * X + beta * C which is called epilogue of the kernel.
|
||||||
We initialize template variable EpilogueOp, which takes the data type of output ElementOutput
|
We initialize template variable EpilogueOp, which takes the data type of output ElementOutput
|
||||||
(int32_t), the number of elements per vector memory access (16), data type of accumulator (int32_t)
|
(int32_t), the number of elements per vector memory access (16), data type of accumulator (int32_t)
|
||||||
and data type of computation of linear combination (alpha * X + beta * C).
|
and data type of computation of linear combination (alpha * X + beta * C).
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ ElementComputeEpilogue (float), ElementInputA (cutlass::int4b_t), ElementInputB
|
|||||||
ElementOutput (int32_t). Communicating just the data type is not enough. As the data is laid out
|
ElementOutput (int32_t). Communicating just the data type is not enough. As the data is laid out
|
||||||
linearly in memory, we have to convey the layout of tensors. We do that by initializing template
|
linearly in memory, we have to convey the layout of tensors. We do that by initializing template
|
||||||
variables LayoutInputA, LayoutInputB and LayoutOutput to TensorNHWC cutlass variable. Next, we setup
|
variables LayoutInputA, LayoutInputB and LayoutOutput to TensorNHWC cutlass variable. Next, we setup
|
||||||
rules to comptue alpha * X + beta * C which is called epilogue of the kernel. We initialize template
|
rules to compute alpha * X + beta * C which is called epilogue of the kernel. We initialize template
|
||||||
variable EpilogueOp, which takes the data type of output ElementOutput (int32_t), the number of
|
variable EpilogueOp, which takes the data type of output ElementOutput (int32_t), the number of
|
||||||
elements per vector memory access (32), data type of accumulator (int32_t) and data type of
|
elements per vector memory access (32), data type of accumulator (int32_t) and data type of
|
||||||
computation of linear combination (alpha * X + beta * C).
|
computation of linear combination (alpha * X + beta * C).
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ public:
|
|||||||
if(args.split_k_mode == SplitKMode::kParallel) {
|
if(args.split_k_mode == SplitKMode::kParallel) {
|
||||||
|
|
||||||
// Split-K parallel: CTAs in k-dimension write the partial results in a temporary workspace.
|
// Split-K parallel: CTAs in k-dimension write the partial results in a temporary workspace.
|
||||||
// The user needs to call a reduction operator to optain the final output tensor
|
// The user needs to call a reduction operator to obtain the final output tensor
|
||||||
workspace_bytes =
|
workspace_bytes =
|
||||||
sizeof(ElementAccumulator) *
|
sizeof(ElementAccumulator) *
|
||||||
size_t(cutlass::conv::implicit_gemm_tensor_c_size(kConvolutionalOperator, args.problem_size_0)) *
|
size_t(cutlass::conv::implicit_gemm_tensor_c_size(kConvolutionalOperator, args.problem_size_0)) *
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ struct Options {
|
|||||||
|
|
||||||
out << "13_fused_two_gemms_grouped_f16_sm80_rf\n\n"
|
out << "13_fused_two_gemms_grouped_f16_sm80_rf\n\n"
|
||||||
<< " This example runs a grouped back-to-back GEMM kernel. A group of independent back-to-back GEMMs are\n"
|
<< " This example runs a grouped back-to-back GEMM kernel. A group of independent back-to-back GEMMs are\n"
|
||||||
<< " run in a single kernel. Each indivdual problem in the group is subject to the same constraints that non-grouped\n"
|
<< " run in a single kernel. Each individual problem in the group is subject to the same constraints that non-grouped\n"
|
||||||
<< " back-to-back GEMMs are subject to.s"
|
<< " back-to-back GEMMs are subject to.s"
|
||||||
<< "Options:\n\n"
|
<< "Options:\n\n"
|
||||||
<< " --help If specified, displays this usage statement.\n\n"
|
<< " --help If specified, displays this usage statement.\n\n"
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ struct B2bGemm {
|
|||||||
typename Epilogue::OutputTileIterator::TensorRef* ref_C1;
|
typename Epilogue::OutputTileIterator::TensorRef* ref_C1;
|
||||||
typename Epilogue::OutputTileIterator::TensorRef* ref_D1;
|
typename Epilogue::OutputTileIterator::TensorRef* ref_D1;
|
||||||
|
|
||||||
// Epilogue params remain constant across all problmes in the group. Thus,
|
// Epilogue params remain constant across all problems in the group. Thus,
|
||||||
// the parameter here is not a pointer.
|
// the parameter here is not a pointer.
|
||||||
typename OutputOp0::Params epilogue0;
|
typename OutputOp0::Params epilogue0;
|
||||||
typename OutputOp1::Params epilogue1;
|
typename OutputOp1::Params epilogue1;
|
||||||
@@ -402,7 +402,7 @@ struct B2bGemm {
|
|||||||
typename Epilogue::OutputTileIterator::TensorRef* ref_C1;
|
typename Epilogue::OutputTileIterator::TensorRef* ref_C1;
|
||||||
typename Epilogue::OutputTileIterator::TensorRef* ref_D1;
|
typename Epilogue::OutputTileIterator::TensorRef* ref_D1;
|
||||||
|
|
||||||
// Epilogue params remain constant across all problmes in the group. Thus,
|
// Epilogue params remain constant across all problems in the group. Thus,
|
||||||
// the parameter here is not a pointer.
|
// the parameter here is not a pointer.
|
||||||
typename OutputOp0::Params output_op_0;
|
typename OutputOp0::Params output_op_0;
|
||||||
typename OutputOp1::Params output_op_1;
|
typename OutputOp1::Params output_op_1;
|
||||||
@@ -434,7 +434,7 @@ struct B2bGemm {
|
|||||||
// Only row-major outputs are currently supported, so no transpose is performed
|
// Only row-major outputs are currently supported, so no transpose is performed
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns non-grouped paramaters to be used as input to the kernel-level
|
/// Returns non-grouped parameters to be used as input to the kernel-level
|
||||||
/// operator for the problem indicated by problem_visitor.
|
/// operator for the problem indicated by problem_visitor.
|
||||||
CUTLASS_HOST_DEVICE
|
CUTLASS_HOST_DEVICE
|
||||||
Params to_single_params(const ProblemVisitor& problem_visitor) const {
|
Params to_single_params(const ProblemVisitor& problem_visitor) const {
|
||||||
|
|||||||
@@ -560,7 +560,7 @@ struct DefaultB2bConv2dFprop <
|
|||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/// Defines a kernel for Conv2dFprop specialization for Optimzed IteratorAlgorithm and
|
/// Defines a kernel for Conv2dFprop specialization for Optimized IteratorAlgorithm and
|
||||||
// multistage pipeline with interleaved layout.
|
// multistage pipeline with interleaved layout.
|
||||||
template <
|
template <
|
||||||
typename ElementA,
|
typename ElementA,
|
||||||
|
|||||||
+1
-1
@@ -606,7 +606,7 @@ struct DefaultB2bConv2dFprop <
|
|||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/// Defines a kernel for Conv2dFprop specialization for Optimzed IteratorAlgorithm and
|
/// Defines a kernel for Conv2dFprop specialization for Optimized IteratorAlgorithm and
|
||||||
// multistage pipeline with interleaved layout.
|
// multistage pipeline with interleaved layout.
|
||||||
/// Accumulator will be staged in shared memory.
|
/// Accumulator will be staged in shared memory.
|
||||||
template <
|
template <
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ public:
|
|||||||
IteratorAccumulatorScaleBias iterator_A1_scale, ///< iterator over A1 operand scale vectors in global memory
|
IteratorAccumulatorScaleBias iterator_A1_scale, ///< iterator over A1 operand scale vectors in global memory
|
||||||
IteratorAccumulatorScaleBias iterator_A1_bias, ///< iterator over A1 operand bias vectors in global memory
|
IteratorAccumulatorScaleBias iterator_A1_bias, ///< iterator over A1 operand bias vectors in global memory
|
||||||
IteratorB1 iterator_B1, ///< iterator over B1 operand in global memory
|
IteratorB1 iterator_B1, ///< iterator over B1 operand in global memory
|
||||||
FragmentC0 const &src_accum, ///< source accumualtor tile
|
FragmentC0 const &src_accum, ///< source accumulator tile
|
||||||
OutputOp output_op_0, ///< epilogue operation after 1st Gemm
|
OutputOp output_op_0, ///< epilogue operation after 1st Gemm
|
||||||
TransformA0 transform_A0 = TransformA0(), ///< transformation applied to A0 fragment
|
TransformA0 transform_A0 = TransformA0(), ///< transformation applied to A0 fragment
|
||||||
TransformB0 transform_B0 = TransformB0(), ///< transformation applied to B0 fragment
|
TransformB0 transform_B0 = TransformB0(), ///< transformation applied to B0 fragment
|
||||||
|
|||||||
@@ -298,7 +298,7 @@ public:
|
|||||||
IteratorAccumulatorScaleBias iterator_accum0_scale, ///< iterator over D0 scale vector in global memory
|
IteratorAccumulatorScaleBias iterator_accum0_scale, ///< iterator over D0 scale vector in global memory
|
||||||
IteratorAccumulatorScaleBias iterator_accum0_bias, ///< iterator over D0 bias vector in global memory
|
IteratorAccumulatorScaleBias iterator_accum0_bias, ///< iterator over D0 bias vector in global memory
|
||||||
IteratorB1 iterator_B1, ///< iterator over B1 operand in global memory
|
IteratorB1 iterator_B1, ///< iterator over B1 operand in global memory
|
||||||
FragmentC0 const &src_accum, ///< source accumualtor tile
|
FragmentC0 const &src_accum, ///< source accumulator tile
|
||||||
OutputOp output_op_0, ///< epilogue operation after 1st Gemm
|
OutputOp output_op_0, ///< epilogue operation after 1st Gemm
|
||||||
TransformA0 transform_A0 = TransformA0(), ///< transformation applied to A0 fragment
|
TransformA0 transform_A0 = TransformA0(), ///< transformation applied to A0 fragment
|
||||||
TransformB0 transform_B0 = TransformB0(), ///< transformation applied to B0 fragment
|
TransformB0 transform_B0 = TransformB0(), ///< transformation applied to B0 fragment
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ template <
|
|||||||
typename InstructionShape_,
|
typename InstructionShape_,
|
||||||
/// Number of stages used in the pipelined mainloop
|
/// Number of stages used in the pipelined mainloop
|
||||||
int Stages,
|
int Stages,
|
||||||
/// Operation perfomed by GEMM
|
/// Operation performed by GEMM
|
||||||
typename Operator,
|
typename Operator,
|
||||||
/// Epilogue output operator
|
/// Epilogue output operator
|
||||||
typename EpilogueOutputOp,
|
typename EpilogueOutputOp,
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ requires any memory for scratch space.
|
|||||||
If yes, we reserve scratch space and pass it along
|
If yes, we reserve scratch space and pass it along
|
||||||
with other arguments to initialize the CUTLASS kernel.
|
with other arguments to initialize the CUTLASS kernel.
|
||||||
|
|
||||||
After lauching the CUTLASS kernel, this example runs
|
After launching the CUTLASS kernel, this example runs
|
||||||
a reference convolution kernel (from CUTLASS utilities)
|
a reference convolution kernel (from CUTLASS utilities)
|
||||||
to check correctness.
|
to check correctness.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ int run() {
|
|||||||
// Construct Gemm ProblemSize with user defined output size
|
// Construct Gemm ProblemSize with user defined output size
|
||||||
cutlass::gemm::GemmCoord problem_size = {1024, 512, 1024};
|
cutlass::gemm::GemmCoord problem_size = {1024, 512, 1024};
|
||||||
|
|
||||||
// Stride factor shows the distance between two elements in the differnet dimensions. The
|
// Stride factor shows the distance between two elements in the different dimensions. The
|
||||||
// first data is the logical distance between two rows, the second is between two columns.
|
// first data is the logical distance between two rows, the second is between two columns.
|
||||||
// CUTLASS has a utility tool cutlass::layout::Affine2Layout_Factory<Layout>::layout_factory
|
// CUTLASS has a utility tool cutlass::layout::Affine2Layout_Factory<Layout>::layout_factory
|
||||||
// to help to convert stride_factor to the two strides.
|
// to help to convert stride_factor to the two strides.
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Define the overal warp-level problem shape
|
// Define the overall warp-level problem shape
|
||||||
int const kM = 27;
|
int const kM = 27;
|
||||||
int const kN = 31;
|
int const kN = 31;
|
||||||
int const kK = 17;
|
int const kK = 17;
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Define the overal warp-level problem shape
|
// Define the overall warp-level problem shape
|
||||||
int const kM = 14;
|
int const kM = 14;
|
||||||
int const kN = 27;
|
int const kN = 27;
|
||||||
int const kK = 17;
|
int const kK = 17;
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
**************************************************************************************************/
|
**************************************************************************************************/
|
||||||
|
|
||||||
// This example fuses gather before GEMM and scatter after GEMM into the same
|
// This example fuses gather before GEMM and scatter after GEMM into the same
|
||||||
// GEMM kernel. Gather and scatter operation is controled by an index vector
|
// GEMM kernel. Gather and scatter operation is controlled by an index vector
|
||||||
// to select rows or columns from A, B, C or D matrices.
|
// to select rows or columns from A, B, C or D matrices.
|
||||||
//
|
//
|
||||||
// Suppose, all matrices are column major. The pseudo code of the fused kernel
|
// Suppose, all matrices are column major. The pseudo code of the fused kernel
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ public:
|
|||||||
using ElementLayernormCompute = ElementLayernormCompute_;
|
using ElementLayernormCompute = ElementLayernormCompute_;
|
||||||
using ThreadblockShape = ThreadblockShape_;
|
using ThreadblockShape = ThreadblockShape_;
|
||||||
|
|
||||||
// Pre-processing has ensured the layout equivelent to RowMajor
|
// Pre-processing has ensured the layout equivalent to RowMajor
|
||||||
using Layout = cutlass::layout::RowMajor;
|
using Layout = cutlass::layout::RowMajor;
|
||||||
|
|
||||||
using TensorVariance = TensorRef<ElementVariance, Layout>;
|
using TensorVariance = TensorRef<ElementVariance, Layout>;
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ parser.add_argument('-la', "--layout_a", default="TensorNHWC", type=str, choices
|
|||||||
"TensorNHWC", "TensorNC32HW32"],
|
"TensorNHWC", "TensorNC32HW32"],
|
||||||
help="Memory layout of input tensor A")
|
help="Memory layout of input tensor A")
|
||||||
parser.add_argument('-aa', '--alignment_a', default=1,
|
parser.add_argument('-aa', '--alignment_a', default=1,
|
||||||
type=int, help="Memory alignement of input tensor A")
|
type=int, help="Memory alignment of input tensor A")
|
||||||
# B
|
# B
|
||||||
parser.add_argument('-lb', "--layout_b", default="TensorNHWC", type=str, choices=[
|
parser.add_argument('-lb', "--layout_b", default="TensorNHWC", type=str, choices=[
|
||||||
"TensorNHWC", "TensorC32RSK32"],
|
"TensorNHWC", "TensorC32RSK32"],
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ parser.add_argument('-la', "--layout_a", default="RowMajor", type=str, choices=[
|
|||||||
"RowMajor", "ColumnMajor", "RowMajorInterleaved32", "ColumnMajorInterleaved32"],
|
"RowMajor", "ColumnMajor", "RowMajorInterleaved32", "ColumnMajorInterleaved32"],
|
||||||
help="Memory layout of input tensor A")
|
help="Memory layout of input tensor A")
|
||||||
parser.add_argument('-aa', '--alignment_a', default=1,
|
parser.add_argument('-aa', '--alignment_a', default=1,
|
||||||
type=int, help="Memory alignement of input tensor A")
|
type=int, help="Memory alignment of input tensor A")
|
||||||
# B
|
# B
|
||||||
parser.add_argument('-lb', "--layout_b", default="RowMajor", type=str, choices=[
|
parser.add_argument('-lb', "--layout_b", default="RowMajor", type=str, choices=[
|
||||||
"RowMajor", "ColumnMajor", "RowMajorInterleaved32", "ColumnMajorInterleaved32"],
|
"RowMajor", "ColumnMajor", "RowMajorInterleaved32", "ColumnMajorInterleaved32"],
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
```
|
```
|
||||||
|
|
||||||
In practice, and for numerical stability reasons,
|
In practice, and for numerical stability reasons,
|
||||||
we also substract the maximum so far (`mi`) before doing
|
we also subtract the maximum so far (`mi`) before doing
|
||||||
the exponential. When we encounter new keys, the maximum
|
the exponential. When we encounter new keys, the maximum
|
||||||
used to compute O so far (`m_prime`) can differ from the
|
used to compute O so far (`m_prime`) can differ from the
|
||||||
current maximum, so we update O before accumulating with
|
current maximum, so we update O before accumulating with
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
```
|
```
|
||||||
|
|
||||||
In practice, and for numerical stability reasons,
|
In practice, and for numerical stability reasons,
|
||||||
we also substract the maximum so far (`mi`) before doing
|
we also subtract the maximum so far (`mi`) before doing
|
||||||
the exponential. When we encounter new keys, the maximum
|
the exponential. When we encounter new keys, the maximum
|
||||||
used to compute O so far (`m_prime`) can differ from the
|
used to compute O so far (`m_prime`) can differ from the
|
||||||
current maximum, so we update O before accumulating with
|
current maximum, so we update O before accumulating with
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
/*! \file
|
/*! \file
|
||||||
\brief Cutlass provides helper template functions to figure out the right
|
\brief Cutlass provides helper template functions to figure out the right
|
||||||
datastructures to instanciate to run a GEMM with various parameters (see
|
datastructures to instantiate to run a GEMM with various parameters (see
|
||||||
`cutlass/gemm/threadblock/default_mma.h`). However, due to template
|
`cutlass/gemm/threadblock/default_mma.h`). However, due to template
|
||||||
instantiation priority rules, it will only create an MmaMultiStage with
|
instantiation priority rules, it will only create an MmaMultiStage with
|
||||||
kStages=3 (otherwise creates an MmePipelined - which is not compatible with
|
kStages=3 (otherwise creates an MmePipelined - which is not compatible with
|
||||||
@@ -83,7 +83,7 @@ template <
|
|||||||
typename InstructionShape,
|
typename InstructionShape,
|
||||||
/// Number of stages used in the pipelined mainloop
|
/// Number of stages used in the pipelined mainloop
|
||||||
int Stages,
|
int Stages,
|
||||||
/// Operation perfomed by GEMM
|
/// Operation performed by GEMM
|
||||||
typename Operator,
|
typename Operator,
|
||||||
typename Enable_ = void>
|
typename Enable_ = void>
|
||||||
struct FindDefaultMma {
|
struct FindDefaultMma {
|
||||||
|
|||||||
@@ -522,7 +522,7 @@ class MmaPipelinedFromSharedMemory : public MmaBaseFromSharedMemory<
|
|||||||
|
|
||||||
// For API compatibility with MmaMultistageFromSharedMemory
|
// For API compatibility with MmaMultistageFromSharedMemory
|
||||||
// but not supported as it worsens perf: older gpus < sm80 don't
|
// but not supported as it worsens perf: older gpus < sm80 don't
|
||||||
// support async tranfers and have to waste registers
|
// support async transfers and have to waste registers
|
||||||
CUTLASS_DEVICE
|
CUTLASS_DEVICE
|
||||||
void set_prologue_done(bool value) {}
|
void set_prologue_done(bool value) {}
|
||||||
CUTLASS_DEVICE
|
CUTLASS_DEVICE
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
*
|
*
|
||||||
**************************************************************************************************/
|
**************************************************************************************************/
|
||||||
/*! \file
|
/*! \file
|
||||||
\brief Instanciates the right WarpIterator to read from shared memory
|
\brief Instantiates the right WarpIterator to read from shared memory
|
||||||
The class `DefaultWarpIteratorAFromSharedMemory` is useful when reading
|
The class `DefaultWarpIteratorAFromSharedMemory` is useful when reading
|
||||||
data dumped with `B2bGemm::accumToSmem`.
|
data dumped with `B2bGemm::accumToSmem`.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+1
-1
@@ -86,7 +86,7 @@ namespace threadblock {
|
|||||||
/// To be efficient, this assumes the iterator will be dereferenced and advanced
|
/// To be efficient, this assumes the iterator will be dereferenced and advanced
|
||||||
/// at least once outside any looping structure to minimize integer arithmetic.
|
/// at least once outside any looping structure to minimize integer arithmetic.
|
||||||
///
|
///
|
||||||
/// Acceses out of bounds are safe so long as `clear_mask()` is called prior to
|
/// Accesses out of bounds are safe so long as `clear_mask()` is called prior to
|
||||||
/// dereferencing the iterator.
|
/// dereferencing the iterator.
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
Description of parameters and tensors used to represent the Blocked-Ellpack (ELL) format
|
Description of parameters and tensors used to represent the Blocked-Ellpack (ELL) format
|
||||||
for this example:
|
for this example:
|
||||||
a_rows - Rows in the sparse matrix.
|
a_rows - Rows in the sparse matrix.
|
||||||
a_cols - Colums in the sparse matrix.
|
a_cols - Columns in the sparse matrix.
|
||||||
a_ell_blocksize - Size of the ELL-Blocks.
|
a_ell_blocksize - Size of the ELL-Blocks.
|
||||||
a_ell_num_columns - Number of columns in the Blocked-Ellpack format (ellValue columns)
|
a_ell_num_columns - Number of columns in the Blocked-Ellpack format (ellValue columns)
|
||||||
tensor_a - ellValue matrix, whose size is (a_rows * a_ell_num_columns)
|
tensor_a - ellValue matrix, whose size is (a_rows * a_ell_num_columns)
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ class gen_device:
|
|||||||
|
|
||||||
warp_M_tile = 32
|
warp_M_tile = 32
|
||||||
|
|
||||||
# Determine maxmimum N_tile
|
# Determine maximum N_tile
|
||||||
Max_Ntile = 0
|
Max_Ntile = 0
|
||||||
for layer in self.fuse_gemm_info:
|
for layer in self.fuse_gemm_info:
|
||||||
n_tile = layer['mnk'][1]
|
n_tile = layer['mnk'][1]
|
||||||
|
|||||||
@@ -76,9 +76,9 @@ class gen_verify:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_params(self, declartion = True):
|
def get_params(self, declaration = True):
|
||||||
code = ""
|
code = ""
|
||||||
if declartion:
|
if declaration:
|
||||||
for param in self.params:
|
for param in self.params:
|
||||||
code += param[0] + " " + param[1] + ";\n"
|
code += param[0] + " " + param[1] + ";\n"
|
||||||
|
|
||||||
|
|||||||
@@ -64,8 +64,8 @@ def write_2_headfile(filename, file_dir, string):
|
|||||||
with open(file_dir + filename, 'w') as f:
|
with open(file_dir + filename, 'w') as f:
|
||||||
f.write("/* Auto Generated code - Do not edit.*/\n\n\n#pragma once\n" + string)
|
f.write("/* Auto Generated code - Do not edit.*/\n\n\n#pragma once\n" + string)
|
||||||
|
|
||||||
def var_idx(varaiable, index):
|
def var_idx(variable, index):
|
||||||
return varaiable + str(index)
|
return variable + str(index)
|
||||||
|
|
||||||
|
|
||||||
def list_2_string(input_list, ):
|
def list_2_string(input_list, ):
|
||||||
|
|||||||
@@ -78,7 +78,7 @@
|
|||||||
a single default value.
|
a single default value.
|
||||||
|
|
||||||
CUTLASS 3.x provides builders for both collective mainloops and epilogues. The particular implementation of
|
CUTLASS 3.x provides builders for both collective mainloops and epilogues. The particular implementation of
|
||||||
the collective is specified via the schedule tags that corresond to the underlying collective's
|
the collective is specified via the schedule tags that correspond to the underlying collective's
|
||||||
dispatch policy. `gemm::collective::KernelScheduleAuto` and `epilogue::collective::EpilogueScheduleAuto`
|
dispatch policy. `gemm::collective::KernelScheduleAuto` and `epilogue::collective::EpilogueScheduleAuto`
|
||||||
are special cases of these schedules that allow the builder to also decide the dispatch policy for you,
|
are special cases of these schedules that allow the builder to also decide the dispatch policy for you,
|
||||||
therefore letting the builder pick the collective specialization.
|
therefore letting the builder pick the collective specialization.
|
||||||
|
|||||||
+1
-1
@@ -425,7 +425,7 @@ int main(int argc, char const **args) {
|
|||||||
// Pipeline Depth to be used i.e number of A, B buffers in shared memory
|
// Pipeline Depth to be used i.e number of A, B buffers in shared memory
|
||||||
constexpr int PipelineStages = 8;
|
constexpr int PipelineStages = 8;
|
||||||
|
|
||||||
// Let's choose a Warp-Specialized Mainloop implemention which uses TMA
|
// Let's choose a Warp-Specialized Mainloop implementation which uses TMA
|
||||||
// Note : This requires / assumes the tensors to be 16B aligned
|
// Note : This requires / assumes the tensors to be 16B aligned
|
||||||
using DispatchPolicy = cutlass::gemm::MainloopSm90TmaGmmaWarpSpecialized<PipelineStages, ClusterShape,
|
using DispatchPolicy = cutlass::gemm::MainloopSm90TmaGmmaWarpSpecialized<PipelineStages, ClusterShape,
|
||||||
cutlass::gemm::KernelTmaWarpSpecialized>;
|
cutlass::gemm::KernelTmaWarpSpecialized>;
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
\brief Example of a Hopper gather+GEMM+scatter kernel fusion.
|
\brief Example of a Hopper gather+GEMM+scatter kernel fusion.
|
||||||
|
|
||||||
This example fuses gather before GEMM and scatter after GEMM into the same
|
This example fuses gather before GEMM and scatter after GEMM into the same
|
||||||
GEMM kernel. Gather and scatter operation is controled by an index vector
|
GEMM kernel. Gather and scatter operation is controlled by an index vector
|
||||||
to select rows or columns from A, B, C or D matrices.
|
to select rows or columns from A, B, C or D matrices.
|
||||||
|
|
||||||
Gather/scatter operations are always performed along a strided dimension
|
Gather/scatter operations are always performed along a strided dimension
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
The approach relies on two things:
|
The approach relies on two things:
|
||||||
- The ability of CUTLASS 3 to naturally perform general tensor contractions (GETT) owing to the
|
- The ability of CUTLASS 3 to naturally perform general tensor contractions (GETT) owing to the
|
||||||
flexibility of CuTe's hierarchical layouts (see example 51_hopper_gett for more details).
|
flexibility of CuTe's hierarchical layouts (see example 51_hopper_gett for more details).
|
||||||
- The harware capabilities of Hopper TMA units that allow for loading multidimensional tensors with
|
- The hardware capabilities of Hopper TMA units that allow for loading multidimensional tensors with
|
||||||
(almost) arbitrary strides, which can be used to represent a permuted view of the data.
|
(almost) arbitrary strides, which can be used to represent a permuted view of the data.
|
||||||
|
|
||||||
In this example we reuse the permutation classes of examples 39_gemm_permute as operation tags.
|
In this example we reuse the permutation classes of examples 39_gemm_permute as operation tags.
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ Running this example on an RTX 3080Ti prints the following performance numbers (
|
|||||||
|
|
||||||
```
|
```
|
||||||
$> ./examples/59_ampere_gather_scatter_conv/59_ampere_gather_scatter_conv --n=131072 --i=128 --no-check
|
$> ./examples/59_ampere_gather_scatter_conv/59_ampere_gather_scatter_conv --n=131072 --i=128 --no-check
|
||||||
Ampere convolution forward propogation kernel supporting both affine and gather/scatter tensors.
|
Ampere convolution forward propagation kernel supporting both affine and gather/scatter tensors.
|
||||||
|
|
||||||
Allocating tensors ... done.
|
Allocating tensors ... done.
|
||||||
Initializing data ... done.
|
Initializing data ... done.
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
*
|
*
|
||||||
**************************************************************************************************/
|
**************************************************************************************************/
|
||||||
/*! \file
|
/*! \file
|
||||||
\brief Example demonstrating CuTe and CUTLASS 3.x based Ampere convolution forward propogation kernel
|
\brief Example demonstrating CuTe and CUTLASS 3.x based Ampere convolution forward propagation kernel
|
||||||
capable of operating on both affine and gather/scatter tensors.
|
capable of operating on both affine and gather/scatter tensors.
|
||||||
|
|
||||||
This example demonstartes a few super cool features of CUTLASS and CuTe. It shows off
|
This example demonstartes a few super cool features of CUTLASS and CuTe. It shows off
|
||||||
@@ -284,7 +284,7 @@ int ampere_gather_scatter_conv_fprop(
|
|||||||
int
|
int
|
||||||
main(int argc, char const** argv) {
|
main(int argc, char const** argv) {
|
||||||
cutlass::CommandLine cmd(argc, argv);
|
cutlass::CommandLine cmd(argc, argv);
|
||||||
std::cout << "Ampere convolution forward propogation kernel supporting both affine and gather/scatter tensors.\n\n";
|
std::cout << "Ampere convolution forward propagation kernel supporting both affine and gather/scatter tensors.\n\n";
|
||||||
if (cmd.check_cmd_line_flag("help")) {
|
if (cmd.check_cmd_line_flag("help")) {
|
||||||
std::cout
|
std::cout
|
||||||
<< "Options:\n"
|
<< "Options:\n"
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ struct Options {
|
|||||||
// Post-process the problem sizes
|
// Post-process the problem sizes
|
||||||
bin_problems();
|
bin_problems();
|
||||||
|
|
||||||
// Initalize alpha array
|
// Initialize alpha array
|
||||||
randomize_alpha_ptr_array(cmd);
|
randomize_alpha_ptr_array(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -358,7 +358,7 @@ void initialize(const Options<RasterOrderOptions> &options) {
|
|||||||
// Layout SFA and SFB represent logically broadcasting data in CuTe.
|
// Layout SFA and SFB represent logically broadcasting data in CuTe.
|
||||||
// E.g., if Layout SFA has shape ((ScaleGranularityM, M / ScaleGranularityM), (ScaleGraunularityK, K / ScaleGranularityK))
|
// E.g., if Layout SFA has shape ((ScaleGranularityM, M / ScaleGranularityM), (ScaleGraunularityK, K / ScaleGranularityK))
|
||||||
// and strides ((0, 1), (0, M / ScaleGraunuarlityM)), then each collection of ScaleGranularityM x ScaleGranularityK
|
// and strides ((0, 1), (0, M / ScaleGraunuarlityM)), then each collection of ScaleGranularityM x ScaleGranularityK
|
||||||
// indecies in the tensor map to the same offset.
|
// indices in the tensor map to the same offset.
|
||||||
|
|
||||||
layout_SFA = ScaleConfig::tile_atom_to_shape_SFA(make_shape(options.m, options.n, options.k, options.l));
|
layout_SFA = ScaleConfig::tile_atom_to_shape_SFA(make_shape(options.m, options.n, options.k, options.l));
|
||||||
layout_SFB = ScaleConfig::tile_atom_to_shape_SFB(make_shape(options.m, options.n, options.k, options.l));
|
layout_SFB = ScaleConfig::tile_atom_to_shape_SFB(make_shape(options.m, options.n, options.k, options.l));
|
||||||
|
|||||||
@@ -61,7 +61,7 @@
|
|||||||
# Heuristic mode with deterministic reduction
|
# Heuristic mode with deterministic reduction
|
||||||
./74_blackwell_gemm_streamk" --m=256 --n=256 --k=16384 --decomposition=Heuristic --reduction=Deterministic
|
./74_blackwell_gemm_streamk" --m=256 --n=256 --k=16384 --decomposition=Heuristic --reduction=Deterministic
|
||||||
|
|
||||||
# Stream-K mode with determinsitic reduction
|
# Stream-K mode with deterministic reduction
|
||||||
./74_blackwell_gemm_streamk" --m=256 --n=256 --k=16384 --decomposition=StreamK --reduction=Deterministic
|
./74_blackwell_gemm_streamk" --m=256 --n=256 --k=16384 --decomposition=StreamK --reduction=Deterministic
|
||||||
|
|
||||||
# Split-K mode with a splitting factor of 2 and deterministic reduction
|
# Split-K mode with a splitting factor of 2 and deterministic reduction
|
||||||
|
|||||||
@@ -850,7 +850,7 @@ int run(Options &options, bool host_problem_shapes_available = true)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
std::cout << " Verfication is turned off for this run." << std::endl;
|
std::cout << " Verification is turned off for this run." << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run profiling loop
|
// Run profiling loop
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
APIs on NVIDIA Blackwell SM100 architecture.
|
APIs on NVIDIA Blackwell SM100 architecture.
|
||||||
|
|
||||||
The basic computation logic of dgrad convolution kernel is, take 3D convolution as an example:
|
The basic computation logic of dgrad convolution kernel is, take 3D convolution as an example:
|
||||||
Xformed Actication (NZPQK) * Weight/Filter (KTRSC) = Activation (NDHWC)
|
Xformed Activation (NZPQK) * Weight/Filter (KTRSC) = Activation (NDHWC)
|
||||||
|
|
||||||
where in terms of GEMM perspective,
|
where in terms of GEMM perspective,
|
||||||
Matrix A = Xformed Activation, Matrix B = Weight/Filter, Matrix C = Activation
|
Matrix A = Xformed Activation, Matrix B = Weight/Filter, Matrix C = Activation
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
APIs on NVIDIA Blackwell SM100 architecture.
|
APIs on NVIDIA Blackwell SM100 architecture.
|
||||||
|
|
||||||
The basic computation logic of fprop convolution kernel is, take 3D convolution as an example:
|
The basic computation logic of fprop convolution kernel is, take 3D convolution as an example:
|
||||||
Activation (NDHWC) * Weight/Filter (KTRSC) = Xformed Actication (NZPQK)
|
Activation (NDHWC) * Weight/Filter (KTRSC) = Xformed Activation (NZPQK)
|
||||||
|
|
||||||
where in terms of GEMM perspective,
|
where in terms of GEMM perspective,
|
||||||
Matrix A = Activation, Matrix B = Weight/Filter, Matrix C = Xformed Activation
|
Matrix A = Activation, Matrix B = Weight/Filter, Matrix C = Xformed Activation
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
APIs on NVIDIA Blackwell SM100 architecture.
|
APIs on NVIDIA Blackwell SM100 architecture.
|
||||||
|
|
||||||
The basic computation logic of wgrad convolution kernel is, take 3D convolution as an example:
|
The basic computation logic of wgrad convolution kernel is, take 3D convolution as an example:
|
||||||
Xformed Actication (NZPQK) * Activation (NDHWC) = Weight/Filter (KTRSC)
|
Xformed Activation (NZPQK) * Activation (NDHWC) = Weight/Filter (KTRSC)
|
||||||
|
|
||||||
where in terms of GEMM perspective,
|
where in terms of GEMM perspective,
|
||||||
Matrix A = Xformed Activation, Matrix B = Activation, Matrix C = Weight/Filter
|
Matrix A = Xformed Activation, Matrix B = Activation, Matrix C = Weight/Filter
|
||||||
|
|||||||
@@ -506,7 +506,11 @@ struct FwdRunner {
|
|||||||
select<0,3>(problem_shape),
|
select<0,3>(problem_shape),
|
||||||
stride_LSE);
|
stride_LSE);
|
||||||
|
|
||||||
fmha_reference(problem_shape, mQ, mK, mV, mO, mLSE, ActiveMask{});
|
auto [Q, K, D, HB] = problem_shape;
|
||||||
|
|
||||||
|
auto problem_shape_ref = cute::make_tuple(Q, K, D, D, HB);
|
||||||
|
|
||||||
|
fmha_reference(problem_shape_ref, mQ, mK, mV, mO, mLSE, ActiveMask{});
|
||||||
|
|
||||||
cudaError_t result = cudaDeviceSynchronize();
|
cudaError_t result = cudaDeviceSynchronize();
|
||||||
if (result != cudaSuccess) {
|
if (result != cudaSuccess) {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
\brief Example implementation of fused multi-head attention for Blackwell using CUTLASS 3.
|
\brief Example implementation of fused multi-head attention for Blackwell using CUTLASS 3.
|
||||||
|
|
||||||
This example showcases the use of CUTLASS to build backward fused
|
This example showcases the use of CUTLASS to build backward fused
|
||||||
multi-head attantion (FMHA) collectives from existing CUTLASS collectives targeting
|
multi-head attention (FMHA) collectives from existing CUTLASS collectives targeting
|
||||||
the NVIDIA Blackwell architecture.
|
the NVIDIA Blackwell architecture.
|
||||||
|
|
||||||
Background and motivation
|
Background and motivation
|
||||||
@@ -117,6 +117,7 @@ struct Options {
|
|||||||
std::vector<int> varlen_q;
|
std::vector<int> varlen_q;
|
||||||
std::vector<int> varlen_k;
|
std::vector<int> varlen_k;
|
||||||
int d = 128;
|
int d = 128;
|
||||||
|
int d_vo = 128;
|
||||||
int iterations = 3;
|
int iterations = 3;
|
||||||
bool verify = false;
|
bool verify = false;
|
||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
@@ -178,6 +179,7 @@ struct Options {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd.get_cmd_line_argument("d", d, defaults.d);
|
cmd.get_cmd_line_argument("d", d, defaults.d);
|
||||||
|
cmd.get_cmd_line_argument("d_vo", d_vo, d);
|
||||||
cmd.get_cmd_line_argument("h", h, -1);
|
cmd.get_cmd_line_argument("h", h, -1);
|
||||||
if (h == -1) h = 2048 / d;
|
if (h == -1) h = 2048 / d;
|
||||||
|
|
||||||
@@ -301,6 +303,7 @@ struct Options {
|
|||||||
<< " --varlen-q=<int>:<int...> Sets the variable Q extent per batch (colon separated)\n"
|
<< " --varlen-q=<int>:<int...> Sets the variable Q extent per batch (colon separated)\n"
|
||||||
<< " --varlen-k=<int>:<int...> Sets the variable K extent per batch (colon separated)\n"
|
<< " --varlen-k=<int>:<int...> Sets the variable K extent per batch (colon separated)\n"
|
||||||
<< " --d=<int> Sets the D extent\n"
|
<< " --d=<int> Sets the D extent\n"
|
||||||
|
<< " --d_vo=<int> Sets the D_VO extent\n"
|
||||||
<< " --iterations=<int> Benchmarking iterations\n"
|
<< " --iterations=<int> Benchmarking iterations\n"
|
||||||
<< " --verify Verify results\n"
|
<< " --verify Verify results\n"
|
||||||
<< " --verbose Print smem and execution time per kernel\n"
|
<< " --verbose Print smem and execution time per kernel\n"
|
||||||
@@ -387,6 +390,7 @@ struct ExampleResult {
|
|||||||
|
|
||||||
template<
|
template<
|
||||||
bool kIsVarlen,
|
bool kIsVarlen,
|
||||||
|
bool kIsMla,
|
||||||
class TileShape,
|
class TileShape,
|
||||||
class DispatchPolicy,
|
class DispatchPolicy,
|
||||||
class ActiveMask,
|
class ActiveMask,
|
||||||
@@ -404,8 +408,8 @@ struct BwdRunner {
|
|||||||
// Q K D (H B)
|
// Q K D (H B)
|
||||||
using ProblemShape = std::conditional_t<
|
using ProblemShape = std::conditional_t<
|
||||||
kIsVarlen,
|
kIsVarlen,
|
||||||
cute::tuple<VariableLength, VariableLength, int, cute::tuple<int, int>>,
|
cute::tuple<VariableLength, VariableLength, int, int, cute::tuple<int, int>>,
|
||||||
cute::tuple<int, int, int, cute::tuple<int, int>>
|
cute::tuple<int, int, int, int, cute::tuple<int, int>>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
using TensorStride = Stride<int, _1, Stride<int, int>>; // Seq D (H B)
|
using TensorStride = Stride<int, _1, Stride<int, int>>; // Seq D (H B)
|
||||||
@@ -461,45 +465,45 @@ struct BwdRunner {
|
|||||||
// Methods
|
// Methods
|
||||||
//
|
//
|
||||||
bool verify(const ProblemShape& problem_shape) {
|
bool verify(const ProblemShape& problem_shape) {
|
||||||
auto [Q, K, D, HB] = problem_shape;
|
auto [Q, K, D, D_VO, HB] = problem_shape;
|
||||||
auto [H, B] = HB;
|
auto [H, B] = HB;
|
||||||
|
|
||||||
Tensor mQ = make_tensor(make_gmem_ptr(block_Q.get()),
|
Tensor mQ = make_tensor(make_gmem_ptr(block_Q.get()),
|
||||||
select<0,2,3>(problem_shape),
|
select<0,2,4>(problem_shape),
|
||||||
stride_Q);
|
stride_Q);
|
||||||
|
|
||||||
Tensor mK = make_tensor(make_gmem_ptr(block_K.get()),
|
Tensor mK = make_tensor(make_gmem_ptr(block_K.get()),
|
||||||
select<1,2,3>(problem_shape),
|
select<1,2,4>(problem_shape),
|
||||||
stride_K);
|
stride_K);
|
||||||
|
|
||||||
Tensor mV = make_tensor(make_gmem_ptr(block_V.get()),
|
Tensor mV = make_tensor(make_gmem_ptr(block_V.get()),
|
||||||
select<1,2,3>(problem_shape),
|
select<1,3,4>(problem_shape),
|
||||||
stride_V);
|
stride_V);
|
||||||
|
|
||||||
Tensor mO = make_tensor(make_gmem_ptr(block_O.get()),
|
Tensor mO = make_tensor(make_gmem_ptr(block_O.get()),
|
||||||
select<0,2,3>(problem_shape),
|
select<0,3,4>(problem_shape),
|
||||||
stride_O);
|
stride_O);
|
||||||
|
|
||||||
// keep going here! (this might be better in cursor)
|
// keep going here! (this might be better in cursor)
|
||||||
|
|
||||||
Tensor mLSE = make_tensor(make_gmem_ptr(block_LSE.get()),
|
Tensor mLSE = make_tensor(make_gmem_ptr(block_LSE.get()),
|
||||||
select<0,3>(problem_shape),
|
select<0,4>(problem_shape),
|
||||||
stride_LSE);
|
stride_LSE);
|
||||||
|
|
||||||
Tensor mDQ = make_tensor(make_gmem_ptr(block_ref_dQ.get()),
|
Tensor mDQ = make_tensor(make_gmem_ptr(block_ref_dQ.get()),
|
||||||
select<0,2,3>(problem_shape),
|
select<0,2,4>(problem_shape),
|
||||||
stride_dQ);
|
stride_dQ);
|
||||||
|
|
||||||
Tensor mDK = make_tensor(make_gmem_ptr(block_ref_dK.get()),
|
Tensor mDK = make_tensor(make_gmem_ptr(block_ref_dK.get()),
|
||||||
select<1,2,3>(problem_shape),
|
select<1,2,4>(problem_shape),
|
||||||
stride_dK);
|
stride_dK);
|
||||||
|
|
||||||
Tensor mDV = make_tensor(make_gmem_ptr(block_ref_dV.get()),
|
Tensor mDV = make_tensor(make_gmem_ptr(block_ref_dV.get()),
|
||||||
select<1,2,3>(problem_shape),
|
select<1,3,4>(problem_shape),
|
||||||
stride_dV);
|
stride_dV);
|
||||||
|
|
||||||
Tensor mDO = make_tensor(make_gmem_ptr(block_dO.get()),
|
Tensor mDO = make_tensor(make_gmem_ptr(block_dO.get()),
|
||||||
select<0,2,3>(problem_shape),
|
select<0,3,4>(problem_shape),
|
||||||
stride_dO);
|
stride_dO);
|
||||||
|
|
||||||
fmha_bwd_reference(problem_shape, mQ, mK, mV, mO, mLSE, mDO, mDQ, mDK, mDV, ActiveMask{});
|
fmha_bwd_reference(problem_shape, mQ, mK, mV, mO, mLSE, mDO, mDQ, mDK, mDV, ActiveMask{});
|
||||||
@@ -595,14 +599,14 @@ struct BwdRunner {
|
|||||||
ProblemShape problem_shape{
|
ProblemShape problem_shape{
|
||||||
{max_seqlen_q, block_cumulative_seqlen_q.get(), total_seqlen_q},
|
{max_seqlen_q, block_cumulative_seqlen_q.get(), total_seqlen_q},
|
||||||
{max_seqlen_kv, block_cumulative_seqlen_kv.get(), total_seqlen_kv},
|
{max_seqlen_kv, block_cumulative_seqlen_kv.get(), total_seqlen_kv},
|
||||||
options.d, {options.h, options.b}
|
options.d, options.d_vo, {options.h, options.b}
|
||||||
};
|
};
|
||||||
auto tensor_shape = make_shape(total_seqlen_q, total_seqlen_kv, options.d, make_shape(options.h, 1));
|
auto tensor_shape = make_shape(total_seqlen_q, total_seqlen_kv, options.d, options.d_vo, make_shape(options.h, 1));
|
||||||
|
|
||||||
return cute::make_tuple(problem_shape, tensor_shape);
|
return cute::make_tuple(problem_shape, tensor_shape);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ProblemShape problem_shape{options.q, options.k, options.d, {options.h, options.b}};
|
ProblemShape problem_shape{options.q, options.k, options.d, options.d_vo, {options.h, options.b}};
|
||||||
return cute::make_tuple(problem_shape, problem_shape);
|
return cute::make_tuple(problem_shape, problem_shape);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -610,24 +614,25 @@ struct BwdRunner {
|
|||||||
/// Initialize operands to be used in the GEMM and reference GEMM
|
/// Initialize operands to be used in the GEMM and reference GEMM
|
||||||
ProblemShape initialize(Options const& options) {
|
ProblemShape initialize(Options const& options) {
|
||||||
auto [problem_shape, tensor_shape] = initialize_problem_shape(options);
|
auto [problem_shape, tensor_shape] = initialize_problem_shape(options);
|
||||||
auto [Q, K, D, HB] = tensor_shape;
|
auto [Q, K, D, D_VO, HB] = tensor_shape;
|
||||||
auto [H, B] = HB;
|
auto [H, B] = HB;
|
||||||
D = cutlass::round_up(D, 8); // Alignment
|
D = cutlass::round_up(D, 8); // Alignment
|
||||||
|
|
||||||
// for varlen, Q == total_Q, K == total_K, B = 1
|
// for varlen, Q == total_Q, K == total_K, B = 1
|
||||||
// but in problem_shape, they've got to be max_Q/max_K, and B = B
|
// but in problem_shape, they've got to be max_Q/max_K, and B = B
|
||||||
|
|
||||||
auto shape_QO = make_shape(Q, D, make_shape(H, B));
|
auto shape_Q = make_shape(Q, D, make_shape(H, B));
|
||||||
auto shape_KV = make_shape(K, D, make_shape(H, B));
|
auto shape_O = make_shape(Q, D_VO, make_shape(H, B));
|
||||||
|
auto shape_K = make_shape(K, D, make_shape(H, B));
|
||||||
|
auto shape_V = make_shape(K, D_VO, make_shape(H, B));
|
||||||
auto shape_LSE = make_shape(Q, make_shape(H, B));
|
auto shape_LSE = make_shape(Q, make_shape(H, B));
|
||||||
|
|
||||||
stride_Q = make_stride(D, _1{}, make_stride(D*Q, B == 1 ? 0 : D*Q*H));
|
stride_Q = make_stride(D, _1{}, make_stride(D*Q, B == 1 ? 0 : D*Q*H));
|
||||||
stride_K = make_stride(D, _1{}, make_stride(D*K, B == 1 ? 0 : D*K*H));
|
stride_K = make_stride(D, _1{}, make_stride(D*K, B == 1 ? 0 : D*K*H));
|
||||||
|
stride_V = make_stride(D_VO, _1{}, make_stride(D_VO*K, B == 1 ? 0 : D_VO*K*H));
|
||||||
|
stride_O = make_stride(D_VO, _1{}, make_stride(D_VO*Q, B == 1 ? 0 : D_VO*Q*H));
|
||||||
stride_LSE = make_stride(_1{}, make_stride(Q, B == 1 ? 0 : Q*H));
|
stride_LSE = make_stride(_1{}, make_stride(Q, B == 1 ? 0 : Q*H));
|
||||||
|
|
||||||
stride_V = stride_K;
|
|
||||||
stride_O = stride_Q;
|
|
||||||
|
|
||||||
stride_dQ = stride_Q;
|
stride_dQ = stride_Q;
|
||||||
stride_dK = stride_K;
|
stride_dK = stride_K;
|
||||||
stride_dV = stride_V;
|
stride_dV = stride_V;
|
||||||
@@ -637,20 +642,20 @@ struct BwdRunner {
|
|||||||
return size(make_shape(1ull, shape));
|
return size(make_shape(1ull, shape));
|
||||||
};
|
};
|
||||||
|
|
||||||
block_Q.reset(lsize(shape_QO));
|
block_Q.reset(lsize(shape_Q));
|
||||||
block_K.reset(lsize(shape_KV));
|
block_K.reset(lsize(shape_K));
|
||||||
block_V.reset(lsize(shape_KV));
|
block_V.reset(lsize(shape_V));
|
||||||
block_O.reset(lsize(shape_QO));
|
block_O.reset(lsize(shape_O));
|
||||||
block_LSE.reset(lsize(shape_LSE));
|
block_LSE.reset(lsize(shape_LSE));
|
||||||
|
|
||||||
block_dQ.reset(lsize(shape_QO));
|
block_dQ.reset(lsize(shape_Q));
|
||||||
block_dK.reset(lsize(shape_KV));
|
block_dK.reset(lsize(shape_K));
|
||||||
block_dV.reset(lsize(shape_KV));
|
block_dV.reset(lsize(shape_V));
|
||||||
block_dO.reset(lsize(shape_QO));
|
block_dO.reset(lsize(shape_O));
|
||||||
|
|
||||||
block_ref_dQ.reset(lsize(shape_QO));
|
block_ref_dQ.reset(lsize(shape_Q));
|
||||||
block_ref_dK.reset(lsize(shape_KV));
|
block_ref_dK.reset(lsize(shape_K));
|
||||||
block_ref_dV.reset(lsize(shape_KV));
|
block_ref_dV.reset(lsize(shape_V));
|
||||||
|
|
||||||
initialize_block(block_Q, seed + 2023, options.init_style_q);
|
initialize_block(block_Q, seed + 2023, options.init_style_q);
|
||||||
initialize_block(block_K, seed + 2022, options.init_style_k);
|
initialize_block(block_K, seed + 2022, options.init_style_k);
|
||||||
@@ -665,23 +670,23 @@ struct BwdRunner {
|
|||||||
initialize_block(block_ref_dV, seed + 2035);
|
initialize_block(block_ref_dV, seed + 2035);
|
||||||
|
|
||||||
Tensor mQ = make_tensor(make_gmem_ptr(block_Q.get()),
|
Tensor mQ = make_tensor(make_gmem_ptr(block_Q.get()),
|
||||||
select<0,2,3>(problem_shape),
|
select<0,2,4>(problem_shape),
|
||||||
stride_Q);
|
stride_Q);
|
||||||
|
|
||||||
Tensor mK = make_tensor(make_gmem_ptr(block_K.get()),
|
Tensor mK = make_tensor(make_gmem_ptr(block_K.get()),
|
||||||
select<1,2,3>(problem_shape),
|
select<1,2,4>(problem_shape),
|
||||||
stride_K);
|
stride_K);
|
||||||
|
|
||||||
Tensor mV = make_tensor(make_gmem_ptr(block_V.get()),
|
Tensor mV = make_tensor(make_gmem_ptr(block_V.get()),
|
||||||
select<1,2,3>(problem_shape),
|
select<1,3,4>(problem_shape),
|
||||||
stride_V);
|
stride_V);
|
||||||
|
|
||||||
Tensor mO = make_tensor(make_gmem_ptr(block_O.get()),
|
Tensor mO = make_tensor(make_gmem_ptr(block_O.get()),
|
||||||
select<0,2,3>(problem_shape),
|
select<0,3,4>(problem_shape),
|
||||||
stride_O);
|
stride_O);
|
||||||
|
|
||||||
Tensor mLSE = make_tensor(make_gmem_ptr(block_LSE.get()),
|
Tensor mLSE = make_tensor(make_gmem_ptr(block_LSE.get()),
|
||||||
select<0,3>(problem_shape),
|
select<0,4>(problem_shape),
|
||||||
stride_LSE);
|
stride_LSE);
|
||||||
|
|
||||||
if (! options.skip_reference) {
|
if (! options.skip_reference) {
|
||||||
@@ -698,7 +703,7 @@ struct BwdRunner {
|
|||||||
|
|
||||||
ExampleResult example_result;
|
ExampleResult example_result;
|
||||||
|
|
||||||
using Operation = cutlass::fmha::device::Sm100FmhaBwd<ProblemShape, Element, ElementAccumulator, TileShape, ActiveMask>;
|
using Operation = cutlass::fmha::device::Sm100FmhaBwd<ProblemShape, Element, ElementAccumulator, TileShape, kIsMla, ActiveMask>;
|
||||||
|
|
||||||
typename Operation::Arguments arguments{
|
typename Operation::Arguments arguments{
|
||||||
problem_shape,
|
problem_shape,
|
||||||
@@ -811,12 +816,12 @@ struct BwdRunner {
|
|||||||
|
|
||||||
runtime_ms /= static_cast<float>(options.iterations);
|
runtime_ms /= static_cast<float>(options.iterations);
|
||||||
|
|
||||||
double flops = 10.0 * (std::is_same_v<ActiveMask, CausalForBackwardMask> ? 0.5 : 1.0);
|
double flops = 2.0 * (std::is_same_v<ActiveMask, CausalForBackwardMask> ? 0.5 : 1.0);
|
||||||
flops *= static_cast<double>(get<0>(problem_shape));
|
flops *= static_cast<double>(get<0>(problem_shape));
|
||||||
flops *= static_cast<double>(get<1>(problem_shape));
|
flops *= static_cast<double>(get<1>(problem_shape));
|
||||||
flops *= static_cast<double>(get<2>(problem_shape));
|
flops *= (3 * static_cast<double>(get<2>(problem_shape)) + 2 * static_cast<double>(get<3>(problem_shape)));
|
||||||
flops *= static_cast<double>(get<3,0>(problem_shape));
|
flops *= static_cast<double>(get<4,0>(problem_shape));
|
||||||
flops *= static_cast<double>(get<3,1>(problem_shape));
|
flops *= static_cast<double>(get<4,1>(problem_shape));
|
||||||
double tflops_s = flops * 1e-12 /*tera*/ / (runtime_ms * 1e-3 /*ms*/);
|
double tflops_s = flops * 1e-12 /*tera*/ / (runtime_ms * 1e-3 /*ms*/);
|
||||||
example_result.tflops_tc_s = tflops_s;
|
example_result.tflops_tc_s = tflops_s;
|
||||||
example_result.runtime_ms = runtime_ms;
|
example_result.runtime_ms = runtime_ms;
|
||||||
@@ -892,7 +897,7 @@ template<class Mask>
|
|||||||
void run_bwd_64(Mask fusion, Options const & options, cutlass::KernelHardwareInfo const& hw_info) {
|
void run_bwd_64(Mask fusion, Options const & options, cutlass::KernelHardwareInfo const& hw_info) {
|
||||||
auto run = [&](auto shape, auto kernel, const char* name, auto... kernel_options) {
|
auto run = [&](auto shape, auto kernel, const char* name, auto... kernel_options) {
|
||||||
dispatch_bool(options.varlen, [&](auto is_varlen) {
|
dispatch_bool(options.varlen, [&](auto is_varlen) {
|
||||||
BwdRunner<decltype(is_varlen)::value, decltype(shape), decltype(kernel), Mask, decltype(kernel_options)...> runner;
|
BwdRunner<decltype(is_varlen)::value, false,decltype(shape), decltype(kernel), Mask, decltype(kernel_options)...> runner;
|
||||||
auto result = runner.run(options, hw_info);
|
auto result = runner.run(options, hw_info);
|
||||||
print_result(name, result, options.verbose);
|
print_result(name, result, options.verbose);
|
||||||
});
|
});
|
||||||
@@ -900,7 +905,7 @@ void run_bwd_64(Mask fusion, Options const & options, cutlass::KernelHardwareInf
|
|||||||
|
|
||||||
using HeadDim = _64;
|
using HeadDim = _64;
|
||||||
|
|
||||||
run(Shape<_128, _128, HeadDim>{}, KernelCoop{}, "tma");
|
run(Shape<_128, _128, HeadDim, HeadDim>{}, KernelCoop{}, "tma");
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -909,7 +914,7 @@ template<class Mask>
|
|||||||
void run_bwd_128(Mask fusion, Options const & options, cutlass::KernelHardwareInfo const& hw_info) {
|
void run_bwd_128(Mask fusion, Options const & options, cutlass::KernelHardwareInfo const& hw_info) {
|
||||||
auto run = [&](auto shape, auto kernel, const char* name, auto... kernel_options) {
|
auto run = [&](auto shape, auto kernel, const char* name, auto... kernel_options) {
|
||||||
dispatch_bool(options.varlen, [&](auto is_varlen) {
|
dispatch_bool(options.varlen, [&](auto is_varlen) {
|
||||||
BwdRunner<decltype(is_varlen)::value, decltype(shape), decltype(kernel), Mask, decltype(kernel_options)...> runner;
|
BwdRunner<decltype(is_varlen)::value, false, decltype(shape), decltype(kernel), Mask, decltype(kernel_options)...> runner;
|
||||||
auto result = runner.run(options, hw_info);
|
auto result = runner.run(options, hw_info);
|
||||||
print_result(name, result, options.verbose);
|
print_result(name, result, options.verbose);
|
||||||
});
|
});
|
||||||
@@ -917,7 +922,22 @@ void run_bwd_128(Mask fusion, Options const & options, cutlass::KernelHardwareIn
|
|||||||
|
|
||||||
using HeadDim = _128;
|
using HeadDim = _128;
|
||||||
|
|
||||||
run(Shape<_128, _128, HeadDim>{}, KernelCoop{}, "tma");
|
run(Shape<_128, _128, HeadDim, HeadDim>{}, KernelCoop{}, "tma");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Mask>
|
||||||
|
void run_bwd_mla_192(Mask fusion, Options const & options, cutlass::KernelHardwareInfo const& hw_info) {
|
||||||
|
auto run = [&](auto shape, auto kernel, const char* name, auto... kernel_options) {
|
||||||
|
dispatch_bool(options.varlen, [&](auto is_varlen) {
|
||||||
|
BwdRunner<decltype(is_varlen)::value, true, decltype(shape), decltype(kernel), Mask, decltype(kernel_options)...> runner;
|
||||||
|
auto result = runner.run(options, hw_info);
|
||||||
|
print_result(name, result, options.verbose);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
using HeadDim = _192;
|
||||||
|
|
||||||
|
run(Shape<_64, _128, HeadDim, _128>{}, KernelCoop{}, "tma");
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -981,7 +1001,7 @@ int main_single(int argc, char const **args) {
|
|||||||
hw_info.sm_count = options.sm_count;
|
hw_info.sm_count = options.sm_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "###### B " << options.b << " H " << options.h << " Q " << options.q << " K " << options.k << " D " << options.d << " ";
|
std::cout << "###### B " << options.b << " H " << options.h << " Q " << options.q << " K " << options.k << " D " << options.d << " D_VO " << options.d_vo << " ";
|
||||||
std::cout << "Backward" << " " << (options.causal ? "Causal" : "Full") << " ";
|
std::cout << "Backward" << " " << (options.causal ? "Causal" : "Full") << " ";
|
||||||
std::cout << "#SM " << hw_info.sm_count << std::endl;
|
std::cout << "#SM " << hw_info.sm_count << std::endl;
|
||||||
|
|
||||||
@@ -998,12 +1018,15 @@ int main_single(int argc, char const **args) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
with_causal([&](auto fusion) {
|
with_causal([&](auto fusion) {
|
||||||
if (options.d <= 64) {
|
if (options.d <= 64 && options.d_vo == options.d) {
|
||||||
run_bwd_64(fusion, options, hw_info);
|
run_bwd_64(fusion, options, hw_info);
|
||||||
}
|
}
|
||||||
else if (options.d <= 128) {
|
else if (options.d <= 128 && options.d_vo == options.d) {
|
||||||
run_bwd_128(fusion, options, hw_info);
|
run_bwd_128(fusion, options, hw_info);
|
||||||
}
|
}
|
||||||
|
else if (options.d == 192 && options.d_vo == 128) {
|
||||||
|
run_bwd_mla_192(fusion, options, hw_info);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
std::cout << "No kernel instantiated for d=" << options.d << std::endl;
|
std::cout << "No kernel instantiated for d=" << options.d << std::endl;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -485,7 +485,11 @@ struct MlaFwdRunner {
|
|||||||
select<0,3>(problem_shape),
|
select<0,3>(problem_shape),
|
||||||
stride_LSE);
|
stride_LSE);
|
||||||
|
|
||||||
fmha_reference(problem_shape, mQ, mK, mV, mO, mLSE, ActiveMask{});
|
auto [Q, K, D, HB] = problem_shape;
|
||||||
|
|
||||||
|
auto problem_shape_ref = cute::make_tuple(Q, K, D, D, HB);
|
||||||
|
|
||||||
|
fmha_reference(problem_shape_ref, mQ, mK, mV, mO, mLSE, ActiveMask{});
|
||||||
|
|
||||||
cudaError_t result = cudaDeviceSynchronize();
|
cudaError_t result = cudaDeviceSynchronize();
|
||||||
if (result != cudaSuccess) {
|
if (result != cudaSuccess) {
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ set(TEST_GEN_REMAP --b=2 --h=4 --h_k=2 --k=512 --d=128 --verify --remap)
|
|||||||
set(TEST_GEN_CACHEONLY --b=2 --h=4 --h_k=2 --k=512 --d=128 --verify --cache-only)
|
set(TEST_GEN_CACHEONLY --b=2 --h=4 --h_k=2 --k=512 --d=128 --verify --cache-only)
|
||||||
|
|
||||||
set(TEST_MLA_BASIC --b=1 --k=512 --page=128 --verify)
|
set(TEST_MLA_BASIC --b=1 --k=512 --page=128 --verify)
|
||||||
|
set(TEST_BWD_MLA_BASIC --b=1 --h=4 --q=512 --k=512 --d=192 --d_vo=128 --verify --mask=no)
|
||||||
|
set(TEST_BWD_MLA_VARLEN --b=1 --h=4 --q=512 --k=512 --d=192 --d_vo=128 --verify --mask=residual --varlen)
|
||||||
|
|
||||||
if(NOT WIN32 AND (NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) AND (CUTLASS_NVCC_ARCHS MATCHES 100a))
|
if(NOT WIN32 AND (NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) AND (CUTLASS_NVCC_ARCHS MATCHES 100a))
|
||||||
|
|
||||||
@@ -174,6 +176,8 @@ if(NOT WIN32 AND (NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) AND (CUTLASS_NVCC
|
|||||||
TEST_VARLEN_12
|
TEST_VARLEN_12
|
||||||
TEST_VARLEN_13
|
TEST_VARLEN_13
|
||||||
TEST_VARLEN_14
|
TEST_VARLEN_14
|
||||||
|
TEST_BWD_MLA_BASIC
|
||||||
|
TEST_BWD_MLA_VARLEN
|
||||||
)
|
)
|
||||||
target_include_directories(77_blackwell_fmha_bwd_${PREC} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(77_blackwell_fmha_bwd_${PREC} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
target_compile_definitions(77_blackwell_fmha_bwd_${PREC} PRIVATE ${PREC_MACRO})
|
target_compile_definitions(77_blackwell_fmha_bwd_${PREC} PRIVATE ${PREC_MACRO})
|
||||||
|
|||||||
@@ -37,13 +37,19 @@ There are three kernels to compute backwards:
|
|||||||
|
|
||||||
`Sm100FmhaBwdKernelTmaWarpSpecialized` is the main point of this sample, as it demonstrates how to use tensor cores to achieve a high performance fused kernel.
|
`Sm100FmhaBwdKernelTmaWarpSpecialized` is the main point of this sample, as it demonstrates how to use tensor cores to achieve a high performance fused kernel.
|
||||||
|
|
||||||
|
## MLA Blackwell Backward
|
||||||
|
|
||||||
|
The sample also provides the feature of MLA backward(d=192, d_vo=128). To enable MLA backward, please specify `--d=192 --d_vo=128` when running the bwd sample.
|
||||||
|
|
||||||
|
`Sm100FmhaBwdMlaKernelTmaWarpSpecialized`is the main point for MLA backward. The MLA approach is slightly different from the original one to enable high performance with the MLA shape.
|
||||||
|
|
||||||
# MLA Inference for Blackwell
|
# MLA Inference for Blackwell
|
||||||
|
|
||||||
This sample provides code for fused multi-head latent attention inference in
|
This sample provides code for fused multi-head latent attention inference in
|
||||||
the weight-absorbed regime, i.e. for latent head dim 512, and rope head dim 64.
|
the weight-absorbed regime, i.e. for latent head dim 512, and rope head dim 64.
|
||||||
It supports fp16, bf16, and fp8 input and output types.
|
It supports fp16, bf16, and fp8 input and output types.
|
||||||
|
|
||||||
To accomodate the large output accumulator due to the large latent head dimension,
|
To accommodate the large output accumulator due to the large latent head dimension,
|
||||||
the sample demonstrates how to leverage 2Sm Blackwell tensor cores.
|
the sample demonstrates how to leverage 2Sm Blackwell tensor cores.
|
||||||
|
|
||||||
Loading can be done via TMA (either without paging or with page size 128), or using `cp.async`
|
Loading can be done via TMA (either without paging or with page size 128), or using `cp.async`
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
|
|
||||||
#include "../device/fmha.hpp"
|
#include "../device/fmha.hpp"
|
||||||
#include "../kernel/sm100_fmha_bwd_kernel_tma_warpspecialized.hpp"
|
#include "../kernel/sm100_fmha_bwd_kernel_tma_warpspecialized.hpp"
|
||||||
|
#include "../kernel/sm100_fmha_bwd_mla_kernel_tma_warpspecialized.hpp"
|
||||||
#include "../kernel/fmha_kernel_bwd_sum_OdO.hpp"
|
#include "../kernel/fmha_kernel_bwd_sum_OdO.hpp"
|
||||||
#include "../kernel/fmha_kernel_bwd_convert.hpp"
|
#include "../kernel/fmha_kernel_bwd_convert.hpp"
|
||||||
|
|
||||||
@@ -55,13 +56,14 @@ template<
|
|||||||
class Element,
|
class Element,
|
||||||
class ElementAccumulator,
|
class ElementAccumulator,
|
||||||
class TileShape,
|
class TileShape,
|
||||||
|
bool IsMla,
|
||||||
class Mask
|
class Mask
|
||||||
>
|
>
|
||||||
class Sm100FmhaBwd {
|
class Sm100FmhaBwd {
|
||||||
public:
|
public:
|
||||||
/// Argument structure: User API
|
/// Argument structure: User API
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
// Q K D HB
|
// Q K D D_VO HB
|
||||||
ProblemShape problem_shape;
|
ProblemShape problem_shape;
|
||||||
|
|
||||||
const Element* ptr_Q;
|
const Element* ptr_Q;
|
||||||
@@ -98,11 +100,20 @@ public:
|
|||||||
cutlass::fmha::kernel::FmhaKernelBwdConvert<ProblemShape, Element, ElementAccumulator>
|
cutlass::fmha::kernel::FmhaKernelBwdConvert<ProblemShape, Element, ElementAccumulator>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
using Operation = cutlass::fmha::device::FMHA<
|
using OperationNormal= cutlass::fmha::device::FMHA<
|
||||||
cutlass::fmha::kernel::Sm100FmhaBwdKernelTmaWarpSpecialized<
|
cutlass::fmha::kernel::Sm100FmhaBwdKernelTmaWarpSpecialized<
|
||||||
ProblemShape, Element, ElementAccumulator, TileShape, Mask
|
ProblemShape, Element, ElementAccumulator, TileShape, Mask
|
||||||
>
|
>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
using OperationMla = cutlass::fmha::device::FMHA<
|
||||||
|
cutlass::fmha::kernel::Sm100FmhaBwdMlaKernelTmaWarpSpecialized<
|
||||||
|
ProblemShape, Element, ElementAccumulator, TileShape, Mask
|
||||||
|
>
|
||||||
|
>;
|
||||||
|
|
||||||
|
using Operation = std::conditional_t<IsMla, OperationMla, OperationNormal>;
|
||||||
|
|
||||||
using Kernel = typename Operation::Kernel;
|
using Kernel = typename Operation::Kernel;
|
||||||
|
|
||||||
struct Params {
|
struct Params {
|
||||||
@@ -121,7 +132,7 @@ private:
|
|||||||
ElementAccumulator* sum_odo = nullptr,
|
ElementAccumulator* sum_odo = nullptr,
|
||||||
ElementAccumulator* scaled_lse = nullptr) {
|
ElementAccumulator* scaled_lse = nullptr) {
|
||||||
using namespace cute;
|
using namespace cute;
|
||||||
auto [Q_, K, D, HB] = args.problem_shape;
|
auto [Q_, K, D, D_VO, HB] = args.problem_shape;
|
||||||
auto [H, B] = HB;
|
auto [H, B] = HB;
|
||||||
D = cutlass::round_up(D, 8); // Alignment
|
D = cutlass::round_up(D, 8); // Alignment
|
||||||
int Q = cutlass::round_up(static_cast<int>(Q_), 8); // Alignment
|
int Q = cutlass::round_up(static_cast<int>(Q_), 8); // Alignment
|
||||||
@@ -141,7 +152,7 @@ private:
|
|||||||
|
|
||||||
static typename OperationConvert::Arguments to_convert_arguments(Arguments const& args, ElementAccumulator* src = nullptr) {
|
static typename OperationConvert::Arguments to_convert_arguments(Arguments const& args, ElementAccumulator* src = nullptr) {
|
||||||
using namespace cute;
|
using namespace cute;
|
||||||
auto [Q_, K, D, HB] = args.problem_shape;
|
auto [Q_, K, D, D_VO, HB] = args.problem_shape;
|
||||||
auto [H, B] = HB;
|
auto [H, B] = HB;
|
||||||
D = cutlass::round_up(D, 8); // Alignment
|
D = cutlass::round_up(D, 8); // Alignment
|
||||||
int Q = cutlass::round_up(static_cast<int>(Q_), 8); // Alignment
|
int Q = cutlass::round_up(static_cast<int>(Q_), 8); // Alignment
|
||||||
@@ -163,6 +174,7 @@ private:
|
|||||||
ElementAccumulator* sum_OdO = nullptr, cute::tuple<cute::_1, cute::tuple<int, int>> const& stride_sum_OdO = {},
|
ElementAccumulator* sum_OdO = nullptr, cute::tuple<cute::_1, cute::tuple<int, int>> const& stride_sum_OdO = {},
|
||||||
ElementAccumulator* scaled_lse = nullptr, cute::tuple<cute::_1, cute::tuple<int, int>> const& stride_scaled_lse = {},
|
ElementAccumulator* scaled_lse = nullptr, cute::tuple<cute::_1, cute::tuple<int, int>> const& stride_scaled_lse = {},
|
||||||
ElementAccumulator* dQ_acc = nullptr, cute::tuple<int, cute::_1, cute::tuple<int, int>> const& stride_dQ = {}) {
|
ElementAccumulator* dQ_acc = nullptr, cute::tuple<int, cute::_1, cute::tuple<int, int>> const& stride_dQ = {}) {
|
||||||
|
|
||||||
return typename Operation::Arguments{
|
return typename Operation::Arguments{
|
||||||
args.problem_shape,
|
args.problem_shape,
|
||||||
{ args.ptr_Q, args.stride_Q,
|
{ args.ptr_Q, args.stride_Q,
|
||||||
@@ -207,7 +219,7 @@ public:
|
|||||||
/// Gets the workspace size
|
/// Gets the workspace size
|
||||||
static size_t
|
static size_t
|
||||||
get_workspace_size(Arguments const& args) {
|
get_workspace_size(Arguments const& args) {
|
||||||
auto [Q_, K, D, HB] = args.problem_shape;
|
auto [Q_, K, D, D_VO, HB] = args.problem_shape;
|
||||||
auto [H, B] = HB;
|
auto [H, B] = HB;
|
||||||
D = cutlass::round_up(D, 8); // Alignment
|
D = cutlass::round_up(D, 8); // Alignment
|
||||||
int Q = cutlass::round_up(static_cast<int>(Q_), 8); // Alignment
|
int Q = cutlass::round_up(static_cast<int>(Q_), 8); // Alignment
|
||||||
@@ -227,7 +239,7 @@ public:
|
|||||||
CUTLASS_TRACE_HOST("Universal::initialize_split() - workspace_dQ="
|
CUTLASS_TRACE_HOST("Universal::initialize_split() - workspace_dQ="
|
||||||
<< workspace_dQ << ", workspace_sum_OdO=" << workspace_sum_OdO << "stream: " << (stream ? "non-null" : "null"));
|
<< workspace_dQ << ", workspace_sum_OdO=" << workspace_sum_OdO << "stream: " << (stream ? "non-null" : "null"));
|
||||||
|
|
||||||
auto [Q_, K, D, HB] = args.problem_shape;
|
auto [Q_, K, D, D_VO, HB] = args.problem_shape;
|
||||||
auto [H, B] = HB;
|
auto [H, B] = HB;
|
||||||
D = cutlass::round_up(D, 8); // Alignment
|
D = cutlass::round_up(D, 8); // Alignment
|
||||||
int Q = cutlass::round_up(static_cast<int>(Q_), 8); // Alignment
|
int Q = cutlass::round_up(static_cast<int>(Q_), 8); // Alignment
|
||||||
@@ -256,7 +268,7 @@ public:
|
|||||||
CUTLASS_TRACE_HOST("Universal::initialize() - workspace "
|
CUTLASS_TRACE_HOST("Universal::initialize() - workspace "
|
||||||
<< workspace << ", stream: " << (stream ? "non-null" : "null"));
|
<< workspace << ", stream: " << (stream ? "non-null" : "null"));
|
||||||
|
|
||||||
auto [Q_, K, D, HB] = args.problem_shape;
|
auto [Q_, K, D, D_VO, HB] = args.problem_shape;
|
||||||
auto [H, B] = HB;
|
auto [H, B] = HB;
|
||||||
D = cutlass::round_up(D, 8); // Alignment
|
D = cutlass::round_up(D, 8); // Alignment
|
||||||
int Q = cutlass::round_up(static_cast<int>(Q_), 8); // Alignment
|
int Q = cutlass::round_up(static_cast<int>(Q_), 8); // Alignment
|
||||||
|
|||||||
@@ -85,11 +85,11 @@ struct FmhaKernelBwdConvert {
|
|||||||
static const int kIterationsSeq = kBlockSeq / kNumThreadsSeq;
|
static const int kIterationsSeq = kBlockSeq / kNumThreadsSeq;
|
||||||
|
|
||||||
static bool can_implement(Arguments const& args) {
|
static bool can_implement(Arguments const& args) {
|
||||||
return get<2>(args.problem_shape) % kElementsPerLoad == 0;
|
return get<2>(args.problem_shape) % kElementsPerLoad == 0 && get<3>(args.problem_shape) % kElementsPerLoad == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static dim3 get_grid_shape(Params const& params) {
|
static dim3 get_grid_shape(Params const& params) {
|
||||||
dim3 grid(size<3,0>(params.problem_shape), size<3,1>(params.problem_shape), ceil_div(std::max(size<0>(params.problem_shape), size<1>(params.problem_shape)), kBlockSeq));
|
dim3 grid(size<4,0>(params.problem_shape), size<4,1>(params.problem_shape), ceil_div(std::max(size<0>(params.problem_shape), size<1>(params.problem_shape)), kBlockSeq));
|
||||||
return grid;
|
return grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ struct FmhaKernelBwdConvert {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<class StrideSrc, class StrideDest, class Count>
|
template<class StrideSrc, class StrideDest, class Count>
|
||||||
CUTLASS_DEVICE void copy(Params const& params, const ElementAcc* ptr_src, StrideSrc const& stride_src, Element* ptr_dest, StrideDest const& stride_dest, Count const& count) {
|
CUTLASS_DEVICE void copy(Params const& params, const ElementAcc* ptr_src, StrideSrc const& stride_src, Element* ptr_dest, StrideDest const& stride_dest, Count const& count, int d_dim) {
|
||||||
auto ptr_src_bh = ptr_src + get<2,0>(stride_src) * blockIdx.x + get<2,1>(stride_src) * blockIdx.y;
|
auto ptr_src_bh = ptr_src + get<2,0>(stride_src) * blockIdx.x + get<2,1>(stride_src) * blockIdx.y;
|
||||||
auto ptr_dest_bh = ptr_dest + get<2,0>(stride_dest) * blockIdx.x + get<2,1>(stride_dest) * blockIdx.y;
|
auto ptr_dest_bh = ptr_dest + get<2,0>(stride_dest) * blockIdx.x + get<2,1>(stride_dest) * blockIdx.y;
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ struct FmhaKernelBwdConvert {
|
|||||||
auto ptr_src_bhs = ptr_src_bh + idx_s * get<0>(stride_src);
|
auto ptr_src_bhs = ptr_src_bh + idx_s * get<0>(stride_src);
|
||||||
auto ptr_dest_bhs = ptr_dest_bh + idx_s * get<0>(stride_dest);
|
auto ptr_dest_bhs = ptr_dest_bh + idx_s * get<0>(stride_dest);
|
||||||
|
|
||||||
for (int idx_d = threadIdx.x * kElementsPerLoad; idx_d < get<2>(params.problem_shape); idx_d += kElementsPerLoad * kNumThreadsD) {
|
for (int idx_d = threadIdx.x * kElementsPerLoad; idx_d < d_dim; idx_d += kElementsPerLoad * kNumThreadsD) {
|
||||||
ElementAcc value_src[kElementsPerLoad];
|
ElementAcc value_src[kElementsPerLoad];
|
||||||
Element value_dest[kElementsPerLoad];
|
Element value_dest[kElementsPerLoad];
|
||||||
|
|
||||||
@@ -139,13 +139,13 @@ struct FmhaKernelBwdConvert {
|
|||||||
|
|
||||||
CUTLASS_DEVICE void operator()(const Params ¶ms, char* smem) {
|
CUTLASS_DEVICE void operator()(const Params ¶ms, char* smem) {
|
||||||
if (params.ptr_src_dQ != nullptr) {
|
if (params.ptr_src_dQ != nullptr) {
|
||||||
copy(params, params.ptr_src_dQ, params.stride_src_dQ, params.ptr_dest_dQ, params.stride_dest_dQ, get<0>(params.problem_shape));
|
copy(params, params.ptr_src_dQ, params.stride_src_dQ, params.ptr_dest_dQ, params.stride_dest_dQ, get<0>(params.problem_shape), get<2>(params.problem_shape));
|
||||||
}
|
}
|
||||||
if (params.ptr_src_dK != nullptr) {
|
if (params.ptr_src_dK != nullptr) {
|
||||||
copy(params, params.ptr_src_dK, params.stride_src_dK, params.ptr_dest_dK, params.stride_dest_dK, get<1>(params.problem_shape));
|
copy(params, params.ptr_src_dK, params.stride_src_dK, params.ptr_dest_dK, params.stride_dest_dK, get<1>(params.problem_shape), get<2>(params.problem_shape));
|
||||||
}
|
}
|
||||||
if (params.ptr_src_dV != nullptr) {
|
if (params.ptr_src_dV != nullptr) {
|
||||||
copy(params, params.ptr_src_dV, params.stride_src_dV, params.ptr_dest_dV, params.stride_dest_dV, get<1>(params.problem_shape));
|
copy(params, params.ptr_src_dV, params.stride_src_dV, params.ptr_dest_dV, params.stride_dest_dV, get<1>(params.problem_shape), get<3>(params.problem_shape));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -86,11 +86,11 @@ struct FmhaKernelBwdSumOdO {
|
|||||||
static const int kIterationsQ = kBlockQ / kNumThreadsQ;
|
static const int kIterationsQ = kBlockQ / kNumThreadsQ;
|
||||||
|
|
||||||
static bool can_implement(Arguments const& args) {
|
static bool can_implement(Arguments const& args) {
|
||||||
return get<2>(args.problem_shape) % kElementsPerLoad == 0;
|
return get<2>(args.problem_shape) % kElementsPerLoad == 0 && get<3>(args.problem_shape) % kElementsPerLoad == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static dim3 get_grid_shape(Params const& params) {
|
static dim3 get_grid_shape(Params const& params) {
|
||||||
dim3 grid(ceil_div(size<0>(params.problem_shape), kBlockQ), size<3,0>(params.problem_shape), size<3,1>(params.problem_shape));
|
dim3 grid(ceil_div(size<0>(params.problem_shape), kBlockQ), size<4,0>(params.problem_shape), size<4,1>(params.problem_shape));
|
||||||
return grid;
|
return grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ struct FmhaKernelBwdSumOdO {
|
|||||||
auto ptr_lse_bhq = ptr_lse_bh + idx_q * get<0>(params.stride_lse);
|
auto ptr_lse_bhq = ptr_lse_bh + idx_q * get<0>(params.stride_lse);
|
||||||
auto ptr_scaled_lse_bhq = ptr_scaled_lse_bh + idx_q * get<0>(params.stride_scaled_lse);
|
auto ptr_scaled_lse_bhq = ptr_scaled_lse_bh + idx_q * get<0>(params.stride_scaled_lse);
|
||||||
|
|
||||||
for (int idx_d = threadIdx.x * kElementsPerLoad; idx_d < get<2>(params.problem_shape); idx_d += kElementsPerLoad * kNumThreadsD) {
|
for (int idx_d = threadIdx.x * kElementsPerLoad; idx_d < get<3>(params.problem_shape); idx_d += kElementsPerLoad * kNumThreadsD) {
|
||||||
Element value_O[kElementsPerLoad];
|
Element value_O[kElementsPerLoad];
|
||||||
Element value_dO[kElementsPerLoad];
|
Element value_dO[kElementsPerLoad];
|
||||||
|
|
||||||
|
|||||||
@@ -344,12 +344,12 @@ struct Sm100FmhaBwdKernelTmaWarpSpecialized {
|
|||||||
|
|
||||||
|
|
||||||
static bool can_implement(Arguments const& args) {
|
static bool can_implement(Arguments const& args) {
|
||||||
auto [Q, K, D, HB] = args.problem_shape;
|
auto [Q, K, D, D_VO, HB] = args.problem_shape;
|
||||||
auto [H, B] = HB;
|
auto [H, B] = HB;
|
||||||
if (Q <= 0 || K <= 0 || D <= 0 || H <= 0 || B <= 0) {
|
if (Q <= 0 || K <= 0 || D <= 0 || D_VO <= 0 || H <= 0 || B <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (D % Alignment != 0) {
|
if (D % Alignment != 0 || D_VO % Alignment != 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -362,7 +362,7 @@ struct Sm100FmhaBwdKernelTmaWarpSpecialized {
|
|||||||
|
|
||||||
|
|
||||||
static Params to_underlying_arguments(Arguments const& args, void*) {
|
static Params to_underlying_arguments(Arguments const& args, void*) {
|
||||||
auto [Q_, K_, D, HB] = args.problem_shape;
|
auto [Q_, K_, D, D_VO, HB] = args.problem_shape;
|
||||||
int Q = Q_;
|
int Q = Q_;
|
||||||
int K = K_;
|
int K = K_;
|
||||||
|
|
||||||
@@ -381,7 +381,7 @@ struct Sm100FmhaBwdKernelTmaWarpSpecialized {
|
|||||||
}, /*workspace=*/nullptr);
|
}, /*workspace=*/nullptr);
|
||||||
|
|
||||||
auto params_vdo = CollectiveMmaVDO::to_underlying_arguments(
|
auto params_vdo = CollectiveMmaVDO::to_underlying_arguments(
|
||||||
make_shape(K, Q, D, HB),
|
make_shape(K, Q, D_VO, HB),
|
||||||
typename CollectiveMmaVDO::Arguments {
|
typename CollectiveMmaVDO::Arguments {
|
||||||
args.mainloop.ptr_v, args.mainloop.stride_v,
|
args.mainloop.ptr_v, args.mainloop.stride_v,
|
||||||
args.mainloop.ptr_do, args.mainloop.stride_do,
|
args.mainloop.ptr_do, args.mainloop.stride_do,
|
||||||
@@ -446,21 +446,21 @@ struct Sm100FmhaBwdKernelTmaWarpSpecialized {
|
|||||||
PipelineLoadComputeSumOdO& pipeline_load_compute_sum_odo,
|
PipelineLoadComputeSumOdO& pipeline_load_compute_sum_odo,
|
||||||
typename PipelineLoadComputeSumOdO::PipelineState& pipeline_load_compute_sum_odo_producer_state) {
|
typename PipelineLoadComputeSumOdO::PipelineState& pipeline_load_compute_sum_odo_producer_state) {
|
||||||
|
|
||||||
auto [Q, K, D, HB] = problem_shape;
|
auto [Q, K, D, D_VO, HB] = problem_shape;
|
||||||
|
|
||||||
using X = Underscore;
|
using X = Underscore;
|
||||||
|
|
||||||
uint16_t mcast_mask = 0;
|
uint16_t mcast_mask = 0;
|
||||||
|
|
||||||
auto mK_in = mainloop_params.tma_load_k.get_tma_tensor(make_shape(K, D, HB));
|
auto mK_in = mainloop_params.tma_load_k.get_tma_tensor(make_shape(K, D, HB));
|
||||||
auto mV_in = mainloop_params.tma_load_v.get_tma_tensor(make_shape(K, D, HB));
|
auto mV_in = mainloop_params.tma_load_v.get_tma_tensor(make_shape(K, D_VO, HB));
|
||||||
auto mQ_in = mainloop_params.tma_load_q.get_tma_tensor(make_shape(Q, D, HB));
|
auto mQ_in = mainloop_params.tma_load_q.get_tma_tensor(make_shape(Q, D, HB));
|
||||||
auto mDO_in = mainloop_params.tma_load_do.get_tma_tensor(make_shape(Q, D, HB));
|
auto mDO_in = mainloop_params.tma_load_do.get_tma_tensor(make_shape(Q, D_VO, HB));
|
||||||
|
|
||||||
auto mK = domain_offset(select<1,2,3>(blk_offset), mK_in);
|
auto mK = domain_offset(select<1,2,4>(blk_offset), mK_in);
|
||||||
auto mV = domain_offset(select<1,2,3>(blk_offset), mV_in);
|
auto mV = domain_offset(select<1,3,4>(blk_offset), mV_in);
|
||||||
auto mQ = domain_offset(select<0,2,3>(blk_offset), mQ_in);
|
auto mQ = domain_offset(select<0,2,4>(blk_offset), mQ_in);
|
||||||
auto mDO = domain_offset(select<0,2,3>(blk_offset), mDO_in);
|
auto mDO = domain_offset(select<0,3,4>(blk_offset), mDO_in);
|
||||||
|
|
||||||
auto gK = local_tile(mK, TileShapeKQ{}, make_coord(_,_,_), Step<_1, X, _1>{});
|
auto gK = local_tile(mK, TileShapeKQ{}, make_coord(_,_,_), Step<_1, X, _1>{});
|
||||||
auto gQ = local_tile(mQ, TileShapeKQ{}, make_coord(_,_,_), Step<X, _1, _1>{});
|
auto gQ = local_tile(mQ, TileShapeKQ{}, make_coord(_,_,_), Step<X, _1, _1>{});
|
||||||
@@ -495,7 +495,7 @@ struct Sm100FmhaBwdKernelTmaWarpSpecialized {
|
|||||||
|
|
||||||
// set up lse and sum_odo
|
// set up lse and sum_odo
|
||||||
|
|
||||||
auto [blk_coord_q, blk_coord_k, blk_coord_d, blk_coord_batch] = blk_coord;
|
auto [blk_coord_q, blk_coord_k, blk_coord_d, blk_coord_dv, blk_coord_batch] = blk_coord;
|
||||||
|
|
||||||
pipeline_load_mma_q.producer_acquire(pipeline_load_mma_q_producer_state);
|
pipeline_load_mma_q.producer_acquire(pipeline_load_mma_q_producer_state);
|
||||||
auto tma_barrier = pipeline_load_mma_q.producer_get_barrier(pipeline_load_mma_q_producer_state);
|
auto tma_barrier = pipeline_load_mma_q.producer_get_barrier(pipeline_load_mma_q_producer_state);
|
||||||
@@ -681,7 +681,7 @@ struct Sm100FmhaBwdKernelTmaWarpSpecialized {
|
|||||||
PipelineMmaComputeDKDV& pipeline_mma_compute_dkdv,
|
PipelineMmaComputeDKDV& pipeline_mma_compute_dkdv,
|
||||||
typename PipelineMmaComputeDKDV::PipelineState& pipeline_mma_compute_dkdv_producer_state) {
|
typename PipelineMmaComputeDKDV::PipelineState& pipeline_mma_compute_dkdv_producer_state) {
|
||||||
|
|
||||||
auto [Q, K, D, HB] = problem_shape;
|
auto [Q, K, D, D_VO, HB] = problem_shape;
|
||||||
|
|
||||||
auto sQ = make_tensor(make_smem_ptr(shared_tensors.smem_q.begin()), SmemLayoutQ{});
|
auto sQ = make_tensor(make_smem_ptr(shared_tensors.smem_q.begin()), SmemLayoutQ{});
|
||||||
auto sK = make_tensor(make_smem_ptr(shared_tensors.smem_k.begin()), SmemLayoutK{});
|
auto sK = make_tensor(make_smem_ptr(shared_tensors.smem_k.begin()), SmemLayoutK{});
|
||||||
@@ -974,11 +974,11 @@ struct Sm100FmhaBwdKernelTmaWarpSpecialized {
|
|||||||
MainloopArguments const& mainloop_args,
|
MainloopArguments const& mainloop_args,
|
||||||
EpilogueArguments const& epilogue_args) {
|
EpilogueArguments const& epilogue_args) {
|
||||||
|
|
||||||
auto [Q, K, D, HB] = problem_shape;
|
auto [Q, K, D, D_VO, HB] = problem_shape;
|
||||||
auto [blk_coord_q, blk_coord_k, blk_coord_d, blk_coord_batch] = blk_coord;
|
auto [blk_coord_q, blk_coord_k, blk_coord_d, blk_coord_dv, blk_coord_batch] = blk_coord;
|
||||||
|
|
||||||
auto mDK_in = make_tensor(make_gmem_ptr(epilogue_args.ptr_dk), make_shape(K, TileShapeDQK{}, HB), epilogue_args.stride_dk);
|
auto mDK_in = make_tensor(make_gmem_ptr(epilogue_args.ptr_dk), make_shape(K, TileShapeDQK{}, HB), epilogue_args.stride_dk);
|
||||||
auto mDK = domain_offset(select<1,2,3>(blk_offset), mDK_in);
|
auto mDK = domain_offset(select<1,2,4>(blk_offset), mDK_in);
|
||||||
auto gDK = local_tile(mDK, TileShapeDSQ{}, make_coord(_,_,_), Step<_1, _1, X>{})
|
auto gDK = local_tile(mDK, TileShapeDSQ{}, make_coord(_,_,_), Step<_1, _1, X>{})
|
||||||
(_, _, blk_coord_k, _0{}, blk_coord_batch);
|
(_, _, blk_coord_k, _0{}, blk_coord_batch);
|
||||||
|
|
||||||
@@ -988,7 +988,7 @@ struct Sm100FmhaBwdKernelTmaWarpSpecialized {
|
|||||||
);
|
);
|
||||||
|
|
||||||
auto mDV_in = make_tensor(make_gmem_ptr(epilogue_args.ptr_dv), make_shape(K, TileShapeDVO{}, HB), epilogue_args.stride_dv);
|
auto mDV_in = make_tensor(make_gmem_ptr(epilogue_args.ptr_dv), make_shape(K, TileShapeDVO{}, HB), epilogue_args.stride_dv);
|
||||||
auto mDV = domain_offset(select<1,2,3>(blk_offset), mDV_in);
|
auto mDV = domain_offset(select<1,3,4>(blk_offset), mDV_in);
|
||||||
auto gDV = local_tile(mDV, TileShapePDO{}, make_coord(_,_,_), Step<_1, _1, X>{})
|
auto gDV = local_tile(mDV, TileShapePDO{}, make_coord(_,_,_), Step<_1, _1, X>{})
|
||||||
(_, _, blk_coord_k, _0{}, blk_coord_batch);
|
(_, _, blk_coord_k, _0{}, blk_coord_batch);
|
||||||
|
|
||||||
@@ -1003,7 +1003,7 @@ struct Sm100FmhaBwdKernelTmaWarpSpecialized {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = threadIdx.x; i < size(gDV); i += blockDim.x) {
|
for (int i = threadIdx.x; i < size(gDV); i += blockDim.x) {
|
||||||
if (elem_less(cDV(i), select<1,2>(problem_shape))) {
|
if (elem_less(cDV(i), select<1,3>(problem_shape))) {
|
||||||
gDV(i) = Element(0);
|
gDV(i) = Element(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1020,8 +1020,8 @@ struct Sm100FmhaBwdKernelTmaWarpSpecialized {
|
|||||||
PipelineMmaComputeDKDV& pipeline_mma_compute_dkdv,
|
PipelineMmaComputeDKDV& pipeline_mma_compute_dkdv,
|
||||||
typename PipelineMmaComputeDKDV::PipelineState& pipeline_mma_compute_dkdv_consumer_state) {
|
typename PipelineMmaComputeDKDV::PipelineState& pipeline_mma_compute_dkdv_consumer_state) {
|
||||||
|
|
||||||
auto [Q, K, D, HB] = problem_shape;
|
auto [Q, K, D, D_VO, HB] = problem_shape;
|
||||||
auto [blk_coord_q, blk_coord_k, blk_coord_d, blk_coord_batch] = blk_coord;
|
auto [blk_coord_q, blk_coord_k, blk_coord_d, blk_coord_dv, blk_coord_batch] = blk_coord;
|
||||||
|
|
||||||
auto load_op = SM100_TMEM_LOAD_32dp32b16x{};
|
auto load_op = SM100_TMEM_LOAD_32dp32b16x{};
|
||||||
|
|
||||||
@@ -1029,7 +1029,7 @@ struct Sm100FmhaBwdKernelTmaWarpSpecialized {
|
|||||||
tDKtDK.data() = TmemAllocation::kDK;
|
tDKtDK.data() = TmemAllocation::kDK;
|
||||||
|
|
||||||
auto mDK_in = make_tensor(make_gmem_ptr(epilogue_args.ptr_dk), make_shape(K, TileShapeDQK{}, HB), epilogue_args.stride_dk);
|
auto mDK_in = make_tensor(make_gmem_ptr(epilogue_args.ptr_dk), make_shape(K, TileShapeDQK{}, HB), epilogue_args.stride_dk);
|
||||||
auto mDK = domain_offset(select<1,2,3>(blk_offset), mDK_in);
|
auto mDK = domain_offset(select<1,2,4>(blk_offset), mDK_in);
|
||||||
auto gDK = local_tile(mDK, TileShapeDSQ{}, make_coord(_,_,_), Step<_1, _1, X>{})
|
auto gDK = local_tile(mDK, TileShapeDSQ{}, make_coord(_,_,_), Step<_1, _1, X>{})
|
||||||
(_, _, blk_coord_k, _0{}, blk_coord_batch);
|
(_, _, blk_coord_k, _0{}, blk_coord_batch);
|
||||||
|
|
||||||
@@ -1065,7 +1065,7 @@ struct Sm100FmhaBwdKernelTmaWarpSpecialized {
|
|||||||
tDVtDV.data() = TmemAllocation::kDV;
|
tDVtDV.data() = TmemAllocation::kDV;
|
||||||
|
|
||||||
auto mDV_in = make_tensor(make_gmem_ptr(epilogue_args.ptr_dv), make_shape(K, TileShapeDVO{}, HB), epilogue_args.stride_dv);
|
auto mDV_in = make_tensor(make_gmem_ptr(epilogue_args.ptr_dv), make_shape(K, TileShapeDVO{}, HB), epilogue_args.stride_dv);
|
||||||
auto mDV = domain_offset(select<1,2,3>(blk_offset), mDV_in);
|
auto mDV = domain_offset(select<1,3,4>(blk_offset), mDV_in);
|
||||||
auto gDV = local_tile(mDV, TileShapePDO{}, make_coord(_,_,_), Step<_1, _1, X>{})
|
auto gDV = local_tile(mDV, TileShapePDO{}, make_coord(_,_,_), Step<_1, _1, X>{})
|
||||||
(_, _, blk_coord_k, _0{}, blk_coord_batch);
|
(_, _, blk_coord_k, _0{}, blk_coord_batch);
|
||||||
|
|
||||||
@@ -1088,7 +1088,7 @@ struct Sm100FmhaBwdKernelTmaWarpSpecialized {
|
|||||||
cute::copy(tiled_t2r_dv, tTR_tDV, tTR_rDV);
|
cute::copy(tiled_t2r_dv, tTR_tDV, tTR_rDV);
|
||||||
|
|
||||||
// store tDVgDV
|
// store tDVgDV
|
||||||
store(tTR_gDV, tTR_rDV, tTR_cDV, select<1,2>(problem_shape));
|
store(tTR_gDV, tTR_rDV, tTR_cDV, select<1,3>(problem_shape));
|
||||||
|
|
||||||
cutlass::arch::fence_view_async_tmem_load();
|
cutlass::arch::fence_view_async_tmem_load();
|
||||||
pipeline_mma_compute_dkdv.consumer_release(pipeline_mma_compute_dkdv_consumer_state);
|
pipeline_mma_compute_dkdv.consumer_release(pipeline_mma_compute_dkdv_consumer_state);
|
||||||
@@ -1140,7 +1140,7 @@ struct Sm100FmhaBwdKernelTmaWarpSpecialized {
|
|||||||
typename PipelineMmaComputeDKDV::PipelineState& pipeline_mma_compute_dkdv_consumer_state) {
|
typename PipelineMmaComputeDKDV::PipelineState& pipeline_mma_compute_dkdv_consumer_state) {
|
||||||
|
|
||||||
|
|
||||||
auto [Q, K, D, HB] = problem_shape;
|
auto [Q, K, D, D_VO, HB] = problem_shape;
|
||||||
|
|
||||||
// in tmem, S & P overlap
|
// in tmem, S & P overlap
|
||||||
// and dP and dQ overlap
|
// and dP and dQ overlap
|
||||||
@@ -1396,9 +1396,9 @@ struct Sm100FmhaBwdKernelTmaWarpSpecialized {
|
|||||||
|
|
||||||
using X = Underscore;
|
using X = Underscore;
|
||||||
|
|
||||||
auto [Q, K, D, HB] = problem_shape;
|
auto [Q, K, D, D_VO, HB] = problem_shape;
|
||||||
|
|
||||||
auto [blk_coord_q, blk_coord_k, blk_coord_d, blk_coord_batch] = blk_coord;
|
auto [blk_coord_q, blk_coord_k, blk_coord_d, blk_coord_dv, blk_coord_batch] = blk_coord;
|
||||||
|
|
||||||
// must match TileShapeDQ
|
// must match TileShapeDQ
|
||||||
auto load_op = SM100_TMEM_LOAD_32dp32b32x{};
|
auto load_op = SM100_TMEM_LOAD_32dp32b32x{};
|
||||||
@@ -1676,7 +1676,7 @@ struct Sm100FmhaBwdKernelTmaWarpSpecialized {
|
|||||||
|
|
||||||
pipeline_init_wait(size(ClusterShape{}));
|
pipeline_init_wait(size(ClusterShape{}));
|
||||||
|
|
||||||
auto blk_coord = make_coord(_0{}, blockIdx.x, _0{}, make_coord(blockIdx.y, blockIdx.z));
|
auto blk_coord = make_coord(_0{}, blockIdx.x, _0{}, _0{}, make_coord(blockIdx.y, blockIdx.z));
|
||||||
auto [problem_shape, blk_offset] = apply_variable_length_offset(
|
auto [problem_shape, blk_offset] = apply_variable_length_offset(
|
||||||
params.problem_shape,
|
params.problem_shape,
|
||||||
blk_coord
|
blk_coord
|
||||||
@@ -1809,7 +1809,7 @@ struct Sm100FmhaBwdKernelTmaWarpSpecialized {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static dim3 get_grid_shape(Params const& params) {
|
static dim3 get_grid_shape(Params const& params) {
|
||||||
auto [Q, K, D, HB] = params.problem_shape;
|
auto [Q, K, D, D_VO, HB] = params.problem_shape;
|
||||||
auto [H, B] = HB;
|
auto [H, B] = HB;
|
||||||
dim3 grid(ceil_div(K, TileShapeK{}), H, B);
|
dim3 grid(ceil_div(K, TileShapeK{}), H, B);
|
||||||
return grid;
|
return grid;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -33,7 +33,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "cute/tensor.hpp"
|
#include "cute/tensor.hpp"
|
||||||
|
#include "collective/fmha_fusion.hpp"
|
||||||
|
|
||||||
|
using namespace cutlass::fmha::collective;
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
template<
|
template<
|
||||||
@@ -61,20 +63,20 @@ void __global__ fmha_bwd_reference_dQ_kernel(
|
|||||||
|
|
||||||
ElementAccumulator softmax_scale = 1.0 / sqrt(ElementAccumulator(size<2>(problem_shape_in)));
|
ElementAccumulator softmax_scale = 1.0 / sqrt(ElementAccumulator(size<2>(problem_shape_in)));
|
||||||
|
|
||||||
for (int idx_L = blockIdx.y; idx_L < size<3>(problem_shape_in); idx_L += gridDim.y) {
|
for (int idx_L = blockIdx.y; idx_L < size<4>(problem_shape_in); idx_L += gridDim.y) {
|
||||||
auto [problem_shape, offset] = apply_variable_length_offset(
|
auto [problem_shape, offset] = apply_variable_length_offset(
|
||||||
problem_shape_in,
|
problem_shape_in,
|
||||||
make_coord(_0{}, _0{}, _0{}, idx2crd(idx_L, get<3>(problem_shape_in)))
|
make_coord(_0{}, _0{}, _0{}, _0{},idx2crd(idx_L, get<4>(problem_shape_in)))
|
||||||
);
|
);
|
||||||
// problem_shape = problem_shape_in;
|
// problem_shape = problem_shape_in;
|
||||||
// offset = repeat_like(problem_shape_in, _0{});
|
// offset = repeat_like(problem_shape_in, _0{});
|
||||||
auto mQ = domain_offset(select<0,2,3>(offset), mQ_in);
|
auto mQ = domain_offset(select<0,2,4>(offset), mQ_in);
|
||||||
auto mK = domain_offset(select<1,2,3>(offset), mK_in);
|
auto mK = domain_offset(select<1,2,4>(offset), mK_in);
|
||||||
auto mV = domain_offset(select<1,2,3>(offset), mV_in);
|
auto mV = domain_offset(select<1,3,4>(offset), mV_in);
|
||||||
auto mO = domain_offset(select<0,2,3>(offset), mO_in);
|
auto mO = domain_offset(select<0,3,4>(offset), mO_in);
|
||||||
auto mLSE = domain_offset(select<0,3>(offset), mLSE_in);
|
auto mLSE = domain_offset(select<0,4>(offset), mLSE_in);
|
||||||
auto mDO = domain_offset(select<0,2,3>(offset), mDO_in);
|
auto mDO = domain_offset(select<0,3,4>(offset), mDO_in);
|
||||||
auto mDQ = domain_offset(select<0,2,3>(offset), mDQ_in);
|
auto mDQ = domain_offset(select<0,2,4>(offset), mDQ_in);
|
||||||
for (int idx_Q = blockIdx.x; idx_Q < size<0>(problem_shape); idx_Q += gridDim.x) {
|
for (int idx_Q = blockIdx.x; idx_Q < size<0>(problem_shape); idx_Q += gridDim.x) {
|
||||||
for (int idx_K = threadIdx.x; idx_K < size<1>(problem_shape); idx_K += blockDim.x) {
|
for (int idx_K = threadIdx.x; idx_K < size<1>(problem_shape); idx_K += blockDim.x) {
|
||||||
ElementAccumulator acc_qk = 0;
|
ElementAccumulator acc_qk = 0;
|
||||||
@@ -82,10 +84,15 @@ void __global__ fmha_bwd_reference_dQ_kernel(
|
|||||||
ElementAccumulator acc_doo = 0;
|
ElementAccumulator acc_doo = 0;
|
||||||
for (int idx_D0 = 0; idx_D0 < size<2>(problem_shape); idx_D0++) {
|
for (int idx_D0 = 0; idx_D0 < size<2>(problem_shape); idx_D0++) {
|
||||||
acc_qk += mQ(idx_Q, idx_D0, idx_L) * mK(idx_K, idx_D0, idx_L);
|
acc_qk += mQ(idx_Q, idx_D0, idx_L) * mK(idx_K, idx_D0, idx_L);
|
||||||
acc_dov += mDO(idx_Q, idx_D0, idx_L) * mV(idx_K, idx_D0, idx_L);
|
// acc_dov += mDO(idx_Q, idx_D0, idx_L) * mV(idx_K, idx_D0, idx_L);
|
||||||
acc_doo += mDO(idx_Q, idx_D0, idx_L) * mO(idx_Q, idx_D0, idx_L);
|
// acc_doo += mDO(idx_Q, idx_D0, idx_L) * mO(idx_Q, idx_D0, idx_L);
|
||||||
} // for idx_D0
|
} // for idx_D0
|
||||||
|
|
||||||
|
for (int idx_D1 = 0; idx_D1 < size<3>(problem_shape); idx_D1++) {
|
||||||
|
acc_dov += mDO(idx_Q, idx_D1, idx_L) * mV(idx_K, idx_D1, idx_L);
|
||||||
|
acc_doo += mDO(idx_Q, idx_D1, idx_L) * mO(idx_Q, idx_D1, idx_L);
|
||||||
|
}
|
||||||
|
|
||||||
auto id = make_identity_tensor(make_shape(1, 1));
|
auto id = make_identity_tensor(make_shape(1, 1));
|
||||||
auto frag = make_tensor<ElementAccumulator>(Shape<_1, _1>{});
|
auto frag = make_tensor<ElementAccumulator>(Shape<_1, _1>{});
|
||||||
frag(0) = acc_qk;
|
frag(0) = acc_qk;
|
||||||
@@ -135,20 +142,20 @@ void __global__ fmha_bwd_reference_dK_kernel(
|
|||||||
|
|
||||||
ElementAccumulator softmax_scale = 1.0 / sqrt(ElementAccumulator(size<2>(problem_shape_in)));
|
ElementAccumulator softmax_scale = 1.0 / sqrt(ElementAccumulator(size<2>(problem_shape_in)));
|
||||||
|
|
||||||
for (int idx_L = blockIdx.y; idx_L < size<3>(problem_shape_in); idx_L += gridDim.y) {
|
for (int idx_L = blockIdx.y; idx_L < size<4>(problem_shape_in); idx_L += gridDim.y) {
|
||||||
auto [problem_shape, offset] = apply_variable_length_offset(
|
auto [problem_shape, offset] = apply_variable_length_offset(
|
||||||
problem_shape_in,
|
problem_shape_in,
|
||||||
make_coord(_0{}, _0{}, _0{}, idx2crd(idx_L, get<3>(problem_shape_in)))
|
make_coord(_0{}, _0{}, _0{}, _0{}, idx2crd(idx_L, get<4>(problem_shape_in)))
|
||||||
);
|
);
|
||||||
// problem_shape = problem_shape_in;
|
// problem_shape = problem_shape_in;
|
||||||
// offset = repeat_like(problem_shape_in, _0{});
|
// offset = repeat_like(problem_shape_in, _0{});
|
||||||
auto mQ = domain_offset(select<0,2,3>(offset), mQ_in);
|
auto mQ = domain_offset(select<0,2,4>(offset), mQ_in);
|
||||||
auto mK = domain_offset(select<1,2,3>(offset), mK_in);
|
auto mK = domain_offset(select<1,2,4>(offset), mK_in);
|
||||||
auto mV = domain_offset(select<1,2,3>(offset), mV_in);
|
auto mV = domain_offset(select<1,3,4>(offset), mV_in);
|
||||||
auto mO = domain_offset(select<0,2,3>(offset), mO_in);
|
auto mO = domain_offset(select<0,3,4>(offset), mO_in);
|
||||||
auto mLSE = domain_offset(select<0,3>(offset), mLSE_in);
|
auto mLSE = domain_offset(select<0,4>(offset), mLSE_in);
|
||||||
auto mDO = domain_offset(select<0,2,3>(offset), mDO_in);
|
auto mDO = domain_offset(select<0,3,4>(offset), mDO_in);
|
||||||
auto mDK = domain_offset(select<1,2,3>(offset), mDK_in);
|
auto mDK = domain_offset(select<1,2,4>(offset), mDK_in);
|
||||||
for (int idx_K = blockIdx.x; idx_K < size<1>(problem_shape); idx_K += gridDim.x) {
|
for (int idx_K = blockIdx.x; idx_K < size<1>(problem_shape); idx_K += gridDim.x) {
|
||||||
for (int idx_Q = threadIdx.x; idx_Q < size<0>(problem_shape); idx_Q += blockDim.x) {
|
for (int idx_Q = threadIdx.x; idx_Q < size<0>(problem_shape); idx_Q += blockDim.x) {
|
||||||
ElementAccumulator acc_qk = 0;
|
ElementAccumulator acc_qk = 0;
|
||||||
@@ -156,10 +163,14 @@ void __global__ fmha_bwd_reference_dK_kernel(
|
|||||||
ElementAccumulator acc_doo = 0;
|
ElementAccumulator acc_doo = 0;
|
||||||
for (int idx_D0 = 0; idx_D0 < size<2>(problem_shape); idx_D0++) {
|
for (int idx_D0 = 0; idx_D0 < size<2>(problem_shape); idx_D0++) {
|
||||||
acc_qk += mQ(idx_Q, idx_D0, idx_L) * mK(idx_K, idx_D0, idx_L);
|
acc_qk += mQ(idx_Q, idx_D0, idx_L) * mK(idx_K, idx_D0, idx_L);
|
||||||
acc_dov += mDO(idx_Q, idx_D0, idx_L) * mV(idx_K, idx_D0, idx_L);
|
// acc_dov += mDO(idx_Q, idx_D0, idx_L) * mV(idx_K, idx_D0, idx_L);
|
||||||
acc_doo += mDO(idx_Q, idx_D0, idx_L) * mO(idx_Q, idx_D0, idx_L);
|
// acc_doo += mDO(idx_Q, idx_D0, idx_L) * mO(idx_Q, idx_D0, idx_L);
|
||||||
} // for idx_D0
|
} // for idx_D0
|
||||||
|
|
||||||
|
for (int idx_D1 = 0; idx_D1 < size<3>(problem_shape); idx_D1++) {
|
||||||
|
acc_dov += mDO(idx_Q, idx_D1, idx_L) * mV(idx_K, idx_D1, idx_L);
|
||||||
|
acc_doo += mDO(idx_Q, idx_D1, idx_L) * mO(idx_Q, idx_D1, idx_L);
|
||||||
|
}
|
||||||
auto id = make_identity_tensor(make_shape(1, 1));
|
auto id = make_identity_tensor(make_shape(1, 1));
|
||||||
auto frag = make_tensor<ElementAccumulator>(Shape<_1, _1>{});
|
auto frag = make_tensor<ElementAccumulator>(Shape<_1, _1>{});
|
||||||
frag(0) = acc_qk;
|
frag(0) = acc_qk;
|
||||||
@@ -209,20 +220,20 @@ void __global__ fmha_bwd_reference_dV_kernel(
|
|||||||
|
|
||||||
ElementAcc softmax_scale = 1.0 / sqrt(ElementAcc(size<2>(problem_shape_in)));
|
ElementAcc softmax_scale = 1.0 / sqrt(ElementAcc(size<2>(problem_shape_in)));
|
||||||
|
|
||||||
for (int idx_L = blockIdx.y; idx_L < size<3>(problem_shape_in); idx_L += gridDim.y) {
|
for (int idx_L = blockIdx.y; idx_L < size<4>(problem_shape_in); idx_L += gridDim.y) {
|
||||||
auto [problem_shape, offset] = apply_variable_length_offset(
|
auto [problem_shape, offset] = apply_variable_length_offset(
|
||||||
problem_shape_in,
|
problem_shape_in,
|
||||||
make_coord(_0{}, _0{}, _0{}, idx2crd(idx_L, get<3>(problem_shape_in)))
|
make_coord(_0{}, _0{}, _0{}, _0{}, idx2crd(idx_L, get<4>(problem_shape_in)))
|
||||||
);
|
);
|
||||||
// problem_shape = problem_shape_in;
|
// problem_shape = problem_shape_in;
|
||||||
// offset = repeat_like(problem_shape_in, _0{});
|
// offset = repeat_like(problem_shape_in, _0{});
|
||||||
auto mQ = domain_offset(select<0,2,3>(offset), mQ_in);
|
auto mQ = domain_offset(select<0,2,4>(offset), mQ_in);
|
||||||
auto mK = domain_offset(select<1,2,3>(offset), mK_in);
|
auto mK = domain_offset(select<1,2,4>(offset), mK_in);
|
||||||
auto mV = domain_offset(select<1,2,3>(offset), mV_in);
|
auto mV = domain_offset(select<1,3,4>(offset), mV_in);
|
||||||
auto mO = domain_offset(select<0,2,3>(offset), mO_in);
|
auto mO = domain_offset(select<0,3,4>(offset), mO_in);
|
||||||
auto mLSE = domain_offset(select<0,3>(offset), mLSE_in);
|
auto mLSE = domain_offset(select<0,4>(offset), mLSE_in);
|
||||||
auto mDO = domain_offset(select<0,2,3>(offset), mDO_in);
|
auto mDO = domain_offset(select<0,3,4>(offset), mDO_in);
|
||||||
auto mDV = domain_offset(select<1,2,3>(offset), mDV_in);
|
auto mDV = domain_offset(select<1,3,4>(offset), mDV_in);
|
||||||
for (int idx_K = blockIdx.x; idx_K < size<1>(problem_shape); idx_K += gridDim.x) {
|
for (int idx_K = blockIdx.x; idx_K < size<1>(problem_shape); idx_K += gridDim.x) {
|
||||||
for (int idx_Q = threadIdx.x; idx_Q < size<0>(problem_shape); idx_Q += blockDim.x) {
|
for (int idx_Q = threadIdx.x; idx_Q < size<0>(problem_shape); idx_Q += blockDim.x) {
|
||||||
ElementAcc acc_qk = 0;
|
ElementAcc acc_qk = 0;
|
||||||
@@ -244,7 +255,7 @@ void __global__ fmha_bwd_reference_dV_kernel(
|
|||||||
|
|
||||||
__syncthreads();
|
__syncthreads();
|
||||||
|
|
||||||
for (int idx_D = threadIdx.x; idx_D < size<2>(problem_shape); idx_D += blockDim.x) {
|
for (int idx_D = threadIdx.x; idx_D < size<3>(problem_shape); idx_D += blockDim.x) {
|
||||||
ElementAcc acc = 0;
|
ElementAcc acc = 0;
|
||||||
for (int idx_Q = 0; idx_Q < size<0>(problem_shape); idx_Q++) {
|
for (int idx_Q = 0; idx_Q < size<0>(problem_shape); idx_Q++) {
|
||||||
ElementAcc rS = static_cast<Element>(mS[idx_Q]);
|
ElementAcc rS = static_cast<Element>(mS[idx_Q]);
|
||||||
|
|||||||
@@ -62,19 +62,20 @@ void __global__ fmha_reference_kernel(
|
|||||||
ElementAccumulator softmax_scale = static_cast<ElementAccumulator>(1.0 / sqrt(1.0 * size<1>(mQ)));
|
ElementAccumulator softmax_scale = static_cast<ElementAccumulator>(1.0 / sqrt(1.0 * size<1>(mQ)));
|
||||||
|
|
||||||
auto id = make_identity_tensor(make_shape(1, 1));
|
auto id = make_identity_tensor(make_shape(1, 1));
|
||||||
for (int idx_L = blockIdx.y; idx_L < size<3>(problem_shape_in); idx_L += gridDim.y) {
|
|
||||||
|
for (int idx_L = blockIdx.y; idx_L < size<4>(problem_shape_in); idx_L += gridDim.y) {
|
||||||
for (int idx_Q = blockIdx.x; idx_Q < size<0>(problem_shape_in); idx_Q += gridDim.x) {
|
for (int idx_Q = blockIdx.x; idx_Q < size<0>(problem_shape_in); idx_Q += gridDim.x) {
|
||||||
|
|
||||||
auto coord_L = idx2crd(idx_L, shape<3>(problem_shape_in));
|
auto coord_L = idx2crd(idx_L, shape<4>(problem_shape_in));
|
||||||
auto get_coord_in = [&]() {
|
auto get_coord_in = [&]() {
|
||||||
if constexpr (rank_v<decltype(get<2>(ProblemShapeIn{}))> == 2) {
|
if constexpr (rank_v<decltype(get<2>(ProblemShapeIn{}))> == 2) {
|
||||||
return cute::make_tuple(idx_Q, _0{}, cute::make_tuple(_0{}, _0{}), coord_L);
|
return cute::make_tuple(idx_Q, _0{}, cute::make_tuple(_0{}, _0{}), cute::make_tuple(_0{}, _0{}), coord_L);
|
||||||
} else {
|
} else {
|
||||||
return cute::make_tuple(idx_Q, _0{}, _0{}, coord_L);
|
return cute::make_tuple(idx_Q, _0{}, _0{}, _0{}, coord_L);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
auto coord_in = get_coord_in();
|
auto coord_in = get_coord_in();
|
||||||
auto [problem_shape, coord] = apply_variable_length(problem_shape_in, coord_in, get<3,1>(coord_in));
|
auto [problem_shape, coord] = apply_variable_length(problem_shape_in, coord_in, get<4,1>(coord_in));
|
||||||
|
|
||||||
int head_qk = 0;
|
int head_qk = 0;
|
||||||
int head_v = 0;
|
int head_v = 0;
|
||||||
@@ -83,7 +84,7 @@ void __global__ fmha_reference_kernel(
|
|||||||
head_qk = size<2, 0>(problem_shape) + size<2, 1>(problem_shape);
|
head_qk = size<2, 0>(problem_shape) + size<2, 1>(problem_shape);
|
||||||
head_v = size<2, 0>(problem_shape);
|
head_v = size<2, 0>(problem_shape);
|
||||||
} else {
|
} else {
|
||||||
head_qk = size<2>(problem_shape);
|
head_qk = size<3>(problem_shape);
|
||||||
head_v = head_qk;
|
head_v = head_qk;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,6 +158,7 @@ void __global__ fmha_reference_kernel(
|
|||||||
mO(idx_Q + offset_Q, idx_D, idx_L) = static_cast<typename TensorO::value_type>(acc * scale);
|
mO(idx_Q + offset_Q, idx_D, idx_L) = static_cast<typename TensorO::value_type>(acc * scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (threadIdx.x == 0 && mLSE.data() != nullptr) {
|
if (threadIdx.x == 0 && mLSE.data() != nullptr) {
|
||||||
mLSE(idx_Q + offset_Q, idx_L) = log(sum) + softmax_scale * maxS;
|
mLSE(idx_Q + offset_Q, idx_L) = log(sum) + softmax_scale * maxS;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -835,7 +835,7 @@ int run(Options &options, bool host_problem_shapes_available = true)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
std::cout << " Verfication is turned off for this run." << std::endl;
|
std::cout << " Verification is turned off for this run." << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run profiling loop
|
// Run profiling loop
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ gemm_device(ATensor mA, // (Gemm_M, Gemm_K)
|
|||||||
|
|
||||||
// Step 2: The Mainloop.
|
// Step 2: The Mainloop.
|
||||||
|
|
||||||
// Set mma accumlate option to zero so that the first MMA instruction will clear the TMEM accumulator.
|
// Set mma accumulate option to zero so that the first MMA instruction will clear the TMEM accumulator.
|
||||||
tiled_mma.accumulate_ = UMMA::ScaleOut::Zero;
|
tiled_mma.accumulate_ = UMMA::ScaleOut::Zero;
|
||||||
|
|
||||||
// Execute a MmaTile_M x MmaTile_N x GEMM_K GEMM
|
// Execute a MmaTile_M x MmaTile_N x GEMM_K GEMM
|
||||||
@@ -394,7 +394,7 @@ void gemm_host_f16xf16_f32_f32_tnt(TypeA const* device_ptr_A, LayoutA layout_A,
|
|||||||
// In SM100, the MMAs are Cluster-local and perform CTA-level partitioning.
|
// In SM100, the MMAs are Cluster-local and perform CTA-level partitioning.
|
||||||
// Thus, SM90 uses a cta_tiler to extract portions of the Problem for the CTA
|
// Thus, SM90 uses a cta_tiler to extract portions of the Problem for the CTA
|
||||||
// and SM100 uses a mma_tiler to extract portions of the Problem for the MMA.
|
// and SM100 uses a mma_tiler to extract portions of the Problem for the MMA.
|
||||||
// The MMA's partitioning then yeilds the CTA-local work.
|
// The MMA's partitioning then yields the CTA-local work.
|
||||||
|
|
||||||
if (not evenly_divides(shape(mma_tiler), tile_shape(tiled_mma))) {
|
if (not evenly_divides(shape(mma_tiler), tile_shape(tiled_mma))) {
|
||||||
std::cerr << "The MMA Shape should evenly divide the MMA Tiler." << std::endl;
|
std::cerr << "The MMA Shape should evenly divide the MMA Tiler." << std::endl;
|
||||||
|
|||||||
@@ -295,7 +295,7 @@ gemm_device(ATensor mA, // (Gemm_M, Gemm_K)
|
|||||||
|
|
||||||
// Step 2: The Mainloop.
|
// Step 2: The Mainloop.
|
||||||
|
|
||||||
// Set mma accumlate option to zero so that the first MMA instruction will clear the TMEM accumulator.
|
// Set mma accumulate option to zero so that the first MMA instruction will clear the TMEM accumulator.
|
||||||
tiled_mma.accumulate_ = UMMA::ScaleOut::Zero;
|
tiled_mma.accumulate_ = UMMA::ScaleOut::Zero;
|
||||||
|
|
||||||
// Execute a MmaTile_M x MmaTile_N x GEMM_K GEMM
|
// Execute a MmaTile_M x MmaTile_N x GEMM_K GEMM
|
||||||
@@ -433,7 +433,7 @@ void gemm_host_f16xf16_f32_f32_tnt(TypeA const* device_ptr_A, LayoutA layout_A,
|
|||||||
// In SM100, the MMAs are Cluster-local and perform CTA-level partitioning.
|
// In SM100, the MMAs are Cluster-local and perform CTA-level partitioning.
|
||||||
// Thus, SM90 uses a cta_tiler to extract portions of the Problem for the CTA
|
// Thus, SM90 uses a cta_tiler to extract portions of the Problem for the CTA
|
||||||
// and SM100 uses a mma_tiler to extract portions of the Problem for the MMA.
|
// and SM100 uses a mma_tiler to extract portions of the Problem for the MMA.
|
||||||
// The MMA's partitioning then yeilds the CTA-local work.
|
// The MMA's partitioning then yields the CTA-local work.
|
||||||
|
|
||||||
if (not evenly_divides(shape(mma_tiler), tile_shape(tiled_mma))) {
|
if (not evenly_divides(shape(mma_tiler), tile_shape(tiled_mma))) {
|
||||||
std::cerr << "The MMA Shape should evenly divide the MMA Tiler." << std::endl;
|
std::cerr << "The MMA Shape should evenly divide the MMA Tiler." << std::endl;
|
||||||
|
|||||||
@@ -333,7 +333,7 @@ gemm_device(ATensor mA, // (Gemm_M, Gemm_K)
|
|||||||
|
|
||||||
// Step 2: The Mainloop.
|
// Step 2: The Mainloop.
|
||||||
|
|
||||||
// Set mma accumlate option to zero so that the first MMA instruction will clear the TMEM accumulator.
|
// Set mma accumulate option to zero so that the first MMA instruction will clear the TMEM accumulator.
|
||||||
tiled_mma.accumulate_ = UMMA::ScaleOut::Zero;
|
tiled_mma.accumulate_ = UMMA::ScaleOut::Zero;
|
||||||
|
|
||||||
// Execute a MmaTile_M x MmaTile_N x GEMM_K GEMM
|
// Execute a MmaTile_M x MmaTile_N x GEMM_K GEMM
|
||||||
@@ -471,7 +471,7 @@ void gemm_host_f16xf16_f32_f32_tnt(TypeA const* device_ptr_A, LayoutA layout_A,
|
|||||||
// In SM100, the MMAs are Cluster-local and perform CTA-level partitioning.
|
// In SM100, the MMAs are Cluster-local and perform CTA-level partitioning.
|
||||||
// Thus, SM90 uses a cta_tiler to extract portions of the Problem for the CTA
|
// Thus, SM90 uses a cta_tiler to extract portions of the Problem for the CTA
|
||||||
// and SM100 uses a mma_tiler to extract portions of the Problem for the MMA.
|
// and SM100 uses a mma_tiler to extract portions of the Problem for the MMA.
|
||||||
// The MMA's partitioning then yeilds the CTA-local work.
|
// The MMA's partitioning then yields the CTA-local work.
|
||||||
|
|
||||||
if (not evenly_divides(shape(mma_tiler), tile_shape(tiled_mma))) {
|
if (not evenly_divides(shape(mma_tiler), tile_shape(tiled_mma))) {
|
||||||
std::cerr << "The MMA Shape should evenly divide the MMA Tiler." << std::endl;
|
std::cerr << "The MMA Shape should evenly divide the MMA Tiler." << std::endl;
|
||||||
|
|||||||
@@ -328,7 +328,7 @@ gemm_device(ATensor mA, // (Gemm_M, Gemm_K)
|
|||||||
|
|
||||||
// Step 2: The Mainloop.
|
// Step 2: The Mainloop.
|
||||||
|
|
||||||
// Set mma accumlate option to zero so that the first MMA instruction will clear the TMEM accumulator.
|
// Set mma accumulate option to zero so that the first MMA instruction will clear the TMEM accumulator.
|
||||||
tiled_mma.accumulate_ = UMMA::ScaleOut::Zero;
|
tiled_mma.accumulate_ = UMMA::ScaleOut::Zero;
|
||||||
|
|
||||||
// Execute a MmaTile_M x MmaTile_N x GEMM_K GEMM
|
// Execute a MmaTile_M x MmaTile_N x GEMM_K GEMM
|
||||||
@@ -473,7 +473,7 @@ void gemm_host_f16xf16_f32_f32_tnt(TypeA const* device_ptr_A, LayoutA layout_A,
|
|||||||
// In SM100, the MMAs are Cluster-local and perform CTA-level partitioning.
|
// In SM100, the MMAs are Cluster-local and perform CTA-level partitioning.
|
||||||
// Thus, SM90 uses a cta_tiler to extract portions of the Problem for the CTA
|
// Thus, SM90 uses a cta_tiler to extract portions of the Problem for the CTA
|
||||||
// and SM100 uses a mma_tiler to extract portions of the Problem for the MMA.
|
// and SM100 uses a mma_tiler to extract portions of the Problem for the MMA.
|
||||||
// The MMA's partitioning then yeilds the CTA-local work.
|
// The MMA's partitioning then yields the CTA-local work.
|
||||||
|
|
||||||
if (not evenly_divides(shape(mma_tiler), tile_shape(tiled_mma))) {
|
if (not evenly_divides(shape(mma_tiler), tile_shape(tiled_mma))) {
|
||||||
std::cerr << "The MMA Shape should evenly divide the MMA Tiler." << std::endl;
|
std::cerr << "The MMA Shape should evenly divide the MMA Tiler." << std::endl;
|
||||||
|
|||||||
@@ -341,7 +341,7 @@ gemm_device(ATensor mA, // (Gemm_M, Gemm_K)
|
|||||||
|
|
||||||
// Step 2: The Mainloop.
|
// Step 2: The Mainloop.
|
||||||
|
|
||||||
// Set mma accumlate option to zero so that the first MMA instruction will clear the TMEM accumulator.
|
// Set mma accumulate option to zero so that the first MMA instruction will clear the TMEM accumulator.
|
||||||
tiled_mma.accumulate_ = UMMA::ScaleOut::Zero;
|
tiled_mma.accumulate_ = UMMA::ScaleOut::Zero;
|
||||||
|
|
||||||
// Execute a MmaTile_M x MmaTile_N x GEMM_K GEMM
|
// Execute a MmaTile_M x MmaTile_N x GEMM_K GEMM
|
||||||
@@ -527,7 +527,7 @@ void gemm_host_f16xf16_f32_f32_tnt(TypeA const* device_ptr_A, LayoutA layout_A,
|
|||||||
// In SM100, the MMAs are Cluster-local and perform CTA-level partitioning.
|
// In SM100, the MMAs are Cluster-local and perform CTA-level partitioning.
|
||||||
// Thus, SM90 uses a cta_tiler to extract portions of the Problem for the CTA
|
// Thus, SM90 uses a cta_tiler to extract portions of the Problem for the CTA
|
||||||
// and SM100 uses a mma_tiler to extract portions of the Problem for the MMA.
|
// and SM100 uses a mma_tiler to extract portions of the Problem for the MMA.
|
||||||
// The MMA's partitioning then yeilds the CTA-local work.
|
// The MMA's partitioning then yields the CTA-local work.
|
||||||
|
|
||||||
if (not evenly_divides(shape(mma_tiler), tile_shape(tiled_mma))) {
|
if (not evenly_divides(shape(mma_tiler), tile_shape(tiled_mma))) {
|
||||||
std::cerr << "The MMA Shape should evenly divide the MMA Tiler." << std::endl;
|
std::cerr << "The MMA Shape should evenly divide the MMA Tiler." << std::endl;
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
// Construct tiled copy, a tiling of copy atoms.
|
// Construct tiled copy, a tiling of copy atoms.
|
||||||
//
|
//
|
||||||
// Note, this assumes the vector and thread layouts are aligned with contigous data
|
// Note, this assumes the vector and thread layouts are aligned with contiguous data
|
||||||
// in GMEM. Alternative thread layouts are possible but may result in uncoalesced
|
// in GMEM. Alternative thread layouts are possible but may result in uncoalesced
|
||||||
// reads. Alternative value layouts are also possible, though incompatible layouts
|
// reads. Alternative value layouts are also possible, though incompatible layouts
|
||||||
// will result in compile time errors.
|
// will result in compile time errors.
|
||||||
|
|||||||
@@ -90,18 +90,17 @@ If you already know the TV layout you want to use for your tiled copy, CuTe DSL
|
|||||||
# Tile input tensor to thread blocks: ((TileM,TileN),(RestM,RestN))
|
# Tile input tensor to thread blocks: ((TileM,TileN),(RestM,RestN))
|
||||||
gA = cute.zipped_divide(mA, tiler_mn)
|
gA = cute.zipped_divide(mA, tiler_mn)
|
||||||
|
|
||||||
where `tiler_mn` is the tile size per thread block and `tv_layout` is the TV layout which maps
|
Then we can build tiled copy for input and output tensors with `cute.make_tiled_copy_tv` utility, which
|
||||||
thread index and inter-thread index of data array per thread to logical coordinates of elements in
|
infers the tiler and tv layout for the tiled copy automatically, where `tiler` is the tile size per thread
|
||||||
input and output tensors.
|
block and `tv_layout` is the TV layout which maps thread index and inter-thread index of data array per
|
||||||
|
thread to logical coordinates of elements in input and output tensors.
|
||||||
Then we can build tiled copy for input and output tensors with `cute.make_tiled_copy` utility.
|
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
blkA = gA[((None, None), bidx)] # (TileM,TileN)
|
blkA = gA[((None, None), bidx)] # (TileM,TileN)
|
||||||
|
|
||||||
copy_atom_load = cute.make_copy_atom(cute.nvgpu.CopyUniversalOp(), gA.element_type)
|
copy_atom_load = cute.make_copy_atom(cute.nvgpu.CopyUniversalOp(), gA.element_type)
|
||||||
tiled_copy_A = cute.make_tiled_copy(copy_atom_load, tv_layout, tiler_mn)
|
tiled_copy_A = cute.make_tiled_copy_tv(copy_atom_load, thr_layout, val_layout)
|
||||||
|
|
||||||
# get slice of tiled_copy_A for current thread
|
# get slice of tiled_copy_A for current thread
|
||||||
thr_copy_A = tiled_copy_A.get_slice(tidx)
|
thr_copy_A = tiled_copy_A.get_slice(tidx)
|
||||||
@@ -140,8 +139,8 @@ def elementwise_add_kernel(
|
|||||||
gC: cute.Tensor,
|
gC: cute.Tensor,
|
||||||
cC: cute.Tensor, # coordinate tensor
|
cC: cute.Tensor, # coordinate tensor
|
||||||
shape: cute.Shape,
|
shape: cute.Shape,
|
||||||
tv_layout: cute.Layout,
|
thr_layout: cute.Layout,
|
||||||
tiler_mn: cute.Shape,
|
val_layout: cute.Layout,
|
||||||
):
|
):
|
||||||
tidx, _, _ = cute.arch.thread_idx()
|
tidx, _, _ = cute.arch.thread_idx()
|
||||||
bidx, _, _ = cute.arch.block_idx()
|
bidx, _, _ = cute.arch.block_idx()
|
||||||
@@ -165,9 +164,9 @@ def elementwise_add_kernel(
|
|||||||
copy_atom_load = cute.make_copy_atom(cute.nvgpu.CopyUniversalOp(), gA.element_type)
|
copy_atom_load = cute.make_copy_atom(cute.nvgpu.CopyUniversalOp(), gA.element_type)
|
||||||
copy_atom_store = cute.make_copy_atom(cute.nvgpu.CopyUniversalOp(), gC.element_type)
|
copy_atom_store = cute.make_copy_atom(cute.nvgpu.CopyUniversalOp(), gC.element_type)
|
||||||
|
|
||||||
tiled_copy_A = cute.make_tiled_copy(copy_atom_load, tv_layout, tiler_mn)
|
tiled_copy_A = cute.make_tiled_copy_tv(copy_atom_load, thr_layout, val_layout)
|
||||||
tiled_copy_B = cute.make_tiled_copy(copy_atom_load, tv_layout, tiler_mn)
|
tiled_copy_B = cute.make_tiled_copy_tv(copy_atom_load, thr_layout, val_layout)
|
||||||
tiled_copy_C = cute.make_tiled_copy(copy_atom_store, tv_layout, tiler_mn)
|
tiled_copy_C = cute.make_tiled_copy_tv(copy_atom_store, thr_layout, val_layout)
|
||||||
|
|
||||||
thr_copy_A = tiled_copy_A.get_slice(tidx)
|
thr_copy_A = tiled_copy_A.get_slice(tidx)
|
||||||
thr_copy_B = tiled_copy_B.get_slice(tidx)
|
thr_copy_B = tiled_copy_B.get_slice(tidx)
|
||||||
@@ -254,7 +253,7 @@ def elementwise_add(mA, mB, mC, copy_bits: cutlass.Constexpr = 128):
|
|||||||
cC = cute.zipped_divide(idC, tiler=tiler_mn)
|
cC = cute.zipped_divide(idC, tiler=tiler_mn)
|
||||||
print(f"[DSL INFO] coord tensor = {cC.type}")
|
print(f"[DSL INFO] coord tensor = {cC.type}")
|
||||||
|
|
||||||
elementwise_add_kernel(gA, gB, gC, cC, mC.shape, tv_layout, tiler_mn).launch(
|
elementwise_add_kernel(gA, gB, gC, cC, mC.shape, thr_layout, val_layout).launch(
|
||||||
grid=[cute.size(gC, mode=[1]), 1, 1],
|
grid=[cute.size(gC, mode=[1]), 1, 1],
|
||||||
block=[cute.size(tv_layout, mode=[0]), 1, 1],
|
block=[cute.size(tv_layout, mode=[0]), 1, 1],
|
||||||
)
|
)
|
||||||
@@ -362,7 +361,7 @@ def run_elementwise_add(
|
|||||||
workspace_generator=generate_tensors,
|
workspace_generator=generate_tensors,
|
||||||
workspace_count=10,
|
workspace_count=10,
|
||||||
warmup_iterations=warmup_iterations,
|
warmup_iterations=warmup_iterations,
|
||||||
profiling_iterations=iterations,
|
iterations=iterations,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Print execution results
|
# Print execution results
|
||||||
|
|||||||
@@ -353,7 +353,7 @@ def run_elementwise_apply_and_verify(
|
|||||||
current_stream,
|
current_stream,
|
||||||
),
|
),
|
||||||
warmup_iterations=warmup_iterations,
|
warmup_iterations=warmup_iterations,
|
||||||
profiling_iterations=iterations,
|
iterations=iterations,
|
||||||
use_cuda_graphs=True,
|
use_cuda_graphs=True,
|
||||||
stream=current_stream,
|
stream=current_stream,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -32,13 +32,13 @@ from typing import Type, Union, Callable
|
|||||||
|
|
||||||
import torch
|
import torch
|
||||||
import cuda.bindings.driver as cuda
|
import cuda.bindings.driver as cuda
|
||||||
|
import cutlass.cute.testing as testing
|
||||||
import cutlass
|
import cutlass
|
||||||
import cutlass.cute as cute
|
import cutlass.cute as cute
|
||||||
from cutlass.cute.nvgpu import cpasync, warp
|
from cutlass.cute.nvgpu import cpasync, warp
|
||||||
import cutlass.torch as cutlass_torch
|
import cutlass.torch as cutlass_torch
|
||||||
from cutlass.cute.runtime import from_dlpack
|
from cutlass.cute.runtime import from_dlpack
|
||||||
import cutlass.utils.ampere_helpers as sm80_utils
|
import cutlass.utils as utils
|
||||||
|
|
||||||
"""
|
"""
|
||||||
A flash attention v2 forward pass example for NVIDIA Ampere SM80 architecture using CUTE DSL.
|
A flash attention v2 forward pass example for NVIDIA Ampere SM80 architecture using CUTE DSL.
|
||||||
@@ -163,7 +163,7 @@ class FlashAttentionForwardAmpere:
|
|||||||
# Check if block size setting is out of shared memory capacity
|
# Check if block size setting is out of shared memory capacity
|
||||||
# Shared memory usage: Q tile + (K tile + V tile) where K and V use the same tile size
|
# Shared memory usage: Q tile + (K tile + V tile) where K and V use the same tile size
|
||||||
smem_usage = (m_block_size * head_dim + n_block_size * head_dim * 2) * 2
|
smem_usage = (m_block_size * head_dim + n_block_size * head_dim * 2) * 2
|
||||||
smem_capacity = sm80_utils.SMEM_CAPACITY["sm80"]
|
smem_capacity = utils.get_smem_capacity_in_bytes("sm_80")
|
||||||
if smem_usage > smem_capacity:
|
if smem_usage > smem_capacity:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -469,21 +469,9 @@ class FlashAttentionForwardAmpere:
|
|||||||
warp.LdMatrix8x8x16bOp(transpose=True, num_matrices=4),
|
warp.LdMatrix8x8x16bOp(transpose=True, num_matrices=4),
|
||||||
self._dtype,
|
self._dtype,
|
||||||
)
|
)
|
||||||
smem_tiled_copy_Q = cute.make_tiled_copy(
|
smem_tiled_copy_Q = cute.make_tiled_copy_A(smem_copy_atom_Q, tiled_mma)
|
||||||
smem_copy_atom_Q,
|
smem_tiled_copy_K = cute.make_tiled_copy_B(smem_copy_atom_K, tiled_mma)
|
||||||
layout_tv=tiled_mma.tv_layout_A_tiled,
|
smem_tiled_copy_V = cute.make_tiled_copy_B(smem_copy_atom_V, tiled_mma)
|
||||||
tiler_mn=(tiled_mma.get_tile_size(0), tiled_mma.get_tile_size(2)),
|
|
||||||
)
|
|
||||||
smem_tiled_copy_K = cute.make_tiled_copy(
|
|
||||||
smem_copy_atom_K,
|
|
||||||
layout_tv=tiled_mma.tv_layout_B_tiled,
|
|
||||||
tiler_mn=(tiled_mma.get_tile_size(1), tiled_mma.get_tile_size(2)),
|
|
||||||
)
|
|
||||||
smem_tiled_copy_V = cute.make_tiled_copy(
|
|
||||||
smem_copy_atom_V,
|
|
||||||
layout_tv=tiled_mma.tv_layout_B_tiled,
|
|
||||||
tiler_mn=(tiled_mma.get_tile_size(1), tiled_mma.get_tile_size(2)),
|
|
||||||
)
|
|
||||||
|
|
||||||
smem_thr_copy_Q = smem_tiled_copy_Q.get_slice(tidx)
|
smem_thr_copy_Q = smem_tiled_copy_Q.get_slice(tidx)
|
||||||
smem_thr_copy_K = smem_tiled_copy_K.get_slice(tidx)
|
smem_thr_copy_K = smem_tiled_copy_K.get_slice(tidx)
|
||||||
@@ -702,11 +690,7 @@ class FlashAttentionForwardAmpere:
|
|||||||
cute.nvgpu.CopyUniversalOp(), self._dtype
|
cute.nvgpu.CopyUniversalOp(), self._dtype
|
||||||
)
|
)
|
||||||
# tiled copy atom for O
|
# tiled copy atom for O
|
||||||
smem_tiled_copy_O = cute.make_tiled_copy(
|
smem_tiled_copy_O = cute.make_tiled_copy_C(smem_copy_atom_O, tiled_mma)
|
||||||
smem_copy_atom_O,
|
|
||||||
layout_tv=tiled_mma.tv_layout_C_tiled,
|
|
||||||
tiler_mn=(tiled_mma.get_tile_size(0), tiled_mma.get_tile_size(1)),
|
|
||||||
)
|
|
||||||
smem_thr_copy_O = smem_tiled_copy_O.get_slice(tidx)
|
smem_thr_copy_O = smem_tiled_copy_O.get_slice(tidx)
|
||||||
taccOrO = smem_thr_copy_O.retile(rO)
|
taccOrO = smem_thr_copy_O.retile(rO)
|
||||||
taccOsO = smem_thr_copy_O.partition_D(sO)
|
taccOsO = smem_thr_copy_O.partition_D(sO)
|
||||||
@@ -1178,7 +1162,7 @@ class FlashAttentionForwardAmpere:
|
|||||||
return cute.arch.exp2(x)
|
return cute.arch.exp2(x)
|
||||||
|
|
||||||
|
|
||||||
def run_flash_attention_fwd(
|
def run(
|
||||||
dtype: Type[cutlass.Numeric],
|
dtype: Type[cutlass.Numeric],
|
||||||
batch_size: int,
|
batch_size: int,
|
||||||
seqlen_q: int,
|
seqlen_q: int,
|
||||||
@@ -1193,6 +1177,8 @@ def run_flash_attention_fwd(
|
|||||||
warmup_iterations: int = 0,
|
warmup_iterations: int = 0,
|
||||||
iterations: int = 1,
|
iterations: int = 1,
|
||||||
skip_ref_check: bool = False,
|
skip_ref_check: bool = False,
|
||||||
|
use_cold_l2: bool = False,
|
||||||
|
**kwargs,
|
||||||
):
|
):
|
||||||
# Skip unsupported testcase
|
# Skip unsupported testcase
|
||||||
if not FlashAttentionForwardAmpere.can_implement(
|
if not FlashAttentionForwardAmpere.can_implement(
|
||||||
@@ -1207,6 +1193,23 @@ def run_flash_attention_fwd(
|
|||||||
f"Unsupported testcase {dtype}, {head_dim}, {m_block_size}, {n_block_size}, {num_threads}, {is_causal}"
|
f"Unsupported testcase {dtype}, {head_dim}, {m_block_size}, {n_block_size}, {num_threads}, {is_causal}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
print(f"Running Ampere SM80 FlashAttentionForward test with:")
|
||||||
|
print(f" dtype: {dtype}")
|
||||||
|
print(f" batch_size: {batch_size}")
|
||||||
|
print(f" seqlen_q: {seqlen_q}")
|
||||||
|
print(f" seqlen_k: {seqlen_k}")
|
||||||
|
print(f" num_head: {num_head}")
|
||||||
|
print(f" head_dim: {head_dim}")
|
||||||
|
print(f" softmax_scale: {softmax_scale}")
|
||||||
|
print(f" m_block_size: {m_block_size}")
|
||||||
|
print(f" n_block_size: {n_block_size}")
|
||||||
|
print(f" num_threads: {num_threads}")
|
||||||
|
print(f" is_causal: {is_causal}")
|
||||||
|
print(f" warmup_iterations: {warmup_iterations}")
|
||||||
|
print(f" iterations: {iterations}")
|
||||||
|
print(f" skip_ref_check: {skip_ref_check}")
|
||||||
|
print(f" use_cold_l2: {use_cold_l2}")
|
||||||
|
|
||||||
# Create tensor Q/K/V/O
|
# Create tensor Q/K/V/O
|
||||||
def create_tensor(
|
def create_tensor(
|
||||||
batch_size: int,
|
batch_size: int,
|
||||||
@@ -1217,22 +1220,28 @@ def run_flash_attention_fwd(
|
|||||||
) -> cute.Tensor:
|
) -> cute.Tensor:
|
||||||
# (batch_size, seqlen, num_head, head_dim)
|
# (batch_size, seqlen, num_head, head_dim)
|
||||||
shape = (batch_size, seqlen, num_head, head_dim)
|
shape = (batch_size, seqlen, num_head, head_dim)
|
||||||
return (
|
torch_tensor = (
|
||||||
torch.empty(*shape, dtype=torch.int32).random_(-2, 2).to(dtype=dtype).cuda()
|
torch.empty(*shape, dtype=torch.int32)
|
||||||
|
.random_(-2, 2)
|
||||||
|
.to(dtype=cutlass_torch.dtype(dtype))
|
||||||
|
.cuda()
|
||||||
)
|
)
|
||||||
|
# assume input is 16B aligned.
|
||||||
|
cute_tensor = (
|
||||||
|
from_dlpack(torch_tensor, assumed_align=16)
|
||||||
|
.mark_layout_dynamic(leading_dim=3)
|
||||||
|
.mark_compact_shape_dynamic(
|
||||||
|
mode=3,
|
||||||
|
stride_order=torch_tensor.dim_order(),
|
||||||
|
divisibility=(128 // dtype.width),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return cute_tensor, torch_tensor
|
||||||
|
|
||||||
q = create_tensor(
|
q, q_torch = create_tensor(batch_size, seqlen_q, num_head, head_dim, dtype)
|
||||||
batch_size, seqlen_q, num_head, head_dim, cutlass_torch.dtype(dtype)
|
k, k_torch = create_tensor(batch_size, seqlen_k, num_head, head_dim, dtype)
|
||||||
)
|
v, v_torch = create_tensor(batch_size, seqlen_k, num_head, head_dim, dtype)
|
||||||
k = create_tensor(
|
o, o_torch = create_tensor(batch_size, seqlen_q, num_head, head_dim, dtype)
|
||||||
batch_size, seqlen_k, num_head, head_dim, cutlass_torch.dtype(dtype)
|
|
||||||
)
|
|
||||||
v = create_tensor(
|
|
||||||
batch_size, seqlen_k, num_head, head_dim, cutlass_torch.dtype(dtype)
|
|
||||||
)
|
|
||||||
o = create_tensor(
|
|
||||||
batch_size, seqlen_q, num_head, head_dim, cutlass_torch.dtype(dtype)
|
|
||||||
)
|
|
||||||
|
|
||||||
fa2_fwd = FlashAttentionForwardAmpere(
|
fa2_fwd = FlashAttentionForwardAmpere(
|
||||||
head_dim,
|
head_dim,
|
||||||
@@ -1241,78 +1250,63 @@ def run_flash_attention_fwd(
|
|||||||
num_threads,
|
num_threads,
|
||||||
is_causal,
|
is_causal,
|
||||||
)
|
)
|
||||||
# assume input is 16B align.
|
|
||||||
q_tensor = (
|
|
||||||
from_dlpack(q, assumed_align=16)
|
|
||||||
.mark_layout_dynamic(leading_dim=3)
|
|
||||||
.mark_compact_shape_dynamic(
|
|
||||||
mode=3, stride_order=q.dim_order(), divisibility=(128 // dtype.width)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
k_tensor = (
|
|
||||||
from_dlpack(k, assumed_align=16)
|
|
||||||
.mark_layout_dynamic(leading_dim=3)
|
|
||||||
.mark_compact_shape_dynamic(
|
|
||||||
mode=3, stride_order=k.dim_order(), divisibility=(128 // dtype.width)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
v_tensor = (
|
|
||||||
from_dlpack(v, assumed_align=16)
|
|
||||||
.mark_layout_dynamic(leading_dim=3)
|
|
||||||
.mark_compact_shape_dynamic(
|
|
||||||
mode=3, stride_order=v.dim_order(), divisibility=(128 // dtype.width)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
o_tensor = (
|
|
||||||
from_dlpack(o, assumed_align=16)
|
|
||||||
.mark_layout_dynamic(leading_dim=3)
|
|
||||||
.mark_compact_shape_dynamic(
|
|
||||||
mode=3, stride_order=o.dim_order(), divisibility=(128 // dtype.width)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
# Get current CUDA stream from PyTorch
|
# Get current CUDA stream from PyTorch
|
||||||
torch_stream = torch.cuda.current_stream()
|
torch_stream = torch.cuda.current_stream()
|
||||||
# Get the raw stream pointer as a CUstream
|
# Get the raw stream pointer as a CUstream
|
||||||
current_stream = cuda.CUstream(torch_stream.cuda_stream)
|
current_stream = cuda.CUstream(torch_stream.cuda_stream)
|
||||||
# compile the fa2 forward pass
|
# compile the fa2 forward pass
|
||||||
compiled_fa2_fwd = cute.compile(
|
compiled_fa2_fwd = cute.compile(fa2_fwd, q, k, v, o, softmax_scale, current_stream)
|
||||||
fa2_fwd, q_tensor, k_tensor, v_tensor, o_tensor, softmax_scale, current_stream
|
|
||||||
|
if not skip_ref_check:
|
||||||
|
compiled_fa2_fwd(q, k, v, o, softmax_scale, current_stream)
|
||||||
|
torch.cuda.synchronize()
|
||||||
|
q_ref = q_torch.permute(0, 2, 1, 3)
|
||||||
|
k_ref = k_torch.permute(0, 2, 1, 3)
|
||||||
|
v_ref = v_torch.permute(0, 2, 1, 3)
|
||||||
|
torch.backends.cuda.enable_flash_sdp(enabled=True)
|
||||||
|
ref_o = torch.nn.functional.scaled_dot_product_attention(
|
||||||
|
q_ref, k_ref, v_ref, scale=softmax_scale, is_causal=is_causal
|
||||||
|
).permute(0, 2, 1, 3)
|
||||||
|
torch.testing.assert_close(o_torch.cpu(), ref_o.cpu(), atol=1e-02, rtol=1e-04)
|
||||||
|
print("Results verified successfully!")
|
||||||
|
|
||||||
|
def generate_tensors():
|
||||||
|
q_workspace, _ = create_tensor(batch_size, seqlen_q, num_head, head_dim, dtype)
|
||||||
|
k_workspace, _ = create_tensor(batch_size, seqlen_k, num_head, head_dim, dtype)
|
||||||
|
v_workspace, _ = create_tensor(batch_size, seqlen_k, num_head, head_dim, dtype)
|
||||||
|
o_workspace, _ = create_tensor(batch_size, seqlen_q, num_head, head_dim, dtype)
|
||||||
|
return testing.JitArguments(
|
||||||
|
q_workspace,
|
||||||
|
k_workspace,
|
||||||
|
v_workspace,
|
||||||
|
o_workspace,
|
||||||
|
softmax_scale,
|
||||||
|
current_stream,
|
||||||
|
)
|
||||||
|
|
||||||
|
workspace_count = 1
|
||||||
|
if use_cold_l2:
|
||||||
|
one_workspace_bytes = (
|
||||||
|
q_torch.numel() * q_torch.element_size()
|
||||||
|
+ k_torch.numel() * k_torch.element_size()
|
||||||
|
+ v_torch.numel() * v_torch.element_size()
|
||||||
|
+ o_torch.numel() * o_torch.element_size()
|
||||||
|
)
|
||||||
|
workspace_count = testing.get_workspace_count(
|
||||||
|
one_workspace_bytes, warmup_iterations, iterations
|
||||||
|
)
|
||||||
|
|
||||||
|
avg_time_us = testing.benchmark(
|
||||||
|
compiled_fa2_fwd,
|
||||||
|
workspace_generator=generate_tensors,
|
||||||
|
workspace_count=workspace_count,
|
||||||
|
stream=current_stream,
|
||||||
|
warmup_iterations=warmup_iterations,
|
||||||
|
iterations=iterations,
|
||||||
)
|
)
|
||||||
# warmup
|
|
||||||
for _ in range(warmup_iterations):
|
|
||||||
compiled_fa2_fwd(
|
|
||||||
q_tensor,
|
|
||||||
k_tensor,
|
|
||||||
v_tensor,
|
|
||||||
o_tensor,
|
|
||||||
softmax_scale,
|
|
||||||
current_stream,
|
|
||||||
)
|
|
||||||
# run the compiled fa2 forward pass
|
|
||||||
for _ in range(iterations):
|
|
||||||
compiled_fa2_fwd(
|
|
||||||
q_tensor,
|
|
||||||
k_tensor,
|
|
||||||
v_tensor,
|
|
||||||
o_tensor,
|
|
||||||
softmax_scale,
|
|
||||||
current_stream,
|
|
||||||
)
|
|
||||||
torch.cuda.synchronize()
|
|
||||||
|
|
||||||
if skip_ref_check:
|
|
||||||
return
|
|
||||||
# reference implementation
|
|
||||||
q_ref = q.permute(0, 2, 1, 3)
|
|
||||||
k_ref = k.permute(0, 2, 1, 3)
|
|
||||||
v_ref = v.permute(0, 2, 1, 3)
|
|
||||||
torch.backends.cuda.enable_flash_sdp(enabled=True)
|
|
||||||
ref_o = torch.nn.functional.scaled_dot_product_attention(
|
|
||||||
q_ref, k_ref, v_ref, scale=softmax_scale, is_causal=is_causal
|
|
||||||
).permute(0, 2, 1, 3)
|
|
||||||
|
|
||||||
torch.testing.assert_close(o.cpu(), ref_o.cpu(), atol=1e-02, rtol=1e-04)
|
|
||||||
|
|
||||||
|
return avg_time_us # Return execution time in microseconds
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
@@ -1334,9 +1328,15 @@ if __name__ == "__main__":
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--skip_ref_check", action="store_true", help="Skip reference check"
|
"--skip_ref_check", action="store_true", help="Skip reference check"
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--use_cold_l2",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="Use circular buffer tensor sets to ensure L2 cold cache",
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
run_flash_attention_fwd(
|
run(
|
||||||
args.dtype,
|
args.dtype,
|
||||||
args.batch_size,
|
args.batch_size,
|
||||||
args.seqlen_q,
|
args.seqlen_q,
|
||||||
@@ -1348,6 +1348,10 @@ if __name__ == "__main__":
|
|||||||
args.n_block_size,
|
args.n_block_size,
|
||||||
args.num_threads,
|
args.num_threads,
|
||||||
args.is_causal,
|
args.is_causal,
|
||||||
|
args.warmup_iterations,
|
||||||
|
args.iterations,
|
||||||
|
args.skip_ref_check,
|
||||||
|
args.use_cold_l2,
|
||||||
)
|
)
|
||||||
|
|
||||||
print("PASS")
|
print("PASS")
|
||||||
|
|||||||
@@ -634,16 +634,50 @@ class SGemm:
|
|||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
def main(
|
def run(
|
||||||
|
mnk: Tuple[int, int, int],
|
||||||
a_major: str,
|
a_major: str,
|
||||||
b_major: str,
|
b_major: str,
|
||||||
c_major: str,
|
c_major: str,
|
||||||
problem_shape: Tuple[int, int, int],
|
static_shape: bool = False,
|
||||||
warmup_iterations: int = 2,
|
warmup_iterations: int = 2,
|
||||||
iterations: int = 100,
|
iterations: int = 100,
|
||||||
skip_ref_check: bool = False,
|
skip_ref_check: bool = False,
|
||||||
|
use_cold_l2: bool = False,
|
||||||
|
**kwargs,
|
||||||
):
|
):
|
||||||
M, N, K = problem_shape
|
"""Execute SIMT GEMM operation and benchmark performance.
|
||||||
|
|
||||||
|
:param mnk: GEMM problem size (M, N, K, L)
|
||||||
|
:type mnk: Tuple[int, int, int, int]
|
||||||
|
:param a_major: Memory layout of tensor A
|
||||||
|
:type a_major: str
|
||||||
|
:param b_major: Memory layout of tensor B
|
||||||
|
:type b_major: str
|
||||||
|
:param c_major: Memory layout of tensor C
|
||||||
|
:type c_major: str
|
||||||
|
:param static_shape: Whether to use static shape optimization, defaults to False
|
||||||
|
:type static_shape: bool, optional
|
||||||
|
:param warmup_iterations: Number of warmup iterations before benchmarking, defaults to 2
|
||||||
|
:type warmup_iterations: int, optional
|
||||||
|
:param iterations: Number of benchmark iterations to run, defaults to 100
|
||||||
|
:type iterations: int, optional
|
||||||
|
:param skip_ref_check: Skip validation against reference implementation, defaults to False
|
||||||
|
:type skip_ref_check: bool, optional
|
||||||
|
:param use_cold_l2: Whether to use circular buffer strategy to ensure cold L2 cache, defaults to False
|
||||||
|
:type use_cold_l2: bool, optional
|
||||||
|
:return: Execution time of the GEMM kernel in microseconds
|
||||||
|
:rtype: float
|
||||||
|
"""
|
||||||
|
print(f"Running Ampere SIMT GEMM example:")
|
||||||
|
print(f"mnk: {mnk}")
|
||||||
|
print(f"A major: {a_major}, B major: {b_major}, C major: {c_major}")
|
||||||
|
print(f"Static shape: {static_shape}")
|
||||||
|
print(f"Warmup iterations: {warmup_iterations}")
|
||||||
|
print(f"Iterations: {iterations}")
|
||||||
|
print(f"Skip reference checking: {skip_ref_check}")
|
||||||
|
print(f"Use cold L2: {use_cold_l2}")
|
||||||
|
M, N, K = mnk
|
||||||
|
|
||||||
# Create and permute tensor A/B/C
|
# Create and permute tensor A/B/C
|
||||||
def create_and_permute_tensor(mode0, mode1, is_mode0_major, dtype):
|
def create_and_permute_tensor(mode0, mode1, is_mode0_major, dtype):
|
||||||
@@ -710,20 +744,6 @@ def main(
|
|||||||
|
|
||||||
print("Executing GEMM kernel...")
|
print("Executing GEMM kernel...")
|
||||||
|
|
||||||
avg_time_us = testing.benchmark(
|
|
||||||
gemm,
|
|
||||||
kernel_arguments=testing.JitArguments(
|
|
||||||
a_tensor, b_tensor, c_tensor, current_stream
|
|
||||||
),
|
|
||||||
warmup_iterations=warmup_iterations,
|
|
||||||
profiling_iterations=iterations,
|
|
||||||
use_cuda_graphs=False,
|
|
||||||
stream=current_stream,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Print execution results
|
|
||||||
print(f"Kernel execution time: {avg_time_us / 1e3:.4f} ms")
|
|
||||||
|
|
||||||
if not skip_ref_check:
|
if not skip_ref_check:
|
||||||
gemm(a_tensor, b_tensor, c_tensor)
|
gemm(a_tensor, b_tensor, c_tensor)
|
||||||
torch.cuda.synchronize()
|
torch.cuda.synchronize()
|
||||||
@@ -732,6 +752,71 @@ def main(
|
|||||||
torch.testing.assert_close(c.cpu(), ref.cpu(), atol=1e-03, rtol=1e-05)
|
torch.testing.assert_close(c.cpu(), ref.cpu(), atol=1e-03, rtol=1e-05)
|
||||||
print("Results verified successfully!")
|
print("Results verified successfully!")
|
||||||
|
|
||||||
|
def generate_tensors():
|
||||||
|
# Create new tensors for each workspace to ensure cold L2 cache
|
||||||
|
a_workspace = create_and_permute_tensor(M, K, a_major == "m", torch.float32)
|
||||||
|
b_workspace = create_and_permute_tensor(N, K, b_major == "n", torch.float32)
|
||||||
|
c_workspace = create_and_permute_tensor(M, N, c_major == "m", torch.float32)
|
||||||
|
|
||||||
|
if static_shape:
|
||||||
|
a_tensor_workspace = (
|
||||||
|
from_dlpack(a_workspace, assumed_align=16)
|
||||||
|
.mark_layout_dynamic(leading_dim=(1 if a_major == "k" else 0))
|
||||||
|
.mark_compact_shape_dynamic(
|
||||||
|
mode=(1 if a_major == "k" else 0),
|
||||||
|
divisibility=divisibility_a,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
a_tensor_workspace = from_dlpack(a_workspace, assumed_align=16)
|
||||||
|
|
||||||
|
b_tensor_workspace = (
|
||||||
|
from_dlpack(b_workspace, assumed_align=16)
|
||||||
|
.mark_layout_dynamic(leading_dim=(1 if b_major == "k" else 0))
|
||||||
|
.mark_compact_shape_dynamic(
|
||||||
|
mode=(1 if b_major == "k" else 0),
|
||||||
|
divisibility=divisibility_b,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
c_tensor_workspace = (
|
||||||
|
from_dlpack(c_workspace, assumed_align=16)
|
||||||
|
.mark_layout_dynamic(leading_dim=(1 if c_major == "n" else 0))
|
||||||
|
.mark_compact_shape_dynamic(
|
||||||
|
mode=(1 if c_major == "n" else 0),
|
||||||
|
divisibility=divisibility_c,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return testing.JitArguments(
|
||||||
|
a_tensor_workspace, b_tensor_workspace, c_tensor_workspace, current_stream
|
||||||
|
)
|
||||||
|
|
||||||
|
workspace_count = 1
|
||||||
|
if use_cold_l2:
|
||||||
|
one_workspace_bytes = (
|
||||||
|
a.numel() * a.element_size()
|
||||||
|
+ b.numel() * b.element_size()
|
||||||
|
+ c.numel() * c.element_size()
|
||||||
|
)
|
||||||
|
workspace_count = testing.get_workspace_count(
|
||||||
|
one_workspace_bytes, warmup_iterations, iterations
|
||||||
|
)
|
||||||
|
|
||||||
|
avg_time_us = testing.benchmark(
|
||||||
|
gemm,
|
||||||
|
workspace_generator=generate_tensors,
|
||||||
|
workspace_count=workspace_count,
|
||||||
|
stream=current_stream,
|
||||||
|
warmup_iterations=warmup_iterations,
|
||||||
|
iterations=iterations,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Print execution results
|
||||||
|
print(f"Kernel execution time: {avg_time_us / 1e3:.4f} ms")
|
||||||
|
|
||||||
|
return avg_time_us # Return execution time in microseconds
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
@@ -753,19 +838,27 @@ if __name__ == "__main__":
|
|||||||
parser.add_argument("--warmup_iterations", default=2, type=int)
|
parser.add_argument("--warmup_iterations", default=2, type=int)
|
||||||
parser.add_argument("--iterations", default=100, type=int)
|
parser.add_argument("--iterations", default=100, type=int)
|
||||||
parser.add_argument("--skip_ref_check", action="store_true")
|
parser.add_argument("--skip_ref_check", action="store_true")
|
||||||
|
parser.add_argument(
|
||||||
|
"--use_cold_l2",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="Use circular buffer tensor sets to ensure L2 cold cache",
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
print("Running SIMT GEMM example:")
|
print("Running SIMT GEMM example:")
|
||||||
|
|
||||||
torch.manual_seed(1024)
|
torch.manual_seed(1024)
|
||||||
|
|
||||||
main(
|
run(
|
||||||
|
args.mnk,
|
||||||
args.a_major,
|
args.a_major,
|
||||||
args.b_major,
|
args.b_major,
|
||||||
args.c_major,
|
args.c_major,
|
||||||
args.mnk,
|
args.static_shape,
|
||||||
args.warmup_iterations,
|
args.warmup_iterations,
|
||||||
args.iterations,
|
args.iterations,
|
||||||
args.skip_ref_check,
|
args.skip_ref_check,
|
||||||
|
args.use_cold_l2,
|
||||||
)
|
)
|
||||||
print("PASS")
|
print("PASS")
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ This GEMM kernel supports the following features:
|
|||||||
- Utilizes Ampere's tensor cores for matrix multiply-accumulate (MMA) operations
|
- Utilizes Ampere's tensor cores for matrix multiply-accumulate (MMA) operations
|
||||||
- Threadblock rasterization to improve data re-use
|
- Threadblock rasterization to improve data re-use
|
||||||
- Supports multi-stage pipeline to overlap computation and memory access
|
- Supports multi-stage pipeline to overlap computation and memory access
|
||||||
- Implements shared memory buffering for epilogue to increase coalesed global memory access
|
- Implements shared memory buffering for epilogue to increase coalesced global memory access
|
||||||
|
|
||||||
This GEMM works as follows:
|
This GEMM works as follows:
|
||||||
1. Load A and B matrices from global memory (GMEM) to shared memory (SMEM) using asynchronous copies.
|
1. Load A and B matrices from global memory (GMEM) to shared memory (SMEM) using asynchronous copies.
|
||||||
@@ -214,7 +214,7 @@ class TensorOpGemm:
|
|||||||
atom_async_copy, mB.element_type, self.b_major_mode, ab_copy_bits
|
atom_async_copy, mB.element_type, self.b_major_mode, ab_copy_bits
|
||||||
)
|
)
|
||||||
|
|
||||||
# Creates a synchonous copy atom and thread layouts for the epilogue
|
# Creates a synchronous copy atom and thread layouts for the epilogue
|
||||||
c_copy_bits = 128
|
c_copy_bits = 128
|
||||||
atom_sync_copy = cute.make_copy_atom(
|
atom_sync_copy = cute.make_copy_atom(
|
||||||
cute.nvgpu.CopyUniversalOp(),
|
cute.nvgpu.CopyUniversalOp(),
|
||||||
@@ -550,16 +550,8 @@ class TensorOpGemm:
|
|||||||
|
|
||||||
# Creates the tiled copy so that it matches the thread-value layout
|
# Creates the tiled copy so that it matches the thread-value layout
|
||||||
# expected by the tiled mma
|
# expected by the tiled mma
|
||||||
tiled_copy_s2r_A = cute.make_tiled_copy(
|
tiled_copy_s2r_A = cute.make_tiled_copy_A(atom_copy_s2r_A, tiled_mma)
|
||||||
atom_copy_s2r_A,
|
tiled_copy_s2r_B = cute.make_tiled_copy_B(atom_copy_s2r_B, tiled_mma)
|
||||||
layout_tv=tiled_mma.tv_layout_A_tiled,
|
|
||||||
tiler_mn=(tiled_mma.get_tile_size(0), tiled_mma.get_tile_size(2)),
|
|
||||||
)
|
|
||||||
tiled_copy_s2r_B = cute.make_tiled_copy(
|
|
||||||
atom_copy_s2r_B,
|
|
||||||
layout_tv=tiled_mma.tv_layout_B_tiled,
|
|
||||||
tiler_mn=(tiled_mma.get_tile_size(1), tiled_mma.get_tile_size(2)),
|
|
||||||
)
|
|
||||||
|
|
||||||
thr_copy_ldmatrix_A = tiled_copy_s2r_A.get_slice(tidx)
|
thr_copy_ldmatrix_A = tiled_copy_s2r_A.get_slice(tidx)
|
||||||
thr_copy_ldmatrix_B = tiled_copy_s2r_B.get_slice(tidx)
|
thr_copy_ldmatrix_B = tiled_copy_s2r_B.get_slice(tidx)
|
||||||
@@ -836,8 +828,7 @@ class TensorOpGemm:
|
|||||||
if major_mode == utils.LayoutEnum.ROW_MAJOR
|
if major_mode == utils.LayoutEnum.ROW_MAJOR
|
||||||
else cute.make_layout((copy_elems, 1))
|
else cute.make_layout((copy_elems, 1))
|
||||||
)
|
)
|
||||||
tiler_mn, layout_tv = cute.make_layout_tv(thread_layout, value_layout)
|
return cute.make_tiled_copy_tv(atom_copy, thread_layout, value_layout)
|
||||||
return cute.make_tiled_copy(atom_copy, layout_tv, tiler_mn)
|
|
||||||
|
|
||||||
def raster_tile(self, i, j, f):
|
def raster_tile(self, i, j, f):
|
||||||
new_i = i // f
|
new_i = i // f
|
||||||
@@ -845,20 +836,33 @@ class TensorOpGemm:
|
|||||||
return (new_i, new_j)
|
return (new_i, new_j)
|
||||||
|
|
||||||
|
|
||||||
def run_tensor_op_gemm(
|
def run(
|
||||||
a_major: str,
|
a_major: str,
|
||||||
b_major: str,
|
b_major: str,
|
||||||
c_major: str,
|
c_major: str,
|
||||||
ab_dtype: Type[cutlass.Numeric],
|
ab_dtype: Type[cutlass.Numeric],
|
||||||
c_dtype: Type[cutlass.Numeric],
|
c_dtype: Type[cutlass.Numeric],
|
||||||
acc_dtype: Type[cutlass.Numeric],
|
acc_dtype: Type[cutlass.Numeric],
|
||||||
problem_shape: Tuple[int, int, int, int],
|
mnkl: Tuple[int, int, int, int],
|
||||||
atom_layout_mnk: Tuple[int, int, int],
|
atom_layout_mnk: Tuple[int, int, int],
|
||||||
warmup_iterations: int = 2,
|
warmup_iterations: int = 2,
|
||||||
iterations: int = 100,
|
iterations: int = 100,
|
||||||
skip_ref_check: bool = False,
|
skip_ref_check: bool = False,
|
||||||
|
use_cold_l2: bool = False,
|
||||||
|
**kwargs,
|
||||||
):
|
):
|
||||||
M, N, K, L = problem_shape
|
print(f"Running Ampere tensor core GEMM example:")
|
||||||
|
print(f"mnkl: {mnkl}")
|
||||||
|
print(
|
||||||
|
f"A dtype: {ab_dtype}, B dtype: {ab_dtype}, C dtype: {c_dtype}, Acc dtype: {acc_dtype}"
|
||||||
|
)
|
||||||
|
print(f"Matrix majors - A: {a_major}, B: {b_major}, C: {c_major}")
|
||||||
|
print(f"Atoms layout: {atom_layout_mnk}")
|
||||||
|
print(f"Warmup iterations: {warmup_iterations}")
|
||||||
|
print(f"Iterations: {iterations}")
|
||||||
|
print(f"Skip reference checking: {skip_ref_check}")
|
||||||
|
print(f"Use cold L2: {use_cold_l2}")
|
||||||
|
M, N, K, L = mnkl
|
||||||
|
|
||||||
# Create and permute tensor A/B/C
|
# Create and permute tensor A/B/C
|
||||||
def create_and_permute_tensor(l, mode0, mode1, is_mode0_major, dtype):
|
def create_and_permute_tensor(l, mode0, mode1, is_mode0_major, dtype):
|
||||||
@@ -866,23 +870,28 @@ def run_tensor_op_gemm(
|
|||||||
# else: (l, mode0, mode1) -> (mode0, mode1, l)
|
# else: (l, mode0, mode1) -> (mode0, mode1, l)
|
||||||
shape = (l, mode1, mode0) if is_mode0_major else (l, mode0, mode1)
|
shape = (l, mode1, mode0) if is_mode0_major else (l, mode0, mode1)
|
||||||
permute_order = (2, 1, 0) if is_mode0_major else (1, 2, 0)
|
permute_order = (2, 1, 0) if is_mode0_major else (1, 2, 0)
|
||||||
|
torch_tensor = (
|
||||||
return (
|
|
||||||
torch.empty(*shape, dtype=torch.int32)
|
torch.empty(*shape, dtype=torch.int32)
|
||||||
.random_(-2, 2)
|
.random_(-2, 2)
|
||||||
.to(dtype=dtype)
|
.to(dtype=cutlass_torch.dtype(dtype))
|
||||||
.permute(permute_order)
|
.permute(permute_order)
|
||||||
.cuda()
|
.cuda()
|
||||||
)
|
)
|
||||||
|
# assume input is 16B aligned
|
||||||
|
cute_tensor = (
|
||||||
|
from_dlpack(torch_tensor, assumed_align=16)
|
||||||
|
.mark_layout_dynamic(leading_dim=(1 if not is_mode0_major else 0))
|
||||||
|
.mark_compact_shape_dynamic(
|
||||||
|
mode=(1 if not is_mode0_major else 0),
|
||||||
|
stride_order=(2, 0, 1) if not is_mode0_major else (2, 1, 0),
|
||||||
|
divisibility=(128 // dtype.width),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return cute_tensor, torch_tensor
|
||||||
|
|
||||||
a = create_and_permute_tensor(
|
mA, a_torch = create_and_permute_tensor(L, M, K, a_major == "m", ab_dtype)
|
||||||
L, M, K, a_major == "m", cutlass_torch.dtype(ab_dtype)
|
mB, b_torch = create_and_permute_tensor(L, N, K, b_major == "n", ab_dtype)
|
||||||
)
|
mC, c_torch = create_and_permute_tensor(L, M, N, c_major == "m", c_dtype)
|
||||||
b = create_and_permute_tensor(
|
|
||||||
L, N, K, b_major == "n", cutlass_torch.dtype(ab_dtype)
|
|
||||||
)
|
|
||||||
c = create_and_permute_tensor(L, M, N, c_major == "m", cutlass_torch.dtype(c_dtype))
|
|
||||||
ref = torch.einsum("mkl,nkl->mnl", a, b).to(cutlass_torch.dtype(c_dtype))
|
|
||||||
|
|
||||||
tensor_op_gemm = TensorOpGemm(
|
tensor_op_gemm = TensorOpGemm(
|
||||||
ab_dtype,
|
ab_dtype,
|
||||||
@@ -891,56 +900,49 @@ def run_tensor_op_gemm(
|
|||||||
atom_layout_mnk,
|
atom_layout_mnk,
|
||||||
)
|
)
|
||||||
|
|
||||||
# assume input is 16B aligned
|
|
||||||
a_tensor = (
|
|
||||||
from_dlpack(a, assumed_align=16)
|
|
||||||
.mark_layout_dynamic(leading_dim=(1 if a_major == "k" else 0))
|
|
||||||
.mark_compact_shape_dynamic(
|
|
||||||
mode=(1 if a_major == "k" else 0),
|
|
||||||
stride_order=(2, 0, 1) if a_major == "k" else (2, 1, 0),
|
|
||||||
divisibility=(128 // ab_dtype.width),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
b_tensor = (
|
|
||||||
from_dlpack(b, assumed_align=16)
|
|
||||||
.mark_layout_dynamic(leading_dim=(1 if b_major == "k" else 0))
|
|
||||||
.mark_compact_shape_dynamic(
|
|
||||||
mode=(1 if b_major == "k" else 0),
|
|
||||||
stride_order=(2, 0, 1) if b_major == "k" else (2, 1, 0),
|
|
||||||
divisibility=(128 // ab_dtype.width),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
c_tensor = (
|
|
||||||
from_dlpack(c, assumed_align=16)
|
|
||||||
.mark_layout_dynamic(leading_dim=(1 if c_major == "n" else 0))
|
|
||||||
.mark_compact_shape_dynamic(
|
|
||||||
mode=(1 if c_major == "n" else 0),
|
|
||||||
stride_order=(2, 0, 1) if c_major == "n" else (2, 1, 0),
|
|
||||||
divisibility=(128 // c_dtype.width),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
print("Compiling kernel with cute.compile ...")
|
print("Compiling kernel with cute.compile ...")
|
||||||
gemm = cute.compile(tensor_op_gemm, a_tensor, b_tensor, c_tensor)
|
compiled_gemm = cute.compile(tensor_op_gemm, mA, mB, mC)
|
||||||
|
|
||||||
print("Executing GEMM kernel...")
|
print("Executing GEMM kernel...")
|
||||||
|
|
||||||
|
if not skip_ref_check:
|
||||||
|
ref = torch.einsum(
|
||||||
|
"mkl,nkl->mnl",
|
||||||
|
a_torch.to(dtype=torch.float32),
|
||||||
|
b_torch.to(dtype=torch.float32),
|
||||||
|
).to(cutlass_torch.dtype(c_dtype))
|
||||||
|
compiled_gemm(mA, mB, mC)
|
||||||
|
print("Verifying results...")
|
||||||
|
torch.testing.assert_close(c_torch.cpu(), ref.cpu(), atol=1e-03, rtol=1e-05)
|
||||||
|
print("Results verified successfully!")
|
||||||
|
|
||||||
|
def generate_tensors():
|
||||||
|
a_workspace, _ = create_and_permute_tensor(L, M, K, a_major == "m", ab_dtype)
|
||||||
|
b_workspace, _ = create_and_permute_tensor(L, N, K, b_major == "n", ab_dtype)
|
||||||
|
c_workspace, _ = create_and_permute_tensor(L, M, N, c_major == "m", c_dtype)
|
||||||
|
return testing.JitArguments(a_workspace, b_workspace, c_workspace)
|
||||||
|
|
||||||
|
workspace_count = 1
|
||||||
|
if use_cold_l2:
|
||||||
|
one_workspace_bytes = (
|
||||||
|
a_torch.numel() * a_torch.element_size()
|
||||||
|
+ b_torch.numel() * b_torch.element_size()
|
||||||
|
+ c_torch.numel() * c_torch.element_size()
|
||||||
|
)
|
||||||
|
workspace_count = testing.get_workspace_count(
|
||||||
|
one_workspace_bytes, warmup_iterations, iterations
|
||||||
|
)
|
||||||
|
|
||||||
avg_time_us = testing.benchmark(
|
avg_time_us = testing.benchmark(
|
||||||
gemm,
|
compiled_gemm,
|
||||||
kernel_arguments=testing.JitArguments(a_tensor, b_tensor, c_tensor),
|
workspace_generator=generate_tensors,
|
||||||
|
workspace_count=workspace_count,
|
||||||
warmup_iterations=warmup_iterations,
|
warmup_iterations=warmup_iterations,
|
||||||
profiling_iterations=iterations,
|
iterations=iterations,
|
||||||
use_cuda_graphs=False,
|
use_cuda_graphs=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"Kernel execution time: {avg_time_us / 1e3:.4f} ms")
|
return avg_time_us # Return execution time in microseconds
|
||||||
|
|
||||||
if not skip_ref_check:
|
|
||||||
gemm(a_tensor, b_tensor, c_tensor)
|
|
||||||
print("Verifying results...")
|
|
||||||
torch.testing.assert_close(c.cpu(), ref.cpu(), atol=1e-03, rtol=1e-05)
|
|
||||||
print("Results verified successfully!")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
@@ -985,10 +987,15 @@ if __name__ == "__main__":
|
|||||||
parser.add_argument("--warmup_iterations", default=2, type=int)
|
parser.add_argument("--warmup_iterations", default=2, type=int)
|
||||||
parser.add_argument("--iterations", default=100, type=int)
|
parser.add_argument("--iterations", default=100, type=int)
|
||||||
parser.add_argument("--skip_ref_check", action="store_true")
|
parser.add_argument("--skip_ref_check", action="store_true")
|
||||||
|
parser.add_argument(
|
||||||
|
"--use_cold_l2",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="Use circular buffer tensor sets to ensure L2 cold cache",
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
print("Running Ampere tensor core GEMM example:")
|
run(
|
||||||
run_tensor_op_gemm(
|
|
||||||
args.a_major,
|
args.a_major,
|
||||||
args.b_major,
|
args.b_major,
|
||||||
args.c_major,
|
args.c_major,
|
||||||
@@ -1000,5 +1007,6 @@ if __name__ == "__main__":
|
|||||||
args.warmup_iterations,
|
args.warmup_iterations,
|
||||||
args.iterations,
|
args.iterations,
|
||||||
args.skip_ref_check,
|
args.skip_ref_check,
|
||||||
|
args.use_cold_l2,
|
||||||
)
|
)
|
||||||
print("PASS")
|
print("PASS")
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -212,7 +212,7 @@ class DenseGemmKernel:
|
|||||||
|
|
||||||
self.occupancy = 1
|
self.occupancy = 1
|
||||||
self.threads_per_cta = 128
|
self.threads_per_cta = 128
|
||||||
self.smem_capacity = sm100_utils.SMEM_CAPACITY["sm100"]
|
self.smem_capacity = utils.get_smem_capacity_in_bytes("sm_100")
|
||||||
|
|
||||||
def _setup_attributes(self):
|
def _setup_attributes(self):
|
||||||
"""Set up configurations that are dependent on GEMM inputs
|
"""Set up configurations that are dependent on GEMM inputs
|
||||||
@@ -1106,11 +1106,7 @@ class DenseGemmKernel:
|
|||||||
copy_atom_r2s = sm100_utils.get_smem_store_op(
|
copy_atom_r2s = sm100_utils.get_smem_store_op(
|
||||||
self.c_layout, self.c_dtype, self.acc_dtype, tiled_copy_t2r
|
self.c_layout, self.c_dtype, self.acc_dtype, tiled_copy_t2r
|
||||||
)
|
)
|
||||||
tiled_copy_r2s = cute.make_tiled_copy(
|
tiled_copy_r2s = cute.make_tiled_copy_D(copy_atom_r2s, tiled_copy_t2r)
|
||||||
copy_atom_r2s,
|
|
||||||
layout_tv=tiled_copy_t2r.layout_dst_tv_tiled,
|
|
||||||
tiler_mn=tiled_copy_t2r.tiler_mn,
|
|
||||||
)
|
|
||||||
# (R2S, R2S_M, R2S_N, PIPE_D)
|
# (R2S, R2S_M, R2S_N, PIPE_D)
|
||||||
thr_copy_r2s = tiled_copy_r2s.get_slice(tidx)
|
thr_copy_r2s = tiled_copy_r2s.get_slice(tidx)
|
||||||
tRS_sC = thr_copy_r2s.partition_D(sC)
|
tRS_sC = thr_copy_r2s.partition_D(sC)
|
||||||
@@ -1772,7 +1768,7 @@ def run_dense_gemm(
|
|||||||
ref_c = ref
|
ref_c = ref
|
||||||
elif c_dtype in {cutlass.Float8E5M2, cutlass.Float8E4M3FN}:
|
elif c_dtype in {cutlass.Float8E5M2, cutlass.Float8E4M3FN}:
|
||||||
# m major: (l, n, m) -> (m, n, l)
|
# m major: (l, n, m) -> (m, n, l)
|
||||||
# k major: (l, m, n) -> (m, n, l)
|
# n major: (l, m, n) -> (m, n, l)
|
||||||
permute_order = (1, 2, 0) if c_major == "n" else (2, 1, 0)
|
permute_order = (1, 2, 0) if c_major == "n" else (2, 1, 0)
|
||||||
shape = (l, m, n) if c_major == "n" else (l, n, m)
|
shape = (l, m, n) if c_major == "n" else (l, n, m)
|
||||||
f8_torch_tensor = cutlass_torch.create_and_permute_torch_tensor(
|
f8_torch_tensor = cutlass_torch.create_and_permute_torch_tensor(
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ from cutlass.cute.nvgpu import cpasync, tcgen05
|
|||||||
import cutlass.torch as cutlass_torch
|
import cutlass.torch as cutlass_torch
|
||||||
import cutlass.utils as utils
|
import cutlass.utils as utils
|
||||||
import cutlass.pipeline as pipeline
|
import cutlass.pipeline as pipeline
|
||||||
|
import cutlass.cute.testing as testing
|
||||||
import cutlass.utils.blackwell_helpers as sm100_utils
|
import cutlass.utils.blackwell_helpers as sm100_utils
|
||||||
from cutlass.cute.runtime import from_dlpack
|
from cutlass.cute.runtime import from_dlpack
|
||||||
|
|
||||||
@@ -226,7 +227,7 @@ class PersistentDenseGemmKernel:
|
|||||||
self.cta_sync_bar_id = 0
|
self.cta_sync_bar_id = 0
|
||||||
self.epilog_sync_bar_id = 1
|
self.epilog_sync_bar_id = 1
|
||||||
self.tmem_ptr_sync_bar_id = 2
|
self.tmem_ptr_sync_bar_id = 2
|
||||||
self.smem_capacity = sm100_utils.SMEM_CAPACITY["sm100"]
|
self.smem_capacity = utils.get_smem_capacity_in_bytes("sm_100")
|
||||||
|
|
||||||
def _setup_attributes(self):
|
def _setup_attributes(self):
|
||||||
"""Set up configurations that are dependent on GEMM inputs
|
"""Set up configurations that are dependent on GEMM inputs
|
||||||
@@ -1308,11 +1309,7 @@ class PersistentDenseGemmKernel:
|
|||||||
copy_atom_r2s = sm100_utils.get_smem_store_op(
|
copy_atom_r2s = sm100_utils.get_smem_store_op(
|
||||||
self.c_layout, self.c_dtype, self.acc_dtype, tiled_copy_t2r
|
self.c_layout, self.c_dtype, self.acc_dtype, tiled_copy_t2r
|
||||||
)
|
)
|
||||||
tiled_copy_r2s = cute.make_tiled_copy(
|
tiled_copy_r2s = cute.make_tiled_copy_D(copy_atom_r2s, tiled_copy_t2r)
|
||||||
copy_atom_r2s,
|
|
||||||
layout_tv=tiled_copy_t2r.layout_dst_tv_tiled,
|
|
||||||
tiler_mn=tiled_copy_t2r.tiler_mn,
|
|
||||||
)
|
|
||||||
# (R2S, R2S_M, R2S_N, PIPE_D)
|
# (R2S, R2S_M, R2S_N, PIPE_D)
|
||||||
thr_copy_r2s = tiled_copy_r2s.get_slice(tidx)
|
thr_copy_r2s = tiled_copy_r2s.get_slice(tidx)
|
||||||
tRS_sC = thr_copy_r2s.partition_D(sC)
|
tRS_sC = thr_copy_r2s.partition_D(sC)
|
||||||
@@ -1824,7 +1821,7 @@ class PersistentDenseGemmKernel:
|
|||||||
return can_implement
|
return can_implement
|
||||||
|
|
||||||
|
|
||||||
def run_dense_gemm(
|
def run(
|
||||||
mnkl: Tuple[int, int, int, int],
|
mnkl: Tuple[int, int, int, int],
|
||||||
ab_dtype: Type[cutlass.Numeric],
|
ab_dtype: Type[cutlass.Numeric],
|
||||||
c_dtype: Type[cutlass.Numeric],
|
c_dtype: Type[cutlass.Numeric],
|
||||||
@@ -1832,17 +1829,58 @@ def run_dense_gemm(
|
|||||||
a_major: str,
|
a_major: str,
|
||||||
b_major: str,
|
b_major: str,
|
||||||
c_major: str,
|
c_major: str,
|
||||||
mma_tiler_mn: Tuple[int, int],
|
mma_tiler_mn: Tuple[int, int] = (256, 256),
|
||||||
cluster_shape_mn: Tuple[int, int],
|
cluster_shape_mn: Tuple[int, int] = (2, 1),
|
||||||
use_2cta_instrs: bool,
|
use_2cta_instrs: bool = True,
|
||||||
use_tma_store: bool,
|
use_tma_store: bool = True,
|
||||||
tolerance: float,
|
tolerance: float = 1e-01,
|
||||||
warmup_iterations: int = 0,
|
warmup_iterations: int = 0,
|
||||||
iterations: int = 1,
|
iterations: int = 1,
|
||||||
skip_ref_check: bool = False,
|
skip_ref_check: bool = False,
|
||||||
|
use_cold_l2: bool = False,
|
||||||
|
**kwargs,
|
||||||
):
|
):
|
||||||
"""
|
"""Execute a persistent batched dense GEMM operation on Blackwell architecture with performance benchmarking.
|
||||||
Prepare A/B/C tensors, launch GPU kernel, and reference checking.
|
|
||||||
|
This function prepares input tensors, configures and launches the persistent GEMM kernel,
|
||||||
|
optionally performs reference validation, and benchmarks the execution performance.
|
||||||
|
|
||||||
|
:param mnkl: Problem size (M, N, K, L)
|
||||||
|
:type mnkl: Tuple[int, int, int, int]
|
||||||
|
:param ab_dtype: Data type for input tensors A and B
|
||||||
|
:type ab_dtype: Type[cutlass.Numeric]
|
||||||
|
:param c_dtype: Data type for output tensor C
|
||||||
|
:type c_dtype: Type[cutlass.Numeric]
|
||||||
|
:param acc_dtype: Data type for accumulation during matrix multiplication
|
||||||
|
:type acc_dtype: Type[cutlass.Numeric]
|
||||||
|
:param a_major/b_major/c_major: Memory layout of tensor A/B/C
|
||||||
|
:type a_major/b_major/c_major: str
|
||||||
|
:param mma_tiler_mn: MMA tiling size. If not specified in the decorator parameters, the autotuner will use the
|
||||||
|
default value of (256, 256). Otherwise, the autotuner will use the value specified in the decorator parameters.
|
||||||
|
:type mma_tiler_mn: Tuple[int, int], optional
|
||||||
|
:param cluster_shape_mn: Cluster shape. If not specified in the decorator parameters, the autotuner will use the
|
||||||
|
default value of (2, 1). Otherwise, the autotuner will use the value specified in the decorator parameters.
|
||||||
|
:type cluster_shape_mn: Tuple[int, int], optional
|
||||||
|
:param use_2cta_instrs: Whether to use 2CTA instructions. If not specified in the decorator parameters, the autotuner
|
||||||
|
will use the default value of True. Otherwise, the autotuner will use the value specified in the decorator parameters.
|
||||||
|
:type use_2cta_instrs: bool, optional
|
||||||
|
:param use_tma_store: Whether to use TMA store. If not specified in the decorator parameters, the autotuner will use
|
||||||
|
the default value of True. Otherwise, the autotuner will use the value specified in the decorator parameters.
|
||||||
|
:type use_tma_store: bool, optional
|
||||||
|
:param tolerance: Tolerance value for reference validation comparison, defaults to 1e-01
|
||||||
|
:type tolerance: float, optional
|
||||||
|
:param warmup_iterations: Number of warmup iterations before benchmarking, defaults to 0
|
||||||
|
:type warmup_iterations: int, optional
|
||||||
|
:param iterations: Number of benchmark iterations to run, defaults to 1
|
||||||
|
:type iterations: int, optional
|
||||||
|
:param skip_ref_check: Whether to skip reference result validation, defaults to False
|
||||||
|
:type skip_ref_check: bool, optional
|
||||||
|
:param use_cold_l2: Whether to use circular buffer strategy to ensure cold L2 cache, defaults to False
|
||||||
|
:type use_cold_l2: bool, optional
|
||||||
|
:raises RuntimeError: If CUDA GPU is not available
|
||||||
|
:raises ValueError: If the configuration is invalid or unsupported by the kernel
|
||||||
|
:return: Execution time of the GEMM kernel
|
||||||
|
:rtype: float
|
||||||
"""
|
"""
|
||||||
print(f"Running Blackwell Persistent Dense GEMM test with:")
|
print(f"Running Blackwell Persistent Dense GEMM test with:")
|
||||||
print(f"mnkl: {mnkl}")
|
print(f"mnkl: {mnkl}")
|
||||||
@@ -1855,6 +1893,7 @@ def run_dense_gemm(
|
|||||||
print(f"Warmup iterations: {warmup_iterations}")
|
print(f"Warmup iterations: {warmup_iterations}")
|
||||||
print(f"Iterations: {iterations}")
|
print(f"Iterations: {iterations}")
|
||||||
print(f"Skip reference checking: {skip_ref_check}")
|
print(f"Skip reference checking: {skip_ref_check}")
|
||||||
|
print(f"Use cold L2: {'True' if use_cold_l2 else 'False'}")
|
||||||
|
|
||||||
# Unpack parameters
|
# Unpack parameters
|
||||||
m, n, k, l = mnkl
|
m, n, k, l = mnkl
|
||||||
@@ -1931,15 +1970,15 @@ def run_dense_gemm(
|
|||||||
is_dynamic_layout=is_dynamic_layout,
|
is_dynamic_layout=is_dynamic_layout,
|
||||||
)
|
)
|
||||||
|
|
||||||
return f32_torch_tensor, cute_tensor, torch_tensor
|
return f32_torch_tensor, cute_tensor, torch_tensor, torch_tensor_cpu
|
||||||
|
|
||||||
a_ref, a_tensor, a_torch = create_and_permute_tensor(
|
a_ref, a_tensor, a_torch, a_torch_cpu = create_and_permute_tensor(
|
||||||
l, m, k, a_major == "m", ab_dtype, is_dynamic_layout=True
|
l, m, k, a_major == "m", ab_dtype, is_dynamic_layout=True
|
||||||
)
|
)
|
||||||
b_ref, b_tensor, b_torch = create_and_permute_tensor(
|
b_ref, b_tensor, b_torch, b_torch_cpu = create_and_permute_tensor(
|
||||||
l, n, k, b_major == "n", ab_dtype, is_dynamic_layout=True
|
l, n, k, b_major == "n", ab_dtype, is_dynamic_layout=True
|
||||||
)
|
)
|
||||||
c_ref, c_tensor, c_torch = create_and_permute_tensor(
|
c_ref, c_tensor, c_torch, c_torch_cpu = create_and_permute_tensor(
|
||||||
l, m, n, c_major == "m", c_dtype, is_dynamic_layout=True
|
l, m, n, c_major == "m", c_dtype, is_dynamic_layout=True
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1967,16 +2006,8 @@ def run_dense_gemm(
|
|||||||
gemm, a_tensor, b_tensor, c_tensor, max_active_clusters, current_stream
|
gemm, a_tensor, b_tensor, c_tensor, max_active_clusters, current_stream
|
||||||
)
|
)
|
||||||
|
|
||||||
# Launch GPU kernel
|
|
||||||
# Warm up
|
|
||||||
for i in range(warmup_iterations):
|
|
||||||
compiled_gemm(a_tensor, b_tensor, c_tensor, current_stream)
|
|
||||||
# Execution
|
|
||||||
for i in range(iterations):
|
|
||||||
compiled_gemm(a_tensor, b_tensor, c_tensor, current_stream)
|
|
||||||
|
|
||||||
# Compute reference result
|
|
||||||
if not skip_ref_check:
|
if not skip_ref_check:
|
||||||
|
compiled_gemm(a_tensor, b_tensor, c_tensor, current_stream)
|
||||||
if ab_dtype in {
|
if ab_dtype in {
|
||||||
cutlass.Int8,
|
cutlass.Int8,
|
||||||
cutlass.Uint8,
|
cutlass.Uint8,
|
||||||
@@ -2028,6 +2059,40 @@ def run_dense_gemm(
|
|||||||
rtol=1e-05,
|
rtol=1e-05,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def generate_tensors():
|
||||||
|
a_tensor, _ = cutlass_torch.cute_tensor_like(
|
||||||
|
a_torch_cpu, ab_dtype, is_dynamic_layout=True, assumed_align=16
|
||||||
|
)
|
||||||
|
b_tensor, _ = cutlass_torch.cute_tensor_like(
|
||||||
|
b_torch_cpu, ab_dtype, is_dynamic_layout=True, assumed_align=16
|
||||||
|
)
|
||||||
|
c_tensor, _ = cutlass_torch.cute_tensor_like(
|
||||||
|
c_torch_cpu, c_dtype, is_dynamic_layout=True, assumed_align=16
|
||||||
|
)
|
||||||
|
return testing.JitArguments(a_tensor, b_tensor, c_tensor, current_stream)
|
||||||
|
|
||||||
|
workspace_count = 1
|
||||||
|
if use_cold_l2:
|
||||||
|
one_workspace_bytes = (
|
||||||
|
a_torch_cpu.numel() * a_torch_cpu.element_size()
|
||||||
|
+ b_torch_cpu.numel() * b_torch_cpu.element_size()
|
||||||
|
+ c_torch_cpu.numel() * c_torch_cpu.element_size()
|
||||||
|
)
|
||||||
|
workspace_count = testing.get_workspace_count(
|
||||||
|
one_workspace_bytes, warmup_iterations, iterations
|
||||||
|
)
|
||||||
|
|
||||||
|
exec_time = testing.benchmark(
|
||||||
|
compiled_gemm,
|
||||||
|
workspace_generator=generate_tensors,
|
||||||
|
workspace_count=workspace_count,
|
||||||
|
stream=current_stream,
|
||||||
|
warmup_iterations=warmup_iterations,
|
||||||
|
iterations=iterations,
|
||||||
|
)
|
||||||
|
|
||||||
|
return exec_time # Return execution time in microseconds
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
@@ -2090,6 +2155,12 @@ if __name__ == "__main__":
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--skip_ref_check", action="store_true", help="Skip reference checking"
|
"--skip_ref_check", action="store_true", help="Skip reference checking"
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--use_cold_l2",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="Use circular buffer tensor sets to ensure L2 cold cache",
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -2102,7 +2173,7 @@ if __name__ == "__main__":
|
|||||||
if len(args.cluster_shape_mn) != 2:
|
if len(args.cluster_shape_mn) != 2:
|
||||||
parser.error("--cluster_shape_mn must contain exactly 2 values")
|
parser.error("--cluster_shape_mn must contain exactly 2 values")
|
||||||
|
|
||||||
run_dense_gemm(
|
run(
|
||||||
args.mnkl,
|
args.mnkl,
|
||||||
args.ab_dtype,
|
args.ab_dtype,
|
||||||
args.c_dtype,
|
args.c_dtype,
|
||||||
@@ -2118,5 +2189,6 @@ if __name__ == "__main__":
|
|||||||
args.warmup_iterations,
|
args.warmup_iterations,
|
||||||
args.iterations,
|
args.iterations,
|
||||||
args.skip_ref_check,
|
args.skip_ref_check,
|
||||||
|
args.use_cold_l2,
|
||||||
)
|
)
|
||||||
print("PASS")
|
print("PASS")
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ class DenseGemmKernel:
|
|||||||
|
|
||||||
self.occupancy = 1
|
self.occupancy = 1
|
||||||
self.threads_per_cta = 128
|
self.threads_per_cta = 128
|
||||||
self.smem_capacity = sm100_utils.SMEM_CAPACITY["sm100"]
|
self.smem_capacity = utils.get_smem_capacity_in_bytes("sm_100")
|
||||||
|
|
||||||
def _setup_attributes(self):
|
def _setup_attributes(self):
|
||||||
"""Set up configurations that are dependent on GEMM inputs
|
"""Set up configurations that are dependent on GEMM inputs
|
||||||
@@ -1063,11 +1063,7 @@ class DenseGemmKernel:
|
|||||||
copy_atom_r2s = sm100_utils.get_smem_store_op(
|
copy_atom_r2s = sm100_utils.get_smem_store_op(
|
||||||
self.c_layout, self.c_dtype, self.acc_dtype, tiled_copy_t2r
|
self.c_layout, self.c_dtype, self.acc_dtype, tiled_copy_t2r
|
||||||
)
|
)
|
||||||
tiled_copy_r2s = cute.make_tiled_copy(
|
tiled_copy_r2s = cute.make_tiled_copy_D(copy_atom_r2s, tiled_copy_t2r)
|
||||||
copy_atom_r2s,
|
|
||||||
layout_tv=tiled_copy_t2r.layout_dst_tv_tiled,
|
|
||||||
tiler_mn=tiled_copy_t2r.tiler_mn,
|
|
||||||
)
|
|
||||||
# (R2S, R2S_M, R2S_N, PIPE_D)
|
# (R2S, R2S_M, R2S_N, PIPE_D)
|
||||||
thr_copy_r2s = tiled_copy_r2s.get_slice(tidx)
|
thr_copy_r2s = tiled_copy_r2s.get_slice(tidx)
|
||||||
tRS_sC = thr_copy_r2s.partition_D(sC)
|
tRS_sC = thr_copy_r2s.partition_D(sC)
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import cutlass.utils as utils
|
|||||||
import cutlass.pipeline as pipeline
|
import cutlass.pipeline as pipeline
|
||||||
import cutlass.torch as cutlass_torch
|
import cutlass.torch as cutlass_torch
|
||||||
import cutlass.utils.blackwell_helpers as sm100_utils
|
import cutlass.utils.blackwell_helpers as sm100_utils
|
||||||
|
import cutlass.cute.testing as testing
|
||||||
from cutlass.cute.runtime import from_dlpack
|
from cutlass.cute.runtime import from_dlpack
|
||||||
from cutlass.cute.typing import Int32, Int64, Float32, Boolean
|
from cutlass.cute.typing import Int32, Int64, Float32, Boolean
|
||||||
|
|
||||||
@@ -90,7 +91,7 @@ Constraints for this example:
|
|||||||
* Number of heads in Q must be divisible by number of heads in K
|
* Number of heads in Q must be divisible by number of heads in K
|
||||||
* mma_tiler_mn must be 128,128
|
* mma_tiler_mn must be 128,128
|
||||||
* Batch size must be the same for Q, K, and V tensors
|
* Batch size must be the same for Q, K, and V tensors
|
||||||
* For causal masking, use --has_casual_mask (note: specify without =True/False)
|
* For causal masking, use --is_causal (note: specify without =True/False)
|
||||||
* For persistent scheduling, use --is_persistent (note: specify without =True/False)
|
* For persistent scheduling, use --is_persistent (note: specify without =True/False)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -2373,11 +2374,7 @@ class BlackwellFusedMultiHeadAttentionForward:
|
|||||||
smem_copy_atom = sm100_utils.get_smem_store_op(
|
smem_copy_atom = sm100_utils.get_smem_store_op(
|
||||||
self.o_layout, self.o_dtype, self.pv_acc_dtype, tiled_tmem_load
|
self.o_layout, self.o_dtype, self.pv_acc_dtype, tiled_tmem_load
|
||||||
)
|
)
|
||||||
tiled_smem_store = cute.make_tiled_copy(
|
tiled_smem_store = cute.make_tiled_copy_D(smem_copy_atom, tiled_tmem_load)
|
||||||
smem_copy_atom,
|
|
||||||
layout_tv=tiled_tmem_load.layout_dst_tv_tiled,
|
|
||||||
tiler_mn=tiled_tmem_load.tiler_mn,
|
|
||||||
)
|
|
||||||
|
|
||||||
tTMEM_LOADtO = thr_tmem_load.partition_S(tOtO_i[(None, None), None])
|
tTMEM_LOADtO = thr_tmem_load.partition_S(tOtO_i[(None, None), None])
|
||||||
tTMEM_LOADsO = thr_tmem_load.partition_D(tOsO_i[(None, None), None])
|
tTMEM_LOADsO = thr_tmem_load.partition_D(tOsO_i[(None, None), None])
|
||||||
@@ -2619,7 +2616,7 @@ class BlackwellFusedMultiHeadAttentionForward:
|
|||||||
return tile_sched_params, grid
|
return tile_sched_params, grid
|
||||||
|
|
||||||
|
|
||||||
def run_fmha_and_verify(
|
def run(
|
||||||
q_shape: Tuple[int, int, int, int] | Tuple[int, Tuple[int, ...], int, int],
|
q_shape: Tuple[int, int, int, int] | Tuple[int, Tuple[int, ...], int, int],
|
||||||
k_shape: Tuple[int, int, int, int] | Tuple[int, Tuple[int, ...], int, int],
|
k_shape: Tuple[int, int, int, int] | Tuple[int, Tuple[int, ...], int, int],
|
||||||
in_dtype: Type[cutlass.Numeric],
|
in_dtype: Type[cutlass.Numeric],
|
||||||
@@ -2628,7 +2625,7 @@ def run_fmha_and_verify(
|
|||||||
pv_acc_dtype: Type[cutlass.Numeric],
|
pv_acc_dtype: Type[cutlass.Numeric],
|
||||||
mma_tiler_mn: Tuple[int, int],
|
mma_tiler_mn: Tuple[int, int],
|
||||||
is_persistent: bool,
|
is_persistent: bool,
|
||||||
has_casual_mask: bool,
|
is_causal: bool,
|
||||||
scale_q: float,
|
scale_q: float,
|
||||||
scale_k: float,
|
scale_k: float,
|
||||||
scale_v: float,
|
scale_v: float,
|
||||||
@@ -2638,6 +2635,8 @@ def run_fmha_and_verify(
|
|||||||
warmup_iterations: int,
|
warmup_iterations: int,
|
||||||
iterations: int,
|
iterations: int,
|
||||||
skip_ref_check: bool,
|
skip_ref_check: bool,
|
||||||
|
use_cold_l2: bool = False,
|
||||||
|
**kwargs,
|
||||||
):
|
):
|
||||||
"""Execute Fused Multi-Head Attention (FMHA) on Blackwell architecture and validate results.
|
"""Execute Fused Multi-Head Attention (FMHA) on Blackwell architecture and validate results.
|
||||||
|
|
||||||
@@ -2670,8 +2669,8 @@ def run_fmha_and_verify(
|
|||||||
:type mma_tiler_mn: Tuple[int, int]
|
:type mma_tiler_mn: Tuple[int, int]
|
||||||
:param is_persistent: Whether to use persistent kernel optimization
|
:param is_persistent: Whether to use persistent kernel optimization
|
||||||
:type is_persistent: bool
|
:type is_persistent: bool
|
||||||
:param has_casual_mask: Whether to apply causal masking
|
:param is_causal: Whether to apply causal masking
|
||||||
:type has_casual_mask: bool
|
:type is_causal: bool
|
||||||
:param scale_q: Scaling factor for query tensor
|
:param scale_q: Scaling factor for query tensor
|
||||||
:type scale_q: float
|
:type scale_q: float
|
||||||
:param scale_k: Scaling factor for key tensor
|
:param scale_k: Scaling factor for key tensor
|
||||||
@@ -2690,9 +2689,13 @@ def run_fmha_and_verify(
|
|||||||
:type iterations: int
|
:type iterations: int
|
||||||
:param skip_ref_check: Skip validation against reference implementation
|
:param skip_ref_check: Skip validation against reference implementation
|
||||||
:type skip_ref_check: bool
|
:type skip_ref_check: bool
|
||||||
|
:param use_cold_l2: Whether to use circular buffer strategy to ensure cold L2 cache
|
||||||
|
:type use_cold_l2: bool
|
||||||
|
|
||||||
:raises ValueError: If input shapes are incompatible or head dimension is unsupported
|
:raises ValueError: If input shapes are incompatible or head dimension is unsupported
|
||||||
:raises RuntimeError: If GPU is unavailable for computation
|
:raises RuntimeError: If GPU is unavailable for computation
|
||||||
|
:return: Execution time of the FMHA kernel in microseconds
|
||||||
|
:rtype: float
|
||||||
"""
|
"""
|
||||||
|
|
||||||
print(f"Running Blackwell SM100 FMHA test with:")
|
print(f"Running Blackwell SM100 FMHA test with:")
|
||||||
@@ -2704,13 +2707,17 @@ def run_fmha_and_verify(
|
|||||||
print(f" pv_acc_dtype: {pv_acc_dtype}")
|
print(f" pv_acc_dtype: {pv_acc_dtype}")
|
||||||
print(f" mma_tiler_mn: {mma_tiler_mn}")
|
print(f" mma_tiler_mn: {mma_tiler_mn}")
|
||||||
print(f" is_persistent: {is_persistent}")
|
print(f" is_persistent: {is_persistent}")
|
||||||
print(f" has_casual_mask: {has_casual_mask}")
|
print(f" is_causal: {is_causal}")
|
||||||
print(f" scale_q: {scale_q}")
|
print(f" scale_q: {scale_q}")
|
||||||
print(f" scale_k: {scale_k}")
|
print(f" scale_k: {scale_k}")
|
||||||
print(f" scale_v: {scale_v}")
|
print(f" scale_v: {scale_v}")
|
||||||
print(f" inv_scale_o: {inv_scale_o}")
|
print(f" inv_scale_o: {inv_scale_o}")
|
||||||
print(f" scale_softmax: {scale_softmax}")
|
print(f" scale_softmax: {scale_softmax}")
|
||||||
print(f" tolerance: {tolerance}")
|
print(f" tolerance: {tolerance}")
|
||||||
|
print(f" warmup_iterations: {warmup_iterations}")
|
||||||
|
print(f" iterations: {iterations}")
|
||||||
|
print(f" skip_ref_check: {skip_ref_check}")
|
||||||
|
print(f" use_cold_l2: {use_cold_l2}")
|
||||||
|
|
||||||
# Unpack parameters
|
# Unpack parameters
|
||||||
b, s_q, h_q, d = q_shape
|
b, s_q, h_q, d = q_shape
|
||||||
@@ -2882,7 +2889,7 @@ def run_fmha_and_verify(
|
|||||||
mma_tiler = (*mma_tiler_mn, d)
|
mma_tiler = (*mma_tiler_mn, d)
|
||||||
|
|
||||||
mask_type = MaskType.NO_MASK
|
mask_type = MaskType.NO_MASK
|
||||||
if has_casual_mask:
|
if is_causal:
|
||||||
mask_type = MaskType.CAUSAL_MASK
|
mask_type = MaskType.CAUSAL_MASK
|
||||||
else:
|
else:
|
||||||
if isinstance(s_k, tuple):
|
if isinstance(s_k, tuple):
|
||||||
@@ -2942,41 +2949,7 @@ def run_fmha_and_verify(
|
|||||||
compilation_time = time.time() - start_time
|
compilation_time = time.time() - start_time
|
||||||
print(f"Compilation time: {compilation_time:.4f} seconds")
|
print(f"Compilation time: {compilation_time:.4f} seconds")
|
||||||
|
|
||||||
# Warmup
|
def run_torch_fmha(q, k, v, scale_softmax=1.0, scale_output=1.0, is_causal=False):
|
||||||
for _ in range(warmup_iterations):
|
|
||||||
compiled_fmha(
|
|
||||||
q_tensor.iterator,
|
|
||||||
k_tensor.iterator,
|
|
||||||
v_tensor.iterator,
|
|
||||||
o_tensor.iterator,
|
|
||||||
problem_size,
|
|
||||||
cum_seqlen_q,
|
|
||||||
cum_seqlen_k,
|
|
||||||
scale_softmax_log2,
|
|
||||||
scale_output,
|
|
||||||
current_stream,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Execute kernel
|
|
||||||
for _ in range(iterations):
|
|
||||||
compiled_fmha(
|
|
||||||
q_tensor.iterator,
|
|
||||||
k_tensor.iterator,
|
|
||||||
v_tensor.iterator,
|
|
||||||
o_tensor.iterator,
|
|
||||||
problem_size,
|
|
||||||
cum_seqlen_q,
|
|
||||||
cum_seqlen_k,
|
|
||||||
scale_softmax_log2,
|
|
||||||
scale_output,
|
|
||||||
current_stream,
|
|
||||||
)
|
|
||||||
|
|
||||||
torch.cuda.synchronize()
|
|
||||||
|
|
||||||
def run_torch_fmha(
|
|
||||||
q, k, v, scale_softmax=1.0, scale_output=1.0, has_casual_mask=False
|
|
||||||
):
|
|
||||||
h_q = q.shape[2]
|
h_q = q.shape[2]
|
||||||
h_k = k.shape[2]
|
h_k = k.shape[2]
|
||||||
|
|
||||||
@@ -3005,7 +2978,7 @@ def run_fmha_and_verify(
|
|||||||
v = v.transpose(1, 2)
|
v = v.transpose(1, 2)
|
||||||
|
|
||||||
# For the situation that torch has not supported, we need to handle it manually
|
# For the situation that torch has not supported, we need to handle it manually
|
||||||
situation1 = has_casual_mask and (q.is_nested or k.is_nested)
|
situation1 = is_causal and (q.is_nested or k.is_nested)
|
||||||
situation2 = (q.is_nested and not k.is_nested) or (
|
situation2 = (q.is_nested and not k.is_nested) or (
|
||||||
not q.is_nested and k.is_nested
|
not q.is_nested and k.is_nested
|
||||||
)
|
)
|
||||||
@@ -3025,8 +2998,9 @@ def run_fmha_and_verify(
|
|||||||
attn_mask=None,
|
attn_mask=None,
|
||||||
dropout_p=0.0,
|
dropout_p=0.0,
|
||||||
scale=scale_softmax,
|
scale=scale_softmax,
|
||||||
is_causal=has_casual_mask,
|
is_causal=is_causal,
|
||||||
)
|
)
|
||||||
|
ref_i = ref_i.transpose(0, 1) * scale_output
|
||||||
ref_list.append(ref_i)
|
ref_list.append(ref_i)
|
||||||
if q.is_nested:
|
if q.is_nested:
|
||||||
ref = torch.nested.nested_tensor(ref_list, layout=torch.jagged)
|
ref = torch.nested.nested_tensor(ref_list, layout=torch.jagged)
|
||||||
@@ -3040,15 +3014,28 @@ def run_fmha_and_verify(
|
|||||||
attn_mask=None,
|
attn_mask=None,
|
||||||
dropout_p=0.0,
|
dropout_p=0.0,
|
||||||
scale=scale_softmax,
|
scale=scale_softmax,
|
||||||
is_causal=has_casual_mask,
|
is_causal=is_causal,
|
||||||
)
|
)
|
||||||
ref = ref.transpose(1, 2) * scale_output
|
ref = ref.transpose(1, 2) * scale_output
|
||||||
return ref
|
return ref
|
||||||
|
|
||||||
if not skip_ref_check:
|
if not skip_ref_check:
|
||||||
|
# Execute kernel once for reference checking
|
||||||
|
compiled_fmha(
|
||||||
|
q_tensor.iterator,
|
||||||
|
k_tensor.iterator,
|
||||||
|
v_tensor.iterator,
|
||||||
|
o_tensor.iterator,
|
||||||
|
problem_size,
|
||||||
|
cum_seqlen_q,
|
||||||
|
cum_seqlen_k,
|
||||||
|
scale_softmax_log2,
|
||||||
|
scale_output,
|
||||||
|
current_stream,
|
||||||
|
)
|
||||||
print("Verifying results...")
|
print("Verifying results...")
|
||||||
o_ref = run_torch_fmha(
|
o_ref = run_torch_fmha(
|
||||||
q_ref, k_ref, v_ref, scale_softmax, scale_output, has_casual_mask
|
q_ref, k_ref, v_ref, scale_softmax, scale_output, is_causal
|
||||||
)
|
)
|
||||||
|
|
||||||
if o_ref.is_nested:
|
if o_ref.is_nested:
|
||||||
@@ -3095,6 +3082,76 @@ def run_fmha_and_verify(
|
|||||||
torch.testing.assert_close(o_result, o_ref, atol=tolerance, rtol=1e-05)
|
torch.testing.assert_close(o_result, o_ref, atol=tolerance, rtol=1e-05)
|
||||||
print("Results verified successfully!")
|
print("Results verified successfully!")
|
||||||
|
|
||||||
|
def generate_tensors():
|
||||||
|
_, q_tensor_workspace, _ = create_and_pad_tensor(
|
||||||
|
qo_shape,
|
||||||
|
qo_padding,
|
||||||
|
in_dtype,
|
||||||
|
s_cumsum=cum_seqlen_q_torch,
|
||||||
|
is_dynamic_layout=True,
|
||||||
|
)
|
||||||
|
_, k_tensor_workspace, _ = create_and_pad_tensor(
|
||||||
|
kv_shape,
|
||||||
|
kv_padding,
|
||||||
|
in_dtype,
|
||||||
|
s_cumsum=cum_seqlen_k_torch,
|
||||||
|
is_dynamic_layout=True,
|
||||||
|
)
|
||||||
|
_, v_tensor_workspace, _ = create_and_pad_tensor(
|
||||||
|
kv_shape,
|
||||||
|
kv_padding,
|
||||||
|
in_dtype,
|
||||||
|
s_cumsum=cum_seqlen_k_torch,
|
||||||
|
is_dynamic_layout=True,
|
||||||
|
)
|
||||||
|
_, o_tensor_workspace, _ = create_and_pad_tensor(
|
||||||
|
qo_shape,
|
||||||
|
qo_padding,
|
||||||
|
out_dtype,
|
||||||
|
s_cumsum=cum_seqlen_q_torch,
|
||||||
|
is_dynamic_layout=True,
|
||||||
|
)
|
||||||
|
return testing.JitArguments(
|
||||||
|
q_tensor_workspace.iterator,
|
||||||
|
k_tensor_workspace.iterator,
|
||||||
|
v_tensor_workspace.iterator,
|
||||||
|
o_tensor_workspace.iterator,
|
||||||
|
problem_size,
|
||||||
|
cum_seqlen_q,
|
||||||
|
cum_seqlen_k,
|
||||||
|
scale_softmax_log2,
|
||||||
|
scale_output,
|
||||||
|
current_stream,
|
||||||
|
)
|
||||||
|
|
||||||
|
workspace_count = 1
|
||||||
|
if use_cold_l2:
|
||||||
|
q_torch_effective = q_torch.values() if q_torch.is_nested else q_torch
|
||||||
|
k_torch_effective = k_torch.values() if k_torch.is_nested else k_torch
|
||||||
|
v_torch_effective = v_torch.values() if v_torch.is_nested else v_torch
|
||||||
|
o_torch_effective = o_torch.values() if o_torch.is_nested else o_torch
|
||||||
|
one_workspace_bytes = (
|
||||||
|
q_torch_effective.numel() * q_torch_effective.element_size()
|
||||||
|
+ k_torch_effective.numel() * k_torch_effective.element_size()
|
||||||
|
+ v_torch_effective.numel() * v_torch_effective.element_size()
|
||||||
|
+ o_torch_effective.numel() * o_torch_effective.element_size()
|
||||||
|
)
|
||||||
|
workspace_count = testing.get_workspace_count(
|
||||||
|
one_workspace_bytes, warmup_iterations, iterations
|
||||||
|
)
|
||||||
|
|
||||||
|
exec_time = testing.benchmark(
|
||||||
|
compiled_fmha,
|
||||||
|
workspace_generator=generate_tensors,
|
||||||
|
workspace_count=workspace_count,
|
||||||
|
stream=current_stream,
|
||||||
|
warmup_iterations=warmup_iterations,
|
||||||
|
iterations=iterations,
|
||||||
|
)
|
||||||
|
|
||||||
|
return exec_time # Return execution time in microseconds
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
def parse_comma_separated_ints(s: str):
|
def parse_comma_separated_ints(s: str):
|
||||||
@@ -3185,7 +3242,7 @@ if __name__ == "__main__":
|
|||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--has_casual_mask",
|
"--is_causal",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Whether to use casual mask",
|
help="Whether to use casual mask",
|
||||||
)
|
)
|
||||||
@@ -3263,6 +3320,13 @@ if __name__ == "__main__":
|
|||||||
help="Skip reference check",
|
help="Skip reference check",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--use_cold_l2",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="Use circular buffer tensor sets to ensure L2 cold cache",
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if len(args.q_shape) != 4:
|
if len(args.q_shape) != 4:
|
||||||
@@ -3279,7 +3343,7 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
torch.manual_seed(1111)
|
torch.manual_seed(1111)
|
||||||
|
|
||||||
run_fmha_and_verify(
|
run(
|
||||||
args.q_shape,
|
args.q_shape,
|
||||||
args.k_shape,
|
args.k_shape,
|
||||||
args.in_dtype,
|
args.in_dtype,
|
||||||
@@ -3288,7 +3352,7 @@ if __name__ == "__main__":
|
|||||||
args.pv_acc_dtype,
|
args.pv_acc_dtype,
|
||||||
args.mma_tiler_mn,
|
args.mma_tiler_mn,
|
||||||
args.is_persistent,
|
args.is_persistent,
|
||||||
args.has_casual_mask,
|
args.is_causal,
|
||||||
args.scale_q,
|
args.scale_q,
|
||||||
args.scale_k,
|
args.scale_k,
|
||||||
args.scale_v,
|
args.scale_v,
|
||||||
@@ -3298,6 +3362,7 @@ if __name__ == "__main__":
|
|||||||
args.warmup_iterations,
|
args.warmup_iterations,
|
||||||
args.iterations,
|
args.iterations,
|
||||||
args.skip_ref_check,
|
args.skip_ref_check,
|
||||||
|
args.use_cold_l2,
|
||||||
)
|
)
|
||||||
|
|
||||||
print("PASS")
|
print("PASS")
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import cuda.bindings.driver as cuda
|
|||||||
|
|
||||||
import cutlass
|
import cutlass
|
||||||
import cutlass.cute as cute
|
import cutlass.cute as cute
|
||||||
|
import cutlass.cute.testing as testing
|
||||||
import cutlass.utils as utils
|
import cutlass.utils as utils
|
||||||
from cutlass.cute.nvgpu import cpasync, tcgen05
|
from cutlass.cute.nvgpu import cpasync, tcgen05
|
||||||
import cutlass.utils.blackwell_helpers as sm100_utils
|
import cutlass.utils.blackwell_helpers as sm100_utils
|
||||||
@@ -157,7 +158,7 @@ class GroupedGemmKernel:
|
|||||||
self.tmem_ptr_sync_bar_id = 2
|
self.tmem_ptr_sync_bar_id = 2
|
||||||
# Barrier ID used by MMA/TMA warps to signal A/B tensormap initialization completion
|
# Barrier ID used by MMA/TMA warps to signal A/B tensormap initialization completion
|
||||||
self.tensormap_ab_init_bar_id = 4
|
self.tensormap_ab_init_bar_id = 4
|
||||||
self.smem_capacity = sm100_utils.SMEM_CAPACITY["sm100"]
|
self.smem_capacity = utils.get_smem_capacity_in_bytes("sm_100")
|
||||||
self.num_tma_load_bytes = 0
|
self.num_tma_load_bytes = 0
|
||||||
|
|
||||||
def _setup_attributes(self):
|
def _setup_attributes(self):
|
||||||
@@ -951,7 +952,7 @@ class GroupedGemmKernel:
|
|||||||
# Specialized MMA warp
|
# Specialized MMA warp
|
||||||
#
|
#
|
||||||
if warp_idx == self.mma_warp_id:
|
if warp_idx == self.mma_warp_id:
|
||||||
# initilize tensormap A, B for TMA warp
|
# initialize tensormap A, B for TMA warp
|
||||||
if cutlass.const_expr(self.delegate_tensormap_ab_init):
|
if cutlass.const_expr(self.delegate_tensormap_ab_init):
|
||||||
tensormap_manager.init_tensormap_from_atom(
|
tensormap_manager.init_tensormap_from_atom(
|
||||||
tma_atom_a, tensormap_a_init_ptr, self.mma_warp_id
|
tma_atom_a, tensormap_a_init_ptr, self.mma_warp_id
|
||||||
@@ -1540,11 +1541,7 @@ class GroupedGemmKernel:
|
|||||||
copy_atom_r2s = sm100_utils.get_smem_store_op(
|
copy_atom_r2s = sm100_utils.get_smem_store_op(
|
||||||
self.c_layout, self.c_dtype, self.acc_dtype, tiled_copy_t2r
|
self.c_layout, self.c_dtype, self.acc_dtype, tiled_copy_t2r
|
||||||
)
|
)
|
||||||
tiled_copy_r2s = cute.make_tiled_copy(
|
tiled_copy_r2s = cute.make_tiled_copy_D(copy_atom_r2s, tiled_copy_t2r)
|
||||||
copy_atom_r2s,
|
|
||||||
layout_tv=tiled_copy_t2r.layout_dst_tv_tiled,
|
|
||||||
tiler_mn=tiled_copy_t2r.tiler_mn,
|
|
||||||
)
|
|
||||||
# (R2S, R2S_M, R2S_N, PIPE_D)
|
# (R2S, R2S_M, R2S_N, PIPE_D)
|
||||||
thr_copy_r2s = tiled_copy_r2s.get_slice(tidx)
|
thr_copy_r2s = tiled_copy_r2s.get_slice(tidx)
|
||||||
tRS_sC = thr_copy_r2s.partition_D(sC)
|
tRS_sC = thr_copy_r2s.partition_D(sC)
|
||||||
@@ -1815,7 +1812,136 @@ class GroupedGemmKernel:
|
|||||||
tensor_memory_management_bytes = 12
|
tensor_memory_management_bytes = 12
|
||||||
|
|
||||||
|
|
||||||
def run_grouped_gemm(
|
# Create tensor and return the pointer, tensor, and stride
|
||||||
|
def create_tensor_and_stride(
|
||||||
|
l: int,
|
||||||
|
mode0: int,
|
||||||
|
mode1: int,
|
||||||
|
is_mode0_major: bool,
|
||||||
|
dtype: type[cutlass.Numeric],
|
||||||
|
is_dynamic_layout: bool = True,
|
||||||
|
torch_tensor_cpu: torch.Tensor = None,
|
||||||
|
) -> tuple[int, torch.Tensor, cute.Tensor, torch.Tensor, tuple[int, int]]:
|
||||||
|
"""Create a GPU tensor from scratch or based on an existing CPU tensor.
|
||||||
|
|
||||||
|
:param torch_tensor_cpu: Optional existing CPU tensor to reuse. If None, creates a new one.
|
||||||
|
:type torch_tensor_cpu: torch.Tensor, optional
|
||||||
|
"""
|
||||||
|
if torch_tensor_cpu is None:
|
||||||
|
# Create new CPU tensor
|
||||||
|
torch_tensor_cpu = cutlass_torch.matrix(l, mode0, mode1, is_mode0_major, dtype)
|
||||||
|
|
||||||
|
# Create GPU tensor from CPU tensor (new or existing)
|
||||||
|
cute_tensor, torch_tensor = cutlass_torch.cute_tensor_like(
|
||||||
|
torch_tensor_cpu, dtype, is_dynamic_layout, assumed_align=16
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
torch_tensor.data_ptr(),
|
||||||
|
torch_tensor,
|
||||||
|
cute_tensor,
|
||||||
|
torch_tensor_cpu,
|
||||||
|
torch_tensor.stride()[:-1],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def create_tensors_for_all_groups(
|
||||||
|
problem_sizes_mnkl: List[tuple[int, int, int, int]],
|
||||||
|
ab_dtype: Type[cutlass.Numeric],
|
||||||
|
c_dtype: Type[cutlass.Numeric],
|
||||||
|
a_major: str,
|
||||||
|
b_major: str,
|
||||||
|
c_major: str,
|
||||||
|
torch_fp32_tensors_abc: List[List[torch.Tensor]] = None,
|
||||||
|
) -> tuple[
|
||||||
|
List[List[int]],
|
||||||
|
List[List[torch.Tensor]],
|
||||||
|
List[tuple],
|
||||||
|
List[List[tuple]],
|
||||||
|
List[List[torch.Tensor]],
|
||||||
|
]:
|
||||||
|
if torch_fp32_tensors_abc is not None and len(torch_fp32_tensors_abc) != len(
|
||||||
|
problem_sizes_mnkl
|
||||||
|
):
|
||||||
|
raise ValueError("torch_fp32_tensors_abc must have one entry per group")
|
||||||
|
|
||||||
|
# Initialize lists to store tensors for all groups
|
||||||
|
new_torch_fp32_tensors_abc = (
|
||||||
|
[] if torch_fp32_tensors_abc is None else torch_fp32_tensors_abc
|
||||||
|
)
|
||||||
|
torch_tensors_abc = []
|
||||||
|
cute_tensors_abc = []
|
||||||
|
strides_abc = []
|
||||||
|
ptrs_abc = []
|
||||||
|
|
||||||
|
# Iterate through all groups and create tensors for each group
|
||||||
|
for group_idx, (m, n, k, l) in enumerate(problem_sizes_mnkl):
|
||||||
|
# Get existing CPU tensors if available, otherwise None
|
||||||
|
existing_cpu_a = (
|
||||||
|
torch_fp32_tensors_abc[group_idx][0] if torch_fp32_tensors_abc else None
|
||||||
|
)
|
||||||
|
existing_cpu_b = (
|
||||||
|
torch_fp32_tensors_abc[group_idx][1] if torch_fp32_tensors_abc else None
|
||||||
|
)
|
||||||
|
existing_cpu_c = (
|
||||||
|
torch_fp32_tensors_abc[group_idx][2] if torch_fp32_tensors_abc else None
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create tensors (reusing CPU tensors if provided)
|
||||||
|
(
|
||||||
|
ptr_a,
|
||||||
|
torch_tensor_a,
|
||||||
|
cute_tensor_a,
|
||||||
|
tensor_fp32_a,
|
||||||
|
stride_mk_a,
|
||||||
|
) = create_tensor_and_stride(
|
||||||
|
l, m, k, a_major == "m", ab_dtype, torch_tensor_cpu=existing_cpu_a
|
||||||
|
)
|
||||||
|
(
|
||||||
|
ptr_b,
|
||||||
|
torch_tensor_b,
|
||||||
|
cute_tensor_b,
|
||||||
|
tensor_fp32_b,
|
||||||
|
stride_nk_b,
|
||||||
|
) = create_tensor_and_stride(
|
||||||
|
l, n, k, b_major == "n", ab_dtype, torch_tensor_cpu=existing_cpu_b
|
||||||
|
)
|
||||||
|
(
|
||||||
|
ptr_c,
|
||||||
|
torch_tensor_c,
|
||||||
|
cute_tensor_c,
|
||||||
|
tensor_fp32_c,
|
||||||
|
stride_mn_c,
|
||||||
|
) = create_tensor_and_stride(
|
||||||
|
l, m, n, c_major == "m", c_dtype, torch_tensor_cpu=existing_cpu_c
|
||||||
|
)
|
||||||
|
|
||||||
|
# Only append to new_torch_fp32_tensors_abc if we created new CPU tensors
|
||||||
|
if torch_fp32_tensors_abc is None:
|
||||||
|
new_torch_fp32_tensors_abc.append(
|
||||||
|
[tensor_fp32_a, tensor_fp32_b, tensor_fp32_c]
|
||||||
|
)
|
||||||
|
|
||||||
|
ptrs_abc.append([ptr_a, ptr_b, ptr_c])
|
||||||
|
torch_tensors_abc.append([torch_tensor_a, torch_tensor_b, torch_tensor_c])
|
||||||
|
strides_abc.append([stride_mk_a, stride_nk_b, stride_mn_c])
|
||||||
|
cute_tensors_abc.append(
|
||||||
|
(
|
||||||
|
cute_tensor_a,
|
||||||
|
cute_tensor_b,
|
||||||
|
cute_tensor_c,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
ptrs_abc,
|
||||||
|
torch_tensors_abc,
|
||||||
|
cute_tensors_abc,
|
||||||
|
strides_abc,
|
||||||
|
new_torch_fp32_tensors_abc,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def run(
|
||||||
num_groups: int,
|
num_groups: int,
|
||||||
problem_sizes_mnkl: tuple[int, int, int, int],
|
problem_sizes_mnkl: tuple[int, int, int, int],
|
||||||
ab_dtype: Type[cutlass.Numeric],
|
ab_dtype: Type[cutlass.Numeric],
|
||||||
@@ -1832,8 +1958,16 @@ def run_grouped_gemm(
|
|||||||
warmup_iterations: int,
|
warmup_iterations: int,
|
||||||
iterations: int,
|
iterations: int,
|
||||||
skip_ref_check: bool,
|
skip_ref_check: bool,
|
||||||
|
use_cold_l2: bool = False,
|
||||||
|
**kwargs,
|
||||||
):
|
):
|
||||||
"""Run grouped GEMM example with specified configurations."""
|
"""Run grouped GEMM example with specified configurations.
|
||||||
|
|
||||||
|
:param use_cold_l2: Whether to use circular buffer strategy to ensure cold L2 cache, defaults to False
|
||||||
|
:type use_cold_l2: bool, optional
|
||||||
|
:return: Execution time of the GEMM kernel in microseconds
|
||||||
|
:rtype: float
|
||||||
|
"""
|
||||||
print(f"Running Blackwell Grouped GEMM test with:")
|
print(f"Running Blackwell Grouped GEMM test with:")
|
||||||
print(f"{num_groups} groups")
|
print(f"{num_groups} groups")
|
||||||
for i, (m, n, k, l) in enumerate(problem_sizes_mnkl):
|
for i, (m, n, k, l) in enumerate(problem_sizes_mnkl):
|
||||||
@@ -1847,6 +1981,7 @@ def run_grouped_gemm(
|
|||||||
print(f"Warmup iterations: {warmup_iterations}")
|
print(f"Warmup iterations: {warmup_iterations}")
|
||||||
print(f"Iterations: {iterations}")
|
print(f"Iterations: {iterations}")
|
||||||
print(f"Skip reference checking: {skip_ref_check}")
|
print(f"Skip reference checking: {skip_ref_check}")
|
||||||
|
print(f"Use cold L2: {'True' if use_cold_l2 else 'False'}")
|
||||||
|
|
||||||
# Skip unsupported types
|
# Skip unsupported types
|
||||||
if ab_dtype not in {
|
if ab_dtype not in {
|
||||||
@@ -1902,66 +2037,22 @@ def run_grouped_gemm(
|
|||||||
if not torch.cuda.is_available():
|
if not torch.cuda.is_available():
|
||||||
raise RuntimeError("GPU is required to run this example!")
|
raise RuntimeError("GPU is required to run this example!")
|
||||||
|
|
||||||
# Create tensor and return the pointer, tensor, and stride
|
# Create tensors for all groups using the new function
|
||||||
def create_tensor_and_stride(
|
(
|
||||||
l: int,
|
ptrs_abc,
|
||||||
mode0: int,
|
torch_tensors_abc,
|
||||||
mode1: int,
|
cute_tensors_abc,
|
||||||
is_mode0_major: bool,
|
strides_abc,
|
||||||
dtype: type[cutlass.Numeric],
|
torch_fp32_tensors_abc,
|
||||||
is_dynamic_layout: bool = True,
|
) = create_tensors_for_all_groups(
|
||||||
) -> tuple[int, torch.Tensor, cute.Tensor, torch.Tensor, tuple[int, int]]:
|
problem_sizes_mnkl,
|
||||||
torch_tensor_cpu = cutlass_torch.matrix(l, mode0, mode1, is_mode0_major, dtype)
|
ab_dtype,
|
||||||
cute_tensor, torch_tensor = cutlass_torch.cute_tensor_like(
|
c_dtype,
|
||||||
torch_tensor_cpu, dtype, is_dynamic_layout, assumed_align=16
|
a_major,
|
||||||
)
|
b_major,
|
||||||
return (
|
c_major,
|
||||||
torch_tensor.data_ptr(),
|
)
|
||||||
torch_tensor,
|
|
||||||
cute_tensor,
|
|
||||||
torch_tensor_cpu,
|
|
||||||
torch_tensor.stride()[:-1],
|
|
||||||
)
|
|
||||||
|
|
||||||
# iterate all groups and create tensors for each group
|
|
||||||
torch_fp32_tensors_abc = []
|
|
||||||
torch_tensors_abc = []
|
|
||||||
cute_tensors_abc = []
|
|
||||||
strides_abc = []
|
|
||||||
ptrs_abc = []
|
|
||||||
for _, (m, n, k, l) in enumerate(problem_sizes_mnkl):
|
|
||||||
(
|
|
||||||
ptr_a,
|
|
||||||
torch_tensor_a,
|
|
||||||
cute_tensor_a,
|
|
||||||
tensor_fp32_a,
|
|
||||||
stride_mk_a,
|
|
||||||
) = create_tensor_and_stride(l, m, k, a_major == "m", ab_dtype)
|
|
||||||
(
|
|
||||||
ptr_b,
|
|
||||||
torch_tensor_b,
|
|
||||||
cute_tensor_b,
|
|
||||||
tensor_fp32_b,
|
|
||||||
stride_nk_b,
|
|
||||||
) = create_tensor_and_stride(l, n, k, b_major == "n", ab_dtype)
|
|
||||||
(
|
|
||||||
ptr_c,
|
|
||||||
torch_tensor_c,
|
|
||||||
cute_tensor_c,
|
|
||||||
tensor_fp32_c,
|
|
||||||
stride_mn_c,
|
|
||||||
) = create_tensor_and_stride(l, m, n, c_major == "m", c_dtype)
|
|
||||||
ptrs_abc.append([ptr_a, ptr_b, ptr_c])
|
|
||||||
torch_tensors_abc.append([torch_tensor_a, torch_tensor_b, torch_tensor_c])
|
|
||||||
torch_fp32_tensors_abc.append([tensor_fp32_a, tensor_fp32_b, tensor_fp32_c])
|
|
||||||
strides_abc.append([stride_mk_a, stride_nk_b, stride_mn_c])
|
|
||||||
cute_tensors_abc.append(
|
|
||||||
(
|
|
||||||
cute_tensor_a,
|
|
||||||
cute_tensor_b,
|
|
||||||
cute_tensor_c,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
# Choose A, B, C with the smallest size to create initial tensormaps
|
# Choose A, B, C with the smallest size to create initial tensormaps
|
||||||
key_size_a = lambda item: item[1][0] * item[1][2]
|
key_size_a = lambda item: item[1][0] * item[1][2]
|
||||||
key_size_b = lambda item: item[1][1] * item[1][2]
|
key_size_b = lambda item: item[1][1] * item[1][2]
|
||||||
@@ -2078,36 +2169,19 @@ def run_grouped_gemm(
|
|||||||
current_stream,
|
current_stream,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Launch GPU kernel
|
|
||||||
# Warm up
|
|
||||||
for _ in range(warmup_iterations):
|
|
||||||
compiled_grouped_gemm(
|
|
||||||
initial_cute_tensors_abc[0],
|
|
||||||
initial_cute_tensors_abc[1],
|
|
||||||
initial_cute_tensors_abc[2],
|
|
||||||
tensor_of_dim_size_mnkl,
|
|
||||||
tensor_of_strides_abc,
|
|
||||||
tensor_of_ptrs_abc,
|
|
||||||
tensor_of_tensormap,
|
|
||||||
current_stream,
|
|
||||||
)
|
|
||||||
# Execution
|
|
||||||
for i in range(iterations):
|
|
||||||
compiled_grouped_gemm(
|
|
||||||
initial_cute_tensors_abc[0],
|
|
||||||
initial_cute_tensors_abc[1],
|
|
||||||
initial_cute_tensors_abc[2],
|
|
||||||
tensor_of_dim_size_mnkl,
|
|
||||||
tensor_of_strides_abc,
|
|
||||||
tensor_of_ptrs_abc,
|
|
||||||
tensor_of_tensormap,
|
|
||||||
current_stream,
|
|
||||||
)
|
|
||||||
|
|
||||||
torch.cuda.synchronize()
|
|
||||||
|
|
||||||
# Compute reference result
|
|
||||||
if not skip_ref_check:
|
if not skip_ref_check:
|
||||||
|
compiled_grouped_gemm(
|
||||||
|
initial_cute_tensors_abc[0],
|
||||||
|
initial_cute_tensors_abc[1],
|
||||||
|
initial_cute_tensors_abc[2],
|
||||||
|
tensor_of_dim_size_mnkl,
|
||||||
|
tensor_of_strides_abc,
|
||||||
|
tensor_of_ptrs_abc,
|
||||||
|
tensor_of_tensormap,
|
||||||
|
current_stream,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Compute reference result
|
||||||
for i, (a, b, c) in enumerate(torch_tensors_abc):
|
for i, (a, b, c) in enumerate(torch_tensors_abc):
|
||||||
ref = torch.einsum(
|
ref = torch.einsum(
|
||||||
"mkl,nkl->mnl",
|
"mkl,nkl->mnl",
|
||||||
@@ -2122,6 +2196,102 @@ def run_grouped_gemm(
|
|||||||
rtol=1e-05,
|
rtol=1e-05,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def generate_tensors():
|
||||||
|
# Reuse existing CPU tensors and create new GPU tensors from them
|
||||||
|
(
|
||||||
|
ptrs_abc_workspace,
|
||||||
|
torch_tensors_abc_workspace,
|
||||||
|
cute_tensors_abc_workspace,
|
||||||
|
strides_abc_workspace,
|
||||||
|
_,
|
||||||
|
) = create_tensors_for_all_groups(
|
||||||
|
problem_sizes_mnkl,
|
||||||
|
ab_dtype,
|
||||||
|
c_dtype,
|
||||||
|
a_major,
|
||||||
|
b_major,
|
||||||
|
c_major,
|
||||||
|
torch_fp32_tensors_abc,
|
||||||
|
)
|
||||||
|
|
||||||
|
initial_cute_tensors_abc_workspace = [
|
||||||
|
cute_tensors_abc_workspace[min_a_idx][0], # A with smallest (m, k)
|
||||||
|
cute_tensors_abc_workspace[min_b_idx][1], # B with smallest (n, k)
|
||||||
|
cute_tensors_abc_workspace[min_c_idx][2], # C with smallest (m, n)
|
||||||
|
]
|
||||||
|
|
||||||
|
# Create new tensors for this workspace
|
||||||
|
tensor_of_strides_abc_workspace, _ = cutlass_torch.cute_tensor_like(
|
||||||
|
torch.tensor(strides_abc_workspace, dtype=torch.int32),
|
||||||
|
cutlass.Int32,
|
||||||
|
is_dynamic_layout=False,
|
||||||
|
assumed_align=16,
|
||||||
|
)
|
||||||
|
|
||||||
|
tensor_of_ptrs_abc_workspace, _ = cutlass_torch.cute_tensor_like(
|
||||||
|
torch.tensor(ptrs_abc_workspace, dtype=torch.int64),
|
||||||
|
cutlass.Int64,
|
||||||
|
is_dynamic_layout=False,
|
||||||
|
assumed_align=16,
|
||||||
|
)
|
||||||
|
|
||||||
|
tensormap_workspace, _ = cutlass_torch.cute_tensor_like(
|
||||||
|
torch.empty(tensormap_shape, dtype=torch.int64),
|
||||||
|
cutlass.Int64,
|
||||||
|
is_dynamic_layout=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
return testing.JitArguments(
|
||||||
|
initial_cute_tensors_abc_workspace[0],
|
||||||
|
initial_cute_tensors_abc_workspace[1],
|
||||||
|
initial_cute_tensors_abc_workspace[2],
|
||||||
|
tensor_of_dim_size_mnkl,
|
||||||
|
tensor_of_strides_abc_workspace,
|
||||||
|
tensor_of_ptrs_abc_workspace,
|
||||||
|
tensormap_workspace,
|
||||||
|
current_stream,
|
||||||
|
)
|
||||||
|
|
||||||
|
workspace_count = 1
|
||||||
|
if use_cold_l2:
|
||||||
|
one_workspace_bytes = (
|
||||||
|
sum(
|
||||||
|
[
|
||||||
|
sum(
|
||||||
|
[
|
||||||
|
torch_tensor.numel() * torch_tensor.element_size()
|
||||||
|
for torch_tensor in group_tensors
|
||||||
|
]
|
||||||
|
)
|
||||||
|
for group_tensors in torch_tensors_abc
|
||||||
|
]
|
||||||
|
)
|
||||||
|
+
|
||||||
|
# Add size of strides tensor
|
||||||
|
tensor_of_strides_abc_torch.numel()
|
||||||
|
* tensor_of_strides_abc_torch.element_size()
|
||||||
|
+
|
||||||
|
# Add size of ptrs tensor
|
||||||
|
tensor_of_ptrs_abc_torch.numel() * tensor_of_ptrs_abc_torch.element_size()
|
||||||
|
+
|
||||||
|
# Add size of tensormap tensor
|
||||||
|
tensor_of_tensormap_torch.numel() * tensor_of_tensormap_torch.element_size()
|
||||||
|
)
|
||||||
|
workspace_count = testing.get_workspace_count(
|
||||||
|
one_workspace_bytes, warmup_iterations, iterations
|
||||||
|
)
|
||||||
|
|
||||||
|
exec_time = testing.benchmark(
|
||||||
|
compiled_grouped_gemm,
|
||||||
|
workspace_generator=generate_tensors,
|
||||||
|
workspace_count=workspace_count,
|
||||||
|
stream=current_stream,
|
||||||
|
warmup_iterations=warmup_iterations,
|
||||||
|
iterations=iterations,
|
||||||
|
)
|
||||||
|
|
||||||
|
return exec_time # Return execution time in microseconds
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
@@ -2218,6 +2388,12 @@ if __name__ == "__main__":
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--skip_ref_check", action="store_true", help="Skip reference checking"
|
"--skip_ref_check", action="store_true", help="Skip reference checking"
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--use_cold_l2",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="Use circular buffer tensor sets to ensure L2 cold cache",
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -2248,7 +2424,7 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
torch.manual_seed(2025)
|
torch.manual_seed(2025)
|
||||||
|
|
||||||
run_grouped_gemm(
|
run(
|
||||||
args.num_groups,
|
args.num_groups,
|
||||||
args.problem_sizes_mnkl,
|
args.problem_sizes_mnkl,
|
||||||
args.ab_dtype,
|
args.ab_dtype,
|
||||||
@@ -2265,5 +2441,6 @@ if __name__ == "__main__":
|
|||||||
args.warmup_iterations,
|
args.warmup_iterations,
|
||||||
args.iterations,
|
args.iterations,
|
||||||
args.skip_ref_check,
|
args.skip_ref_check,
|
||||||
|
args.use_cold_l2,
|
||||||
)
|
)
|
||||||
print("PASS")
|
print("PASS")
|
||||||
|
|||||||
@@ -29,13 +29,14 @@
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
from typing import List, Type, Tuple, Optional
|
from typing import List, Type, Tuple, Optional
|
||||||
from cuda import cuda
|
import cuda.bindings.driver as cuda
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
import torch.nn.functional as F
|
import torch.nn.functional as F
|
||||||
|
|
||||||
import cutlass
|
import cutlass
|
||||||
import cutlass.cute as cute
|
import cutlass.cute as cute
|
||||||
|
import cutlass.cute.testing as testing
|
||||||
import cutlass.utils as utils
|
import cutlass.utils as utils
|
||||||
import cutlass.pipeline as pipeline
|
import cutlass.pipeline as pipeline
|
||||||
from cutlass.cute.nvgpu import cpasync, tcgen05
|
from cutlass.cute.nvgpu import cpasync, tcgen05
|
||||||
@@ -43,13 +44,16 @@ import cutlass.torch as cutlass_torch
|
|||||||
import cutlass.utils.blackwell_helpers as sm100_utils
|
import cutlass.utils.blackwell_helpers as sm100_utils
|
||||||
from cutlass.cute.runtime import from_dlpack
|
from cutlass.cute.runtime import from_dlpack
|
||||||
|
|
||||||
from .mamba2_ssd_reference import (
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
sys.path.append(str(Path(__file__).resolve().parent))
|
||||||
|
from mamba2_ssd_reference import (
|
||||||
ssd_reference_fp32_all,
|
ssd_reference_fp32_all,
|
||||||
ssd_reference_lowprecision_intermediates,
|
ssd_reference_lowprecision_intermediates,
|
||||||
analyze_relative_diffs,
|
analyze_relative_diffs,
|
||||||
)
|
)
|
||||||
|
from mamba2_ssd_tile_scheduler import (
|
||||||
from .mamba2_ssd_tile_scheduler import (
|
|
||||||
Mamba2SSDTileSchedulerParams,
|
Mamba2SSDTileSchedulerParams,
|
||||||
Mamba2SSDTileScheduler,
|
Mamba2SSDTileScheduler,
|
||||||
)
|
)
|
||||||
@@ -122,7 +126,7 @@ class SSDKernel:
|
|||||||
*self.epilog_warp_id,
|
*self.epilog_warp_id,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.smem_capacity = sm100_utils.SMEM_CAPACITY["sm100"]
|
self.smem_capacity = utils.get_smem_capacity_in_bytes("sm_100")
|
||||||
|
|
||||||
# Named barriers
|
# Named barriers
|
||||||
self.pre_inter_sync_bar_id = 1
|
self.pre_inter_sync_bar_id = 1
|
||||||
@@ -1522,7 +1526,10 @@ class SSDKernel:
|
|||||||
# ((R2S_ATOM_V, R2S_REST_V), R2S_M, R2S_N)
|
# ((R2S_ATOM_V, R2S_REST_V), R2S_M, R2S_N)
|
||||||
# ((R2S_ATOM_V, R2S_REST_V), R2S_M, R2S_N, INTERNAL_STAGE)
|
# ((R2S_ATOM_V, R2S_REST_V), R2S_M, R2S_N, INTERNAL_STAGE)
|
||||||
tiled_r2s_b, tBrB_r2s, tBsB_r2s = self.pre_inter_smem_store_and_partition_b(
|
tiled_r2s_b, tBrB_r2s, tBsB_r2s = self.pre_inter_smem_store_and_partition_b(
|
||||||
local_tidx, smem_bt_internal_, tiled_s2r_b, tBrB_s2r
|
local_tidx,
|
||||||
|
smem_bt_internal_,
|
||||||
|
tiled_s2r_b,
|
||||||
|
tBrB_s2r,
|
||||||
)
|
)
|
||||||
|
|
||||||
# (MMA, MMA_M, MMA_K, INPUT_STAGE)
|
# (MMA, MMA_M, MMA_K, INPUT_STAGE)
|
||||||
@@ -3053,7 +3060,7 @@ class SSDKernel:
|
|||||||
|
|
||||||
# SegSum
|
# SegSum
|
||||||
# fadd2 + fsel + fmul2/mufu + fmul2
|
# fadd2 + fsel + fmul2/mufu + fmul2
|
||||||
for subtile_idx in range(0, cute.size(tTR_rQ), 2):
|
for subtile_idx in cutlass.range(0, cute.size(tTR_rQ), 2, unroll_full=True):
|
||||||
(
|
(
|
||||||
tCompute[subtile_idx],
|
tCompute[subtile_idx],
|
||||||
tCompute[subtile_idx + 1],
|
tCompute[subtile_idx + 1],
|
||||||
@@ -3061,11 +3068,11 @@ class SSDKernel:
|
|||||||
(tCrDeltaA_Col[subtile_idx], tCrDeltaA_Col[subtile_idx + 1]),
|
(tCrDeltaA_Col[subtile_idx], tCrDeltaA_Col[subtile_idx + 1]),
|
||||||
(-tCrDeltaA_Row[subtile_idx], -tCrDeltaA_Row[subtile_idx + 1]),
|
(-tCrDeltaA_Row[subtile_idx], -tCrDeltaA_Row[subtile_idx + 1]),
|
||||||
)
|
)
|
||||||
for subtile_idx in range(cute.size(tTR_rQ)):
|
for subtile_idx in cutlass.range(cute.size(tTR_rQ), unroll_full=True):
|
||||||
m, n = tCoord[subtile_idx]
|
m, n = tCoord[subtile_idx]
|
||||||
if m < n:
|
if m < n:
|
||||||
tCompute[subtile_idx] = cutlass.Float32(-float("inf"))
|
tCompute[subtile_idx] = cutlass.Float32(-float("inf"))
|
||||||
for subtile_idx in range(0, cute.size(tTR_rQ), 2):
|
for subtile_idx in cutlass.range(0, cute.size(tTR_rQ), 2, unroll_full=True):
|
||||||
# TODO: use math.exp directly
|
# TODO: use math.exp directly
|
||||||
(
|
(
|
||||||
tCompute[subtile_idx],
|
tCompute[subtile_idx],
|
||||||
@@ -3130,11 +3137,7 @@ class SSDKernel:
|
|||||||
dtype,
|
dtype,
|
||||||
num_bits_per_copy=128,
|
num_bits_per_copy=128,
|
||||||
)
|
)
|
||||||
tiled_r2s_b = cute.make_tiled_copy(
|
tiled_r2s_b = cute.make_tiled_copy_S(copy_atom_r2s_b, tiled_s2r_b)
|
||||||
copy_atom_r2s_b,
|
|
||||||
layout_tv=tiled_s2r_b.layout_tv_tiled,
|
|
||||||
tiler_mn=tiled_s2r_b.tiler_mn,
|
|
||||||
)
|
|
||||||
thr_r2s_b = tiled_r2s_b.get_slice(local_tidx)
|
thr_r2s_b = tiled_r2s_b.get_slice(local_tidx)
|
||||||
|
|
||||||
# Partition shared tensor for smem store Bt
|
# Partition shared tensor for smem store Bt
|
||||||
@@ -3333,17 +3336,24 @@ class SSDKernel:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def run_ssd(
|
def run(
|
||||||
gbehcdln: Tuple[int, int, int, int, int, int, int, int],
|
gbehcdln: Tuple[int, int, int, int, int, int, int, int],
|
||||||
io_dtype: Type[cutlass.Numeric],
|
io_dtype: Type[cutlass.Numeric],
|
||||||
cumsum_delta_dtype: Type[cutlass.Numeric],
|
cumsum_delta_dtype: Type[cutlass.Numeric],
|
||||||
acc_dtype: Type[cutlass.Numeric],
|
acc_dtype: Type[cutlass.Numeric],
|
||||||
has_d: bool,
|
fuse_scale_d: str,
|
||||||
d_has_hdim: bool,
|
|
||||||
tolerance: float,
|
tolerance: float,
|
||||||
print_rtol_stats: bool,
|
print_rtol_stats: bool,
|
||||||
ref_lower_precision: bool,
|
ref_lower_precision: bool,
|
||||||
|
warmup_iterations: int,
|
||||||
|
iterations: int,
|
||||||
|
skip_ref_check: bool,
|
||||||
|
use_cold_l2: bool = False,
|
||||||
|
**kwargs,
|
||||||
):
|
):
|
||||||
|
has_d = fuse_scale_d != "none"
|
||||||
|
d_has_hdim = fuse_scale_d == "vector"
|
||||||
|
|
||||||
print(f"Running B100 Mamba2 SSD with:")
|
print(f"Running B100 Mamba2 SSD with:")
|
||||||
print(f"GBEHCDLN: {gbehcdln}")
|
print(f"GBEHCDLN: {gbehcdln}")
|
||||||
print(
|
print(
|
||||||
@@ -3353,6 +3363,10 @@ def run_ssd(
|
|||||||
f"Has D (True means fuse Y+=X*D): {has_d}, D has Hdim (True means D.shape DxEH, False means 1xEH): {d_has_hdim}"
|
f"Has D (True means fuse Y+=X*D): {has_d}, D has Hdim (True means D.shape DxEH, False means 1xEH): {d_has_hdim}"
|
||||||
)
|
)
|
||||||
print(f"Tolerance: {tolerance}")
|
print(f"Tolerance: {tolerance}")
|
||||||
|
print(f"Warmup iterations: {warmup_iterations}")
|
||||||
|
print(f"Iterations: {iterations}")
|
||||||
|
print(f"Skip reference checking: {skip_ref_check}")
|
||||||
|
print(f"Use cold L2: {'True' if use_cold_l2 else 'False'}")
|
||||||
|
|
||||||
# Unpack parameters
|
# Unpack parameters
|
||||||
G, B, E, H, C, D, L, N = gbehcdln
|
G, B, E, H, C, D, L, N = gbehcdln
|
||||||
@@ -3515,39 +3529,146 @@ def run_ssd(
|
|||||||
stream,
|
stream,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Launch compiled ssd kernel
|
# Launch compiled ssd kernel for reference check
|
||||||
compiled_ssd(
|
if not skip_ref_check:
|
||||||
x_tensor,
|
compiled_ssd(
|
||||||
cumsum_delta_tensor,
|
x_tensor,
|
||||||
delta_tensor,
|
cumsum_delta_tensor,
|
||||||
b_tensor,
|
delta_tensor,
|
||||||
c_tensor,
|
b_tensor,
|
||||||
y_tensor,
|
c_tensor,
|
||||||
fstate_tensor,
|
y_tensor,
|
||||||
d_tensor,
|
fstate_tensor,
|
||||||
stream,
|
d_tensor,
|
||||||
|
stream,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Reference check
|
||||||
|
if print_rtol_stats:
|
||||||
|
print("\nY's Relative diffs:")
|
||||||
|
analyze_relative_diffs(
|
||||||
|
y_torch.cpu(), y_ref.to(cutlass_torch.dtype(io_dtype))
|
||||||
|
)
|
||||||
|
print("\nFstate's Relative diffs:")
|
||||||
|
analyze_relative_diffs(
|
||||||
|
fstate_torch.cpu(), fstate_ref.to(cutlass_torch.dtype(io_dtype))
|
||||||
|
)
|
||||||
|
torch.testing.assert_close(
|
||||||
|
y_torch.cpu(),
|
||||||
|
y_ref.to(cutlass_torch.dtype(io_dtype)),
|
||||||
|
atol=tolerance,
|
||||||
|
rtol=1e-02,
|
||||||
|
)
|
||||||
|
torch.testing.assert_close(
|
||||||
|
fstate_torch.cpu(),
|
||||||
|
fstate_ref.to(cutlass_torch.dtype(io_dtype)),
|
||||||
|
atol=tolerance,
|
||||||
|
rtol=1e-05,
|
||||||
|
)
|
||||||
|
|
||||||
|
def generate_tensors():
|
||||||
|
# Reuse existing CPU reference tensors and create new GPU tensors from them
|
||||||
|
_, x_tensor_new, _ = create_and_permute_tensor(
|
||||||
|
[B, EH, D, C, L],
|
||||||
|
[2, 4, 3, 1, 0],
|
||||||
|
io_dtype,
|
||||||
|
ref_tensor=x_ref,
|
||||||
|
dynamic_modes=[2, 3, 4],
|
||||||
|
)
|
||||||
|
_, cumsum_delta_tensor_new, _ = create_and_permute_tensor(
|
||||||
|
[B, EH, C, L],
|
||||||
|
[3, 2, 1, 0],
|
||||||
|
cumsum_delta_dtype,
|
||||||
|
ref_tensor=cumsum_delta_ref,
|
||||||
|
dynamic_modes=[1, 2, 3],
|
||||||
|
)
|
||||||
|
_, delta_tensor_new, _ = create_and_permute_tensor(
|
||||||
|
[B, EH, C, L],
|
||||||
|
[3, 2, 1, 0],
|
||||||
|
io_dtype,
|
||||||
|
ref_tensor=delta_ref,
|
||||||
|
dynamic_modes=[1, 2, 3],
|
||||||
|
)
|
||||||
|
_, b_tensor_new, _ = create_and_permute_tensor(
|
||||||
|
[B, G, N, C, L],
|
||||||
|
[4, 2, 3, 1, 0],
|
||||||
|
io_dtype,
|
||||||
|
ref_tensor=b_ref,
|
||||||
|
dynamic_modes=[2, 3, 4],
|
||||||
|
)
|
||||||
|
_, c_tensor_new, _ = create_and_permute_tensor(
|
||||||
|
[B, G, N, C, L],
|
||||||
|
[4, 2, 3, 1, 0],
|
||||||
|
io_dtype,
|
||||||
|
ref_tensor=c_ref,
|
||||||
|
dynamic_modes=[2, 3, 4],
|
||||||
|
)
|
||||||
|
_, y_tensor_new, _ = create_and_permute_tensor(
|
||||||
|
[B, EH, D, C, L],
|
||||||
|
[4, 2, 3, 1, 0],
|
||||||
|
io_dtype,
|
||||||
|
ref_tensor=y_ref,
|
||||||
|
dynamic_modes=[2, 3, 4],
|
||||||
|
)
|
||||||
|
_, fstate_tensor_new, _ = create_and_permute_tensor(
|
||||||
|
[B, EH, D, N],
|
||||||
|
[2, 3, 1, 0],
|
||||||
|
io_dtype,
|
||||||
|
ref_tensor=fstate_ref,
|
||||||
|
dynamic_modes=[2, 3],
|
||||||
|
)
|
||||||
|
|
||||||
|
if has_d:
|
||||||
|
_, d_tensor_new, _ = create_and_permute_tensor(
|
||||||
|
[EH, D if d_has_hdim else 1],
|
||||||
|
[1, 0],
|
||||||
|
io_dtype,
|
||||||
|
ref_tensor=d_ref,
|
||||||
|
dynamic_modes=[1],
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
d_tensor_new = d_tensor
|
||||||
|
|
||||||
|
return testing.JitArguments(
|
||||||
|
x_tensor_new,
|
||||||
|
cumsum_delta_tensor_new,
|
||||||
|
delta_tensor_new,
|
||||||
|
b_tensor_new,
|
||||||
|
c_tensor_new,
|
||||||
|
y_tensor_new,
|
||||||
|
fstate_tensor_new,
|
||||||
|
d_tensor_new,
|
||||||
|
stream,
|
||||||
|
)
|
||||||
|
|
||||||
|
workspace_count = 1
|
||||||
|
if use_cold_l2:
|
||||||
|
one_workspace_bytes = (
|
||||||
|
x_torch.numel() * x_torch.element_size()
|
||||||
|
+ cumsum_delta_torch.numel() * cumsum_delta_torch.element_size()
|
||||||
|
+ delta_torch.numel() * delta_torch.element_size()
|
||||||
|
+ b_torch.numel() * b_torch.element_size()
|
||||||
|
+ c_torch.numel() * c_torch.element_size()
|
||||||
|
+ y_torch.numel() * y_torch.element_size()
|
||||||
|
+ fstate_torch.numel() * fstate_torch.element_size()
|
||||||
|
)
|
||||||
|
if has_d:
|
||||||
|
one_workspace_bytes += d_torch.numel() * d_torch.element_size()
|
||||||
|
|
||||||
|
workspace_count = testing.get_workspace_count(
|
||||||
|
one_workspace_bytes, warmup_iterations, iterations
|
||||||
|
)
|
||||||
|
|
||||||
|
exec_time = testing.benchmark(
|
||||||
|
compiled_ssd,
|
||||||
|
workspace_generator=generate_tensors,
|
||||||
|
workspace_count=workspace_count,
|
||||||
|
stream=stream,
|
||||||
|
warmup_iterations=warmup_iterations,
|
||||||
|
iterations=iterations,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Reference check
|
return exec_time # Return execution time in microseconds
|
||||||
if print_rtol_stats:
|
|
||||||
print("\nY's Relative diffs:")
|
|
||||||
analyze_relative_diffs(y_torch.cpu(), y_ref.to(cutlass_torch.dtype(io_dtype)))
|
|
||||||
print("\nFstate's Relative diffs:")
|
|
||||||
analyze_relative_diffs(
|
|
||||||
fstate_torch.cpu(), fstate_ref.to(cutlass_torch.dtype(io_dtype))
|
|
||||||
)
|
|
||||||
torch.testing.assert_close(
|
|
||||||
y_torch.cpu(),
|
|
||||||
y_ref.to(cutlass_torch.dtype(io_dtype)),
|
|
||||||
atol=tolerance,
|
|
||||||
rtol=1e-02,
|
|
||||||
)
|
|
||||||
torch.testing.assert_close(
|
|
||||||
fstate_torch.cpu(),
|
|
||||||
fstate_ref.to(cutlass_torch.dtype(io_dtype)),
|
|
||||||
atol=tolerance,
|
|
||||||
rtol=1e-05,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@@ -3586,15 +3707,53 @@ if __name__ == "__main__":
|
|||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--ref_lower_precision",
|
"--ref_lower_precision",
|
||||||
type=bool,
|
action="store_true",
|
||||||
default=True,
|
default=True,
|
||||||
help="Use lower precision for reference check",
|
help="Use lower precision for reference check",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-ref_lower_precision",
|
||||||
|
action="store_false",
|
||||||
|
dest="ref_lower_precision",
|
||||||
|
default=False,
|
||||||
|
help="Disable lower precision for reference check",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--tolerance", type=float, default=5e-02, help="Tolerance for validation"
|
"--tolerance", type=float, default=5e-02, help="Tolerance for validation"
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--print_rtol_stats", type=bool, default=True, help="Print rtol stats"
|
"--print_rtol_stats",
|
||||||
|
action="store_true",
|
||||||
|
default=True,
|
||||||
|
help="Enable print rtol stats",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-print_rtol_stats",
|
||||||
|
action="store_false",
|
||||||
|
dest="print_rtol_stats",
|
||||||
|
default=False,
|
||||||
|
help="Disable print rtol stats",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--warmup_iterations",
|
||||||
|
type=int,
|
||||||
|
default=0,
|
||||||
|
help="Number of warmup iterations",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--iterations",
|
||||||
|
type=int,
|
||||||
|
default=1,
|
||||||
|
help="Number of iterations",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--skip_ref_check", action="store_true", help="Skip reference checking"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--use_cold_l2",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="Use circular buffer tensor sets to ensure L2 cold cache",
|
||||||
)
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
@@ -3602,18 +3761,18 @@ if __name__ == "__main__":
|
|||||||
if len(args.gbehcdln) != 8:
|
if len(args.gbehcdln) != 8:
|
||||||
parser.error("--gbehcdln must contain exactly 8 values")
|
parser.error("--gbehcdln must contain exactly 8 values")
|
||||||
|
|
||||||
has_d = args.fuse_scale_d != "none"
|
run(
|
||||||
d_has_hdim = args.fuse_scale_d == "vector"
|
|
||||||
|
|
||||||
run_ssd(
|
|
||||||
args.gbehcdln,
|
args.gbehcdln,
|
||||||
args.io_dtype,
|
args.io_dtype,
|
||||||
args.cumsum_delta_dtype,
|
args.cumsum_delta_dtype,
|
||||||
args.acc_dtype,
|
args.acc_dtype,
|
||||||
has_d,
|
args.fuse_scale_d,
|
||||||
d_has_hdim,
|
|
||||||
args.tolerance,
|
args.tolerance,
|
||||||
args.print_rtol_stats,
|
args.print_rtol_stats,
|
||||||
args.ref_lower_precision,
|
args.ref_lower_precision,
|
||||||
|
args.warmup_iterations,
|
||||||
|
args.iterations,
|
||||||
|
args.skip_ref_check,
|
||||||
|
args.use_cold_l2,
|
||||||
)
|
)
|
||||||
print("PASS")
|
print("PASS")
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import torch
|
|||||||
|
|
||||||
import cutlass
|
import cutlass
|
||||||
import cutlass.cute as cute
|
import cutlass.cute as cute
|
||||||
|
import cutlass.cute.testing as testing
|
||||||
import cutlass.utils as utils
|
import cutlass.utils as utils
|
||||||
import cutlass.pipeline as pipeline
|
import cutlass.pipeline as pipeline
|
||||||
import cutlass.torch as cutlass_torch
|
import cutlass.torch as cutlass_torch
|
||||||
@@ -166,6 +167,24 @@ def parse_arguments() -> argparse.Namespace:
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--tolerance", type=float, default=1e-01, help="Tolerance for validation"
|
"--tolerance", type=float, default=1e-01, help="Tolerance for validation"
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--warmup_iterations", type=int, default=0, help="Warmup iterations"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--iterations",
|
||||||
|
type=int,
|
||||||
|
default=1,
|
||||||
|
help="Number of iterations to run the kernel",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--skip_ref_check", action="store_true", help="Skip reference checking"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--use_cold_l2",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="Use circular buffer tensor sets to ensure L2 cold cache",
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -264,7 +283,7 @@ class HopperWgmmaGemmKernel:
|
|||||||
self.mma_warp_groups = math.prod(self.atom_layout_mnk)
|
self.mma_warp_groups = math.prod(self.atom_layout_mnk)
|
||||||
self.num_threads_per_warp_group = 128
|
self.num_threads_per_warp_group = 128
|
||||||
self.threads_per_cta = self.mma_warp_groups * self.num_threads_per_warp_group
|
self.threads_per_cta = self.mma_warp_groups * self.num_threads_per_warp_group
|
||||||
self.smem_capacity = sm90_utils.SMEM_CAPACITY["sm90"]
|
self.smem_capacity = utils.get_smem_capacity_in_bytes("sm_90")
|
||||||
|
|
||||||
self.ab_stage = None
|
self.ab_stage = None
|
||||||
self.epi_stage = None
|
self.epi_stage = None
|
||||||
@@ -1309,7 +1328,7 @@ class HopperWgmmaGemmKernel:
|
|||||||
}:
|
}:
|
||||||
is_valid = False
|
is_valid = False
|
||||||
# tested acc_dtype
|
# tested acc_dtype
|
||||||
if acc_dtype != cutlass.Float32:
|
if acc_dtype not in {cutlass.Float32, cutlass.Float16}:
|
||||||
is_valid = False
|
is_valid = False
|
||||||
# tested c_dtype
|
# tested c_dtype
|
||||||
if c_dtype not in {
|
if c_dtype not in {
|
||||||
@@ -1335,7 +1354,7 @@ class HopperWgmmaGemmKernel:
|
|||||||
return is_valid
|
return is_valid
|
||||||
|
|
||||||
|
|
||||||
def run_dense_gemm(
|
def run(
|
||||||
mnkl: Tuple[int, int, int, int],
|
mnkl: Tuple[int, int, int, int],
|
||||||
a_dtype: Type[cutlass.Numeric],
|
a_dtype: Type[cutlass.Numeric],
|
||||||
b_dtype: Type[cutlass.Numeric],
|
b_dtype: Type[cutlass.Numeric],
|
||||||
@@ -1347,9 +1366,43 @@ def run_dense_gemm(
|
|||||||
tile_shape_mnk: Tuple[int, int, int],
|
tile_shape_mnk: Tuple[int, int, int],
|
||||||
cluster_shape_mn: Tuple[int, int],
|
cluster_shape_mn: Tuple[int, int],
|
||||||
tolerance: float,
|
tolerance: float,
|
||||||
|
warmup_iterations: int,
|
||||||
|
iterations: int,
|
||||||
|
skip_ref_check: bool,
|
||||||
|
use_cold_l2: bool = False,
|
||||||
|
**kwargs,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Prepare A/B/C tensors, launch GPU kernel, and reference checking.
|
Prepare A/B/C tensors, launch GPU kernel, and reference checking.
|
||||||
|
|
||||||
|
:param mnkl: Problem size (M, N, K, L)
|
||||||
|
:type mnkl: Tuple[int, int, int, int]
|
||||||
|
:param a_dtype: Data type for input tensor A
|
||||||
|
:type a_dtype: Type[cutlass.Numeric]
|
||||||
|
:param b_dtype: Data type for input tensor B
|
||||||
|
:type b_dtype: Type[cutlass.Numeric]
|
||||||
|
:param c_dtype: Data type for output tensor C
|
||||||
|
:type c_dtype: Type[cutlass.Numeric]
|
||||||
|
:param acc_dtype: Data type for accumulation during matrix multiplication
|
||||||
|
:type acc_dtype: Type[cutlass.Numeric]
|
||||||
|
:param a_major/b_major/c_major: Memory layout of tensor A/B/C
|
||||||
|
:type a_major/b_major/c_major: str
|
||||||
|
:param tile_shape_mnk: CTA tile shape (M, N, K)
|
||||||
|
:type tile_shape_mnk: Tuple[int, int, int]
|
||||||
|
:param cluster_shape_mn: Cluster shape (M, N)
|
||||||
|
:type cluster_shape_mn: Tuple[int, int]
|
||||||
|
:param tolerance: Tolerance value for reference validation comparison
|
||||||
|
:type tolerance: float
|
||||||
|
:param warmup_iterations: Number of warmup iterations before benchmarking, defaults to 0
|
||||||
|
:type warmup_iterations: int, optional
|
||||||
|
:param iterations: Number of benchmark iterations to run, defaults to 1
|
||||||
|
:type iterations: int, optional
|
||||||
|
:param skip_ref_check: Whether to skip reference result validation, defaults to False
|
||||||
|
:type skip_ref_check: bool, optional
|
||||||
|
:param use_cold_l2: Whether to use circular buffer strategy to ensure cold L2 cache, defaults to False
|
||||||
|
:type use_cold_l2: bool, optional
|
||||||
|
:return: Execution time of the GEMM kernel in microseconds
|
||||||
|
:rtype: float
|
||||||
"""
|
"""
|
||||||
|
|
||||||
print(f"Running Hopper Dense GEMM with:")
|
print(f"Running Hopper Dense GEMM with:")
|
||||||
@@ -1360,6 +1413,10 @@ def run_dense_gemm(
|
|||||||
print(f"Matrix majors - A: {a_major}, B: {b_major}, C: {c_major}")
|
print(f"Matrix majors - A: {a_major}, B: {b_major}, C: {c_major}")
|
||||||
print(f"Tile Shape: {tile_shape_mnk}, Cluster Shape: {cluster_shape_mn}")
|
print(f"Tile Shape: {tile_shape_mnk}, Cluster Shape: {cluster_shape_mn}")
|
||||||
print(f"Tolerance: {tolerance}")
|
print(f"Tolerance: {tolerance}")
|
||||||
|
print(f"Warmup iterations: {warmup_iterations}")
|
||||||
|
print(f"Iterations: {iterations}")
|
||||||
|
print(f"Skip reference checking: {skip_ref_check}")
|
||||||
|
print(f"Use cold L2: {use_cold_l2}")
|
||||||
|
|
||||||
# Unpack parameters
|
# Unpack parameters
|
||||||
m, n, k, l = mnkl
|
m, n, k, l = mnkl
|
||||||
@@ -1437,46 +1494,76 @@ def run_dense_gemm(
|
|||||||
stream = cuda.CUstream(torch_stream.cuda_stream)
|
stream = cuda.CUstream(torch_stream.cuda_stream)
|
||||||
# compile gemm kernel
|
# compile gemm kernel
|
||||||
compiled_gemm = cute.compile(gemm, mA, mB, mC, stream)
|
compiled_gemm = cute.compile(gemm, mA, mB, mC, stream)
|
||||||
# execution
|
|
||||||
compiled_gemm(mA, mB, mC, stream)
|
|
||||||
|
|
||||||
torch.cuda.synchronize()
|
if not skip_ref_check:
|
||||||
|
# execution
|
||||||
|
compiled_gemm(mA, mB, mC, stream)
|
||||||
|
|
||||||
# Ref check
|
torch.cuda.synchronize()
|
||||||
ref = (torch.einsum("mkl,nkl->mnl", a, b)).cpu()
|
|
||||||
|
|
||||||
if c_dtype in (cutlass.Float8E4M3FN, cutlass.Float8E5M2):
|
# Ref check
|
||||||
# m major: (l, n, m) -> (m, n, l)
|
ref = (torch.einsum("mkl,nkl->mnl", a, b)).cpu()
|
||||||
# k major: (l, m, n) -> (m, n, l)
|
|
||||||
permute_order = (1, 2, 0) if c_major == "n" else (2, 1, 0)
|
if c_dtype in (cutlass.Float8E4M3FN, cutlass.Float8E5M2):
|
||||||
shape = (l, m, n) if c_major == "n" else (l, n, m)
|
# m major: (l, n, m) -> (m, n, l)
|
||||||
f8_torch_tensor = cutlass_torch.create_and_permute_torch_tensor(
|
# n major: (l, m, n) -> (m, n, l)
|
||||||
shape,
|
permute_order = (1, 2, 0) if c_major == "n" else (2, 1, 0)
|
||||||
torch.uint8,
|
shape = (l, m, n) if c_major == "n" else (l, n, m)
|
||||||
permute_order=permute_order,
|
f8_torch_tensor = cutlass_torch.create_and_permute_torch_tensor(
|
||||||
init_type=cutlass_torch.TensorInitType.SKIP,
|
shape,
|
||||||
).cuda()
|
torch.uint8,
|
||||||
# Create dtype cute tensor (gpu)
|
permute_order=permute_order,
|
||||||
ref_c_tensor = from_dlpack(
|
init_type=cutlass_torch.TensorInitType.SKIP,
|
||||||
f8_torch_tensor, assumed_align=16
|
).cuda()
|
||||||
).mark_layout_dynamic(leading_dim=(1 if c_major == "n" else 0))
|
# Create dtype cute tensor (gpu)
|
||||||
ref_c_tensor.element_type = c_dtype
|
ref_c_tensor = from_dlpack(
|
||||||
ref_c_tensor = cutlass_torch.convert_cute_tensor(
|
f8_torch_tensor, assumed_align=16
|
||||||
ref,
|
).mark_layout_dynamic(leading_dim=(1 if c_major == "n" else 0))
|
||||||
ref_c_tensor,
|
ref_c_tensor.element_type = c_dtype
|
||||||
c_dtype,
|
ref_c_tensor = cutlass_torch.convert_cute_tensor(
|
||||||
is_dynamic_layout=True,
|
ref,
|
||||||
|
ref_c_tensor,
|
||||||
|
c_dtype,
|
||||||
|
is_dynamic_layout=True,
|
||||||
|
)
|
||||||
|
ref_c = f8_torch_tensor.cpu()
|
||||||
|
else:
|
||||||
|
ref_c = ref.to(cutlass_torch.dtype(c_dtype))
|
||||||
|
|
||||||
|
torch.testing.assert_close(c_torch.cpu(), ref_c, atol=tolerance, rtol=1e-03)
|
||||||
|
|
||||||
|
def generate_tensors():
|
||||||
|
_, mA_workspace, _ = create_and_permute_tensor(l, m, k, a_major == "m", a_dtype)
|
||||||
|
_, mB_workspace, _ = create_and_permute_tensor(l, n, k, b_major == "n", b_dtype)
|
||||||
|
_, mC_workspace, _ = create_and_permute_tensor(l, m, n, c_major == "m", c_dtype)
|
||||||
|
return testing.JitArguments(mA_workspace, mB_workspace, mC_workspace, stream)
|
||||||
|
|
||||||
|
workspace_count = 1
|
||||||
|
if use_cold_l2:
|
||||||
|
one_workspace_bytes = (
|
||||||
|
a_torch.numel() * a_torch.element_size()
|
||||||
|
+ b_torch.numel() * b_torch.element_size()
|
||||||
|
+ c_torch.numel() * c_torch.element_size()
|
||||||
|
)
|
||||||
|
workspace_count = testing.get_workspace_count(
|
||||||
|
one_workspace_bytes, warmup_iterations, iterations
|
||||||
)
|
)
|
||||||
ref_c = f8_torch_tensor.cpu()
|
|
||||||
else:
|
|
||||||
ref_c = ref.to(cutlass_torch.dtype(c_dtype))
|
|
||||||
|
|
||||||
torch.testing.assert_close(c_torch.cpu(), ref_c, atol=tolerance, rtol=1e-03)
|
exec_time = testing.benchmark(
|
||||||
|
compiled_gemm,
|
||||||
|
workspace_generator=generate_tensors,
|
||||||
|
workspace_count=workspace_count,
|
||||||
|
stream=stream,
|
||||||
|
warmup_iterations=warmup_iterations,
|
||||||
|
iterations=iterations,
|
||||||
|
)
|
||||||
|
|
||||||
|
return exec_time # Return execution time in microseconds
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
args = parse_arguments()
|
args = parse_arguments()
|
||||||
run_dense_gemm(
|
run(
|
||||||
args.mnkl,
|
args.mnkl,
|
||||||
args.a_dtype,
|
args.a_dtype,
|
||||||
args.b_dtype,
|
args.b_dtype,
|
||||||
@@ -1488,5 +1575,9 @@ if __name__ == "__main__":
|
|||||||
args.tile_shape_mnk,
|
args.tile_shape_mnk,
|
||||||
args.cluster_shape_mn,
|
args.cluster_shape_mn,
|
||||||
args.tolerance,
|
args.tolerance,
|
||||||
|
args.warmup_iterations,
|
||||||
|
args.iterations,
|
||||||
|
args.skip_ref_check,
|
||||||
|
args.use_cold_l2,
|
||||||
)
|
)
|
||||||
print("PASS")
|
print("PASS")
|
||||||
|
|||||||
@@ -399,6 +399,70 @@
|
|||||||
"\n",
|
"\n",
|
||||||
"tensor_print_example3()"
|
"tensor_print_example3()"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"To print the tensor in device memory, you can use `cute.print_tensor` within CuTe JIT kernels."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 13,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"@cute.kernel\n",
|
||||||
|
"def print_tensor_gpu(src: cute.Tensor):\n",
|
||||||
|
" print(src)\n",
|
||||||
|
" cute.print_tensor(src)\n",
|
||||||
|
"\n",
|
||||||
|
"@cute.jit\n",
|
||||||
|
"def print_tensor_host(src: cute.Tensor):\n",
|
||||||
|
" print_tensor_gpu(src).launch(grid=(1,1,1), block=(1,1,1))"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 15,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"tensor<ptr<f32, gmem> o (4,3):(3,1)>\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"tensor(raw_ptr(0x00007f5f81200400: f32, gmem, align<4>) o (4,3):(3,1), data=\n",
|
||||||
|
" [[-0.690547, -0.274619, -1.659539, ],\n",
|
||||||
|
" [-1.843524, -1.648711, 1.163431, ],\n",
|
||||||
|
" [-0.716668, -1.900705, 0.592515, ],\n",
|
||||||
|
" [ 0.711333, -0.552422, 0.860237, ]])\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"import torch\n",
|
||||||
|
"def tensor_print_example4():\n",
|
||||||
|
" a = torch.randn(4, 3, device=\"cuda\")\n",
|
||||||
|
" cutlass.cuda.initialize_cuda_context()\n",
|
||||||
|
" print_tensor_host(from_dlpack(a))\n",
|
||||||
|
"\n",
|
||||||
|
"tensor_print_example4()"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"Currently, `cute.print_tensor` only supports tensor with integer data types and `Float16`/`Float32`/`Float64` floating point data types. We will support more data types in the future."
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
|
|||||||
@@ -256,16 +256,6 @@
|
|||||||
" cute.printf(\"a[2,3] = {}\", a[2,3])\n",
|
" cute.printf(\"a[2,3] = {}\", a[2,3])\n",
|
||||||
" cute.printf(\"a[(2,4)] = {}\", a[(2,4)])\n",
|
" cute.printf(\"a[(2,4)] = {}\", a[(2,4)])\n",
|
||||||
"\n",
|
"\n",
|
||||||
"@cute.kernel\n",
|
|
||||||
"def print_tensor_gpu(ptr: cute.Pointer):\n",
|
|
||||||
" layout = cute.make_layout((8, 5), stride=(5, 1))\n",
|
|
||||||
" tensor = cute.make_tensor(ptr, layout)\n",
|
|
||||||
"\n",
|
|
||||||
" tidx, _, _ = cute.arch.thread_idx()\n",
|
|
||||||
"\n",
|
|
||||||
" if tidx == 0:\n",
|
|
||||||
" cute.print_tensor(tensor)\n",
|
|
||||||
"\n",
|
|
||||||
"\n",
|
"\n",
|
||||||
"# Create a tensor with sequential data using torch\n",
|
"# Create a tensor with sequential data using torch\n",
|
||||||
"data = torch.arange(0, 8*5, dtype=torch.float32).reshape(8, 5)\n",
|
"data = torch.arange(0, 8*5, dtype=torch.float32).reshape(8, 5)\n",
|
||||||
|
|||||||
@@ -363,7 +363,7 @@
|
|||||||
"| | \"few_channels\" | optimized for small `C` and requires `C % alignment_input == 0`|\n",
|
"| | \"few_channels\" | optimized for small `C` and requires `C % alignment_input == 0`|\n",
|
||||||
"| | \"fixed_channels\" | optimized for small `C` and requires `C == alignment_input` |\n",
|
"| | \"fixed_channels\" | optimized for small `C` and requires `C == alignment_input` |\n",
|
||||||
"|Dgrad | \"analytic\" | Functionally correct in all cases but lower performance |\n",
|
"|Dgrad | \"analytic\" | Functionally correct in all cases but lower performance |\n",
|
||||||
"| | \"optimized\" | Optimzed for and require `R <= 32`, `S<= 32`, `K % alignment_grad_output == 0`, and `C % alignment_weight == 0`|\n",
|
"| | \"optimized\" | Optimized for and require `R <= 32`, `S<= 32`, `K % alignment_grad_output == 0`, and `C % alignment_weight == 0`|\n",
|
||||||
"|Wgrad | \"analytic\" | Functionally correct in all cases but lower performance |\n",
|
"|Wgrad | \"analytic\" | Functionally correct in all cases but lower performance |\n",
|
||||||
"| | \"optimized\" | Optimized for and require `K % alignment_grad_output == 0`, and `C % alignment_input == 0`|\n",
|
"| | \"optimized\" | Optimized for and require `K % alignment_grad_output == 0`, and `C % alignment_input == 0`|\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ struct WmmaToCutlassDataType<__nv_bfloat16> {
|
|||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// WMMA template structure defines nvcuda::wmma::fragments and static assertion chaeks
|
// WMMA template structure defines nvcuda::wmma::fragments and static assertion chaeks
|
||||||
// for a specific template paramterized data type (Element[A|B|C]), layout (Layout[A|B|C]),
|
// for a specific template parameterized data type (Element[A|B|C]), layout (Layout[A|B|C]),
|
||||||
// and native wmma size (Shape)
|
// and native wmma size (Shape)
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
template <
|
template <
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ struct Wmma<
|
|||||||
nvcuda::wmma::mma_sync(D, A, B, C);
|
nvcuda::wmma::mma_sync(D, A, B, C);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static_assert(false, "wmma.mma.sync for floating point multiplicands is avialable only for SM70 and beyond");
|
static_assert(false, "wmma.mma.sync for floating point multiplicands is available only for SM70 and beyond");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ struct Wmma<
|
|||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
static_assert(false, "wmma.mma.sync interger type multiplicands is avialable only for SM72 and beyond");
|
static_assert(false, "wmma.mma.sync integer type multiplicands is available only for SM72 and beyond");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -197,7 +197,7 @@ struct Wmma<
|
|||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
static_assert(false, "wmma.mma.sync interger type multiplicands is avialable only for SM72 and beyond");
|
static_assert(false, "wmma.mma.sync integer type multiplicands is available only for SM72 and beyond");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ struct Wmma<
|
|||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
static_assert(false, "wmma.mma.sync interger type multiplicands is avialable only for SM75 and beyond");
|
static_assert(false, "wmma.mma.sync integer type multiplicands is available only for SM75 and beyond");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -194,7 +194,7 @@ struct Wmma<
|
|||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
static_assert(false, "wmma.mma.sync interger type multiplicands is avialable only for SM75 and beyond");
|
static_assert(false, "wmma.mma.sync integer type multiplicands is available only for SM75 and beyond");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ struct Array<T, N, false> {
|
|||||||
// result[0] = xxx;
|
// result[0] = xxx;
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
// Will leads to compiler warning on use of unintialized member variable. Although we know
|
// Will leads to compiler warning on use of uninitialized member variable. Although we know
|
||||||
// this read of uninitialized member variable is harmeless.
|
// this read of uninitialized member variable is harmeless.
|
||||||
|
|
||||||
#if defined(__clang__)
|
#if defined(__clang__)
|
||||||
|
|||||||
@@ -1056,7 +1056,7 @@ struct DefaultConv2dFprop <
|
|||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/// Defines a kernel for Conv2dFprop specialization for Optimzed IteratorAlgorithm and
|
/// Defines a kernel for Conv2dFprop specialization for Optimized IteratorAlgorithm and
|
||||||
/// multistage pipeline.
|
/// multistage pipeline.
|
||||||
template <
|
template <
|
||||||
typename ElementA,
|
typename ElementA,
|
||||||
@@ -1184,7 +1184,7 @@ struct DefaultConv2dFprop <
|
|||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/// Defines a kernel for Conv2dFprop specialization for Optimzed IteratorAlgorithm and
|
/// Defines a kernel for Conv2dFprop specialization for Optimized IteratorAlgorithm and
|
||||||
// multistage pipeline with interleaved layout.
|
// multistage pipeline with interleaved layout.
|
||||||
template <
|
template <
|
||||||
typename ElementA,
|
typename ElementA,
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ struct DefaultConv2dFpropFusion <
|
|||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/// Defines a kernel for Conv2dFprop specialization for Optimzed IteratorAlgorithm and
|
/// Defines a kernel for Conv2dFprop specialization for Optimized IteratorAlgorithm and
|
||||||
/// multistage pipeline.
|
/// multistage pipeline.
|
||||||
template <
|
template <
|
||||||
typename ElementA,
|
typename ElementA,
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ struct DefaultConv3dFpropFusion <
|
|||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/// Defines a kernel for Conv3dFprop specialzation for Optimzed IteratorAlgorithm and
|
/// Defines a kernel for Conv3dFprop specialzation for Optimized IteratorAlgorithm and
|
||||||
/// multistage pipeline.
|
/// multistage pipeline.
|
||||||
template <
|
template <
|
||||||
typename ElementA,
|
typename ElementA,
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
**************************************************************************************************/
|
**************************************************************************************************/
|
||||||
|
|
||||||
/*! \file
|
/*! \file
|
||||||
\brief Interface betweeen a CUTLASS device-wide operator and CUDA.
|
\brief Interface between a CUTLASS device-wide operator and CUDA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
@@ -392,7 +392,7 @@ protected:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Fills a buffer in Global Memory with a byte sequence copied from host memory.
|
* Fills a buffer in Global Memory with a byte sequence copied from host memory.
|
||||||
* This function can be overriden to dispatch to the appropriate cuMemsetD*Async API
|
* This function can be overridden to dispatch to the appropriate cuMemsetD*Async API
|
||||||
*/
|
*/
|
||||||
virtual Status memsetDeviceImpl(
|
virtual Status memsetDeviceImpl(
|
||||||
void* destination, ///< Device memory pointer to be filled
|
void* destination, ///< Device memory pointer to be filled
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ struct CollectiveBuilder<
|
|||||||
|
|
||||||
// Construct TileShape for SFB load from GMEM to SMEM.
|
// Construct TileShape for SFB load from GMEM to SMEM.
|
||||||
// It is required to keep consistency with BlockScaled granularity defined in Sm1xxBlkScaledConfig.
|
// It is required to keep consistency with BlockScaled granularity defined in Sm1xxBlkScaledConfig.
|
||||||
// So that TileShape for scaling factor needs to be defined as a mutliple of Blk_MN.
|
// So that TileShape for scaling factor needs to be defined as a multiple of Blk_MN.
|
||||||
using TileShapeSf_MNK = decltype(make_shape(ceil_div(size<0>(TileShape_MNK{}), Blk_MN{}) * Blk_MN{},
|
using TileShapeSf_MNK = decltype(make_shape(ceil_div(size<0>(TileShape_MNK{}), Blk_MN{}) * Blk_MN{},
|
||||||
ceil_div(size<1>(TileShape_MNK{}), Blk_MN{}) * Blk_MN{},
|
ceil_div(size<1>(TileShape_MNK{}), Blk_MN{}) * Blk_MN{},
|
||||||
shape<2>(TileShape_MNK{})));
|
shape<2>(TileShape_MNK{})));
|
||||||
|
|||||||
@@ -153,13 +153,13 @@ struct CollectiveMma<
|
|||||||
// Asymmetric buffering
|
// Asymmetric buffering
|
||||||
// Tensor A/B could have different buffering, with TILEK, and STAGEs.
|
// Tensor A/B could have different buffering, with TILEK, and STAGEs.
|
||||||
// It let AsymmetricKRatio equals TILEK_A / TILEK_B, to make sure A/B's
|
// It let AsymmetricKRatio equals TILEK_A / TILEK_B, to make sure A/B's
|
||||||
// pipeline keep same steps when procude / consume data.
|
// pipeline keep same steps when produce / consume data.
|
||||||
// Currently, AsymmetricKRatio = {1, 2} is the only support.
|
// Currently, AsymmetricKRatio = {1, 2} is the only support.
|
||||||
static constexpr int AsymmetricKRatio = DispatchPolicy::StagesA != DispatchPolicy::StagesB ? 2 : 1;
|
static constexpr int AsymmetricKRatio = DispatchPolicy::StagesA != DispatchPolicy::StagesB ? 2 : 1;
|
||||||
|
|
||||||
// Construct TileShape for SFB load from GMEM to SMEM.
|
// Construct TileShape for SFB load from GMEM to SMEM.
|
||||||
// It is required to keep consistency with BlockScaled granularity defined in Sm1xxBlkScaledConfig.
|
// It is required to keep consistency with BlockScaled granularity defined in Sm1xxBlkScaledConfig.
|
||||||
// So that TileShape for scaling factor needs to be defined as a mutliple of Blk_MN.
|
// So that TileShape for scaling factor needs to be defined as a multiple of Blk_MN.
|
||||||
using Blk_MN = typename Sm1xxBlkScaledConfig::Blk_MN;
|
using Blk_MN = typename Sm1xxBlkScaledConfig::Blk_MN;
|
||||||
using TileShapeSF = decltype(make_shape(ceil_div(size<0>(CtaShape_MNK{}), Blk_MN{}) * Blk_MN{},
|
using TileShapeSF = decltype(make_shape(ceil_div(size<0>(CtaShape_MNK{}), Blk_MN{}) * Blk_MN{},
|
||||||
ceil_div(size<1>(CtaShape_MNK{}), Blk_MN{}) * Blk_MN{},
|
ceil_div(size<1>(CtaShape_MNK{}), Blk_MN{}) * Blk_MN{},
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ struct CollectiveMma<
|
|||||||
// Asymmetric buffering
|
// Asymmetric buffering
|
||||||
// Tensor A/B could have different buffering, with TILEK, and STAGEs.
|
// Tensor A/B could have different buffering, with TILEK, and STAGEs.
|
||||||
// It let AsymmetricKRatio equals TILEK_A / TILEK_B, to make sure A/B's
|
// It let AsymmetricKRatio equals TILEK_A / TILEK_B, to make sure A/B's
|
||||||
// pipeline keep same steps when procude / consume data.
|
// pipeline keep same steps when produce / consume data.
|
||||||
static constexpr int AsymmetricKRatio = DispatchPolicy::StagesA != DispatchPolicy::StagesB ? 2 : 1;
|
static constexpr int AsymmetricKRatio = DispatchPolicy::StagesA != DispatchPolicy::StagesB ? 2 : 1;
|
||||||
|
|
||||||
using TileShapeB = decltype(make_shape(size<0>(TileShape{}),
|
using TileShapeB = decltype(make_shape(size<0>(TileShape{}),
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ struct CollectiveMma<
|
|||||||
using TransformB = TransformB_;
|
using TransformB = TransformB_;
|
||||||
using ArchTag = typename DispatchPolicy::ArchTag;
|
using ArchTag = typename DispatchPolicy::ArchTag;
|
||||||
// Follow the change in TestSmall: TileShape switch to CtaShape
|
// Follow the change in TestSmall: TileShape switch to CtaShape
|
||||||
// For sm80 arch, CtaShape should euqal to TileShape
|
// For sm80 arch, CtaShape should equal to TileShape
|
||||||
using CtaShape_MNK = TileShape;
|
using CtaShape_MNK = TileShape;
|
||||||
|
|
||||||
static_assert(cute::rank(SmemLayoutAtomA{}) == 2, "SmemLayoutAtom must be rank 2 (M/N, K)");
|
static_assert(cute::rank(SmemLayoutAtomA{}) == 2, "SmemLayoutAtom must be rank 2 (M/N, K)");
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ namespace device {
|
|||||||
|
|
||||||
Description of parameters and tensors used to represent the Blocked-Ellpack (ELL) format:
|
Description of parameters and tensors used to represent the Blocked-Ellpack (ELL) format:
|
||||||
a_rows - Rows in the sparse matrix.
|
a_rows - Rows in the sparse matrix.
|
||||||
a_cols - Colums in the sparse matrix.
|
a_cols - Columns in the sparse matrix.
|
||||||
BlockedEllA - Packed matrix (ellValue matrix) that stores non-zero values in
|
BlockedEllA - Packed matrix (ellValue matrix) that stores non-zero values in
|
||||||
consecutive blocks, whose size is (a_rows * a_ell_num_columns)
|
consecutive blocks, whose size is (a_rows * a_ell_num_columns)
|
||||||
ell_idx - Blocked-ELL Column indices (ellColInd) matrix, whose size is
|
ell_idx - Blocked-ELL Column indices (ellColInd) matrix, whose size is
|
||||||
@@ -715,7 +715,7 @@ public:
|
|||||||
/// Constructs the GEMM.
|
/// Constructs the GEMM.
|
||||||
EllGemm() { }
|
EllGemm() { }
|
||||||
|
|
||||||
/// Helper to construct a transposed equivalent for the underying GEMM operator
|
/// Helper to construct a transposed equivalent for the underlying GEMM operator
|
||||||
static UnderlyingArguments to_underlying_arguments(Arguments const &args) {
|
static UnderlyingArguments to_underlying_arguments(Arguments const &args) {
|
||||||
return UnderlyingArguments(
|
return UnderlyingArguments(
|
||||||
{args.problem_size.n(), args.problem_size.m(), args.problem_size.k()},
|
{args.problem_size.n(), args.problem_size.m(), args.problem_size.k()},
|
||||||
|
|||||||
@@ -696,7 +696,7 @@ public:
|
|||||||
/// Constructs the GEMM.
|
/// Constructs the GEMM.
|
||||||
Gemm() { }
|
Gemm() { }
|
||||||
|
|
||||||
/// Helper to construct a transposed equivalent for the underying GEMM operator
|
/// Helper to construct a transposed equivalent for the underlying GEMM operator
|
||||||
static UnderlyingArguments to_underlying_arguments(Arguments const &args) {
|
static UnderlyingArguments to_underlying_arguments(Arguments const &args) {
|
||||||
return UnderlyingArguments(
|
return UnderlyingArguments(
|
||||||
{args.problem_size.n(), args.problem_size.m(), args.problem_size.k()},
|
{args.problem_size.n(), args.problem_size.m(), args.problem_size.k()},
|
||||||
|
|||||||
@@ -653,7 +653,7 @@ public:
|
|||||||
/// Constructs the GEMM.
|
/// Constructs the GEMM.
|
||||||
GemmArray() { }
|
GemmArray() { }
|
||||||
|
|
||||||
/// Helper to construct a transposed equivalent for the underying GEMM operator
|
/// Helper to construct a transposed equivalent for the underlying GEMM operator
|
||||||
static UnderlyingArguments to_underlying_arguments(Arguments const &args) {
|
static UnderlyingArguments to_underlying_arguments(Arguments const &args) {
|
||||||
|
|
||||||
GemmCoord problem_size{
|
GemmCoord problem_size{
|
||||||
|
|||||||
@@ -626,7 +626,7 @@ public:
|
|||||||
/// Constructs the GEMM.
|
/// Constructs the GEMM.
|
||||||
GemmBatched() { }
|
GemmBatched() { }
|
||||||
|
|
||||||
/// Helper to construct a transposed equivalent for the underying GEMM operator
|
/// Helper to construct a transposed equivalent for the underlying GEMM operator
|
||||||
static UnderlyingArguments to_underlying_arguments(Arguments const &args) {
|
static UnderlyingArguments to_underlying_arguments(Arguments const &args) {
|
||||||
return UnderlyingArguments(
|
return UnderlyingArguments(
|
||||||
{args.problem_size.n(), args.problem_size.m(), args.problem_size.k()},
|
{args.problem_size.n(), args.problem_size.m(), args.problem_size.k()},
|
||||||
|
|||||||
@@ -645,7 +645,7 @@ public:
|
|||||||
/// Constructs the GEMM.
|
/// Constructs the GEMM.
|
||||||
GemmComplex() { }
|
GemmComplex() { }
|
||||||
|
|
||||||
/// Helper to construct a transposed equivalent for the underying GEMM operator
|
/// Helper to construct a transposed equivalent for the underlying GEMM operator
|
||||||
static UnderlyingArguments to_underlying_arguments(Arguments const &args) {
|
static UnderlyingArguments to_underlying_arguments(Arguments const &args) {
|
||||||
return UnderlyingArguments(
|
return UnderlyingArguments(
|
||||||
{args.problem_size.n(), args.problem_size.m(), args.problem_size.k()},
|
{args.problem_size.n(), args.problem_size.m(), args.problem_size.k()},
|
||||||
|
|||||||
@@ -561,7 +561,7 @@ public:
|
|||||||
/// Constructs the GEMM.
|
/// Constructs the GEMM.
|
||||||
GemmSplitKParallel() { }
|
GemmSplitKParallel() { }
|
||||||
|
|
||||||
/// Helper to construct a transposed equivalent for the underying GEMM operator
|
/// Helper to construct a transposed equivalent for the underlying GEMM operator
|
||||||
static UnderlyingArguments to_underlying_arguments(Arguments const &args) {
|
static UnderlyingArguments to_underlying_arguments(Arguments const &args) {
|
||||||
return UnderlyingArguments(
|
return UnderlyingArguments(
|
||||||
{args.problem_size.n(), args.problem_size.m(), args.problem_size.k()},
|
{args.problem_size.n(), args.problem_size.m(), args.problem_size.k()},
|
||||||
|
|||||||
@@ -367,7 +367,7 @@ public:
|
|||||||
/// Constructs the GEMM.
|
/// Constructs the GEMM.
|
||||||
GemmUniversal() { }
|
GemmUniversal() { }
|
||||||
|
|
||||||
/// Helper to construct a transposed equivalent for the underying GEMM operator
|
/// Helper to construct a transposed equivalent for the underlying GEMM operator
|
||||||
static Arguments to_underlying_arguments(Arguments const &args) {
|
static Arguments to_underlying_arguments(Arguments const &args) {
|
||||||
return args.transposed_problem();
|
return args.transposed_problem();
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user