cutlass Documentation

CUDA Templates and Python DSLs for High-Performance Linear Algebra

10,633 stars C++ C++ 1 file · ~271,747 tokens #cpp#cuda#deep-learning#deep-learning-library#gpu#nvidia#python
RAW Doc

Technical Documentation: NVIDIA/cutlass

ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference

Overview (README.md)

Overview

CUTLASS 4.8.1

CUTLASS 4.8.1 - Oct. 2026

CUTLASS is a collection of abstractions for implementing high-performance matrix-matrix multiplication (GEMM)
and related computations at all levels and scales within CUDA. It incorporates strategies for
hierarchical decomposition and data movement. CUTLASS decomposes these "moving parts" into reusable, modular
software components and abstractions.

Primitives for different levels of a conceptual parallelization hierarchy can be specialized and tuned
via custom tiling sizes, data types, and other algorithmic policy. The resulting flexibility simplifies
their use as building blocks within custom kernels and applications.

CUTLASS has been providing CUDA C++ template abstractions for high-performance linear algebra since 2017 and
these abstractions provide extensive support for a wide range of computations including
mixed-precision computations, specialized data-movement (async copy) and
multiply-accumulate abstractions for FP64, FP32, TF32, FP16, BF16,
FP32 emulation via tensor core instruction,
8b floating point types (e5m2 and e4m3),
block scaled data types (NVIDIA NVFP4 and OCP standard MXFP4, MXFP6, MXFP8),
narrow integer types (4 and 8b signed and unsigned integers),
and binary 1b data types (where architectures allow for the
native support of such data types) across NVIDIA's Volta, Turing, Ampere, Ada, Hopper, and Blackwell architectures.

To this rich ecosystem of C++ based kernel programming abstractions, CUTLASS 4 adds CUTLASS DSLs. These are Python native interfaces for writing high-performance CUDA kernels based on core CUTLASS and CuTe concepts without any performance compromises. This allows for a much smoother learning curve, orders of magnitude faster compile times, native integration with DL frameworks without writing glue code, and much more intuitive metaprogramming that does not require deep C++ expertise.

Overall, we envision CUTLASS DSLs as a family of domain-specific languages (DSLs). With the release of 4.0, we released the first of these in CuTe DSL. This is a low-level programming model that is fully consistent with CuTe C++ abstractions - exposing core concepts such as layouts, tensors, hardware atoms, and full control over the hardware thread and data hierarchy.

CuTe DSL demonstrates optimal matrix multiply and other linear algebra operations
targeting the programmable, high-throughput Tensor Cores implemented by
NVIDIA's Ampere, Hopper, and Blackwell architectures.

We believe it will become an indispensable tool for students, researchers, and performance
engineers alike - flattening the learning curve of GPU programming, rapidly prototyping kernel
designs, and bringing optimized solutions into production.

CuTe DSL is currently in public beta and will graduate out of beta by end of summer 2026.

To get started quickly - please refer :

What's New in CUTLASS 4.8

CuTe DSL

  • New features

    • Initial Rubin support to accelerate dense GEMMs. The following features are available:

      • CuTe DSL and CuTe extensions
        • Support for higher-throughput FP8 (MMA_K=64) and FP4 (MMA_K=128) Tensor Core MMA instructions
        • B collector reuse
        • Extended TMEM size from 512 COL to 576 COL
        • Larger shared memory allocations (328KB)
        • Enhanced mixed precision throughput (FP8/FP4)
        • Softmax acceleration related features
      • Primitives
        • Support for higher-throughput FP8 (MMA_K=64) and FP4 (MMA_K=128) Tensor Core MMA instructions
        • B collector reuse
        • Extended TMEM size from 512 COL to 576 COL
        • Larger shared memory allocations (328KB)
        • Enhanced mixed precision throughput (FP8/FP4)
        • Softmax acceleration related feature
        • 2:4 sparsity support for FP4
    • CuTe DSL extensions has several new features:

      • CTA-V maps are now inferred automatically for cute_ext TMA load, store, multicast, and reduce-store operations. Explicit CTA-V maps remain supported as overrides.
      • Added asynchronous atomic TMA reduce-store and sparse MMA operations.
      • Added reusable cute_ext GEMM mainloop and TMA epilogue helpers.
      • Added opt-in TMEM accumulator-buffer planning, including overlapping ping-pong storage for capacity-constrained kernels.
      • Improved device-side TMA descriptor updates and grouped GEMM performance through SMEM-staged updates, workspace reuse, and reduced prologue and synchronization overhead.
    • This release includes an opt-in preview of the CuTe DSL extensions (cute_ext) compiler pipeline for ordinary Cute DSL kernels. This pipeline lets user mix cute_ext APIs directly into @cute.jit and @cute.kernel code and is required for kernels that mix the two API surfaces. You may test this feature with the following:
      CUTE_DSL_USE_EXTENSION_COMPILER=1 python your_program.py
      The pipeline is expected to preserve program behavior and performance, but generated PTX/SASS may differ. Note that this pipeline will become the default in the future, no earlier than 4.10.

    • Added examples for better control over Primitives' compiler warnings/errors introduced in 4.7.0. See the CuTeDSL/experimental/compiler_diagnostic/ directory.

    • IKET Profiler Tool

      • Rubin kernels (sm107) can now be profiled.
      • It is now possible to only dump timing data for a specific cluster to reduce profiling overhead. Previously all clusters were profiled.
      • Task Scheduling can instrument the schedule with IKET ranges when constructing TaskManager objects (iket_enable_profiling=True). Task execution will generate an IKET range and individual pipeline stages in a schedule may generate separate ranges (iket_profiling_stages).
    • A number of new examples were added in this release:

      • Rubin (CuTe):
        • Dense GEMM for legacy data type with B collector reuse as applicable
        • Grouped GEMM with B collector reuse
        • Dense blockscaled GEMM with FP4/FP6/FP8 mixed precision and UE5M3 / block-32 scale-factor support
        • Grouped blockscaled GEMM with B collector reuse as applicable
        • Blockwise GEMM
      • Rubin (CuTe extension):
        • Support for higher-throughput FP8 (MMA_K=64) and FP4 (MMA_K=128) blockscaled GEMM with UE5M3 scale-factor
        • Grouped GEMM with B collector reuse
      • Blackwell (CuTe extension):
        • Dense GEMMs
          • Back-to-back GEMM
          • Blockscaled GEMM
          • Persistent GEMM with alpha/beta scaling
          • CLC scheduler/dynamic persistent GEMM
          • GLU GEMM
          • Mixed input GEMM
          • Planar complex GEMM
          • Input transform GEMM
          • GeForce pingpong dense GEMM
          • Blackwell Ultra blockscaled GEMM
        • Dense Convolutions
          • Implicit-Gemm Fprop Conv
          • Blocksclaed Implicit-Gemm Fprop Conv
          • GeForce Implicit-Gemm Fprop Conv
          • GeForce Blockscaled Implicit-Gemm Fporp Conv
        • Attention
          • GQA Decode
        • Grouped GEMM
          • Unscaled and blockscaled grouped GEMM
        • Top-K
      • Ampere (CuTe extension):
        • SIMT GEMM
    • CuTe DSL now supports x86_64 Windows

    • CuTe DSL AoT now supports new host target: QNX8.0

    • Notebooks are restructured under examples/python/CuTeDSL/cute/notebooks and new notebooks for primitives will be added under examples/python/CuTeDSL/notebooks

    • Numpy is now not a default dependency

  • Bug fixes and improvements:

    • nvidia-cuda-nvdisasm is now an optional dependency of nvidia-cutlass-dsl via the optional [sass] extra. SASS dumping (CUTE_DSL_KEEP=sass / KeepSASS) now resolves nvdisasm from the bundled wheel (recommended since its version matches the DSL toolchain) or from a local CUDA Toolkit (CUDA_HOME/CUDA_PATH). A locally-provided nvdisasm must come from a CUDA Toolkit at least as new as the toolchain that produced the CUBIN. Installations that never dump SASS are unaffected.
    • Reduced the protobuf version requirement of IKET profiler from 6.30 to 4.21. This should make protobuf an easier requirement to satisfy in preparation for transioning IKET to an optional extra.
    • Improved JAX PyTree input/output aliasing for cutlass.jax.cutlass_call
    • Fixed a regression from 4.6.0 where cute.autovec_copy emitted per-element instead of
      vectorized instructions for tensors with a dynamic stride (!3463)
    • Fixed TVM-FFI env stream detection for GPU tensors in tuple
      (!3444)
    • Fixed GPU link-libraries compile-option order so it is stable across processes
      (!3564)
    • Fixed preprocessor IndexError on staged bool() with no arguments
      (!3506)
    • Rejected cute.compile on @cute.kernel with a user error instead of an ICE
      (!3429)
    • Fixed CuTe DSL crashing the Python interpreter when used in a REPL
      (!3413)
    • Fixed a cuDNN Frontend FROST SDPA backward compilation failure issue (!3594)
    • Fixed SIGABRTs in TVM-FFI launch for cuDNN Frontend SM100 ragged SDPA kernel (!3595)
    • Fixed an issue with cute.extern where functions with zero arguments would generate invalid type signatures. Compiling such functions a second time would fail.
    • Clear error/warning are now emitted for CopyUniversalOp with memory attributes specified (!3711).
    • CUTLASS Primitives' tensormap_replace now keeps device-side TMA descriptor updates safe when a descriptor is shrunk below its original extent (e.g. grouped GEMM) and fixes a possible illegal memory access on Blackwell/Rubin. This behavior can be opted out by specifying keep_oob_fetch_window=True. See https://docs.nvidia.com/datacenter/tesla/tesla-release-notes-595-91-07/index.html#known-issues for more details on the known cuTensorMapEncodeTiled bug.
    • Fixed unintended sys.path modification that could break imports in some environments where Torch was not installed e.g. JAX.
  • Known issues

    • Due to a bug in CTK, the use of some packed instructions result in invalid code generation. Given the nature of the instructions and how they are explicitly used, we have chosen to not silently rewrite these, but rather are flagging until the upstream compiler fixes are available.
    • Avoid packed higher-throughput FP8 (MMA_K=128) subtraction via inline PTX sub.rn.e4m3x4.e4m3x4 or sub.rn.e5m2x4.e5m2x4, as incorrect code generation can produce wrong results.
    • Some CUTLASS Primitives packed mixed-input ops compile incorrectly on Rubin (sm107a/sm107f), resulting in wrong code generation. The specific patterns include:
      • Packed mul, sub, and add ops with bf16x2 or fp16x2 result type and f32x2 input type when result is converted to fp32 later. APIs affected: add_packed_f16x2_f32x2_f32x2, add_packed_bf16x2_f32x2_f32x2, mul_packed_f16x2_f32x2_f32x2, mul_packed_bf16x2_f32x2_f32x2, sub_packed_f16x2_f32x2_f32x2, sub_packed_bf16x2_f32x2_f32x2
        • For example:
          text
          x = prims.mul_packed_bf16x2_f32x2_f32x2(a, b)
          out[0], out[1] = f32(x[0]), f32(x[1])  # out[0], out[1] are wrong
      • Packed mul op with bf16x2 or fp16x2 result type and mixed bf16x2 and fp16x2 input types when the first operand is a compilation-time constant. APIs affected: mul_packed_bf16x2_bf16x2_f16x2, mul_packed_f16x2_f16x2_bf16x2
        • For example:
          text
          x = prims.mul_packed_bf16x2_bf16x2_f16x2(2.0, b) # x is wrong
          x = prims.mul_packed_f16x2_f16x2_bf16x2(2.0, b)  # x is wrong

This release has been tested against the following packages:

CUTLASS Operator API

  • Dense and blockscaled GEMMs in Operator API have preliminary Rubin support. These are provided as a preview and may need additional performance tuning.

    • Updated GEMMs include:

      • Dense GEMMs: FP8xFP8
      • Blockscaled GEMM: {MXFP8}x{MXFP4, MXFP8} and {MXFP4, NVFP4}x{MXFP4, NVFP4} (including support for the new UE5M3 scale factor dtype for NVFP4).
    • These kernels utilize the below new features in Rubin:

      • Higher SMEM (328KB) and TMEM capacity (288KB)
      • B-buffer reuse
      • Enhanced mixed precision throughput
  • Operators can now be ranked by their estimated performance when nvMatmulHeuristics is available. See tutorial here. NOTE: This currently only supports Blackwell kernels as nvMatmulHeuristics does not yet support Rubin.

  • Standalone kernel implementations are now exposed through cutlass.kernels. These kernels can be used directly, in addition to being discoverable and usable via the Operator interface in cutlass.operators.

  • Custom Epilogue fusions now support per-row or per-column reductions.

  • IndexPtrGroupedGemmArguments is now used to represent Grouped GEMM with contiguous-offset/index-pointers. Existing GroupedGemmArguments is deprecated and will be removed in a future release.

C++

  • Added initial Rubin support (SM107) with CuTe C++ building blocks:
  • CuTe examples that demonstrate the use of Rubin SM107 Tensor Core instructions:
  • Adjusted shared-memory and tensor-memory capacity handling for Rubin SM107:

Note: Executing Rubin kernels (SM107) requires the R615 driver which will be released
with CUDA Toolkit 13.4 GA. R610 from CUDA Toolkit 13.4 Developer Preview is not sufficient.

See the CHANGELOG for details of all past releases and updates.

Performance

CUTLASS primitives are very efficient. When used to construct device-wide GEMM kernels,
they exhibit nearly optimal utilization of peak theoretical throughput. The figure below
shows CUTLASS 3.8's performance as a % of theoretical peak utilization
on various input and output data types when run on NVIDIA Blackwell SM100 architecture GPU.

The two figures below show the continual CUTLASS performance improvements
on an NVIDIA H100 (NVIDIA Hopper architecture) since
CUTLASS 3.1.
CUTLASS 3.5.1 was compiled with the CUDA 12.5u1 Toolkit.
Tensor Core operations are implemented using CUDA's
mma and
wgmma instructions.

CuTe

CUTLASS 3.0 introduced a new core library, CuTe, to describe and manipulate tensors of threads and data.
CuTe is a collection of C++ CUDA template abstractions for
defining and operating on hierarchically multidimensional layouts of threads and data.
CuTe provides Layout and Tensor objects that compactly package the type,
shape, memory space, and layout of data, while performing the complicated indexing for the user.
This lets programmers focus on the logical descriptions of their algorithms while
CuTe does the mechanical bookkeeping for them. With these tools, we can quickly design,
implement, and modify all dense linear algebra operations.

The core abstractions of CuTe are hierarchically multidimensional layouts
which can be composed with data arrays to represent tensors.
The representation of layouts is powerful enough to represent nearly
everything we need to implement efficient dense linear algebra.
Layouts can also be combined and manipulated via functional composition, on which we build a large set of common operations such as tiling and partitioning.

CUTLASS 3.0 and beyond adopts CuTe throughout the GEMM hierarchy in its templates.
This greatly simplifies the design and improves code composability and readability.
More documentation specific to CuTe can be found in its
dedicated documentation directory.

Compatibility

Minimum requirements:

  • Architecture: Volta (compute capability 7.0)
  • Compiler: Must support at least C++17
  • CUDA Toolkit version: 11.4

CUTLASS requires a C++17 host compiler and
performs best when built with the CUDA 12.8 Toolkit.
It is also compatible with CUDA 11.4, CUDA 11.5, CUDA 11.6, CUDA 11.7, CUDA 11.8, and all other CUDA 12.x versions.

Operating Systems

We have tested the following environments.

Operating System Compiler
Ubuntu 18.04 GCC 7.5.0
Ubuntu 20.04 GCC 10.3.0
Ubuntu 22.04 GCC 11.2.0

Note: GCC 8.5.0 has known regressions regarding fold expressions and overloaded operators. Using GCC 7.5.0 or (preferred) GCC >= 9 is recommended.

Note: CUTLASS 3.x builds are known to be down on Windows platforms for all CUDA toolkits.
CUTLASS team is working on a fix.

Hardware

CUTLASS runs successfully on the following NVIDIA GPUs, and it is expected to be efficient on Volta, Turing, Ampere, Ada, and Hopper architecture based NVIDIA GPUs.

GPU CUDA Compute Capability Minimum CUDA Toolkit Required by CUTLASS-3
NVIDIA V100 Tensor Core GPU 7.0 11.4
NVIDIA TitanV 7.0 11.4
NVIDIA GeForce RTX 20x0 series 7.5 11.4
NVIDIA T4 7.5 11.4
NVIDIA A100 Tensor Core GPU 8.0 11.4
NVIDIA A10 8.6 11.4
NVIDIA GeForce RTX 30x0 series 8.6 11.4
NVIDIA GeForce RTX 40x0 series 8.9 11.8
NVIDIA L40 8.9 11.8
NVIDIA H100 Tensor Core GPU 9.0 11.8
NVIDIA H200 Tensor Core GPU 9.0 11.8
NVIDIA B200 Tensor Core GPU 10.0 12.8
NVIDIA B300 Tensor Core GPU 10.3 13.0
NVIDIA DRIVE Thor 11.0 13.0
NVIDIA GeForce RTX 50x0 series 12.0 12.8
NVIDIA DGX Spark 12.1 13.0

Target Architecture

In general, PTX code generated for one target architecture can be run on future architectures
(i.e., it is forward compatible).
However, CUDA 12.0 introduced the concept of "architecture-accelerated features" whose
PTX does not have forward compatibility guarantees.
Several Hopper and Blackwell PTX instructions fall under this category of
architecture-accelerated features, and thus require a sm_90a or sm100a target architecture
(note the "a" appended). For more details on this and other architecture-accelerated instructions,
please refer to the CUDA Documentation.

The target architecture information is passed on to CUTLASS via the cmake flag
CUTLASS_NVCC_ARCHS. In order to maximize performance on Hopper GH100,
users are required to build CUTLASS with 90a as the target architecture.
If a user accidentally builds a kernel which uses SM90a features
(e.g. Hopper Tensor Core Instructions), using the SM90 target
(note the lack of "a"), with either CUDA Toolkit 12 or 11.8,
the kernel is expected to fail with a runtime error.

text
cmake .. -DCUTLASS_NVCC_ARCHS="90a"

Or

text
cmake .. -DCUTLASS_NVCC_ARCHS="100a"

Note: The NVIDIA Blackwell SM100 architecture used in the datacenter
products has a different compute capability than the one underpinning
NVIDIA Blackwell GeForce RTX 50 series GPUs (SM120). As a result, kernels
compiled for Blackwell SM100 architecture with arch conditional features
(using sm100a) are not compatible with RTX 50 series GPUs.

Please refer to the functionality documentation
for details on which kernels require which target architectures.

Documentation

CUTLASS is described in the following documents and the accompanying
Doxygen documentation.

  • Quick Start Guide - basics of building and running CUTLASS
  • Functionality - summarizes functionality available in CUTLASS
  • Efficient GEMM in CUDA - describes how GEMM kernels may be implemented efficiently in CUDA
  • CUTLASS 3.x Design - describes the CUTLASS 3.x design, its benefits, and how CuTe enables us to write much more composable components
  • GEMM API 3.x - describes the CUTLASS 3.x GEMM model and C++ template concepts
  • GEMM API 2.x - describes the CUTLASS 2.x GEMM model and C++ template concepts
  • Implicit GEMM Convolution - describes 2-D and 3-D convolution in CUTLASS
  • Code Organization - describes the organization and contents of the CUTLASS project
  • Terminology - describes terms used in the code
  • Programming Guidelines - guidelines for writing efficient modern CUDA C++
  • Fundamental types - describes basic C++ classes used in CUTLASS to represent numeric quantities and arrays
  • Layouts - describes layouts of matrices and tensors in memory
  • Tile Iterators - describes C++ concepts for iterating over tiles of matrices in memory
  • CUTLASS Profiler - command-line driven profiling application
  • CUTLASS Utilities - additional templates used to facilitate rapid development
  • Dependent kernel launch - describes a new feature in Hopper which allows overlapping dependent
    kernels in the same stream, and how it is used in CUTLASS.

Resources

We have also described the structure of an efficient GEMM in our talk at the
GPU Technology Conference 2018.

Building CUTLASS

CUTLASS is a header-only template library and does not need to be built to be used by other
projects. Client applications should target CUTLASS's include/ directory in their include
paths.

CUTLASS unit tests, examples, and utilities can be build with CMake.
The minimum version of CMake is given in the Quickstart guide.
Make sure the CUDACXX environment variable points to NVCC in the CUDA Toolkit installed
on your system.

bash
$ export CUDACXX=${CUDA_INSTALL_PATH}/bin/nvcc

Create a build directory within the CUTLASS project, then run CMake. By default CUTLASS will build kernels
for CUDA architecture versions 5.0, 6.0, 6.1, 7.0, 7.5, 8.0, 8.6, 8.9, and 9.0.
To reduce compile time you can specify
the architectures to build CUTLASS for by changing the CMake configuration setting
CUTLASS_NVCC_ARCHS.

bash
$ mkdir build && cd build

$ cmake .. -DCUTLASS_NVCC_ARCHS=80               # compiles for NVIDIA's Ampere Architecture

From the build/ directory, compile and run the CUTLASS unit tests by building the target test_unit with make.

The unit tests are organized as several binaries mirroring the top-level namespaces of CUTLASS,
and they may be executed in parallel via make's -j command line argument.

bash
$ make test_unit -j
...
...
...
[----------] Global test environment tear-down
[==========] 946 tests from 57 test cases ran. (10812 ms total)
[  PASSED  ] 946 tests.

All tests should pass on supported platforms, though the exact number of tests may vary over time.

Project Structure

CUTLASS is arranged as a header-only library along with Utilities, Tools, Examples, and unit tests.
Doxygen documentation provides a complete list of files, classes,
and template concepts defined in the CUTLASS project.

A detailed explanation of the source code organization may be found in the
CUTLASS documentation, but several main components are summarized below.

CUTLASS Template Library

text
/* Detailed source-code truncated for AI context efficiency. */

CUTLASS SDK Examples

CUTLASS SDK examples apply CUTLASS templates to implement basic computations.

Tools

text
tools/
  library/                   # CUTLASS Instance Library - contains instantiations of all supported CUTLASS templates
    include/
      cutlass/
        library/

  profiler/                  # CUTLASS Profiler         - command-line utility for executing operations in the
                             #                            CUTLASS Library

  util/                      # CUTLASS Utilities        - contains numerous helper classes for
    include/                 #                            managing tensors in device memory, reference
      cutlass/               #                            implementations for GEMM, random initialization
        util/                #                            of tensors, and I/O.

Test

The test/unit/ directory consist of unit tests implemented with Google Test that demonstrate
basic usage of Core API components and complete tests of the CUTLASS GEMM computations.

Instructions for building and running the Unit tests are described in the Quickstart guide.

Performance Profiling

The tools/profiler/ directory contains a command-line utility for launching each of the GEMM kernels.
It can be built as follows:

bash
$ make cutlass_profiler -j16

Building all GEMM and Convolution kernels (_long_ build times)

By default, only one tile size is instantiated for each data type, math instruction, and layout.
To instantiate all, set the following environment variable when running CMake from an empty build/ directory.
Beware, this results in tens of thousands of kernels and long build times.
This would also result in a large binary size and on some platforms linker to fail on building the library.
Therefore, it's highly recommended to generate only a subset of kernels as demonstrated in the sub-section below.

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS=90a -DCUTLASS_LIBRARY_KERNELS=all
...
$ make cutlass_profiler -j16

Building a subset of GEMM and Convolution kernels (_reduced_ build times)

To compile strictly one kernel or a small set of kernels, a comma-delimited list of kernel names with
wildcard characters may be used to reduce the set of kernels. The following examples show building exactly one
or a subset of kernels for NVIDIA Ampere and Turing architecture:

Building a subset Tensor Core GEMM kernels

To compile a subset of Tensor Core GEMM kernels with FP32 accumulation and FP16 input targeting NVIDIA Ampere and Turing architecture,
use the below cmake command line:

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS='75;80' -DCUTLASS_LIBRARY_KERNELS=cutlass_tensorop_s*gemm_f16_*_nt_align8
...
$ make cutlass_profiler -j16

Example command line for profiling a subset of Tensor Core GEMM kernels is as follows:

bash
./tools/profiler/cutlass_profiler --kernels=cutlass_tensorop_s*gemm_f16_*_nt_align8 --m=3456 --n=4096 --k=4096

...
=============================
  Problem ID: 1

        Provider: CUTLASS
   OperationKind: gemm
       Operation: cutlass_tensorop_s1688gemm_f16_256x128_32x2_nt_align8

          Status: Success
    Verification: ON
     Disposition: Passed

reference_device: Passed
          cuBLAS: Passed

       Arguments: --gemm_kind=universal --m=3456 --n=4096 --k=4096 --A=f16:column --B=f16:row --C=f32:column --alpha=1  \
                  --beta=0 --split_k_slices=1 --batch_count=1 --op_class=tensorop --accum=f32 --cta_m=256 --cta_n=128  \
                  --cta_k=32 --stages=2 --warps_m=4 --warps_n=2 --warps_k=1 --inst_m=16 --inst_n=8 --inst_k=8 --min_cc=75  \
                  --max_cc=1024

           Bytes: 118489088  bytes
           FLOPs: 115992428544  flops

         Runtime: 1.55948  ms
          Memory: 70.7616 GiB/s

            Math: 74378.8 GFLOP/s



=============================
...

Building one CUDA Core GEMM kernel

To compile one SGEMM kernel targeting NVIDIA Ampere and Turing architecture, use the below cmake command line:

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS='75;80' -DCUTLASS_LIBRARY_KERNELS=cutlass_simt_sgemm_128x128_8x2_nn_align1
...
$ make cutlass_profiler -j16

Example command line for profiling single SGEMM CUDA kernel is as follows:

bash
$ ./tools/profiler/cutlass_profiler --kernels=sgemm --m=3456 --n=4096 --k=4096

=============================
  Problem ID: 1

        Provider: CUTLASS
   OperationKind: gemm
       Operation: cutlass_simt_sgemm_128x128_8x2_nn_align1

          Status: Success
    Verification: ON
     Disposition: Passed

          cuBLAS: Passed

       Arguments: --m=3456 --n=4096 --k=4096 --A=f32:column --B=f32:column --C=f32:column --alpha=1 --beta=0 --split_k_slices=1  \
                  --batch_count=1 --op_class=simt --accum=f32 --cta_m=128 --cta_n=128 --cta_k=8 --stages=2 --warps_m=4  \
                  --warps_n=2 --warps_k=1 --inst_m=1 --inst_n=1 --inst_k=1 --min_cc=50 --max_cc=1024

           Bytes: 180355072  bytes
           FLOPs: 115992428544  flops

         Runtime: 6.73655  ms
          Memory: 24.934 GiB/s

            Math: 17218.4 GFLOP/s

=============================

Building a subset of Tensor Core Convolution kernels

To compile a subset of Tensor core convolution kernels implementing forward propagation (fprop) with FP32 accumulation
and FP16 input targeting NVIDIA Ampere and Turing architecture, use the below cmake command line:

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS='75;80' -DCUTLASS_LIBRARY_KERNELS=cutlass_tensorop_s*fprop_optimized_f16
...
$ make cutlass_profiler -j16

Example command line for profiling a subset of Tensor Core convolution kernels is as follows:

bash
$ ./tools/profiler/cutlass_profiler --kernels=cutlass_tensorop_s*fprop_optimized_f16 --n=8 --h=224 --w=224 --c=128 --k=128 --r=3 --s=3

...
=============================
  Problem ID: 1

        Provider: CUTLASS
   OperationKind: conv2d
       Operation: cutlass_tensorop_s16816fprop_optimized_f16_128x128_32x5_nhwc

          Status: Success
    Verification: ON
     Disposition: Passed

reference_device: Passed

       Arguments: --conv_kind=fprop --n=8 --h=224 --w=224 --c=128 --k=128 --r=3 --s=3 --p=224 --q=224 --pad_h=1 --pad_w=1  \
                  --stride_h=1 --stride_w=1 --dilation_h=1 --dilation_w=1 --Activation=f16:nhwc --Filter=f16:nhwc --Output=f32:nhwc  \
                  --conv_mode=cross --iterator_algorithm=optimized --alpha=1 --beta=0 --split_k_mode=serial --split_k_slices=1  \
                  --eq_gemm_provider=none --op_class=tensorop --accum=f32 --cta_m=128 --cta_n=128 --cta_k=32 --stages=5  \
                  --warps_m=2 --warps_n=2 --warps_k=1 --inst_m=16 --inst_n=8 --inst_k=16 --min_cc=80 --max_cc=1024

           Bytes: 1130659840  bytes
           FLOPs: 118482796544  flops

         Runtime: 0.711496  ms
          Memory: 1479.99 GiB/s

            Math: 166526 GFLOP/s

=============================
...

Building one Convolution CUDA kernel

To compile and run one CUDA Core convolution kernel implementing forward propagation (fprop) with F32 accumulation
and FP32 input targeting NVIDIA Ampere and Turing architecture, use the below cmake command line:

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS='75;80' -DCUTLASS_LIBRARY_KERNELS=cutlass_simt_sfprop_optimized_128x128_8x2_nhwc
...
$ make cutlass_profiler -j16

Example command line for profiling one CUDA Core convolution kernel:

bash
$ ./tools/profiler/cutlass_profiler --kernels=cutlass_simt_sfprop_optimized_128x128_8x2_nhwc --n=8 --h=224 --w=224 --c=128 --k=128 --r=3 --s=3


=============================
  Problem ID: 1

        Provider: CUTLASS
   OperationKind: conv2d
       Operation: cutlass_simt_sfprop_optimized_128x128_8x2_nhwc

          Status: Success
    Verification: ON
     Disposition: Passed

reference_device: Passed

       Arguments: --conv_kind=fprop --n=8 --h=224 --w=224 --c=128 --k=128 --r=3 --s=3 --p=224 --q=224 --pad_h=1 --pad_w=1  \
                  --stride_h=1 --stride_w=1 --dilation_h=1 --dilation_w=1 --Activation=f32:nhwc --Filter=f32:nhwc --Output=f32:nhwc  \
                  --conv_mode=cross --iterator_algorithm=optimized --alpha=1 --beta=0 --split_k_mode=serial --split_k_slices=1  \
                  --eq_gemm_provider=none --op_class=simt --accum=f32 --cta_m=128 --cta_n=128 --cta_k=8 --stages=2 --warps_m=4  \
                  --warps_n=2 --warps_k=1 --inst_m=1 --inst_n=1 --inst_k=1 --min_cc=50 --max_cc=1024

           Bytes: 2055798784  bytes
           FLOPs: 118482796544  flops

         Runtime: 7.34266  ms
          Memory: 260.752 GiB/s

            Math: 16136.2 GFLOP/s


=============================

More Details on Compiling CUTLASS Kernels and CUTLASS Profiler

About

CUTLASS is released by NVIDIA Corporation as Open Source software under the
3-clause "New" BSD license.

Contributors

The official list of CUTLASS developers and contributors is available here: CONTRIBUTORS.

Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Quickstart (`media/docs/cpp/quickstart.md`)

Quickstart

Prerequisites

CUTLASS requires:

  • NVIDIA CUDA Toolkit (11.4 or later required, 12.0 recommended)
  • CMake 3.18+
  • host compiler supporting C++17 or greater (minimum g++ 7.5.0)
  • Python 3.6+

CUTLASS may be optionally compiled and linked with

  • cuBLAS
  • cuDNN v7.6 or later

Initial build steps

Construct a build directory and run CMake.

bash
$ export CUDACXX=${CUDA_INSTALL_PATH}/bin/nvcc

$ mkdir build && cd build

$ cmake .. -DCUTLASS_NVCC_ARCHS=90a            # compiles for NVIDIA Hopper GPU architecture
$ cmake .. -DCUTLASS_NVCC_ARCHS=100a           # compiles for NVIDIA Blackwell SM100 GPU architecture

If your goal is strictly to build only the CUTLASS Profiler and to minimize compilation time, we suggest
executing the following CMake command in an empty build/ directory.

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS=90a -DCUTLASS_ENABLE_TESTS=OFF -DCUTLASS_UNITY_BUILD_ENABLED=ON

This reduces overall compilation time by excluding unit tests and enabling the unity build.

You may reduce build times by compiling only certain operations by setting the CUTLASS_LIBRARY_OPERATIONS flag as shown below,
executed from an empty build/ directory. This only compiles 2-D convolution kernels.

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS=90a -DCUTLASS_LIBRARY_OPERATIONS=conv2d

You may also filter kernels by name by supplying a filter string with flag CUTLASS_LIBRARY_KERNELS. For example the below command selects only CUTLASS-3 kernels.

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS=90a -DCUTLASS_LIBRARY_KERNELS=cutlass3x*

See more examples on selectively compiling CUTLASS GEMM and convolution kernels here.

You may explicitly exclude cuBLAS and cuDNN as dependencies with the following CMake flags.

  • -DCUTLASS_ENABLE_CUBLAS=OFF
  • -DCUTLASS_ENABLE_CUDNN=OFF

Build and run the CUTLASS Profiler

From the build/ directory created above, compile the CUTLASS Profiler.

bash
$ make cutlass_profiler -j12

Then execute the CUTLASS Profiler computing GEMM, execute the following command.

bash
$ ./tools/profiler/cutlass_profiler --kernels=sgemm --m=4352 --n=4096 --k=4096

=============================
  Problem ID: 1

    Provider: CUTLASS
   Operation: cutlass_simt_sgemm_128x128_nn

 Disposition: Passed
      Status: Success

   Arguments:  --m=4352 --n=4096 --k=4096 --A=f32:column --B=f32:column --C=f32:column --alpha=1 --beta=0  \
               --split_k_slices=1 --batch_count=1 --op_class=simt --accum=f32 --cta_m=128 --cta_n=128 --cta_k=8  \
               --stages=2 --warps_m=2 --warps_n=2 --warps_k=1 --inst_m=1 --inst_n=1 --inst_k=1 --min_cc=50  \
               --max_cc=1024

       Bytes: 52428800  bytes
       FLOPs: 146064539648  flops

     Runtime: 10.5424  ms
      Memory: 4.63158 GiB/s

        Math: 13854.9 GFLOP/s

To execute the CUTLASS Profiler for convolution, run the following example.

bash
$ ./tools/profiler/cutlass_profiler --kernels=s1688fprop --n=8 --h=224 --w=224 --c=128 --k=128 --r=3 --s=3 --pad_h=1 --pad_w=1

To execute all CUTLASS 2-D convolution operators, execute the following.

bash
$ ./tools/profiler/cutlass_profiler --operation=conv2d --n=8 --h=224 --w=224 --c=128 --k=128 --r=3 --s=3


=============================
  Problem ID: 1

        Provider: CUTLASS
   OperationKind: conv2d
       Operation: cutlass_simt_sfprop_optimized_128x128_8x2_nhwc

          Status: Success
    Verification: ON
     Disposition: Passed

reference_device: Passed

       Arguments: --conv_kind=fprop --n=8 --h=224 --w=224 --c=128 --k=128 --r=3 --s=3 --p=224 --q=224 --pad_h=1 --pad_w=1  \
                  --stride_h=1 --stride_w=1 --dilation_h=1 --dilation_w=1 --Activation=f32:nhwc --Filter=f32:nhwc --Output=f32:nhwc  \
                  --conv_mode=cross --iterator_algorithm=optimized --alpha=1 --beta=0 --split_k_mode=serial --split_k_slices=1  \
                  --eq_gemm_provider=none --op_class=simt --accum=f32 --cta_m=128 --cta_n=128 --cta_k=8 --stages=2 --warps_m=4  \
                  --warps_n=2 --warps_k=1 --inst_m=1 --inst_n=1 --inst_k=1 --min_cc=50 --max_cc=1024

           Bytes: 2055798784  bytes
           FLOPs: 118482796544  flops

         Runtime: 8.13237  ms
          Memory: 235.431 GiB/s

            Math: 14569.3 GFLOP/s

See documentation for the CUTLASS Profiler for more details.

Build and run CUTLASS Unit Tests

From the build/ directory created above, simply build the target test_unit to compile and run
all unit tests.

bash
$ make test_unit -j
...
...
...
[----------] Global test environment tear-down
[==========] 946 tests from 57 test cases ran. (10812 ms total)
[  PASSED  ] 946 tests.
$

The exact number of tests run is subject to change as we add more functionality.

No tests should fail. Unit tests automatically construct the appropriate runtime filters
to avoid executing on architectures that do not support all features under test.

The unit tests are arranged hierarchically mirroring the CUTLASS Template Library. This enables
parallelism in building and running tests as well as reducing compilation times when a specific
set of tests are desired.

For example, the following executes strictly the warp-level GEMM tests.

bash
$ make test_unit_gemm_warp -j
...
...
[----------] 3 tests from SM75_warp_gemm_tensor_op_congruous_f16
[ RUN      ] SM75_warp_gemm_tensor_op_congruous_f16.128x128x8_32x128x8_16x8x8
[       OK ] SM75_warp_gemm_tensor_op_congruous_f16.128x128x8_32x128x8_16x8x8 (0 ms)
[ RUN      ] SM75_warp_gemm_tensor_op_congruous_f16.128x128x32_64x64x32_16x8x8
[       OK ] SM75_warp_gemm_tensor_op_congruous_f16.128x128x32_64x64x32_16x8x8 (2 ms)
[ RUN      ] SM75_warp_gemm_tensor_op_congruous_f16.128x128x32_32x32x32_16x8x8
[       OK ] SM75_warp_gemm_tensor_op_congruous_f16.128x128x32_32x32x32_16x8x8 (1 ms)
[----------] 3 tests from SM75_warp_gemm_tensor_op_congruous_f16 (3 ms total)
...
...
[----------] Global test environment tear-down
[==========] 104 tests from 32 test cases ran. (294 ms total)
[  PASSED  ] 104 tests.
[100%] Built target test_unit_gemm_warp

Building for Multiple Architectures

To minimize compilation time, specific GPU architectures can be enabled via the CMake command,
selected by CUDA Compute Capability.

NVIDIA Blackwell Architecture.

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS=100a              # compiles for NVIDIA Blackwell GPU architecture

NVIDIA Hopper Architecture.

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS=90a              # compiles for NVIDIA Hopper GPU architecture

NVIDIA Ampere Architecture.

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS=80               # compiles for NVIDIA Ampere GPU architecture

NVIDIA Turing Architecture.

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS=75               # compiles for NVIDIA Turing GPU architecture

NVIDIA Volta Architecture.

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS=70               # compiles for NVIDIA Volta GPU architecture

NVIDIA Pascal Architecture.

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS="60;61"          # compiles for NVIDIA Pascal GPU architecture

NVIDIA Maxwell Architecture.

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS="50;53"          # compiles for NVIDIA Maxwell GPU architecture

Using CUTLASS within other applications

Applications should list /include within their include paths. They must be
compiled as C++17 or greater.

Example: print the contents of a variable storing half-precision data.

c++
#include <iostream>
#include <cutlass/cutlass.h>
#include <cutlass/numeric_types.h>
#include <cutlass/core_io.h>

int main() {

  cutlass::half_t x = 2.25_hf;

  std::cout << x << std::endl;

  return 0;
}

Launching a GEMM kernel in CUDA

Example: launch a mixed-precision GEMM targeting Turing Tensor Cores.

Note, this example uses CUTLASS Utilities. Be sure tools/util/include is listed as an include path.

text
/* Detailed source-code truncated for AI context efficiency. */

Note, the above could be simplified as follows using helper methods defined in HostTensor.

c++
cutlass::HostTensor<cutlass::half_t, cutlass::layout::ColumnMajor> A({M, K});
  cutlass::HostTensor<cutlass::half_t, cutlass::layout::ColumnMajor> B({K, N});
  cutlass::HostTensor<cutlass::half_t, cutlass::layout::ColumnMajor> C({M, N});

  //
  // Use the TensorRef returned by HostTensor::device_ref().
  //

  status = gemm_op({
    {M, N, K},
    A.device_ref(),            // TensorRef to A device tensor
    B.device_ref(),            // TensorRef to B device tensor
    C.device_ref(),            // TensorRef to C device tensor
    C.device_ref(),            // TensorRef to D device tensor - may be the same as C
    {alpha, beta}              // epilogue operation arguments
  });

Launching a GEMM kernel using CUTLASS 3.0 or newer

Example: launch a mixed-precision GEMM targeting Hopper Tensor Cores.

text
/* Detailed source-code truncated for AI context efficiency. */

CUTLASS Library

The CUTLASS Library defines an API for managing and executing collections of compiled
kernel instances and launching them from host code without template instantiations in client code.

The host-side launch API is designed to be analogous to BLAS implementations for convenience, though its
kernel selection procedure is intended only to be functionally sufficient. It may not launch the
optimal tile size for a given problem. It chooses the first available kernel whose data types,
layouts, and alignment constraints satisfy the given problem. Kernel instances and a data structure
describing them are completely available to client applications which may choose to implement their
own selection logic.

cuBLAS offers the best performance and functional coverage
for dense matrix computations on NVIDIA GPUs.

The CUTLASS Library is used by the CUTLASS Profiler to manage kernel instances, and it is also used
by several SDK examples.

The CUTLASS Library defines enumerated types describing numeric data types, matrix and tensor
layouts, math operation classes, complex transformations, and more.

Client applications should specify tools/library/include in their
include paths and link against libcutlas_lib.so.

The CUTLASS SDK example 10_planar_complex specifies
its dependency on the CUTLASS Library with the following CMake command.

text
target_link_libraries(
  10_planar_complex
  PRIVATE
  cutlass_lib
  cutlass_tools_util_includes
)

A sample kernel launch from host-side C++ is shown as follows.

text
/* Detailed source-code truncated for AI context efficiency. */

Example CMake Commands

To instantiate all operations supporting all tile sizes, data types, and alignment constraints, specify
-DCUTLASS_LIBRARY_KERNELS=all when running cmake.

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS='70;75;80' -DCUTLASS_LIBRARY_KERNELS=all

The above command line generates about twenty thousand kernels targeting NVIDIA Ampere, Turing, and Volta architectures.
Compiling thousands of kernels for three different architectures is time-consuming. Additionally, this would also result
in a large binary size and on some platforms linker to fail on building the library.

Enabling the "unity build" instantiates multiple kernel instances in each compilation unit, thereby reducing binary size
and avoiding linker limitations on some platforms.

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS="70;75;80" -DCUTLASS_LIBRARY_KERNELS=all -DCUTLASS_UNITY_BUILD_ENABLED=ON

It is advised to only compile CUTLASS kernels for NVIDIA architectures one plans on running. Furthermore, kernels
can be selectively included in the CUTLASS Library by specifying filter strings and wildcard characters when executing CMake.

Several examples are defined below for convenience. They may be combined as a comma-delimited list.
Compling only the kernels desired reduces compilation time.

GEMM CMake Examples

Example. All GEMM kernels targeting NVIDIA Ampere Tensor Cores.

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS=80 -DCUTLASS_LIBRARY_KERNELS=tensorop*gemm

Example. All GEMM kernels targeting NVIDIA Turing Tensor Cores.

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS=75 -DCUTLASS_LIBRARY_KERNELS=tensorop*gemm

Example. All GEMM kernels with FP32 accumulation targeting NVIDIA Ampere, Turing, and Volta architectures.

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS="70;75;80" -DCUTLASS_LIBRARY_KERNELS=s*gemm

Example. All kernels which expect A and B to be column-major or row-major targeting NVIDIA Ampere, Turing, and Volta architectures.

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS="70;75;80" -DCUTLASS_LIBRARY_KERNELS=gemm*nn,gemm*tt

Example. All planar complex GEMM variants targeting NVIDIA Ampere, Turing, and Volta architectures.

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS="70;75;80" -DCUTLASS_LIBRARY_KERNELS=planar_complex

Convolution CMake Examples

Example. All convolution kernels targeting NVIDIA Ampere's 16816 Tensor Core operation

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS='80' -DCUTLASS_LIBRARY_KERNELS=s16816fprop,s16816dgrad,s16816wgrad

Example. All forward propagation (fprop) convolution kernels targeting CUDA Cores for multiple NVIDIA architectures

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS='50;60;61;70;75;80' -DCUTLASS_LIBRARY_KERNELS=sfprop

Example. All forward propagation (fprop) convolution kernels with FP32 accumulation and FP16 input targeting NVIDIA Ampere's 16816 Tensor Core operation

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS='80' -DCUTLASS_LIBRARY_KERNELS=s16816fprop_*_f16

Example. All backward weight gradient (wgrad) convolution kernels with FP32 accumulation, FP16 input, and optimized global memory iterator
targeting NVIDIA Ampere, Turing, and Volta Tensor Core operations

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS='70;75;80' -DCUTLASS_LIBRARY_KERNELS=tensorop*s*wgrad_optimized_f16

Instantiating a Blackwell SM100 GEMM kernel

Blackwell SM100 kernels are instantiated very similarly to Hopper kernels. Let us start with an
FP8 GEMM without blockscaling
as an example.

The kernel starts with setting up datatypes and cluster shapes.

c++
using LayoutA = cutlass::layout::RowMajor;
  using LayoutB = cutlass::layout::ColumnMajor;
  using LayoutC = cutlass::layout::ColumnMajor;
  using ElementA = cutlass::float_e4m3_t;
  using ElementB = cutlass::float_e4m3_t;
  using ElementC = cutlass::float_e4m3_t;
  using ElementD = cutlass::float_e4m3_t;
  using ElementAccumulator = float;
  using ElementCompute = float;
  using ElementBias = cutlass::half_t;
  using MmaTileShape = cute::Shape<_128,_64,Int<128 / sizeof(ElementA)>>;
  using ClusterShape = cute::Shape<_1,_1,_1>;

The epilogue needs to be instantiated first as the mainloop collective builder takes the shared memory budget of epilogue in the template parameter list. The 3.x epilogue collective builder API has not changed
for Blackwell, so the epilogue fusion is built in a same way as an SM90 epilogue.

c++
using EpilogueSchedule = cutlass::epilogue::TmaWarpSpecialized1Sm;

  using FusionOperation = cutlass::epilogue::fusion::LinearCombination<
    ElementD,
    ElementCompute,
    ElementC
  >;

  using CollectiveEpilogue = typename cutlass::epilogue::collective::CollectiveBuilder<
      cutlass::arch::Sm100, cutlass::arch::OpClassTensorOp,
      MmaTileShape, ClusterShape,
      cutlass::epilogue::collective::EpilogueTileAuto,
      ElementAccumulator, ElementCompute,
      ElementC, LayoutC, 16 / sizeof(ElementC),
      ElementD, LayoutC, 16 / sizeof(ElementD),
      EpilogueSchedule,
      FusionOperation
    >::CollectiveOp;

One can refer to our Sm100 unit tests as examples of how to correctly
choose mainloop schedules. All of our dispatch policies can be found in dispatch_policy.hpp
and more comprehensive Blackwell specific documentation for valid
dispatch policies can be in blackwell_functionality.md.

c++
using MainloopSchedule = cutlass::gemm::KernelTmaWarpSpecialized1SmSm100;
  using CollectiveMainloop = typename cutlass::gemm::collective::CollectiveBuilder<
      cutlass::arch::Sm100, cutlass::arch::OpClassTensorOp,
      ElementA, LayoutA, 16 / sizeof(ElementA),
      ElementB, LayoutB, 16 / sizeof(ElementB),
      ElementAccumulator,
      MmaTileShape, ClusterShape,
      cutlass::gemm::collective::StageCountAutoCarveout<static_cast<int>(sizeof(typename CollectiveEpilogue::SharedStorage))>,
      MainloopSchedule
    >::CollectiveOp;

  using GemmKernel = cutlass::gemm::kernel::GemmUniversal<
      Shape<int,int,int,int>,
      CollectiveMainloop,
      CollectiveEpilogue
  >;

Instantiating a blockscaled GEMM kernel is slightly different. Referring to an MXFP8 GEMM sample unit test, it takes a different tensor operation class:

c++
using ElementA = cutlass::mx_float8_t<cutlass::float_e4m3_t>;
  using ElementB = cutlass::mx_float8_t<cutlass::float_e4m3_t>;

are needed in the mainloop builder:

c++
using CollectiveMainloop = typename cutlass::gemm::collective::CollectiveBuilder<
      cutlass::arch::Sm100, cutlass::arch::OpClassTensorOp,
      ElementA, LayoutA, 16,
      ElementB, LayoutB, 16,
      ElementAccumulator,
      MmaTileShape, ClusterShape,
      cutlass::gemm::collective::StageCountAutoCarveout<static_cast<int>(sizeof(typename CollectiveEpilogue::SharedStorage))>,
      cutlass::gemm::KernelScheduleAuto
    >::CollectiveOp;

We encourage a user to refer to Sm100 unit tests and the generated profiler-based kernels as more comprehensive samples.

Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Cute 00 Quickstart (`media/docs/cpp/cute/00_quickstart.md`)

Getting Started With CuTe

CuTe is a collection of C++ CUDA template abstractions for defining and operating on hierarchically multidimensional layouts of threads and data. CuTe provides Layout and Tensor objects that compactly packages the type, shape, memory space, and layout of data, while performing the complicated indexing for the user. This lets programmers focus on the logical descriptions of their algorithms while CuTe does the mechanical bookkeeping for them. With these tools, we can quickly design, implement, and modify all dense linear algebra operations.

The core abstraction of CuTe are the hierarchically multidimensional layouts which can be composed with data arrays to represent tensors. The representation of layouts is powerful enough to represent nearly everything we need to implement efficient dense linear algebra. Layouts can also be combined and manipulated via functional composition, on which we build a large set of common operations such as tiling and partitioning.

System Requirements

CuTe shares CUTLASS 3.x's software requirements,
including NVCC with a C++17 host compiler.

Knowledge prerequisites

CuTe is a CUDA C++ header-only library. It requires C++17
(the revision of the C++ Standard that was released in 2017).

Throughout this tutorial, we assume intermediate C++ experience.
For example, we assume that readers know
how to read and write templated functions and classes, and
how to use the auto keyword to deduce a function's return type.
We will be gentle with C++ and explain some things
that you might already know.

We also assume intermediate CUDA experience.
For example, readers must know
the difference between device and host code,
and how to launch kernels.

Building Tests and Examples

CuTe's tests and examples build and run as part of CUTLASS's normal build process.

CuTe's unit tests live in the test/unit/cute subdirectory.

CuTe's examples live in the examples/cute subdirectory.

Library Organization

CuTe is a header-only C++ library, so there is no source code that needs building. Library headers are contained within the top level include/cute directory, with components of the library grouped by directories that represent their semantics.

Directory Contents
include/cute Each header in the top level corresponds to one of the fundamental building blocks of CuTe, such as Layout and Tensor.
include/cute/container Implementations of STL-like objects, such as tuple, array, and aligned array.
include/cute/numeric Fundamental numeric data types that include nonstandard floating-point types, nonstandard integer types, complex numbers, and integer sequence.
include/cute/algorithm Implementations of utility algorithms such as copy, fill, and clear that automatically leverage architecture-specific features if available.
include/cute/arch Wrappers for architecture-specific matrix-matrix multiply and copy instructions.
include/cute/atom Meta-information for instructions in arch and utilities like partitioning and tiling.

Tutorial

This directory contains a CuTe tutorial in Markdown format.
The file
0x_gemm_tutorial.md
explains how to implement dense matrix-matrix multiply using CuTe components.
It gives a broad overview of CuTe and thus would be a good place to start.

Other files in this directory discuss specific parts of CuTe.

  • 01_layout.md describes Layout, CuTe's core abstraction.

  • 02_layout_algebra.md describes more advanced Layout operations and the CuTe layout algebra.

  • 03_tensor.md describes Tensor,
    a multidimensional array abstraction which composes Layout
    with an array of data.

  • 04_algorithms.md summarizes CuTe's
    generic algorithms that operate on Tensors.

  • 0t_mma_atom.md demonstrates CuTe's meta-information and interface to our GPUs'
    architecture-specific Matrix Multiply-Accumulate (MMA) instructions.

  • 0x_gemm_tutorial.md walks through building a GEMM from scratch using CuTe.

  • 0y_predication.md explains what to do
    if a tiling doesn't fit evenly into a matrix.

  • 0z_tma_tensors.md explains an advanced Tensor type that CuTe uses to support TMA loads and stores.

Quick Tips

How do I print CuTe objects on host or device?

The cute::print function has overloads for almost all CuTe types, including Pointers, Integers, Strides, Shapes, Layouts, and Tensors. When in doubt, try calling print on it.

CuTe's print functions work on either host or device.
Note that on device, printing is expensive.
Even just leaving print code in place on device,
even if it is never called
(e.g., printing in an if branch that is not taken at run time),
may generate slower code.
Thus, be sure to remove code that prints on device after debugging.

You might also only want to print on thread 0 of each threadblock, or threadblock 0 of the grid. The thread0() function returns true only for global thread 0 of the kernel, that is, for thread 0 of threadblock 0. A common idiom for printing CuTe objects to print only on global thread 0.

c++
if (thread0()) {
  print(some_cute_object);
}

Some algorithms depend on some thread or threadblock,
so you may need to print on threads or threadblocks other than zero.
The header file
cute/util/debug.hpp,
among other utilities,
includes the function bool thread(int tid, int bid)
that returns true if running on thread tid and threadblock bid.

Other output formats

Some CuTe types have special printing functions that use a different output format.

The cute::print_layout function will display any rank-2 layout in a plain text table. This is excellent for visualizing the map from coordinates to indices.

The cute::print_tensor function will display any rank-1, rank-2, rank-3, or rank-4 tensor in a plain text multidimensional table. The values of the tensor are printed so you can verify the tile of data is what you expect after a copy, for example.

The cute::print_latex function will print LaTeX commands that you can use to build a nicely formatted and colored tables via pdflatex. This work for Layout, TiledCopy, and TiledMMA, which can be very useful to get a sense of layout patterns and partitioning patterns within CuTe.

Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Blackwell (`media/docs/cpp/blackwell.rst`)

.. _blackwell:

Blackwell Specific

.. toctree::
:maxdepth: 2

Blackwell SM100/SM120 GEMMs
Blackwell Cluster Launch Control


Media Docs Cpp Blackwell Cluster Launch Control (`media/docs/cpp/blackwell_cluster_launch_control.md`)

Blackwell Cluster Launch Control

Overview

A GEMM workload usually consists of three phases: prologue, mainloop and epilogue. Each SM will process multiple output tiles in series if the number of output tiles are much more than the number of SMs, completely exposing the overhead of prologue and epilogue.

Consider a GEMM that has 20x20x1 output tiles, running on a GPU with 100 SMs. There is another kernel occupying all the resources of 20 SMs so only 80 SMs can be used. Assume cluster shape is 1x1x1. The following diagram shows how the schedule would look like for such a kernel.

Static Scheduler

CUTLASS has adopted a software technique named persistent kernels. Persistent clusters, or Workers, can stay on the GPU throughout kernel execution and process multiple tiles, hiding prologue and epilogue costs. The tile scheduler statically determines the next output tile to process with zero overhead.

However, static scheduler is susceptible to workload imbalance if the resources of some SMs are unavailable. The following diagram illustrates this issue.

Dynamic Scheduler with Cluster Launch Control

A fundamental limitation of persistent scheduling is that the number of SMs this kernel can utilize is unknown in real time. Some SMs might be occupied by another kernel and thus their resources are unavailable. This makes it challenging to load-balance work across SMs.

Blackwell introduces cluster launch control (CLC) for dynamic scheduling. (See https://docs.nvidia.com/cuda/parallel-thread-execution). With this feature, the kernel launches a grid containing as many threadblocks as there are output tiles to compute in the kernel -- just like one would in a non-persistent kernel. Here we define ClcID to be a coordinate from the 3D grid launched on GPU.

Cluster launch control follows the below rules:

  1. A ClcID will be launched as a Worker when there are available resources.
  2. A ClcID can be queried by an existing Worker via clusterlaunchcontrol.try_cancel instruction.
  3. Every ClcID is guaranteed to be processed by either (1) or (2).
  4. Each worker uses the {blockIdx.x, blockIdx.y, blockIdx.z} coordinate as the first output tile to process and uses the CLC query for subsequent processing of output tiles.
  5. clusterlaunchcontrol.try_cancel instruction returns either a success signal with a ClcID or a decline signal. The most common reason of a decline is that all ClcIDs have been processed.
  6. Cluster launch control works on the granularity of clusters. For example, a 2x2 persistent worker cluster's query will consume 2x2 ClcIDs at once.

The following diagram shows how the schedule would look like with cluster launch control.

Programming Model

Pseudo Code

Non-persistent kernel

c++
// Non-persistent kernel
__device__ non_persistent_kernel(...) {
  setup_common_data_structures();
  dim3 workCoordinates = blockIdx;
  coordinate_specific_compute(workCoordinates);
}

Static Persistent Kernel

c++
// Static Persistent Kernel
__device__ static_persistent_kernel(...) {
  setup_common_data_structures(...);
  dim3 workCoordinates = blockIdx;
  bool isValidId;
  do {
    coordinate_specific_compute(workCoordinates);
    std::tie(isValidId, workCoordinates) = staticTileScheduler.fetch_next_work();
  } while (isValidId);
}

Blackwell Dynamic Persistent Kernel

c++
// Dynamic Persistent Kernel
__device__ clc_dynamic_persistent_kernel(...) {
  setup_common_data_structures(...);
  dim3 workCoordinates = blockIdx;
  dim3 newClcID;
  bool isValidId;
  do {
    coordinate_specific_compute(workCoordinates);
    std::tie(isValidId, newClcID) = clcTileScheduler.fetch_next_work();
    workCoordinates = newClcID;
  } while (isValidId);
}

Cluster Launch Control Pipeline Class

Please refer to the PipelineCLCFetchAsync pipeline class defined in Cluster launch control pipeline class. Cluster launch control queries can be pipelined and managed by an asynchronous pipeline with producer-consumer relationship (See
pipeline document). The producer is the scheduler warp of the 0th CTA in the cluster and the consumers are all warps that need ClcIDs.

To setup a CLC pipeline correctly, we need to make sure the params are set to the right values:

  • transaction_bytes is 16 as CLC will return a 16B response and store it in the specified shared memory address.
  • consumer_arv_count is the thread count of all the consumer warps in the cluster.
  • producer_arv_count is 1 because only one thread from scheduler warp will be elected to issue clusterlaunchcontrol.try_cancel.
  • producer_blockid is 0 to denote that the first CTA in the cluster is producing.

Dynamic tile scheduler class

Please refer to PersistentTileSchedulerSm100 class defined in sm100 dynamic persistent tile scheduler.

There are two important methods of the CLC scheduler class. The first is advance_to_next_work, which is intended to be executed by one elected thread from the scheduler warp. It effectively sends out the CLC query to the CLC. A CLC query response will be broadcast to the same shared memory address of all CTAs in the cluster.

The other method is named get_current_work. It simply loads the CLC response from the shared memory buffer indexed by a pipeline state.

The CLC pipeline and scheduler classes are used together to ensure correct functionality and necessary synchronization of CLC feature. Please refer to cluster launch control pipeline unit test.

Blackwell Warp-specialized Persistent Kernel

Now, let's take a look at how CLC feature is used in our Blackwell dense GEMM kernel.

This particular warp-specialized kernel has the following warp assignment:

Warp Role Warp
MMA 0
Scheduler 1
Mainloop Load 2
Epilogue Load 3
Epilogue 4, 5, 6, 7

Scheduler warp is the producer of the CLC pipeline. The consumers are the MMA, Mainloop Load, Epilogue Load and Epilogue warps. In addition, the scheduler warp is its own consumer! This is because it needs the success information from the query to terminate the persistent loop on end-of-grid.

The CLC pipeline has a depth of 3 to overlap the CLC operations of multiple waves for latency hiding. The first ClcID is the preloaded blockIdx, which does not require CLC query and is fully static.

Copyright (c) 2025 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Blackwell Functionality (`media/docs/cpp/blackwell_functionality.md`)

Blackwell SM100 GEMMs

TLDR; jump to block scaled GEMM example

Blackwell SM100 introduces tcgen05.mma instructions. tcgen05.mma instructions support all legacy types (tfloat32_t, half_t, bfloat16_t, int8_t, uint8_t) and
the new 4, 6, and 8-bits floating point datatypes with and without scale factors.
This document explains the new tcgen05.mma instructions supported by CUTLASS and how one can leverage CUTLASS to create
efficient SM100 GEMM kernels targeting these new mma instructions.

Blackwell SM100 has 7 new tcgen05.mma instructions. These instructions are 2x to 4x faster then Hopper Architecture's WGMMA instructions.

Ptx Instruction Throughput Notes
tcgen05.mma(.sp).cta_group::[1|2].kind::tf32 2x Hopper Tf32 Tensor Core MMA with A={tf32} x B={tf32} TN, NT, TT, NN layouts
tcgen05.mma(.sp).cta_group::[1|2].kind::f16 2x Hopper Fp16 Tensor Core MMA with A={f16} x B={f16} or A={bf16} x B={bf16} TN, NT, TT, NN layouts
tcgen05.mma(.sp).cta_group::[1|2].kind::i8 2x Hopper I8 Tensor Core MMA with A={i8} x B={i8} or A={u8} x B={u8} TN, NT, TT, NN layouts
tcgen05.mma(.sp).cta_group::[1|2].kind::f8f6f4 2x Hopper Fp8 Tensor Core Mixed precision MMA with A={f4,f6,f8} x B={f4,f6,f8} TN, NT, TT, NN layouts
tcgen05.mma(.sp).cta_group::[1|2].kind::mxf8f6f4.block_scale 2x Hopper Fp8 Tensor Core Block scaled mixed precision MMA with A={mxf4,mxf6,mxf8} x B={mxf4,mxf6,mxf8} with TN, NT, TT, NN layouts
tcgen05.mma(.sp).cta_group::[1|2].kind::mxf4.block_scale 4x Hopper Fp8 Tensor Core Block scaled MMA with A={mxf4} x B={mxf4} with TN layouts
tcgen05.mma(.sp).cta_group::[1|2].kind::mxf4nvf4.block_scale.scale_vec_size::[2X|4X] 4x Hopper Fp8 Tensor Core Block scaled MMA with A={mxf4} x B={mxf4} or A={nvf4} x B={nvf4} with TN layouts

For more detailed information see tcgen05.mma PTX documentation.

New in Blackwell SM100

Block Scaled GEMMs

Instructions with kind modifiers mxf8f6f4, mxf4, and nvf4mxf4 perform matrix multiplication operations with scale
factors of the form $D = C +( A \times SFA) * (B \times SFB)$. Scale factors are applied to GEMM-K dimension such that
every 16 or 32 elements of $A$ and $B$ matrices in K dimension have an associated scale factor (32 or 64 elements for sparse as sparse gemm compress 2x along k-dim). For example, an $M\times K$,
$A$ matrix has an associated $M \times \lceil K/32 \rceil$ SFA matrix; and an $N\times K$ $B$, matrix has an associated
$N \times \lceil K/32 \rceil$ SFB matrix. For block scaled GEMMs, an entry of output D matrix is
$D_{ij} = C_{ij} + \sum_{k} (A_{i,k} \times SFA_{i,k/SV}) \times (B_{j,k}\times SFB_{j,k/SV})$, in index notation, we SV is the scale factor vector size (16 or 32).
Further details can be found in
PTX documentation on block scaling.

Blackwell Narrow Precision Data Types

Narrow-precision tcgen05.mma instructions can operate on several 4, 6, and 8-bit data types. Blackwell MMAs can operate
on five different 8-bit floating point values, of which only two (float_ue8m0_t and float_ue4m3_t) can be used as scale factor data types.
There are two 6-bit floating point types and one 4-bit floating point data type.
See PTX documentation for narrow precision data types for details.

Blackwell Narrow Precision Data Types

Data Type Exponent Bits Mantissa Bits Signed Bit Size
float_e4m3_t 4 3 Yes 8
float_e5m2_t 5 2 Yes 8
float_e2m3_t 2 3 Yes 6
float_e3m2_t 3 2 Yes 6
float_e2m1_t 2 1 Yes 4
float_ue8m0_t[^1] 8 0 No 8
float_ue4m3_t[^1] 4 3 No 8

[^1]: Only valid as scale factor data types.

Block scaled MMAs use mx and nv types which are a pair of float8_t, float6_t, float4_t with 2 of the scale factor data types with a predetermined scale factor vector size. mx types follow OCP specification (see OCP Specification). The following types provided by CUTLASS can be used as inputs to collective builders to generate the block scaled kernels:

Blackwell Block Scaled Narrow Precision Data Types

Mx/Nv Data Type Scale Factor Type SF Vector Size (Dense) SF Vector Size (Sparse) OCP Compliant
mx_float8_t<Any F8type> float_ue8m0_t 32 64 Yes
mx_float6_t<Any F6Type> float_ue8m0_t 32 64 Yes
mx_float4_t float_ue8m0_t 32 64 Yes
nv_float4_t float_ue4m3_t 16 32 No

Layouts, Tensor Alignment Requirements to Target `tcgen05.mma` Instructions

Tables below list valid data type, and AB layout combinations. Note that the alignment is reported as number of elements. A and B matrix layouts are
represented with T and N. T represents row-major layouts, and N represents column-major layouts. For instance, TN is
row-major A matrix with column-major B matrix.

For legacy types (tf32, f16, bf16, i8 and u8) alignment requirements for A and B matrices are the same as in Hopper.
All four layouts (TT, NN, NT, TT) are supported for all legacy data types.

Table 1: Valid Data Type, Alignment, and Layout Combinations For MMAs with Legacy Types

Dense / Sparse A Type B Type AB Layout A Alignment B Alignment Target tcgen05.mma.kind Unit Test
1 Dense tfloat32_t tfloat32_t TN, NN, NT, TT 4 4 tf32
2 Dense half_t half_t TN, NN, NT, TT 8 8 f16 Unit tests
3 Dense bfloat16_t bfloat16_t TN, NN, NT, TT 8 8 f16 Similar to half_t unit tests
4 Dense int8_t int8_t TN, NN, NT, TT 16 16 i8 Unit tests
5 Dense uint8_t uint8_t TN, NN, NT, TT 16 16 i8 Similar to int8_t unit tests
6 Sparse tfloat32_t tfloat32_t TN, NN, NT, TT 4 (N) / 8 (T) 4 tf32 Unit tests
7 Sparse half_t half_t TN, NN, NT, TT 8 (N) / 16 (T) 8 f16 Unit tests
8 Sparse bfloat16_t bfloat16_t TN, NN, NT, TT 8 (N) / 16 (T) 8 f16 Similar to half_t unit tests
9 Sparse int8_t int8_t TN, NN, NT, TT 16 (N) / 32 (T) 16 i8 Unit tests
10 Sparse uint8_t uint8_t TN, NN, NT, TT 16 (N) / 32 (T) 16 i8 Similar to int8_t unit tests

For narrow precision Mmas, not all A/B type, and A/B layout combinations are supported by every tcgen05.mma instructions.
Furthermore, tensor copy instructions for subbyte types impose additional alignment requirements while loading narrow-precision
tensors from global memory to shared memory
(see PTX doc for details).

Below tables list valid layout, and alignment values for each A and B data type combination and their target tcgen05.mma
instructions supported by CUTLASS.

Table 2: Valid Data Type, Alignment, and Layout Combinations For Narrow Precision MMAs Without Block Scaling

Dense / Sparse A Type B Type AB Layout A Alignment B Alignment Target tcgen05.mma.kind Unit Test
1 Dense float4_t float4_t TN, NN, NT, TT 128 128 f8f6f4 TN unit tests NT unit tests NN unit tests TT unit tests
2 Dense float4_t float6_t TN, NN, NT, TT 128 128 f8f6f4 TN unit tests NT unit tests NN unit tests TT unit tests
3 Dense float6_t float4_t TN, NN, NT, TT 128 128 f8f6f4 TN unit tests NT unit tests NN unit tests TT unit tests
4 Dense float4_t float8_t TN, NN, NT, TT 128 16 f8f6f4 TN unit tests NT unit tests
5 Dense float8_t float4_t TN, NN, NT, TT 16 128 f8f6f4 TN unit tests NT unit tests
6 Dense float6_t float6_t TN, NN, NT, TT 128 128 f8f6f4 TN unit tests NT unit tests NN unit tests TT unit tests
7 Dense float6_t float8_t TN, NN, NT, TT 128 16 f8f6f4 TN unit tests NT unit tests
8 Dense float8_t float6_t TN, NN, NT, TT 16 128 f8f6f4 TN unit tests NT unit tests
9 Dense float8_t float8_t TN, NN, NT, TT 16 16 f8f6f4 Unit tests
10 Sparse float4_t float4_t TN, NN, NT, TT 128 (N) / 256 (T) 128 f8f6f4 TN unit tests
11 Sparse float4_t float6_t TN, NN, NT, TT 128 (N) / 256 (T) 128 f8f6f4 TN unit tests
12 Sparse float6_t float4_t TN, NN, NT, TT 128 (N) / 256 (T) 128 f8f6f4 TN unit tests
13 Sparse float4_t float8_t TN, NN, NT, TT 128 (N) / 256 (T) 16 f8f6f4 TN unit tests
14 Sparse float8_t float4_t TN, NN, NT, TT 16 (N) / 32 (T) 128 f8f6f4 TN unit tests
15 Sparse float6_t float6_t TN, NN, NT, TT 128 (N) / 256 (T) 128 f8f6f4 TN unit tests
16 Sparse float6_t float8_t TN, NN, NT, TT 128 (N) / 256 (T) 16 f8f6f4 TN unit tests
17 Sparse float8_t float6_t TN, NN, NT, TT 16 (N) / 32 (T) 128 f8f6f4 TN unit tests
18 Sparse float8_t float8_t TN, NN, NT, TT 16 (N) / 32 (T) 16 f8f6f4 TN unit tests

Table 3: Valid Data Type, Alignment, and Layout Combinations for Block Scaled Narrow Precision MMAs

Dense / Sparse A Type B Type AB Layout A Alignment B Alignment Target tcgen05.mma.kind Unit Test
1 Dense nv_float4_t nv_float4_t TN 32 32 mxf4nvf4 TN unit tests
2 Dense mx_float4_t mx_float4_t TN 32 32 mxf4, mxf4nvf4 TN unit tests
3 Dense mx_float4_t mx_float4_t TN, NN, NT, TT 128 128 mxf8f6f4 NT unit tests
4 Dense mx_float4_t mx_float6_t TN, NN, NT, TT 128 128 mxf8f6f4 TN unit testsNT unit tests
5 Dense mx_float6_t mx_float4_t TN, NN, NT, TT 128 128 mxf8f6f4 TN unit testsNT unit tests
6 Dense mx_float4_t mx_float8_t TN, NN, NT, TT 128 16 mxf8f6f4 TN unit testsNT unit tests
7 Dense mx_float8_t mx_float4_t TN, NN, NT, TT 16 128 mxf8f6f4 TN unit testsNT unit tests
8 Dense mx_float6_t mx_float6_t TN, NN, NT, TT 128 128 mxf8f6f4 TN unit testsNT unit tests
9 Dense mx_float6_t mx_float8_t TN, NN, NT, TT 128 16 mxf8f6f4 TN unit testsNT unit tests
10 Dense mx_float8_t mx_float6_t TN, NN, NT, TT 16 128 mxf8f6f4 TN unit testsNT unit tests
11 Dense mx_float8_t mx_float8_t TN, NN, NT, TT 16 16 mxf8f6f4 TN unit testsNT unit tests
12 Sparse nv_float4_t nv_float4_t TN 32 (N) / 64 (T) 32 mxf4nvf4 TN unit tests
13 Sparse mx_float4_t mx_float4_t TN 32 (N) / 64 (T) 32 mxf4, mxf4nvf4 TN unit tests
14 Sparse mx_float4_t mx_float4_t TN, NN, NT, TT 128 (N) / 256 (T) 128 mxf8f6f4 TN unit tests
15 Sparse mx_float4_t mx_float6_t TN, NN, NT, TT 128 (N) / 256 (T) 128 mxf8f6f4 TN unit tests
16 Sparse mx_float6_t mx_float4_t TN, NN, NT, TT 128 (N) / 256 (T) 128 mxf8f6f4 TN unit tests
17 Sparse mx_float4_t mx_float8_t TN, NN, NT, TT 128 (N) / 256 (T) 16 mxf8f6f4 TN unit tests
18 Sparse mx_float8_t mx_float4_t TN, NN, NT, TT 16 (N) / 32 (T) 128 mxf8f6f4 TN unit tests
19 Sparse mx_float6_t mx_float6_t TN, NN, NT, TT 128 (N) / 256 (T) 128 mxf8f6f4 TN unit tests
20 Sparse mx_float6_t mx_float8_t TN, NN, NT, TT 128 (N) / 256 (T) 16 mxf8f6f4 TN unit tests
21 Sparse mx_float8_t mx_float6_t TN, NN, NT, TT 16 (N) / 32 (T) 128 mxf8f6f4 TN unit tests
22 Sparse mx_float8_t mx_float8_t TN, NN, NT, TT 16 (N) / 32 (T) 16 mxf8f6f4 TN unit tests

MMA tile shapes supported

The alignment restrictions also limit the options for Mma Tile Shapes. Tables below list the supported/valid MmaTileShape,
Layout, and Dispatch Policy combinations for each row of Table 1, Table 2, and Table 3.

Table 4: Valid Tile Shapes and Dispatch Policies for legacy types (All rows of Table 1)

Dense / Sparse 1/2 SM Mma Tile Shape TN TT NT NN Dispatch Policy
Dense 1SM 64x64x(4*MMA-K) Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 64x128x(4*MMA-K) Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 64x192x(4*MMA-K) Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 64x256x(4*MMA-K) Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 128x64x(4*MMA-K) Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 128x128x(4*MMA-K) Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 128x192x(4*MMA-K) Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 128x256x(4*MMA-K) Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 2SM 128x64x(4*MMA-K) Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 128x128x(4*MMA-K) Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 128x192x(4*MMA-K) Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 128x256x(4*MMA-K) Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 256x64x(4*MMA-K) Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 256x128x(4*MMA-K) Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 256x192x(4*MMA-K) Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 256x256x(4*MMA-K) Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Sparse 1SM 128x64x(2/4*MMA-K) Y Y Y Y KernelSparseTmaWarpSpecialized1SmSm100
Sparse 1SM 128x128x(2/4*MMA-K) Y Y Y Y KernelSparseTmaWarpSpecialized1SmSm100
Sparse 1SM 128x192x(2/4*MMA-K) Y Y Y Y KernelSparseTmaWarpSpecialized1SmSm100
Sparse 1SM 128x256x(2/4*MMA-K) Y Y Y Y KernelSparseTmaWarpSpecialized1SmSm100
Sparse 2SM 256x64x(2/4*MMA-K) Y Y Y Y KernelSparseTmaWarpSpecialized2SmSm100
Sparse 2SM 256x128x(2/4*MMA-K) Y Y Y Y KernelSparseTmaWarpSpecialized2SmSm100
Sparse 2SM 256x192x(2/4*MMA-K) Y Y Y Y KernelSparseTmaWarpSpecialized2SmSm100
Sparse 2SM 256x256x(2/4*MMA-K) Y Y Y Y KernelSparseTmaWarpSpecialized2SmSm100

Table 5: Valid Tile Shapes and Dispatch Policies for {float4_t, float6_t} x {float4_t, float6_t} (Rows 1,2,3,6,10,11,12,and 15 of Table 2)

Dense / Sparse 1/2 SM Mma Tile Shape TN TT NT NN Dispatch Policy
Dense 1SM 64x64x128 Y N N N KernelTmaWarpSpecialized1SmSm100
Dense 1SM 64x128x128 Y Y N N KernelTmaWarpSpecialized1SmSm100
Dense 1SM 64x192x128 Y N N N KernelTmaWarpSpecialized1SmSm100
Dense 1SM 64x256x128 Y Y N N KernelTmaWarpSpecialized1SmSm100
Dense 1SM 128x64x128 Y N N Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 128x128x128 Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 128x192x128 Y N N Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 128x256x128 Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 2SM 128x64x128 Y N N N KernelTmaWarpSpecialized2SmSm100
Dense 2SM 128x128x128 Y N N N KernelTmaWarpSpecialized2SmSm100
Dense 2SM 128x192x128 Y N N N KernelTmaWarpSpecialized2SmSm100
Dense 2SM 128x256x128 Y Y N N KernelTmaWarpSpecialized2SmSm100
Dense 2SM 256x64x128 Y N N Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 256x128x128 Y N N Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 256x192x128 Y N N Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 256x256x128 Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Sparse 1SM 128x128x128 N N Y Y KernelSparseTmaWarpSpecialized1SmSm100
Sparse 1SM 128x128x256 Y Y Y Y KernelSparseTmaWarpSpecialized1SmSm100
Sparse 1SM 128x256x128 N N Y Y KernelSparseTmaWarpSpecialized1SmSm100
Sparse 1SM 128x256x256 Y Y Y Y KernelSparseTmaWarpSpecialized1SmSm100
Sparse 2SM 256x128x128 N N N Y KernelSparseTmaWarpSpecialized2SmSm100
Sparse 2SM 256x128x256 Y N N Y KernelSparseTmaWarpSpecialized2SmSm100
Sparse 2SM 256x256x128 N N Y Y KernelSparseTmaWarpSpecialized2SmSm100
Sparse 2SM 256x256x256 Y Y Y Y KernelSparseTmaWarpSpecialized2SmSm100

Table 6: Valid Tile Shapes and Dispatch Policies for float8_t x {float4_t, float6_t} (Rows 5,8,14,and 17 of Table 2)

Dense / Sparse 1/2 SM Mma Tile Shape TN TT NT NN Dispatch Policy
Dense 1SM 64x64x128 Y N N Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 64x128x128 Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 64x192x128 Y N N Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 64x256x128 Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 128x64x128 Y N N Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 128x128x128 Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 128x192x128 Y N N Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 128x256x128 Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 2SM 128x64x128 Y N N Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 128x128x128 Y N N Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 128x192x128 Y N N Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 128x256x128 Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 256x64x128 Y N N Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 256x128x128 Y N N Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 256x192x128 Y N N Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 256x256x128 Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Sparse 1SM 128x128x128 Y Y Y Y KernelSparseTmaWarpSpecialized1SmSm100
Sparse 1SM 128x128x256 Y Y Y Y KernelSparseTmaWarpSpecialized1SmSm100
Sparse 1SM 128x256x128 Y Y Y Y KernelSparseTmaWarpSpecialized1SmSm100
Sparse 1SM 128x256x256 Y Y Y Y KernelSparseTmaWarpSpecialized1SmSm100
Sparse 2SM 256x128x128 Y Y N Y KernelSparseTmaWarpSpecialized2SmSm100
Sparse 2SM 256x128x256 Y N N Y KernelSparseTmaWarpSpecialized2SmSm100
Sparse 2SM 256x256x128 Y Y Y Y KernelSparseTmaWarpSpecialized2SmSm100
Sparse 2SM 256x256x256 Y Y Y Y KernelSparseTmaWarpSpecialized2SmSm100

Table 7: Valid Tile Shapes and Dispatch Policies for {float4_t, float6_t} x float8_t (Rows 4,7,13,and 16 of Table 2)

Dense / Sparse 1/2 SM Mma Tile Shape TN TT NT NN Dispatch Policy
Dense 1SM 64x64x128 Y Y N N KernelTmaWarpSpecialized1SmSm100
Dense 1SM 64x128x128 Y Y N N KernelTmaWarpSpecialized1SmSm100
Dense 1SM 64x192x128 Y Y N N KernelTmaWarpSpecialized1SmSm100
Dense 1SM 64x256x128 Y Y N N KernelTmaWarpSpecialized1SmSm100
Dense 1SM 128x64x128 Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 128x128x128 Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 128x192x128 Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 128x256x128 Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 2SM 128x64x128 Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 128x128x128 Y Y N N KernelTmaWarpSpecialized2SmSm100
Dense 2SM 128x192x128 Y Y N N KernelTmaWarpSpecialized2SmSm100
Dense 2SM 128x256x128 Y Y N N KernelTmaWarpSpecialized2SmSm100
Dense 2SM 256x64x128 Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 256x128x128 Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 256x192x128 Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 256x256x128 Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Sparse 1SM 128x128x128 N N Y Y KernelSparseTmaWarpSpecialized1SmSm100
Sparse 1SM 128x128x256 Y Y Y Y KernelSparseTmaWarpSpecialized1SmSm100
Sparse 1SM 128x256x128 N N Y Y KernelSparseTmaWarpSpecialized1SmSm100
Sparse 1SM 128x256x256 Y Y Y Y KernelSparseTmaWarpSpecialized1SmSm100
Sparse 2SM 256x128x128 N N Y Y KernelSparseTmaWarpSpecialized2SmSm100
Sparse 2SM 256x128x256 Y Y Y Y KernelSparseTmaWarpSpecialized2SmSm100
Sparse 2SM 256x256x128 N N Y Y KernelSparseTmaWarpSpecialized2SmSm100
Sparse 2SM 256x256x256 Y Y Y Y KernelSparseTmaWarpSpecialized2SmSm100

Table 8: Valid Tile Shapes and Dispatch Policies for float8_t x float8_t (Row 9,18 of Table 2)

Dense / Sparse 1/2 SM Mma Tile Shape TN TT NT NN Dispatch Policy
Dense 1SM 64x64x128 Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 64x128x128 Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 64x192x128 Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 64x256x128 Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 128x64x128 Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 128x128x128 Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 128x192x128 Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 1SM 128x256x128 Y Y Y Y KernelTmaWarpSpecialized1SmSm100
Dense 2SM 128x64x128 Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 128x128x128 Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 128x192x128 Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 128x256x128 Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 256x64x128 Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 256x128x128 Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 256x192x128 Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Dense 2SM 256x256x128 Y Y Y Y KernelTmaWarpSpecialized2SmSm100
Sparse 1SM 128x64x128 Y Y Y Y KernelSparseTmaWarpSpecialized1SmSm100
Sparse 1SM 128x128x128 Y Y Y Y KernelSparseTmaWarpSpecialized1SmSm100
Sparse 1SM 128x192x128 Y Y Y Y KernelSparseTmaWarpSpecialized1SmSm100
Sparse 1SM 128x256x128 Y Y Y Y KernelSparseTmaWarpSpecialized1SmSm100
Sparse 2SM 256x64x128 Y Y Y Y KernelSparseTmaWarpSpecialized2SmSm100
Sparse 2SM 256x128x128 Y Y Y Y KernelSparseTmaWarpSpecialized2SmSm100
Sparse 2SM 256x192x128 Y Y Y Y KernelSparseTmaWarpSpecialized2SmSm100
Sparse 2SM 256x256x128 Y Y Y Y KernelSparseTmaWarpSpecialized2SmSm100

Table 9: Valid Tile Shapes for nv_float4_t x nv_float4_t (Row 1 and 12 of Table 3)

Dense / Sparse 1/2 SM Mma Tile Shape TN TT NT NN Dispatch Policy
Dense 1SM 128x128x256 Y N N N KernelTmaWarpSpecialized1SmNvf4Sm100
Dense 1SM 128x192x256 Y N N N KernelTmaWarpSpecialized1SmNvf4Sm100
Dense 1SM 128x256x256 Y N N N KernelTmaWarpSpecialized1SmNvf4Sm100
Dense 2SM 256x128x256 Y N N N KernelTmaWarpSpecialized2SmNvf4Sm100
Dense 2SM 256x192x256 Y N N N KernelTmaWarpSpecialized2SmNvf4Sm100
Dense 2SM 256x256x256 Y N N N KernelTmaWarpSpecialized2SmNvf4Sm100
Sparse 1SM 128x128x256 Y N N N KernelSparseTmaWarpSpecialized1SmNvf4Sm100
Sparse 1SM 128x256x256 Y N N N KernelSparseTmaWarpSpecialized1SmNvf4Sm100
Sparse 2SM 256x128x256 Y N N N KernelSparseTmaWarpSpecialized2SmNvf4Sm100
Sparse 2SM 256x256x256 Y N N N KernelSparseTmaWarpSpecialized2SmNvf4Sm100

Table 10: Valid Tile Shapes and Dispatch Policies for mx_float4_t x mx_float4_t (Row 2 and 13 of Table 3)

Dense / Sparse 1/2 SM Mma Tile Shape TN TT NT NN Dispatch Policy
Dense 1SM 128x128x256 Y N N N KernelTmaWarpSpecialized1SmMxf4Sm100
Dense 1SM 128x192x256 Y N N N KernelTmaWarpSpecialized1SmMxf4Sm100
Dense 1SM 128x256x256 Y N N N KernelTmaWarpSpecialized1SmMxf4Sm100
Dense 2SM 256x128x256 Y N N N KernelTmaWarpSpecialized2SmMxf4Sm100
Dense 2SM 256x192x256 Y N N N KernelTmaWarpSpecialized2SmMxf4Sm100
Dense 2SM 256x256x256 Y N N N KernelTmaWarpSpecialized2SmMxf4Sm100
Sparse 1SM 128x128x256 Y N N N KernelSparseTmaWarpSpecialized1SmNvf4Sm100
Sparse 1SM 128x256x256 Y N N N KernelSparseTmaWarpSpecialized1SmNvf4Sm100
Sparse 2SM 256x128x256 Y N N N KernelSparseTmaWarpSpecialized2SmNvf4Sm100
Sparse 2SM 256x256x256 Y N N N KernelSparseTmaWarpSpecialized2SmNvf4Sm100

Table 11: Valid Tile Shapes and Dispatch Policies for mx_float4_t x mx_float4_t (Row 3 and 14 of Table 3)

Dense / Sparse 1/2 SM Mma Tile Shape TN TT NT NN Dispatch Policy
Dense 1SM 128x128x128 Y Y Y Y KernelTmaWarpSpecialized1SmMxf8f6f4Sm100
Dense 1SM 128x192x128 Y N N Y KernelTmaWarpSpecialized1SmMxf8f6f4Sm100
Dense 1SM 128x256x128 Y Y Y Y KernelTmaWarpSpecialized1SmMxf8f6f4Sm100
Dense 2SM 256x128x128 Y N N Y KernelTmaWarpSpecialized2SmMxf8f6f4Sm100
Dense 2SM 256x192x128 Y N N Y KernelTmaWarpSpecialized2SmMxf8f6f4Sm100
Dense 2SM 256x256x128 Y Y Y Y KernelTmaWarpSpecialized2SmMxf8f6f4Sm100
Sparse 1SM 128x128x256 Y Y Y Y KernelSparseTmaWarpSpecialized1SmMxf8f6f4Sm100
Sparse 1SM 128x192x256 Y Y Y Y KernelSparseTmaWarpSpecialized1SmMxf8f6f4Sm100
Sparse 1SM 128x256x256 Y Y Y Y KernelSparseTmaWarpSpecialized1SmMxf8f6f4Sm100
Sparse 2SM 256x128x256 Y N N Y KernelSparseTmaWarpSpecialized2SmMxf8f6f4Sm100
Sparse 2SM 256x192x256 Y N N Y KernelSparseTmaWarpSpecialized2SmMxf8f6f4Sm100
Sparse 2SM 256x256x256 Y Y Y Y KernelSparseTmaWarpSpecialized2SmMxf8f6f4Sm100

Table 12: Valid Tile Shapes and Dispatch Policies for {mx_float4_t, mx_float6_t, mx_float8_t} x {mx_float4_t, mx_float6_t} (Rows 4, 5, 7, 8, 10, 15, 16, 18, 19, and 21 of Table 3)

Dense / Sparse 1/2 SM Mma Tile Shape TN TT NT NN Dispatch Policy
Dense 1SM 128x128x128 Y Y Y Y KernelTmaWarpSpecialized1SmMxf8f6f4Sm100
Dense 1SM 128x192x128 Y N N Y KernelTmaWarpSpecialized1SmMxf8f6f4Sm100
Dense 1SM 128x256x128 Y Y Y Y KernelTmaWarpSpecialized1SmMxf8f6f4Sm100
Dense 2SM 256x128x128 Y N N Y KernelTmaWarpSpecialized2SmMxf8f6f4Sm100
Dense 2SM 256x192x128 Y N N Y KernelTmaWarpSpecialized2SmMxf8f6f4Sm100
Dense 2SM 256x256x128 Y Y Y Y KernelTmaWarpSpecialized2SmMxf8f6f4Sm100
Sparse 1SM 128x128x256 Y Y Y Y KernelSparseTmaWarpSpecialized1SmMxf8f6f4Sm100
Sparse 1SM 128x192x256 Y Y Y Y KernelSparseTmaWarpSpecialized1SmMxf8f6f4Sm100
Sparse 1SM 128x256x256 Y Y Y Y KernelSparseTmaWarpSpecialized1SmMxf8f6f4Sm100
Sparse 2SM 256x128x256 Y N N Y KernelSparseTmaWarpSpecialized2SmMxf8f6f4Sm100
Sparse 2SM 256x192x256 Y N N Y KernelSparseTmaWarpSpecialized2SmMxf8f6f4Sm100
Sparse 2SM 256x256x256 Y Y Y Y KernelSparseTmaWarpSpecialized2SmMxf8f6f4Sm100

Table 13: Valid Tile Shapes and Dispatch Policies for {mx_float4_t, mx_float6_t, mx_float8_t} x mx_float8_t (Rows 6, 9, 11, 17, 20, and 22 of Table 3)

Dense / Sparse 1/2 SM Mma Tile Shape TN TT NT NN Dispatch Policy
Dense 1SM 128x128x128 Y Y Y Y KernelTmaWarpSpecialized1SmMxf8f6f4Sm100
Dense 1SM 128x192x128 Y Y Y Y KernelTmaWarpSpecialized1SmMxf8f6f4Sm100
Dense 1SM 128x256x128 Y Y Y Y KernelTmaWarpSpecialized1SmMxf8f6f4Sm100
Dense 2SM 256x128x128 Y Y Y Y KernelTmaWarpSpecialized2SmMxf8f6f4Sm100
Dense 2SM 256x192x128 Y Y Y Y KernelTmaWarpSpecialized2SmMxf8f6f4Sm100
Dense 2SM 256x256x128 Y Y Y Y KernelTmaWarpSpecialized2SmMxf8f6f4Sm100
Sparse 1SM 128x128x256 Y Y Y Y KernelSparseTmaWarpSpecialized1SmMxf8f6f4Sm100
Sparse 1SM 128x192x256 Y Y Y Y KernelSparseTmaWarpSpecialized1SmMxf8f6f4Sm100
Sparse 1SM 128x256x256 Y Y Y Y KernelSparseTmaWarpSpecialized1SmMxf8f6f4Sm100
Sparse 2SM 256x128x256 Y Y Y Y KernelSparseTmaWarpSpecialized2SmMxf8f6f4Sm100
Sparse 2SM 256x192x256 Y Y Y Y KernelSparseTmaWarpSpecialized2SmMxf8f6f4Sm100
Sparse 2SM 256x256x256 Y Y Y Y KernelSparseTmaWarpSpecialized2SmMxf8f6f4Sm100

Epilogue config supported

Table 14: Epilogue Dispatch Policy

Dense / Sparse Legacy / Narrow Precision 1/2 SM Epilogue Dispatch Policy
Dense Legacy & Narrow Precision 1SM cutlass::epilogue::TmaWarpSpecialized1Sm
Dense Legacy & Narrow Precision 1SM cutlass::epilogue::NoSmemWarpSpecialized1Sm
Dense Legacy & Narrow Precision 2SM cutlass::epilogue::TmaWarpSpecialized2Sm
Dense Legacy & Narrow Precision 2SM cutlass::epilogue::NoSmemWarpSpecialized2Sm
Sparse Legacy 1SM cutlass::epilogue::TmaWarpSpecialized1Sm
Sparse Legacy 1SM cutlass::epilogue::NoSmemWarpSpecialized1Sm
Sparse Legacy 2SM cutlass::epilogue::TmaWarpSpecialized2Sm
Sparse Legacy 2SM cutlass::epilogue::NoSmemWarpSpecialized2Sm
Sparse Narrow Precision (nvf4) 1SM cutlass::epilogue::TmaWarpSpecialized1SmNvf4
Sparse Narrow Precision (nvf4) 2SM cutlass::epilogue::TmaWarpSpecialized2SmNvf4
Sparse Narrow Precision (mxf4) 1SM cutlass::epilogue::TmaWarpSpecialized1SmMxf4
Sparse Narrow Precision (mxf4) 2SM cutlass::epilogue::TmaWarpSpecialized2SmMxf4
Sparse Narrow Precision (mxf8f6f4) 1SM cutlass::epilogue::TmaWarpSpecialized1SmMxf8f6f4
Sparse Narrow Precision (mxf8f6f4) 2SM cutlass::epilogue::TmaWarpSpecialized2SmMxf8f6f4

Table 15: Epilogue PerSmTileShape_MNK

1/2 SM MMA tile Shape PerSmTileShape_MNK
1SM 64x64xMMA_TileShape_K 64x64xMMA_TileShape_K
1SM 64x128xMMA_TileShape_K 64x128xMMA_TileShape_K
1SM 64x192xMMA_TileShape_K 64x192xMMA_TileShape_K
1SM 64x256xMMA_TileShape_K 64x256xMMA_TileShape_K
1SM 128x64xMMA_TileShape_K 128x64xMMA_TileShape_K
1SM 128x128xMMA_TileShape_K 128x128xMMA_TileShape_K
1SM 128x192xMMA_TileShape_K 128x192xMMA_TileShape_K
1SM 128x256xMMA_TileShape_K 128x256xMMA_TileShape_K
2SM 128x64xMMA_TileShape_K 64x64xMMA_TileShape_K
2SM 128x128xMMA_TileShape_K 64x128xMMA_TileShape_K
2SM 128x192xMMA_TileShape_K 64x192xMMA_TileShape_K
2SM 128x256xMMA_TileShape_K 64x256xMMA_TileShape_K
2SM 256x64xMMA_TileShape_K 128x64xMMA_TileShape_K
2SM 256x128xMMA_TileShape_K 128x128xMMA_TileShape_K
2SM 256x192xMMA_TileShape_K 128x192xMMA_TileShape_K
2SM 256x256xMMA_TileShape_K 128x256xMMA_TileShape_K

MMA_TileShape_K is is generally 4 * MMA-Instruction-K. It depends on the config we defined in MMA tile shapes supported section.

Auto Kernel Dispatch Policies

In addition to direct dispatch policies listed above, the user can also use auto policies for both non-block scaled narrow-precision
GEMMs (both sparse and dense), and block scaled narrow-precision GEMMs (only dense).

CUTLASS will do its best to find the most efficient kernel for given parameters, however, the preferred method for building
these kernels is to use direct kernel dispatch policies shown in the above tables.

  • cutlass::gemm::collective::KernelScheduleAuto: For a given Mma Tile Size, data type and layout combinations choose instr kind (mxf8f6f4, mxf4, nvf4mxf4) and 1/2 SM tcgen05.mma(.sp).
  • KernelTmaWarpSpecialized1SmBlockScaledSm100: Use 1 SM tcgen05.mma instruction and choose instr kind (mxf8f6f4, mxf4, nvf4mxf4) automatically.
  • KernelTmaWarpSpecialized2SmBlockScaledSm100: Use 2 SM tcgen05.mma instruction and choose instr kind (mxf8f6f4, mxf4, nvf4mxf4) automatically.
  • KernelSparseTmaWarpSpecialized1SmBlockScaledSm100: Use 1 SM tcgen05.mma.sp instruction and choose instr kind (mxf8f6f4, mxf4, nvf4mxf4) automatically.
  • KernelSparseTmaWarpSpecialized2SmBlockScaledSm100: Use 2 SM tcgen05.mma.sp instruction and choose instr kind (mxf8f6f4, mxf4, nvf4mxf4) automatically.

Similarly for epilogues, we can use cutlass::epilogue::collective::EpilogueScheduleAuto.

Building a Block Scaled Kernel

For non-blockscaled dense GEMM refer to quick start page. An example dense GEMM can be found:

  1. Blackwell FP16 GEMM example.

An example sparse GEMM can be found:

  1. Blackwell FP16 Sparse GEMM example.

Narrow precision and block scaled narrow precision kernels can be built using CUTLASS 3.x collective builder interface
(as described in CUTLASS 3.0 GEMM API). However, special attention needs to be given to
A and B matrix layouts, alignment requirements, and dispatch policies to obtain a functionally correct and performant kernel
which are listed above.

Several examples of block scaled dense GEMM kernels can be found in examples/72_blackwell_narrow_precision_gemm directory:

  1. NVF4 Gemm with block scaling
  2. NVF4 Gemm with block scaling and NVF4 output matrix
  3. Mixed precision Mxf8 x Mxf8 GEMM with block scaling

Several examples of block scaled sparse GEMM kernels can be found in examples/84_blackwell_narrow_precision_sparse_gemm directory:

  1. NVF4 Gemm with block scaling
  2. Mixed precision Mxf4 x Mxf8 GEMM with block scaling

Collective builder interface expects the same arguments as any other CUTLASS 3.x kernels as described
here with a small difference for Collective MMA builder interface.
As in all Blackwell kernels, the TileShape_MNK argument expects the MmaTileShape_MNK which is the tile shape needed
by 1 or 2 SM tcgen05.mma instructions.

Let's consider building a block scaled GEMM where the A matrix is of type mx_float4_t and column-major (N), and the
B matrix is of type mx_float4_t and row-major (T). We first need to describe the A and B tensors, and find the
instruction that can support the selected A and B type and layout pair. Then, we will choose the performance parameters.

The skeleton C++ code is shown below:

text
/* Detailed source-code truncated for AI context efficiency. */

From the valid type and layout combinations Table 3, we see that only row 3 can support mx_float4_txmx_float4_t
combination with NT layout. As a result, we need to use the tcgen05.mma.kind:mxf8f6f4 instruction. Additionally, in order
to use tcgen05.mma.kind:mxf8f6f4, we see that A and B tensors both should be 128-element aligned.
Thus, we can describe A and B tensors as follows:

cpp
///////////////////////////////////////////////////////////
  //                Mainloop Builder Setup
  ///////////////////////////////////////////////////////////
  
  ///////////////////////////////////////////
  // 1. Describe A and B tensors
  ///////////////////////////////////////////
  using ElementA       = mx_float4_t;
  constexpr int AlignA = 128;
  using GmemLayoutA    = cutlass::layout::ColumnMajor;
  using ElementB       = mx_float4_t;
  constexpr int AlignB = 128;
  using GmemLayoutB    = cutlass::layout::RowMajor;

Next, we need to choose the performance parameters such as MmaTileShape_MNK, KernelMainloopPolicy,
and ClusterShape_MNK.

MmaTileShape_MNK supported for mx_float4_txmx_float4_t with mxf8f6f4 are listed in Table 11.
For NT layout, we see that 3 MmaTileShape_MNK are supported: 128x128x128, and 128x256x128 with 1SM instruction;
and 256x256x128 with 2SM instruction. Let's say, we expect to get the best performance with 256x256x128 MMA tile shape
for our GEMM problem. Then, we need to set the KernelMainloopPolicy to KernelTmaWarpSpecialized2SmMxf8f6f4Sm100.
Now, we need to choose the ClusterShape_MNK. Since we have selected a 2SM mma instruction, ClusterShape_MNK should be
compatible and its first mode should be a multiple of 2. ClusterShape_MNK = cute::Shape<_2, [_1|_2|_4], _1> or
ClusterShape_MNK = cute::Shape<_4, [_1|_2|_4], _1> would be valid options. Let's choose cute::Shape<_4,_4,_1>.
Our performance parameters looks like below:

cpp
//////////////////////////////////////////
  // 2. Choose Performance Parameters
  //////////////////////////////////////////

  // Tile and cluster shapes
  // Collective MMA takes tile shape of the MMA operation as input
  using KernelMainloopPolicy     = cutlass::gemm::KernelTmaWarpSpecialized2SmMxf8f6f4Sm100;
  using MmaTileShape_MNK         = cute::Shape<_256,_256,_128>;
  using ClusterShape_MNK         = cute::Shape<_4,_4,_1>;

After we config the main-loop, let's setup the epilogue.
A normal epilogue looks like below, we need to specify the output layout, datatype, alignment and PerSmTileShape_MNK, and let others to be default/auto.

PerSmTileShape_MNK should be deduced from the mainloop setup. For example, in above mainloop setup, the MmaTileShape_MNK is
256x256x128 and the KernelMainloopPolicy is 2sm policy.
It means each CTA is doing (256 / 2sm) x 256 x 128 output, so the PerSmTileShape_MNK is 128x256x128. The possible PerSmTileShape_MNK
is listed in Table 15

The epilogue scheduling policy is configurable, and it is common to set cutlass::epilogue::collective::EpilogueScheduleAuto
to allow the epilogue builder to automatically select the appropriate policy. However, it can also be explicitly defined to
use other policies based on the 1sm or 2sm MMA instruction. The available policies are listed in Table 14.

cpp
// Describe C and D tensors
  using ElementC = cutlass::half_t;
  constexpr int AlignC = 8;
  using GmemLayoutC = cutlass::layout::RowMajor;
  using ElementD = cutlass::float_e2m1_t;
  constexpr int AlignD = 32;
  using GmemLayoutD = cutlass::layout::RowMajor;
  // Mma's accumulator type
  using ElementAccumulator = float;
  // Epilogue computation's precision type
  using ElementCompute = float;
  
  //
  // Construct CollectiveEpilogue
  //

  using CollectiveEpilogue = typename cutlass::epilogue::collective::CollectiveBuilder<
      cutlass::arch::Sm100, cutlass::arch::OpClassBlockScaledTensorOp,      // Arch and Tensorop spec
      MmaTileShape_MNK, ClusterShape_MNK,                                   // MMA tile shape, and cluster shape
      cutlass::epilogue::collective::EpilogueTileAuto,                      // Epilogue subtile shape. Auto will find a suitable tile shape
      ElementAccumulator, ElementCompute,                                   // Mma instr's accumulator type and compute precision for epilogue
      ElementC, GmemLayoutC, AlignC,                                        // C tensor description
      ElementD, GmemLayoutD, AlignD,                                        // D tensor description
      cutlass::epilogue::TmaWarpSpecialized2Sm                              // Epilogue schedule policy
    >::CollectiveOp;

If we want to let the epilogue generate mxf4/nvf4/mxf6/mxf8 (i.e. elements + block-scalefactor), we need to setup the epilogue fusion into the builder.
First, we need to choose a SFDVectorSize indicates how many elements sharing the same block-scalefactor.
Then, we need to choose ElementSFD and GmemLayoutSFD which indicates the output datatype and which output-dim is used to generate the block-scalefactor.
Typically, GmemLayoutSFD would be same as the GmemLayoutD.

cpp
//
  // Construct FusionOperation
  //
  constexpr int SFDVectorSize = 16;
  // Define the fusion operation applied during epilogue
  using FusionOperation = cutlass::epilogue::fusion::LinCombBlockScaleFactor<
      SFDVectorSize,
      ElementD, ElementCompute, 
      ElementSFD, GmemLayoutSFD,
      ElementC
    >;

  using CollectiveEpilogue = typename cutlass::epilogue::collective::CollectiveBuilder<
      cutlass::arch::Sm100, cutlass::arch::OpClassBlockScaledTensorOp,      // Arch and Tensorop spec
      MmaTileShape_MNK, ClusterShape_MNK,                                   // MMA tile shape, and cluster shape
      cutlass::epilogue::collective::EpilogueTileAuto,                      // Epilogue subtile shape. Auto will find a suitable tile shape
      ElementAccumulator, ElementCompute,                                   // Mma instr's accumulator type and compute precision for epilogue
      ElementC, GmemLayoutC, AlignC,                                        // C tensor description
      ElementD, GmemLayoutD, AlignD,                                        // D tensor description
      cutlass::epilogue::TmaWarpSpecialized2Sm                              // Epilogue schedule policy
      FusionOperation                                                       // <================================== Pass the fusion config into epilogue builder.
    >::CollectiveOp;

Above example made a gentle introduction to using the fusion operations in the epilogue. For more detailed example, see
Blackwell GEMM with collective builder

Note that we have first discussed the CollectiveMainloop, then the CollectiveEpilogue for clarity.
However, the CollectiveMainloop needs to know the SMEM utilization of the epilogue. Therefore, it needs to be setup before the CollectiveMainloop. See examples/72_blackwell_narrow_precision_gemm directory for full kernel and run setup.

Scale Factor Layouts

The scale factor layout consists of a 512B basic-block structure, as illustrated in the diagram below. Each block contains 128 M/N dimension and 4 scale factors (SF) along the K dimension.
The byte order of the basic storage chunk is row-major, meaning that M0SF0 to M0SF3, M32SF0 to M32SF3, M64SF0 to M64SF3, and M96SF0 to M96SF3 are stored consecutively in GMEM.

If the scale factor tensor exceeds M128xSF4, it indicates that there are multiple basic blocks along both the M and SFK dimensions. The arrangement of these basic blocks follows a K-major order. Here is a diagram illustrating the scenario where M equals 512 and the SFK is 16.

The creation of scale factor tensors' layouts are tedious. CUTLASS provides Sm1xxBlockScaledConfig to create these layouts easily
(See sm100_blockscaled_layout.hpp).
The interface to create SFA and SFB tensor layouts is as follows:

cpp
auto problem_shape = make_shape(M, N, K, L);
using SfConfig = Sm1xxBlockScaledConfig<SFVecSize>;

// SFA shape: ((32,4), ceil(M/128)), ((SFVecSize,4), ceil(K/4), L)
auto layout_sfa = SfConfig::tile_atom_to_shape_SFA(problem_shape);
// SFB shape: ((32,4), ceil(N/128)), ((SFVecSize,4), ceil(K/4), L)
auto layout_sfb = SfConfig::tile_atom_to_shape_SFB(problem_shape);

auto tensor_sfa = make_tensor(aptr, layout_sfa);
auto tensor_sfb = make_tensor(bptr, layout_sfb);
// Access SF for for element m,k of A tensor
auto val_a_mk = tensor_sfa(make_coord(m,k,0));

Blackwell SM120 GEMMs

The NVIDIA RTX 5000 Series GPUs introduce support for new narrow precision (4bit and 6bit) block-scaled and non-block-scaled tensor cores. The PTX ISA has extended the mma instructions to support these data formats which are 1x to 4x faster than Ada architecture's fp8 tensor cores. For more detailed information see mma PTX documentation.

CUTLASS 4.0 has added support for these newly introduced narrow precision GEMMs. Similar to the Blackwell SM100 GEMMs, the SM120 GEMMs can be built using the collective builder interface. See examples in examples/79_blackwell_geforce_gemm/ and unit tests listed below.

The data types supported and tensor alignment requirements are the same as the Blackwell SM100 GEMMs. The scale factor layout is also the same as SM100 mentioned above. OpClassTensorOp is used for non-blockscaled narrow precision GEMMs and OpClassBlockScaledTensorOp is used for blockscaled narrow precision GEMMs.

Ptx Instruction Throughput Notes Unit Test
mma.sync.aligned.kind::f8f6f4 1x Ada Fp8 Tensor Core(2x for FP32 accumulator) Mixed precision MMA with A={f4,f6,f8} x B={f4,f6,f8} TN layouts unit test
mma.sync.aligned.kind::mxf8f6f4.block_scale 1x Ada Fp8 Tensor Core(2x for FP32 accumulator) Block scaled mixed precision MMA with A={mxf4,mxf6,mxf8} x B={mxf4,mxf6,mxf8} with TN layouts unit test
mma.sync.aligned.kind::mxf4.block_scale 2x Ada Fp8 Tensor Core(4x for FP32 accumulator) Block scaled MMA with A={mxf4} x B={mxf4} with TN layouts unit test
mma.sync.aligned.kind::mxf4nvf4.block_scale.scale_vec::[2X|4X] 2x Ada Fp8 Tensor Core(4x for FP32 accumulator) Block scaled MMA with A={mxf4} x B={mxf4} or A={nvf4} x B={nvf4} with TN layouts unit test

Besides the similarities, there are some key differences from the Blackwell SM100 GEMMs:

Cluster Size

On Geforce series graphics card, there is no multicast feature therefore the cluster shape is fixed to 1x1x1.

Tensor Layout

Only TN layout is supported. Matrix A is row major and matrix B is column major.

Pingpong v.s. cooperative kernel schedule

Similar to Hopper's warp-group GEMM, SM120 GEMMs support both pingpong and cooperative kernel schedules. Pingpong kernel schedule has two groups of 4 MMA warps working on different output tiles, overlapping the mainloop and epilogue, while the cooperative kernel schedule has only one group of 8 MMA warps working on the same output tile. If KernelScheduleAuto is specified, KernelTmaWarpSpecializedCooperative will be selected by default.

Epilogue schedule:

EpilogueScheduleAuto must be used.

Tile size:

Below are tables that summarize the valid tile shapes and dispatch policies for SM120 GEMMs. If the output is float_6_t, the tile size in the leading dimension of output tensor must be 128.

Table 16: Valid Tile Shapes and Dispatch Policies for {float8_t, float_6_t, float_4_t} x {float8_t, float_6_t, float_4_t} of SM120 GEMMs

Mma Tile Shape TN TT NT NN Dispatch Policy
64x64x128 Y N N N KernelTmaWarpSpecializedPingpong or KernelTmaWarpSpecializedCooperative
64x128x128 Y N N N KernelTmaWarpSpecializedPingpong or KernelTmaWarpSpecializedCooperative
128x64x128 Y N N N KernelTmaWarpSpecializedPingpong or KernelTmaWarpSpecializedCooperative
128x128x128 Y N N N KernelTmaWarpSpecializedPingpong or KernelTmaWarpSpecializedCooperative

Table 17: Valid Tile Shapes for nv_float4_t x nv_float4_t of SM120 GEMMs

Mma Tile Shape TN TT NT NN Dispatch Policy
128x128x128 Y N N N KernelTmaWarpSpecializedPingpong or KernelTmaWarpSpecializedCooperative
256x128x128 Y N N N KernelTmaWarpSpecializedCooperative
128x128x256 Y N N N KernelTmaWarpSpecializedPingpong or KernelTmaWarpSpecializedCooperative

Table 18: Valid Tile Shapes and Dispatch Policies for mx_float4_t x mx_float4_t of SM120 GEMMs

Mma Tile Shape TN TT NT NN Dispatch Policy
128x128x128 Y N N N KernelTmaWarpSpecializedPingpong or KernelTmaWarpSpecializedCooperative
256x128x128 Y N N N KernelTmaWarpSpecializedCooperative
128x128x256 Y N N N KernelTmaWarpSpecializedPingpong or KernelTmaWarpSpecializedCooperative

Table 19: Valid Tile Shapes and Dispatch Policies for mx_float4_t x mx_float4_t of SM120 GEMMs

Mma Tile Shape TN TT NT NN Dispatch Policy
128x128x128 Y N N N KernelTmaWarpSpecializedMxf8f6f4Sm120 or KernelTmaWarpSpecializedPingpongMxf8f6f4Sm120
256x128x128 Y N N N KernelTmaWarpSpecializedMxf8f6f4Sm120
128x128x256 Y N N N KernelTmaWarpSpecializedMxf8f6f4Sm120 or KernelTmaWarpSpecializedPingpongMxf8f6f4Sm120

Specialized policies must be used to generate mixed-input-datatype mx_float4_t kernels.

Table 20: Valid Tile Shapes and Dispatch Policies for {mx_float4_t, mx_float6_t, mx_float8_t} x {mx_float4_t, mx_float6_t, mx_float8_t}

Mma Tile Shape TN TT NT NN Dispatch Policy
128x128x128 Y N N N KernelTmaWarpSpecializedPingpong or KernelTmaWarpSpecializedCooperative

Copyright (c) 2025 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Code Organization (`media/docs/cpp/code_organization.md`)

Code Organization

This document describes the layout of the CUTLASS repository. The main components are:

  • CUTLASS Template Library - CUDA Templates for Linear Algebra Subroutines and Solvers (header only)
  • CuTe Template Library - CUTLASS's core vocabulary layout type and associated algebra (header only)
  • CUTLASS Utilities - Additional templates
  • CUTLASS Instance Library - instantiations of CUTLASS templates covering the design space
  • CUTLASS Profiler - CUTLASS Library, Profiler, and Utilities
  • Examples - SDK examples of CUTLASS Template Library and components
  • Media - supporting documentation and media content
  • Tests - test components for CUTLASS Template Library and tools

CUTLASS Template Library

CUDA Templates for Linear Algebra Subroutines and Solvers is a library of CUDA C++ template classes for
performing efficient matrix computations on NVIDIA GPUs.

Like NVIDIA CUB, the components of CUTLASS are organized hierarchically based on the scope of cooperative
elements. For example, warp-level GEMM components perform a matrix multiply collectively by the
set of threads within a warp. The following figure illustrates each layer.

Components are designed to be usable by client applications accessing functionailty at each scope.

CUTLASS Templates are implemented by header files in the following directory structure:

text
/* Detailed source-code truncated for AI context efficiency. */

See Programming Guidelines for further details about
conventions and design patterns used throughout CUTLASS.

CuTe

CuTe is a collection of C++ CUDA template abstractions for defining and operating on hierarchically multidimensional layouts of threads and data. CuTe provides Layout and Tensor objects that compactly packages the type, shape, memory space, and layout of data, while performing the complicated indexing for the user. This lets programmers focus on the logical descriptions of their algorithms while CuTe does the mechanical bookkeeping for them. With these tools, we can quickly design, implement, and modify all dense linear algebra operations. More documentation
for CuTe can be found in cute/.

Tools

The tools/ directory contains clients of the CUTLASS Template library and includes the following.

CUTLASS Instance Library

The CUTLASS Instance Library contains instantiations of the above CUTLASS templates covering supported configurations,
data types, block structure, and tile sizes. These instantiations are procedurally generated using a set of
scripts to span the design space.

text
tools/
  library/                   # static/dynamic library containing all kernel instantiations of interest
                             # (with some build-level filter switches to compile specific subsets)

    include/
      cutlass/
        library/             # header files for CUTLASS Deliverables Library (in cutlass::library:: namespace)

          handle.h           # implements a host-side API for launching kernels, similar to cuBLAS
          library.h          # defines enums and structs to describe the tiled structure of operator instances          
          manifest.h         # collection of all instances

    src/

python/
    cutlass_library/       # scripts to procedurally generate CUTLASS template instances

      gemm_operations.py
      library.py
      generator.py            # entry point of procedural generation scripts - invoked by cmake
      manifest.py

When CMake is executed, the CUTLASS Instance Library generator scripts are executed to construct a set of
instantiations in build/tools/library/generated/.

CUTLASS Profiler

The CUTLASS Profiler is designed to load the CUTLASS Instance Library and execute all operations contained therein.
This command-line driven application constructs an execution environment for evaluating functionality and performance.
It is implemented in

text
tools/
  profiler/

and may be built as follows.

text
$ make cutlass_profiler -j

Further details about the CUTLASS Profiler are described here.

CUTLASS Utilities

tools/util/ defines a companion library of headers and sources that support the CUTLASS test programs, examples, and other client applications. Its structure is as follows:

text
tools/
  util/
    include/
      cutlass/
        util/                   # CUTLASS Utility companion library

          reference/            #  functional reference implementation of CUTLASS operators
                                #    (minimal consideration for performance)
            
            detail/
              *

            device/             #  device-side reference implementations of CUTLASS operators
              thread/
              kernel/
                *
            host/               #  host-side reference implementations of CUTLASS operators
              *
          *

More details about CUTLASS Utilities may be found here.

Examples

To demonstrate CUTLASS components, several SDK examples are implemented in examples/.

CUTLASS SDK examples apply CUTLASS templates to implement basic computations.

text
examples/
  00_basic_gemm/             # launches a basic GEMM with single precision inputs and outputs

  01_cutlass_utilities/      # demonstrates CUTLASS Utilities for allocating and initializing tensors
  
  02_dump_reg_smem/          # debugging utilities for printing register and shared memory contents
  
  03_visualize_layout/       # utility for visualizing all layout functions in CUTLASS

  04_tile_iterator/          # example demonstrating an iterator over tiles in memory

  05_batched_gemm/           # example demonstrating CUTLASS's batched strided GEMM operation

  06_splitK_gemm/            # exmaple demonstrating CUTLASS's Split-K parallel reduction kernel

  07_volta_tensorop_gemm/    # example demonstrating mixed precision GEMM using Volta Tensor Cores

  08_turing_tensorop_gemm/   # example demonstrating integer GEMM using Turing Tensor Cores

  10_planar_complex/         # example demonstrating planar complex GEMM kernels

  11_planar_complex_array/   # example demonstrating planar complex kernels with batch-specific problem sizes

  12_gemm_bias_relu/         # example demonstrating GEMM fused with bias and relu activation function

  13_fused_two_gemms/        # example demonstrating two GEMMs fused into one kernel

Media

This directory contains documentation, images, and performance result data which accompanies the CUTLASS library and components.

Tests

Test programs for CUTLASS. Tests are organized hierarchically, mirroring the organization of source files.

text
test/                        # unit tests for CUTLASS Template Library
  unit/
    arch/
    core/
    gemm/
      device/
      kernel/
      thread/
      threadblock/
      warp/
    reduction/
      kernel/
      thread/
    transform/
      threadblock/
      *

Tests can be built and run at the top level scope by invoking make test_unit or by building
and explicitly executing each individual target, e.g. cutlass_test_unit_gemm_device.

Tests are configured to specify appropriate GTest filter strings to avoid running except on
architectures where they are expected to pass. Thus, no tests should fail. The actual number
of tests run may vary over time as more are added.

Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Cutlass 2x (`media/docs/cpp/cutlass_2x.rst`)

.. _cutlass_2_x:

CUTLASS 2.x

.. toctree::
:maxdepth: 2

Layouts and Tensors<layout.md>
GEMM API<gemm_api.md>
Tile Iterator Concepts<tile_iterator_concept.md>
Utilities


Media Docs Cpp Cutlass 3x (`media/docs/cpp/cutlass_3x.rst`)

.. _cutlass_3_x:

CUTLASS 3.x

.. toctree::
:maxdepth: 2

Design <cutlass_3x_design.md>
GEMM Backwards Compatibility <cutlass_3x_backwards_compatibility.md>
GEMM API <gemm_api_3x.md>


Media Docs Cpp Cutlass 3x Backwards Compatibility (`media/docs/cpp/cutlass_3x_backwards_compatibility.md`)

CUTLASS 3.0 GEMM Backwards Compatibility

Although CUTLASS 3.0 restructures the GEMM hierarchy and introduces new types for the
threadblock layer and below, we intend the entire source code to be usable in user applications.
We expect users to be able to #include any source file from CUTLASS 3.0, whether
they implement the 2.x or the 3.x API, without breaking user builds. This means that a single
translation unit should be able to contain any valid kernel regardless of its API version. The
sections below discuss how device and kernel layer type names are made compatible across the
two API versions, and what the users can expect out of the threadblock layer API going forward.

Compatible Device API

The entry point for CUTLASS's Device GEMM API
is the class
cutlass::gemm::device::GemmUniversalAdapter.
This class lives in the header file
include/cutlass/gemm/device/gemm_universal_adapter.h.

GemmUniversalAdapter is a "universal adapter"
and serves as a common device interface
for both CUTLASS 3.x and CUTLASS 2.x kernels.
Its template parameter GemmKernel,
the GEMM kernel type, can be any of the following:

  • cutlass::gemm::kernel::GemmUniversal,
    implementing CUTLASS 3.x API kernels;
  • cutlass::gemm::kernel::GemmUniversal,
    implementing CUTLASS 2.x API kernels;
  • Any valid CUTLASS 2.x kernel layer GEMM that
    was previously composable with device::GemmUniversalAdapter

Users implementing new kernels in either API should prefer
using kernel::GemmUniversal as the kernel type
and compose it with device::GemmUniversalAdapter.
Users with existing kernel::Gemm kernels
can continue to use them as template arguments
of device::GemmUniversalAdapter. They can adopt
GemmUniversal as a gradual migration path,
since GemmUniversal accepts either 3.0 or 2.x collectives.
Please see the next section for kernel::GemmUniversal for details.

GemmUniversalAdapter presents a single
host-side interface to both 3.0 and 2.x kernels.
CUTLASS accomplishes this by
specializing GemmUniversalAdapter's implementation
on either 2.x API implementing kernel layer GEMMs, or 3.x API
implementing kernel layer GEMMs (as detected by gemm::detail::IsCutlass3GemmKernel
discussed below). As a result, GemmUniversalAdapter's behavior
might differ between the two specializations.

Device API design differences

In CUTLASS 2.x, the Device API was more closely tied
to the Kernel API. In CUTLASS 3.0, the Device API
accepts any kernel type that meets the Kernel API
interface requirements. CUTLASS 3.0's Device API code is
parameterized by the kernel type, but this code
is generic; the same code works for any kernel type.

The device layer compatibility interface, device::GemmUniversalAdapter,
also provides reflective mappings from 3.0-specific types
back to the closest possible 2.x equivalent types. This is discussed further in the section below.

CUTLASS 3.0's device::GemmUniversalAdapter also exposes some new APIs that the 2.x device::GemmUniversalAdapter implementation does not. Most notably, this includes the ability to bypass the GemmKernel::Arguments to GemmKernel::Params lowering.

c++
// Primary run() entry point API that is static allowing users to create and manage their own params.
static Status
run(Params& params, cudaStream_t stream = nullptr);

This new API is useful for the following scenarios.

  • Running again does not require reinvoking GemmKernel::to_underlying_arguments()
  • Manual control over construction of GemmKernel::Params for custom kernels with custom stride types
  • Fully static problem shapes and strides for bespoke kernels where no argument mapping needs to take place

Compatible Kernel API

CUTLASS 3.x API shares the kernel layer API with CUTLASS 2.x
through the single entry point type cutlass::gemm::kernel::GemmUniversal.
All kernel layer GEMMs are viewed as a composition of a collective mainloop
and a collective epilogue.

kernel::GemmUniversal implements both 2.x and 3.x APIs

The entry point for CUTLASS's kernel API is the class
cutlass::gemm::kernel::GemmUniversal.
This class' declaration lives in the header file
include/cutlass/gemm/kernel/gemm_universal.hpp.

c++
/*
 * Stateless universal device GEMM kernel type that treats GEMM as
 * a composition of a collective mainloop and a collective epilogue.
 * SFIANE shims both 2.x and 3.0 API kernels based on ProblemShapeOrThreadblockMma_.
**/
template <
  class ProblemShapeOrThreadblockMma_,
  class CollectiveMainloopOrEpilogue_,
  class CollectiveEpilogueOrThreadblockSwizzle_,
  class TileScheduler_ = void,
  class Enable = void
>
class GemmUniversal;

We call this class "universal" because it can be built
using either the CUTLASS 3.0 or the 2.x mainloops and epilogues.
If GemmUniversal's first template argument
(ProblemShapeOrThreadblockMma_) is a cute::tuple,
then GemmUniversal assumes that
the remaining three template arguments
(the mainloop, epilogue, and grid swizzle)
implement the 3.0 APIs.
Otherwise, GemmUniversal assumes that
the remaining three template arguments
implement the 2.x APIs.
All the template arguments must be either
CUTLASS 3.0 or CUTLASS 2.x types. For example,
GemmUniversal does not permit using
a 2.x mainloop with a 3.0 collective epilogue.

CUTLASS 3.x implements various embodiments of kernel::GemmUniversal.
Each kernel layer schedule is specialized
for a GEMM scheduling algorithm and GPU architecture.
Specializations of kernel::GemmUniversal for 3.0 APIs live in
any of various gemm_*.hpp files in the directory
include/cutlass/gemm/kernel/.
The specialization to which to dispatch is decided through the dispatch policy's Schedule type.

Specializations for 2.x APIs live in the header file
include/cutlass/gemm/kernel/gemm_universal.h.

Kernel API design differences

The CUTLASS 2.x Kernel API was more closely tied
to the Device API, as we mentioned above.
In particular, the 2.x Device API specified the grid shape
used to launch the Kernel API.
In CUTLASS 3.0, the Kernel API controls its own grid shape,
while the device adapter simply queries the kernel with which it needs to be launched.

This change is required to support various kernel schedules
that may need their own schedule specific grid planning logic.
For example, persistent kernel schedules generally only launch with
as many threadblocks as the number of multiprocessors on the GPU.

All CUTLASS 3 kernel::GemmUniversal specializations expose the following (static) API:

c++
// Returns true if the kernel can execute the provided GEMM arguments.
static bool
can_implement(Arguments const& args);

// Returns a dim3 representing the threadblock shape.
static dim3
get_block_shape();

// Returns a dim3 representing the grid shape in terms of threadblocks.
static dim3
get_grid_shape(Params const& params);

The device adapter simply queries the kernel for these three before launching it on the device.
CUTLASS 3.0 provides a meta-function to detect whether a cutlass::gemm::kernel::* implements
the 3.x API or 2.x API:

c++
// include/cutlass/gemm/gemm.h

namespace cutlass:gemm::detail {

// The following metafunction is used to detect whether a
// `kernel::Gemm` or `kernel::GemmUniversal` implements the CUTLASS 3.x API,
// by checking whether the problem shape type is aliased within.
template <class GemmKernel, class = void>
struct IsCutlass3GemmKernel;

} // namespace cutlass:gemm::detail

Users can dispatch their generic code against 2.x and 3.x specializations with
this as a type trait for the kernel API version.

Threadblock API and Inner Loops

Much of the CUTLASS 3 GEMM hierarchy for mainloops and inner loops diverges
from that of CUTLASS 2.x. With that also comes the introduction of the
cutlass::gemm::collective layer as a direct replacement and a superset
of the 2.x cutlass::gemm::threadblock layer. Going forward,
CUTLASS 3.x will discontinue new developments in the following namespaces.

  • cutlass::*::threadblock::*
  • cutlass::*::warp::*
  • cutlass::gemm::thread::*
  • cutlass::arch::* (except barrier.h)

cutlass::gemm::collectives are a superset of the threadblock layer where
all new mainloops will be developed. Users should look to the CollectiveMma type
if they wish to author custom mainloop code in the 3.x API.

Similarly, for the GEMM inner loops, cute::MMA_Atoms replace the
gemm::warp and gemm::thread layer code. Going forward, all new PTX instructions
and associated metadata development will occur directly inside cute/arch/*.hpp and cute/atom/*.hpp.

The desired inner loop MMA iteration order and tiling can be achieved through careful
selection of the atom layout, value layout, and permutations of the cute::TiledMma.

For epilogues, the cutlass::epilogue::collective layer replaces cutlass::threadblock::collective. However, the thread-level epilogue elementwise operations
in cutlass::epilogue::thread will continue to be used in 3.x kernels as well, albeit, with
a more idiomatic epilogue vectorization strategy.
Example 50
shows how to use 2.x epilogue thread operators with 3.0 API kernels.

Porting from 2.x to 3.0 API

CUTLASS 2.x layout tags and CUTLASS 3.0 major modes

CUTLASS 2.x and CUTLASS 3.0 use both
different wording and different types
to describe the permitted layouts
of GEMM's input matrices A and B.

CUTLASS 3.0 does not use the terms "column major"
or "row major" to describe matrix layouts.
Starting with CUTLASS 3.0, adoption of CuTe allows us to decouple

  • the coordinate mode order (logical shape) of layouts from

  • the index space stride order of the backing storage.

In line with our switch to a conceptual GEMM hierarchy, we view the major modes not from a BLAS-3 perspective.
Rather, we divide the modes into two categories.

  • "Inner modes" or "K-modes" are contracted over during the GEMM.
    Therefore, they are not present in the output tensor.

  • "Outer modes" or "MN-modes" are preserved in the output.

Now, instead of RowMajor or ColumnMajor, whose major stride depends on whether we are referring to the
A or the B matrix, we uniformly employ the "K major" or "MN major" terminology and enforce the convention of all tensors having the shape [M/N, K, L] regardless of which mode is major. That is,

  • the input matrix A has shape M x K,
  • the input matrix B has shape N x K, and
  • the input/output matrices C/D have shape M x N.

Note that this convention for B
differs from the BLAS's GEMM interface,
which specifies that B has shape K x N.

CUTLASS 3.0 uses these names of the modes
to specify which mode of a matrix has stride 1.
For the matrix A,

  • "M major" means that the matrix is stride 1
    in the M mode, and
  • "K major" means that the matrix is stride 1
    in the K mode.

For the matrix B,

  • "N major" means that the matrix is stride 1
    in the N mode (which for B is mode 0,
    because the convention is that B is N x K); and
  • "K major" means that the matrix is stride 1
    in the K mode (which for B is mode 1).

CUTLASS 2.x defines "layout tag" classes
cutlass::layout::ColumnMajor and cutlass::layout::RowMajor,
that live in the header file
cutlass/layout/matrix.h.
The interpretation of these layouts in GEMM
depends on whether they are applied
to the input matrix A or B. For the matrix A, "column major" means
that mode corresponding to M extent has stride 1,
and "row major" means that mode corresponding to K extent has stride 1.
This is the usual computer science definition
of column major and row major for a rank-2 array.
For the matrix B, the opposite holds:
"column major" means that mode corresponding to N extent has stride 1,
and "row major" means that mode corresponding to K extent has stride 1.

Using the convention of [outer, inner, batch] mode order for tensor logical shapes
avoids potential confusion with the meaning of column major and row major
changing depending on whether they are applied to A or B.

The table below summarizes our mode order convention and
mapping of 2.x layout tags to corresponding M-major, N-major, or K-major strides.

Matrix CUTLASS 2.x layout 2.x Shape Logical major mode 3.x Shape/Stride Major ordinal
A ColumnMajor M x K M major M x K x L 0 (outer)
A RowMajor M x K K major M x K x L 1 (inner)
B RowMajor K x N N major N x K x L 0 (outer)
B ColumnMajor K x N K major N x K x L 1 (inner)
C ColumnMajor M x N M major M x N x L 0 (outer)
C RowMajor M x N N major M x N x L 1 (inner)

Notice that in CUTLASS 3.0, interpretation of layouts no longer changes based on
whether we are talking about the A or B matrix. M and N major inputs always have a
static size-1 stride in their 0th (outer) mode. Similarly, K major inputs
always contain the static size-1 stride in their 1st mode. This uniformity in stride order
allows us to represent tensor layouts much more cleanly and treat both A and B equally in our interfaces.
See for example the following snippet from our kernel/sm70_gemm.hpp
for Ampere kernel schedules.

c++
// Represent the full tensors
Tensor mA_mkl = make_tensor(make_gmem_ptr(params.mainloop.ptr_A), make_shape(M,K,L), params.mainloop.dA); // (m,k,l)
Tensor mB_nkl = make_tensor(make_gmem_ptr(params.mainloop.ptr_B), make_shape(N,K,L), params.mainloop.dB); // (n,k,l)

// Get batch slice
Tensor mA_mk = mA_mkl(_,_,get<3>(blk_coord_mnkl)); // (m,k)
Tensor mB_nk = mB_nkl(_,_,get<3>(blk_coord_mnkl)); // (n,k)

// Slice to get the tiles for which this thread block is responsible
Tensor gA = local_tile(mA_mk, blk_shape, take<0,3>(blk_coord_mnkl), Step<_1, X,_1>{}); // (BLK_M,BLK_K,k)
Tensor gB = local_tile(mB_nk, blk_shape, take<0,3>(blk_coord_mnkl), Step< X,_1,_1>{}); // (BLK_N,BLK_K,k)

As seem in this snippet, all input tensors have the logical shape [outer, inner, batch],
and the strides could represent either outer or inner
(or any other complex hierarchical stride) major storage.
CuTe layouts always maintain the logical consistency of the coordinate spaces regardless of the strides.

By convention, in CUTLASS 3.0, we treat the M and N mode as the 0th mode,
and K mode as the 1st mode of the stride.

Conversions between 2.x tags and 3.0 types

Starting with CUTLASS 3.0, all layouts are described using
cute::Shape and cute::Stride which compose into a cute::Layout<Shape, Stride>.
In CUTLASS 2.x, various layout tags such as cutlass::layout::RowMajor are used to specialize
template implementations. These tag types only encode information about the tensor strides,
as 2.x layouts did not incorporate any concept of tensor shape in the layout tags themselves.
Users may find a need to convert between CUTLASS 2.x layout tags, and 3.0
CuTe stride types. CUTLASS 3.0 gemm::collective::CollectiveBuilder interfaces
also accept these 2.x layout tags as input parameters in their template API as a convenience for users.
At every entry point into CUTLASS 3.0, these tags get converted to their corresponding CuTe Stride type with
metafunctions that best approximate their corresponding cute::Stride.

  • cutlass::gemm::detail::TagToStrideA_t<LayoutTag>
  • cutlass::gemm::detail::TagToStrideB_t<LayoutTag>
  • cutlass::gemm::detail::TagToStrideC_t<LayoutTag>

By convention, and to match user expectations, the cute::Stride types that these
map onto always contain one static mode corresponding to the layout tag, and two 64-bit
dynamic stride modes corresponding to the minor mode and the batch mode. Batch
mode is included by default as all CUTLASS 3.0 kernels support packed batch-mode GEMMs
out of the box.

The cutlass/gemm/gemm.h#440
header file includes functions
that can be useful for converting
from CUTLASS 3.0 cute::Strides back to CUTLASS 2.x layout tags.

  • cutlass::gemm::detail::StrideToLayoutTagA_t<CuteStride>
  • cutlass::gemm::detail::StrideToLayoutTagB_t<CuteStride>
  • cutlass::gemm::detail::StrideToLayoutTagC_t<CuteStride>

These metafunctions take the CuTe Stride as a template parameter and
attempt to find the size-1 stride in the idiomatic M, N, or K modes
to best approximate a corresponding 2.x layout tag type.
Note that this may not work in general for any cute::Stride
as the mapping between the stride and tag type is not bijective.

These mapping utilities are kept in a detail namespace
as we do not guarantee stability of their implementation.
Their behavior may change in future releases as we add new features.
However, we do expect these type names to remain stable. For users who want
these 2.x reflective types from an assembled kernel with a more stable API,
the specialization of cutlass::gemm::device::GemmUniversalAdapter
for CUTLASS 3.0 kernel provides all aliases for all 2.x type aliases
in addition to the layout tags. You can see how they are used in the header file
cutlass/gemm/device/gemm_universal_adapter.h.
Here is an excerpt.

text
/* Detailed source-code truncated for AI context efficiency. */

CUTLASS's library and profiler use these reflective interfaces to
obtain the kernel's configuration parameters. Users can use these to approximate the CUTLASS 2.x types
for 3.0 API kernels. However, the reflective interfaces cannot always match the types exactly,
as the mappings are not always bijective.

Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Cutlass 3x Design (`media/docs/cpp/cutlass_3x_design.md`)

CUTLASS 3.0 Design

CUTLASS 3.0 is a major enhancement over the abstractions of CUTLASS 2.x
and aims to make usage of all layers of the GEMM hierarchy easier and more composable
while still achieving peak performance on Hardware.

CUTLASS 3.0 design goals

CUTLASS 3.0 has the following design goals, in no particular order.

  • Simplify expressing and manipulating data and thread layouts across
    the GEMM hierarchy with CuTe layouts and layout algebra.

  • Improve code readability and learning curve by
    reducing the number of named types.

  • Functional correctness by default,
    actionable static asserts otherwise.

  • Single, clear points of performance tuning and custom kernel extensions.

  • Support for NVIDIA Hopper GPUs with great performance using
    features such as Tensor Cores, tensor memory accelerator, and thread block clusters.

A new Conceptual GEMM Hierarchy

CUTLASS 2.x decomposes the moving parts of a GEMM operation
across a hierarchy that closely mirrors the organization of GPU
architectures. This discussed in detail within the
CUTLASS 2.x GEMM API documentation.
This design, however, sometimes results in a coupling that is too tight
to extend to newer GPU features that might not fit into the same architectural
hierarchy. For instance, Hopper's warp-group wide instructions do not naturally
fit into any warp or thread layer GEMM concept in CUTLASS 2.x. Even for Volta tensor cores,
instructions that atomically exist at the quad-pair granularity are first tiled at
the warp level before use. This hints at the brittleness of the abstraction power.

CUTLASS 3.0 detaches its interface layers from the hardware,
centering them instead around the natural structure of GEMM algorithms
not tied to any particular GPU generation.
This makes CUTLASS's code more robust to GPU architecture evolution,
less prone to implementation detail leakage, and provides users
with a consistent interface to hardware acceleration regardless of
the architecture specific details.

The new conceptual GEMM hierarchy is discussed in detail in the dedicated
CUTLASS 3.0 GEMM API documentation readme,
along with code examples of the core concepts and types.

Adoption of CuTe Layout and Tensors

CUTLASS 3.0 introduces a new core library, CuTe, to describe and manipulate tensors of threads and data.
CuTe is a collection of C++ CUDA template abstractions for defining and operating on hierarchically multidimensional layouts of threads and data. CuTe provides Layout and Tensor objects that compactly packages the type, shape, memory space, and layout of data, while performing the complicated indexing for the user.

CUTLASS 3.0 adopts CuTe throughout the GEMM hierarchy in its templates, greatly simplifying the design,
improving code composability, and readability. More documentation specific to CuTe can be found in its dedicated documentation directory.

Programming massively parallel systems with various layers of logical thread and data hierarchies is not a trivial task.

  • cute::Layouts always maintain logical consistency of their coordinates,
    allowing us to check pre- and post-conditions at compile time for all static inner loops.
  • Explicit thread to data mapping allows users and kernel authors to inspect and reason about operations
    from a single point in the source code.
  • Layouts provide a single point of performance tuning, as most optimizations can be done by careful
    selection of thread and data layouts.
  • Formalized algebra makes manipulation of and reasoning about thread->data mapping explicit in source code.
  • Single vocabulary type (cute::Layout) subsumes every iterator and layout in CUTLASS 2.x CUTLASS 2.x uses many bespoke thread maps, iterators, and data layouts. Iterators are fundamentally 1-D, whereas most layouts we encounter in the GPU hierarchy are fundamentally n-D.

Reducing the number of named types and iterator concepts

CUTLASS 2.x design preferred introducing bespoke named types for each
architecture specific thread and data layout. For instance, gemm::treadblock namespace
contains implementation for MmaMultistage, MmaPlanarComplexMultistage, MmaPipelined etc.
despite them providing mainloops for GEMMs. To spell these types the same way in generic code,
CUTLASS 2.x provides aliases through its default_x_configuration.h files, however,
these aliases make the code much harder to read as the user has to perform type substitution
mentally in order to understand the codebase.

CUTLASS 3.0 greatly reduces the number of named types used throughout by

  • Replacing all iterator concepts for all memory domains with cute::Tensors
  • Dispatching mainloop and epilogue implementations on tag-dispatch policies rather than naming new types
  • Dispatching kernel layer schedules on tag-dispatch policies rather than naming new types

Reducing the number of named types has many benefits:

  • It makes writing generic code easier, as the primary type names share the same lexical
    without aliasing through configuration providers.
  • It flattens the learning curve of CUTLASS by greatly reducing the mental context required
    as the library only exposes a handful of named types.
  • It provides a clear, singular extension point for users to plug in their customizations
    through the dispatch policies.

Correctness by default, Performance through clear, individual points of tuning

CUTLASS 2.x maintained its thread layouts as implicit indexing math implemented
as a part of 1D iterators. This meant that the thread to data layout mapping
was implicit in the imperative structure of the C++ code itself and did not have
a formal algebra we could use to manipulate these mappings. Each iterator
had to re-implement its indexing and mapping logic. This made it hard to learn
how this mapping was performed for existing iterators, and even harder to
implement custom layout functions for the core inner loops of a GEMM.

CUTLASS 3.0 replaces all iterator concepts from CUTLASS 2.x
with a single layout type for thread and data tensors.
CuTe's formalized layout algebra is then used at every layer of
the GEMM hierarchy to manipulate the mapping between the two.
CuTe layouts always maintain logical consistency, and for fully static layouts
(such as in the core unrolled inner loops), provide
compile time checks that break builds if this consistency is violated.
In this way, CuTe reifies the thread-to-data-layout mapping,
makes it easier to write code that is "correct by construction".
If the code compiles, it's probably correct.

Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Dependent Kernel Launch (`media/docs/cpp/dependent_kernel_launch.md`)

Dependent kernel launches

The Hopper and Blackwell architectures supports a new feature through which two kernels in the same stream can
overlap their execution, named
Programmatic Dependent Launch (PDL).
This allows kernels with conflict in global memory to programmatically and safely overlap portions
of their execution. Primary kernel can signal it is about to finish execution, and the next kernel is expected to
programmatically wait on the previous kernel to finish flushing its memory.

We enable PDL by setting a flag through the extended CUDA launch APIs. All CUTLASS kernels with PDL support
will wait on the prior kernel to flush its output to memory and signal the next kernel to start. This means
they can safely be dropped in with any other set of kernels using PDL as long as they also adhere to waiting on
the prior to flush its memory as well.

For more information, we refer you to the PDL section in the CUDA Programming Guide.

Using dependent launch in CUTLASS

When building CUTLASS, you can use the CUTLASS_ENABLE_GDC_FOR_SM90 and CUTLASS_ENABLE_GDC_FOR_SM100 macro
respectively to enable PDL-related instructions:

text
cmake . -DCUTLASS_ENABLE_GDC_FOR_SM90=1

Note that this only adds PDL-related instructions to the kernels, but to actually allow a dependent
launch, you must also run your GEMM kernel with PDL:

text
gemm.run(
  /* stream = */ stream,
  /* cuda_adapter = */ nullptr,
  /* launch_with_pdl = */ true
);_

Model-Aware Optimizations with PDL

In example 63, we use PDL to explicitly optimize for
performance of kernels where we know that one of the input matrices (our weights) will not be produced by a prior
kernel. In that case, we only need to wait on the prior kernels memory flush in order to load the other input matrix
(our activations). During our prologue, we can prefetch our weights to improve performance for memory bandwidth-bound
problem sizes. For more information, we refer the reader to the example.

Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Doxygen Mainpage (`media/docs/cpp/doxygen_mainpage.md`)

CUTLASS 3.0

CUTLASS 3.0 - January 2023

CUTLASS is a collection of CUDA C++ template abstractions for implementing
high-performance matrix-multiplication (GEMM) at all levels and scales within CUDA.
It incorporates strategies for hierarchical decomposition and data movement similar
to those used to implement cuBLAS. CUTLASS decomposes these "moving parts" into
reusable, modular software components abstracted by C++ template classes. These
components can be specialized
and tuned via custom tiling sizes, data types, and other algorithmic policies. The
resulting flexibility simplifies their use as building blocks within custom kernels
and applications.

To support a wide variety of applications, CUTLASS provides extensive support for
mixed-precision computations, providing specialized data-movement and
multiply-accumulate abstractions for 8-bit integer, half-precision floating
point (FP16), single-precision floating point (FP32), and double-precision floating
point (FP64) types. Furthermore, CUTLASS exploits the Tensor Cores and asynchronous
memory copy operations of the latest NVIDIA GPU architectures.

What's New in CUTLASS 3.0

For an overview of CUTLASS 3.0's GEMM interface levels,
please refer to the
CUTLASS 3.0 GEMM API document.
To learn how to migrate code using CUTLASS 2.x's interface
to CUTLASS 3.0, please refer to the
backwards compatibility document.

GEMM examples

For a code example showing how to define
a GEMM kernel using CUTLASS, please refer to
the quickstart guide.
The examples directory
has a variety of examples.

Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Efficient Gemm (`media/docs/cpp/efficient_gemm.md`)

Efficient GEMM in CUDA

CUTLASS implements the hierarchically blocked structure described in
CUTLASS: Fast Linear Algebra in CUDA C++
and the CUTLASS GTC2018 talk.

Hierarchical Structure

The basic triple loop nest computing matrix multiply may be blocked and tiled to match
concurrency in hardware, memory locality, and parallel programming models. In CUTLASS,
GEMM is mapped to NVIDIA GPUs with the structure illustrated by the following loop nest.

text
/* Detailed source-code truncated for AI context efficiency. */

This tiled loop nest targets concurrency among

  • threadblocks,
  • warps, and
  • CUDA and Tensor Cores.

It takes advantage of memory locality within

  • shared memory and
  • registers.

The figure below illustrates the flow of data within this structure.
This is the hierarchical GEMM computation embodied by CUTLASS. Each stage depicts a
nested level of tiling which corresponds to a layer of concurrency within the CUDA execution model and to a
level within the memory hierarchy, becoming increasingly finer moving left to right.

Threadblock-level GEMM

Each threadblock computes its portion of the output GEMM by iteratively loading tiles of input
matrices and computing an accumulated matrix product. At the threadblock level, data are loaded from
global memory. The blocking strategy in general is key to achieving efficiency. However, the programmer
must balance multiple conflicting goals. A
larger threadblock means fewer fetches from global memory, thereby ensuring that DRAM bandwidth
does not become a bottleneck.
However, large threadblock tiles may not match the dimensions of the problem well. If either the
GEMM M or N dimension is small, some threads within the threadblock may not perform meaningful
work, as the threadblock may be partially outside the bounds of the problem. If both M and N
are small while K is large, this scheme may launch relatively few threadblocks and fail to
make full use of all multiprocessors within the GPU. Strategies to optimize performance for this case,
as described in the section Parallelized Reductions,
partition the GEMM K dimension across multiple threadblocks or multiple warps. These threadblocks
or warps compute matrix products in parallel; the products are then reduced to compute the result.

In CUTLASS, the dimensions of the threadblock tile are specified as ThreadblockShape::{kM, kN, kK}
and may be tuned to specialize the GEMM computation for the target processor and dimensions of
the GEMM problem.

Warp-level GEMM

The warp-level GEMM maps to the warp-level parallelism within the CUDA execution model. Multiple
warps within a threadblock fetch data from shared memory into registers and perform computations.
Warp-level GEMMs may be implemented either by TensorCores issuing
mma.sync
or wmma
instructions, or by thread-level matrix computations issued to CUDA cores.
For maximum performance, access to shared memory should be bank conflict free. To maximize data
reuse within the warp, a large warp-level GEMM tile should be chosen.

Thread-level GEMM

At the lowest level of blocking, each thread is responsible for processing a certain number of
elements. Threads cannot access each other's registers, so we choose an organization that enables
reuse of values held in registers for multiple math instructions. This results in a 2D tiled
structure within a thread, in which each thread issues a sequence of independent math instructions
to the CUDA cores and computes an accumulated outer product.

SGEMM, IGEMM, HGEMM, and DGEMM are computed by SIMT math instructions issued by thread-level matrix multiply
procedures.

Epilogue

The above code focuses only on the matrix multiply computation C = AB whose result is
held in the registers of each thread within the threadblock. The mapping of logical elements
in the output tile to each thread is chosen to maximize performance of the matrix multiply
computation but does not result in efficient, coalesced loads and stores to global memory.

The epilogue is a separate phase in which threads exchange data through shared memory then
cooperatively access global memory using efficient striped access patterns. It is also
the phase in which linear scaling and other elementwise operations may be conveniently
computed using the matrix product results as inputs.

CUTLASS defines several typical epilogue operations such as linear scaling and clamping,
but other device-side function call operators may be used to perform custom operations.

Optimizations

The hierarchical structure described above yields an efficient mapping to the CUDA execution model and
CUDA/TensorCores in NVIDIA GPUs. The following sections describe strategies for obtaining peak performance
for all corners of the design space, maximizing parallelism and exploiting data locality wherever possible.

Pipelining

The blocked structure demands a large storage allocation within the registers of each CUDA thread. The
accumulator elements typically occupy at least half a thread's total register budget. Consequently,
occupancy -- the number of concurrent threads, warps, and threadblocks -- is relatively low compared
to other classes of GPU workloads. This limits the GPU's ability to hide memory latency and other stalls
by context switching to other concurrent threads within an SM.

To mitigate the effects of memory latency, CUTLASS uses software pipelining to overlap memory accesses
with other computation within a thread. CUTLASS accomplishes this by double buffering at the
following scopes.

  • Threadblock-scoped shared memory tiles: two tiles are allocated in shared memory.
    One is used to load data for the current matrix operation,
    while the other tile is used to buffer data loaded from global memory
    for the next mainloop iteration.

  • Warp-scoped matrix fragments: two fragments are allocated within registers.
    One fragment is passed to CUDA and TensorCores during the current matrix computation,
    while the other is used to receive shared memory fetch returns
    for the next warp-level matrix operation.

The following diagram illustrates the efficient, pipelined mainloop body used in CUTLASS GEMMs.

Threadblock Rasterization

To maximize reuse of data held in the last level cache, CUTLASS defines several functions to
affect the mapping of threadblocks to logical partitions of the GEMM problem. These map
consecutively launched threadblocks to packed two-dimensional regions of the partitioned GEMM
problem to increase the probability that these will access the same tiles of global memory at
approximately the same time.

Several functions are defined in cutlass/gemm/threadblock_swizzle.h.

Parallelized Reductions

Split K - reduction across threadblocks

Matrix product computations expose parallelism among O(MN) independent inner product
computations. For sufficiently large problem sizes, a GEMM kernel in CUTLASS may approach
the theoretical maximum computational throughput. For small problems, however, there are
too few threadblocks to efficiently occupy the entire GPU.

As a recourse, parallelizing the reduction performed during the inner product computation
enables more threadblocks to execute concurrently while still taking advantage of the throughput
benefits of large threadblock-level GEMM tiles.

CUTLASS implements parallel reductions across threadblocks by partitioning the GEMM K dimension
and launching an additional set of threadblocks for each partition. Consequently, we refer to
this strategy within CUTLASS as "parallel reduction splitK." The "parallel reduction splitK" strategy
requires the execution of 2 kernels: partitionedK GEMM, and batched reduction.

PartitionedK GEMM resembles one flavor of batched strided GEMM. Instead of requiring users
to specify the problem size of each batch, partitionedK GEMM asks for the overall problem size and the
number of partitions that will be applied along the K dimension for operands A and B. For example,
parameters of m=128, n=128, k=4096 and partition=16 will result in 16 batched strided GEMMs
with each batch of m=128, n=128, k=256. PartitionedK also allows scenario where k is not divisible
by the partition count.

For example, parameters of m=128, n=128, k=4096 and partition=20
will result in 20 batched strided GEMMs.
The first 19 batches will have m=128, n=128, and k=4096/20=204,
and the last batch will have m=128, n=128, and k=220.

The batched reduction kernel takes as input the output (C) of partitionedK GEMM,
and performs a reduction along the K-dimension.
Users must manage workspace memory to store this intermediate result.

Sliced K - reduction across warps

Similar to the split-k scenario, sliced-k aims at improving the efficiency of kernels
with smaller M and N dimensions, but large K dimension.
At the thread-block level, the parameters CtaTileN and CtaTileM expose parallelism
by partitioning the work among warps.
Larger warpTiles expose better instruction-level parallelism (ILP) and reuse,
but also limit the number of warps running per threadblock, which reduces efficiency.

In order to improve efficiency in such scenarios, partitioning the warpTiles also along ctaTileK
helps use the hardware more efficiently by allowing more warps to run concurrently in a CTA.
Sliced-k kernels break down a threadblock's computation among participating warps
not just among the CtaTileN, CtaTileM dimension, but also the CtaTileK dimension.
Thus, sliced-k entails a small cost in form of a reduction
which has to happen at the end among the participating warps.
This is because each warp computes using only a "slice" of CtaTileK,
so each warp only has a partial sum before the reduction.

Hopper Warp Specialization

Note: the following section on warp-specialization contains details that are specific
to the Hopper kernel design. Blackwell SM100 kernels have a substantially different warp-specialization structure,
however, the concept of separating out producer and consumer agents still applies.

Starting with Hopper, CUTLASS 3.0 incorporates the concept of Warp Specialization
as part of the kernel design. A thread block is partitioned into two sets of warps, producer warp group and consumer warp group. The producer warp group loads data from global memory into shared memory buffers using the new Tensor Memory Accelerator (TMA).

Producer warp group (DMA) waits for the shared memory buffers to be signaled as empty by the consumer warp group using the newly added Async Pipeline class (refer). Once the data is written into the shared memory, TMA is also updates the barrier associated with that stage to notify affected threads that the buffer has been filled. The Consumer warp group (MMA) on the other hand waits for the producer warp group to signal that the buffer is filled and then launches tensor core MMA operations. Finally, the consumer warp group releases the buffers for the next set of TMA loads to happens.

Warp-Specialized Persistent Cooperative kernel design

Another flavor of Warp-Specialized kernel design being introduced starting with Hopper is the Warp-Specialized Persistent Cooperative kernel. Like the Warp-Specialized kernel, the concepts of warp groups and barrier synchronization between warp groups remain the same in the cooperative design.
The distinctive feature of the Warp-Specialized Persistent Cooperative kernel are the following :

  • Persistent thread blocks launched to occupy as many SMs as mentioned in the KernelHardwareInfo struct. These persistent thread blocks are used to tile the output and thus (potentially) compute multiple output tiles through their lifetime. The main benefit this adds is amortization of the thread-block launch and kernel prologue overheads which are typical of all kernels.
  • Presence of two consumer warp groups cooperating on the same output tile by splitting the tile in half across the M dimension. This allows for larger tile sizes to be enabled - since the register pressure per consumer warp group is reduced - and hence improving performance.

Since each thread block now computes multiple output tiles, the shape of the grid launch and the scheduling of tiles to the thread blocks is managed using the new Tile Scheduler. The Tile Scheduler considers the shape of the clusters as well as the available number of available SMs to compute a valid scheduling of the output tiles to launched thread blocks.

Warp-Specialized Persistent Ping-Pong kernel design

The third kernel design is the Warp-Specialized Persistent Ping-Pong kernel.
Like the Warp-Specialized Persistent Cooperative, kernel the concepts of warp groups, barrier synchronization between warp groups, and the shape of the grid launch remain the same in the persistent ping-pong design.
The distinctive feature of the Warp-Specialized Persistent Ping-Pong kernel is the following :

  • The two consumer warp groups are assigned a different output tile using the Tile Scheduler. This allows for epilogue of one consumer warp group to be overlapped with the math operations of the other consumer warp group - thus maximizing tensor core utilization.
  • The producer warp group synchronizes using the Ordered Sequence Barrier to fill buffers of the two consumer warp groups one after the other in order.

Resources

The following additional resources describe design and implementation details of GEMMs
targeting NVIDIA GPUs.

Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Functionality (`media/docs/cpp/functionality.md`)

Functionality

Note : CUTLASS-3 requires users to use CUDA 11.4 or newer, and SM70 or newer, for the target toolkit and architecture, respectively.

  • N - Column Major Matrix
  • T - Row Major matrix
  • {N,T} x {N,T} - All combinations, i.e., NN, NT, TN, TT
  • NHWC - 4 dimension tensor used for convolution
  • NCxHWx - Interleaved 4 dimension tensor used for convolution
  • f - floating point
  • s - signed int
  • b - bit
  • cf - complex float
  • bf16 - bfloat16
  • tf32 - tfloat32
  • Simt - Use Simt CUDA Core MMA
  • TensorOp - Use Tensor Core MMA
  • SpTensorOp - Use Sparse Tensor Core MMA
  • WmmaTensorOp - Use WMMA abstraction to use Tensor Core MMA

Device-level GEMM

The following tables summarize device-level GEMM kernels in CUTLASS, organized by opcode class, data type, and layout.
Hyperlinks to relevant unit tests demonstrate how specific template instances may be defined.

CUTLASS 3.x Kernels

Opcode Class Compute Capability CUDA Toolkit Data Type Layouts Unit Test
TensorOp 90a 12.0+ f16 * f16 + { f16, f32 } => { f16, f32 } {N,T} x {N,T} => {N,T} example
TensorOp 90a 12.0+ bf16 * bf16 + { f16, f32 } => { bf16, f32 } {N,T} x {N,T} => {N,T} example
TensorOp 90a 12.0+ {f32, tf32} * {f32, tf32} + f32 => f32 { T } x { N } => {N,T} example
TensorOp 90a 12.0+ s8 * s8 + s32 => {s32, s8} { T } x { N } => {N,T} example

CUTLASS 2.x Kernels

Opcode Class Compute Capability CUDA Toolkit Data Type Layouts Unit Test
Simt 50+ 11.4+ f32 * f32 + f32 => f32 {N,T} x {N,T} => {N,T} example
Simt 50+ 11.4+ f64 * f64 + f64 => f64 {N,T} x {N,T} => {N,T} example
Simt 60+ 11.4+ f16 * f16 + f16 => f16 {N,T} x {N,T} => {N,T} example
Simt 61+ 11.4+ s8 * s8 + s32 => {s32,s8} {N,T} x {N,T} => {N,T} example
WmmaTensorOp 70+ 11.4+ f16 * f16 + f16 => f16 {N,T} x {N,T} => {N,T} example
WmmaTensorOp 70+ 11.4+ f16 * f16 + f32 => {f16, f32} {N,T} x {N,T} => {N,T} example
WmmaTensorOp 75+ 11.4+ s8 * s8 + s32 => {s32, s8} {N,T} x {N,T} => {N,T} example
WmmaTensorOp 75+ 11.4+ s4 * s4 + s32 => {s32, s4} {N,T} x {N,T} => {N,T} example
WmmaTensorOp 75+ 11.4+ b1 ^ b1 + s32 => {s32, b1} { T } x { N } => {N,T} example
TensorOp 70+ 11.4+ f16 * f16 + f16 => f16 {N,T} x {N,T} => {N,T} example
TensorOp 70+ 11.4+ f16 * f16 + f32 => {f16, f32} {N,T} x {N,T} => {N,T} example
TensorOp 75+ 11.4+ f16 * f16 + f16 => f16 {N,T} x {N,T} => {N,T} example
TensorOp 75+ 11.4+ f16 * f16 + f32 => {f16, f32} {N,T} x {N,T} => {N,T} example
TensorOp 75+ 11.4+ s8 * s8 + s32 => {s32, s8} { T } x { N } => {N,T} example
TensorOp 75+ 11.4+ s4 * s4 + s32 => {s32, s4} { T } x { N } => {N,T} example
TensorOp 75+ 11.4+ b1 ^ b1 + s32 => {s32, b1} { T } x { N } => {N,T} example
TensorOp 80+ 11.4+ f16 * f16 + f16 => f16 {N,T} x {N,T} => {N,T} example
TensorOp 80+ 11.4+ f16 * f16 + f32 => {f16, f32} {N,T} x {N,T} => {N,T} example
TensorOp 80+ 11.4+ bf16 * bf16 + f32 => {bf16, f32} {N,T} x {N,T} => {N,T} example
TensorOp 80+ 11.4+ tf32 * tf32 + f32 => f32 {N,T} x {N,T} => {N,T} example
TensorOp 80+ 11.4+ s8 * s8 + s32 => {s32, s8} { T } x { N } => {N,T} example
TensorOp 80+ 11.4+ s4 * s4 + s32 => {s32, s4} { T } x { N } => {N,T} example
TensorOp 80+ 11.4+ b1 ^ b1 + s32 => {s32, b1} { T } x { N } => {N,T} example
TensorOp 80+ 11.4+ f64 * f64 + f64 => f64 {N,T} x {N,T} => {N,T} example
TensorOp 80+ 11.4+ cf32 * cf32 + cf32 => cf32 {N,T} x {N,T} => {N,T} example
TensorOp 80+ 11.4+ cf64 * cf64 + cf64 => cf64 {N,T} x {N,T} => {N,T} example, Gaussian 3m
SpTensorOp 80+ 11.4+ f16 * f16 + f32 => {f16, f32} {N,T} x {N,T} => {N,T} example
SpTensorOp 80+ 11.4+ bf16 * bf16 + f32 => {bf16, f32} {N,T} x {N,T} => {N,T} example
SpTensorOp 80+ 11.4+ tf32 * tf32 + f32 => f32 {N,T} x {N,T} => {N,T} example
SpTensorOp 80+ 11.4+ s8 * s8 + s32 => {s8, s32} {N,T} x {N,T} => {N,T} example
SpTensorOp 80+ 11.4+ s4 * s4 + s32 => {s4, s32} {N,T} x {N,T} => {N,T} example
TensorOp 90+ 11.8+ f64 * f64 + f64 => f64 {N,T} x {N,T} => {N,T} example

Device-level Implicit GEMM convolution

The following table summarizes device-level implicit GEMM convolution kernels in CUTLASS, organized by opcode class, data type, and layout.
Hyperlinks to relevant conv2d fprop unit tests demonstrate how specific template instances may be defined.
One can find and/or create equivalent dgrad and wgrad convolutional operators.

Opcode Class Compute Capability CUDA Toolkit Data Type Layouts Unit Test
Simt 50+ 11.4+ f32 * f32 + f32 => f32 NHWC example
Simt 50+ 11.4+ cf32 * cf32 + cf32 => cf32 NHWC example
TensorOp 70+ 11.4+ f16 * f16 + f32 => {f16, f32} NHWC example
TensorOp 75+ 11.4+ f16 * f16 + f32 => {f16, f32} NHWC example
TensorOp 75+ 11.4+ s8 * s8 + s32 => {s32, s8} NHWC, NCxHWx example, ncxhwx
TensorOp 75+ 11.4+ s4 * s4 + s32 => {s32, s4} NHWC, NCxHWx example, ncxhwx
Simt 80+ 11.4+ f32 * f32 + f32 => f32 NHWC example
Simt 80+ 11.4+ cf32 * cf32 + cf32 => cf32 NHWC example
TensorOp 80+ 11.4+ f16 * f16 + f32 => {f16, f32} NHWC example
TensorOp 80+ 11.4+ f16 * f16 + f16 => f16 NHWC example
TensorOp 80+ 11.4+ tf32 * tf32 + f32 => f32 NHWC example
TensorOp 80+ 11.4+ s8 * s8 + s32 => {s32, s8} NHWC, NCxHWx example, ncxhwx
TensorOp 80+ 11.4+ s4 * s4 + s32 => {s32, s4} NHWC, NCxHWx example, ncxhwx

Warp-level Matrix Multiply with Tensor Cores

The following table summarizes supported warp level shapes for each TensorOp instruction.

Opcode Class Instruction Shape Warp Shapes
TensorOp 8-by-8-by-4 32x32x4, 32x64x4, 64x32x4, 64x64x4
TensorOp 16-by-8-by-8 32x32x8, 32x64x8, 64x32x8, 64x64x8
TensorOp 16-by-8-by-16 32x32x16, 32x64x16, 64x32x16, 64x64x16
TensorOp 8-by-8-by-16 32x32x16, 32x64x16, 64x32x16, 64x64x16
TensorOp 8-by-8-by-32 32x32x32, 32x64x32, 64x32x32, 64x64x32
TensorOp 16-by-8-by-32 32x32x32, 32x64x32, 64x32x32, 64x64x32
TensorOp 16-by-8-by-64 32x32x64, 32x64x64, 64x32x64, 64x64x64
TensorOp 8-by-8-by-128 32x32x128, 32x64x128, 64x32x128, 64x64x128
TensorOp 16-by-8-by-256 32x32x256, 32x64x256, 64x32x256, 64x64x256
SpTensorOp 16-by-8-by-16 64x64x16, 64x32x16, 32x64x16, 32x32x16
SpTensorOp 16-by-8-by-32 64x64x32, 64x32x32, 32x64x32, 32x32x32
SpTensorOp 16-by-8-by-64 64x64x64, 64x32x64, 32x64x64, 32x32x64
SpTensorOp 16-by-8-by-128 64x64x128, 64x32x128, 32x64x128, 32x32x128

TensorOp instructions depend on a permuted shared memory layout that can be efficiently
loaded from. The following tables summarize the destination shared memory layout that
can be targeted by matrix operands. It is assumed that each thread loads 128b vectors
from global memory with layout specified in the column "GMEM Layout."

TensorOp 8-by-8-by-4.

Operand Element GMEM Layout SMEM Layout
A half_t ColumnMajor ColumnMajorVoltaTensorOpCongruous<16>
A half_t RowMajor RowMajorVoltaTensorOpCrosswise<16>
B half_t ColumnMajor ColumnMajorVoltaTensorOpCrosswise<16>
B half_t RowMajor RowMajorVoltaTensorOpCongruous<16>
C half_t RowMajor RowMajor
C float RowMajor RowMajor

TensorOp 16-by-8-by-8.

Operand Element GMEM Layout SMEM Layout
A half_t ColumnMajor ColumnMajorTensorOpCongruous<16>
A half_t RowMajor RowMajorTensorOpCrosswise<16>
B half_t ColumnMajor ColumnMajorTensorOpCrosswise<16>
B half_t RowMajor RowMajorTensorOpCongruous<16>
C half_t RowMajor RowMajor
C float RowMajor RowMajor

TensorOp 16-by-8-by-8.

Operand Element GMEM Layout SMEM Layout
A tfloat32_t ColumnMajor ColumnMajorTensorOpCongruous<32>
A tfloat32_t RowMajor RowMajorTensorOpCrosswise<32>
B tfloat32_t ColumnMajor ColumnMajorTensorOpCrosswise<32>
B tfloat32_t RowMajor RowMajorTensorOpCongruous<32>
C float RowMajor RowMajor

TensorOp 16-by-8-by-16.

Operand Element GMEM Layout SMEM Layout
A half_t, bfloat16_t ColumnMajor ColumnMajorTensorOpCongruous<16>
A half_t, bfloat16_t RowMajor RowMajorTensorOpCrosswise<16>
B half_t, bfloat16_t ColumnMajor ColumnMajorTensorOpCrosswise<16>
B half_t, bfloat16_t RowMajor RowMajorTensorOpCongruous<16>
C half_t RowMajor RowMajor
C float RowMajor RowMajor

TensorOp 8-by-8-by-4.

Operand Element GMEM Layout SMEM Layout
A double ColumnMajor ColumnMajorTensorOpCongruous<64>
A double RowMajor RowMajorTensorOpCrosswise<64>
B double ColumnMajor ColumnMajorTensorOpCrosswise<64>
B double RowMajor RowMajorTensorOpCongruous<64>
C double RowMajor RowMajor

TensorOp 8-by-8-by-16.

Operand Element GMEM Layout SMEM Layout
A int8_t RowMajor RowMajorTensorOpCrosswise<8>
B int8_t ColumnMajor ColumnMajorTensorOpCongruous<8>
C int32_t RowMajor RowMajor

TensorOp 16-by-8-by-32.

Operand Element GMEM Layout SMEM Layout
A int8_t RowMajor RowMajorTensorOpCrosswise<8>
B int8_t ColumnMajor ColumnMajorTensorOpCongruous<8>
C int32_t RowMajor RowMajor

TensorOp 8-by-8-by-32.

Operand Element GMEM Layout SMEM Layout
A int4b_t RowMajor RowMajorTensorOpCrosswise<4>
B int4b_t ColumnMajor ColumnMajorTensorOpCongruous<4>
C int32_t RowMajor RowMajor

TensorOp 16-by-8-by-64.

Operand Element GMEM Layout SMEM Layout
A int4b_t RowMajor RowMajorTensorOpCrosswise<4>
B int4b_t ColumnMajor ColumnMajorTensorOpCongruous<4>
C int32_t RowMajor RowMajor

TensorOp 8-by-8-by-128.

Operand Element GMEM Layout SMEM Layout
A bin1_t RowMajor RowMajorTensorOpCrosswise<4>
B bin1_t ColumnMajor ColumnMajorTensorOpCongruous<4>
C int32_t RowMajor RowMajor

SpTensorOp 16-by-8-by-16.

Operand Element GMEM Layout SMEM Layout
A tfloat32_t RowMajor RowMajorTensorOpCrosswise<32, 32>
B tfloat32_t ColumnMajor ColumnMajorTensorOpCrosswise<32, 32>
C float RowMajor RowMajor

SpTensorOp 16-by-8-by-32.

Operand Element GMEM Layout SMEM Layout
A half_t RowMajor RowMajorTensorOpCrosswise<16, 64>
B half_t ColumnMajor ColumnMajorTensorOpCrosswise<16, 64>
C float RowMajor RowMajor

SpTensorOp 16-by-8-by-64.

Operand Element GMEM Layout SMEM Layout
A int8_t RowMajor RowMajorTensorOpCrosswise<8, 128>
B int8_t ColumnMajor ColumnMajorTensorOpCrosswise<8, 128>
C int32_t RowMajor RowMajor

SpTensorOp 16-by-8-by-128.

Operand Element GMEM Layout SMEM Layout
A int4b_t RowMajor RowMajorTensorOpCrosswise<4, 256>
B int4b_t ColumnMajor ColumnMajorTensorOpCrosswise<4, 256>
C int32_t RowMajor RowMajor

Warp-level Matrix Multiply with CUDA WMMA API

The following table summarizes supported warp level shapes for each WmmaTensorOp instruction.

Opcode Class Instruction Shape Warp Shapes
WmmaTensorOp 16-by-16-by-16 32x32x16, 32x64x16, 64x32x16
WmmaTensorOp 8-by-32-by-16 32x32x16, 32x64x16, 64x32x16
WmmaTensorOp 32-by-8-by-16 32x32x16, 32x64x16, 64x32x16
WmmaTensorOp 8-by-8-by-32 32x32x32, 32x64x32, 64x32x32, 64x64x32
WmmaTensorOp 8-by-8-by-128 32x32x128, 32x64x128, 64x32x128, 64x64x128

CUDA exposes warp-level matrix operations in the CUDA C++ WMMA API. The CUDA C++ WMMA API exposes Tensor Cores via a set of functions and types in the nvcuda::wmma namespace. The functions and types in nvcuda::wmma provide target-independent APIs and implement architecture-specific tensor operation using TensorOp instruction underneath. CUTLASS exposes WMMA API through WmmaTensorOp. The WmmaTensorOp supports canonical shared memory layouts. The following table summarizes the destination shared memory layout that can be targeted by matrix operands. The WMMA API expects that matrices in shared memory loaded by nvcuda::wmma::load_matrix_sync() satisfy 128 bit alignment.

WmmaTensorOp (all matrix sizes and data types).

Operand GMEM Layout SMEM Layout
A RowMajor, ColumnMajor RowMajor, ColumnMajor
B RowMajor, ColumnMajor RowMajor, ColumnMajor
C RowMajor, ColumnMajor RowMajor, ColumnMajor

Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Fundamental Types (`media/docs/cpp/fundamental_types.md`)

Fundamental Types

CUTLASS defies several fundamental numeric and container classes upon which computations and
algorithms algorithms for linear algebra computations are implemented.

Where possible, CUTLASS fundamental types mirror the C++ Standard Library. However, there are circumstances that necessitate divergence from the Standard Library's specification. In such cases, the CUTLASS implementation adopts unique capitalization to distinguish that standard vocabulary types may not be safely substituted in all cases.

Most types in CUTLASS are usable in both host code and device code. Moreover, they are functional regardless of compute capability, but they may only be efficient when hardware support is present.

Numeric Types

CUTLASS defines classes for the following numeric data types.

  • half_t: IEEE half-precision floating point (exponent: 5b, mantissa: 10b; literal suffix _hf)
  • bfloat16_t: BFloat16 data type (exponent: 8b, mantissa: 7b; literal suffix _bf16)
  • tfloat32_t: Tensor Float 32 data type (exponent: 8b, mantissa: 10b; literal suffix _tf32)
  • int4_t, uint4_t: 4b signed and unsigned integer (literal suffx _s4, _u4)
  • bin1_t: 1b binary numeric type (literal suffix _b1)
  • float_e5m2_t: 8bits signed float (exponent: 5 bits, mantissa: 2 bits)
  • float_e4m3_t: 8bits signed float (exponent: 4 bits, mantissa: 3 bits)
  • float_ue4m3_t: 8bits unsigned float (exponent: 4 bits, mantissa: 3 bits)
  • float_ue8m0_t: 8bits unsigned float (exponent: 8 bits, mantissa: 0 bits)
  • float_e3m2_t: 6bits signed float (exponent: 3 bits, mantissa: 2 bits)
  • float_e2m3_t: 6bits signed float (exponent: 2 bits, mantissa: 3 bits)
  • float_e2m1_t: 4bits signed float (exponent: 2 bits, mantissa: 1 bits)
  • type_erased_dynamic_float8_t: Type agnostic 8 bits signed float allowing the user to provide a specific datatype as runtime argument.
  • type_erased_dynamic_float6_t: Type agnostic 6 bits signed float allowing the user to provide a specific datatype as runtime argument.
  • type_erased_dynamic_float4_t: Type agnostic 4 bits signed float allowing the user to provide a specific datatype as runtime argument.
  • mx_float8_t<float_e5m2_t> or mx_float8_t<float_e4m3_t> : Block scaled data type with fp8 element type and float_ue8m0_t scale factor and vector size of 32.
  • mx_float6_t<float_e3m2_t> or mx_float6_t<float_e2m3_t> : Block scaled data type with fp6 element type and float_ue8m0_t scale factor and vector size of 32.
  • mx_float4_t<float_e2m1_t> : Block scaled data type with signed e2m1 element type and float_ue8m0_t scale factor and vector size of 32.
  • nv_float4_t<float_e2m1_t> : Block scaled data type with signed e2m1 element type and float_ue4m3_t scale factor and vector size of 16.
  • complex<T>: defines complex-valued data type based on the supplied real-valued numeric type

Numeric types in CUTLASS may be used in both host and device code and are intended to function
like any other plain-old-data type.

If CUTLASS is compiled with CUTLASS_F16C_ENABLED, then hardware conversion is used for
half-precision types in host code. Regardless, cutlass::half_t uses the most efficient
NVIDIA GPU hardware instructions available in device code.

Example:

c++
#include <iostream>
#include <cutlass/numeric_types.h>

__global__ void kernel(cutlass::half_t x) {
  printf("Device: %f\n", float(x * 2.0_hf));
}

int main() {

  cutlass::half_t x = 0.5_hf;

  std::cin >> x;

  std::cout << "Host: " << 2.0_hf * x << std::endl;

  kernel<<< dim3(1,1), dim3(1,1,1) >>>(x);

  return 0;
}

Containers

CUTLASS uses the following containers extensively for implementing efficient CUDA kernels.

Array

c++
template <
  typename T,       // element type
  int N             // number of elements
>
struct Array;

Array<class T, int N> defines a statically sized array of elements of type T and size N. This class is similar to
std::array<> in the Standard Library with one notable exception:
partial specializations exist to pack or unpack elements smaller than one byte.

Array<> is intended to be a convenient and uniform container class to store arrays of numeric elements regardless of data type or vector length. The storage needed is expected to be the minimum necessary given the logical size of each numeric type in bits (numeric types smaller than one byte are densely packed). Nevertheless, the size reported by sizeof(Array<T, N>) is always an integer multiple of bytes.

Storing numeric elements in a C++ STL-style container class enables useful modern C++ mechanisms such as range-based for loops. For example, to print the elements of Array<>, the following range-based for loop syntax is always valid regardless of numeric data type, compute capability, or context in host or device code.

Example:

c++
int const kN;
Array<T, kN> elements;

CUTLASS_PRAGMA_UNROLL                        // required to ensure array remains in registers
for (auto x : elements) {
  printf("%d, %f", int64_t(x), double(x));   // explictly convert to int64_t or double
}

When copying Array<> objects or passing them as arguments to methods, it is best to avoid accessing individual elements. This enables the use of vector instructions to perform the operation more efficiently. For example, setting all elements to zero is best performed by calling the clear() method. Copies should be performed by assigning the entire object.

Example:

c++
#include <cutlass/array.h>

int const kN;
Array<T, kN> source;
Array<T, kN> destination;

source.clear();         // set all elements to value of zero

destination = source;   // copy to `destination`

Array<> may be used to store elements smaller than one byte such as 4b integers.

c++
Array<int4b_t, 2> packed_integers;

static_assert(
  sizeof(packed_integers) == 1,
 "Packed storage of sub-byte data types is compact.");

// Access array elements using usual indirection and assignment operators
packed_integers[0] = 2_s4;
packed_integers[1] = 3_s4;

CUTLASS_PRAGMA_UNROLL
for (auto x : elements) {
  printf("%d", int(x));       // access elements normally
}

AlignedArray

c++
template <
  typename T,          // element type
  int N,               // number of elements
  int Alignment        // alignment requirement in bytes
>
class AlignedArray;

AlignedArray is derived from Array<T, N> and supports an optional alignment field. Pointers to objects of type AlignedArray<> reliably yield vectorized memory accesses when dereferenced.

Example:

c++
int const kN = 8;
ArrayAligned<half_t, kN> source;
ArrayAligned<half_t, kN> const *ptr = ...;

source = *ptr;          // 128b aligned memory access

AlignedBuffer

c++
template <
  typename T,          // element type
  int N,               // number of elements
  int Alignment        // alignment requirement in bytes
>
class AlignedBuffer;

AlignedBuffer provides a uniform way to define aligned memory allocations for all data types. This is particularly
useful in defining allocations within shared memory with guaranteed memory alignment needed for vectorized access.
Note, constructors of the elements within AlignedBuffer<> are not called, and so the elements are initially in an
undefined state.

Use AlignedBuffer<>::data() to obtain a pointer to the first element of the buffer.

Example: Guaranteed aligned shared memory allocation. Note, shared memory contents are uninitialized.

c++
int const kN = 32;
int const kAlignment = 16;                  // alignment in bytes

// Define a shared memory allocation in device code
__shared__ AlignedBuffer<complex<half_t>, kN, kAlignment> matrix_tile;

complex<half_t> *ptr = matrix_tile.data();  // ptr is guaranteed to have 128b (16 Byte) alignment

Note, AlignedBuffer<> only guarantees that its internal memory allocation is aligned, obtained by AlignedBuffer<>::data(). There is no guarantee that the AlignedBuffer<> object itself satisfies alignment constraints or that its internal memory allocation is contiguous. Device code performing vectorized memory accesses should use the AlignedArray<> type.

Example: Vectorized memory access to shared memory allocations.

c++
int const kN = 1024;

__shared__ AlignedBuffer<half_t, kN> smem_buffer;

AlignedArray<half_t, 8> *ptr = reinterpret_cast<AlignedArray<half_t, 8> *>(smem_buffer.data());

AlignedArray<half_t, 8> x = ptr[threadIdx.x];     // 128b shared memory load

Numeric Conversion

CUTLASS defines procedures for performing numeric conversion between data types in cutlass/numeric_conversion.h.
Where possible, these target hardware acceleration on the target architecture and support multiple rounding modes.

c++
#include "cutlass/numeric_conversion.h"
#include "cutlass/numeric_types.h"

NumericConverter<half_t, float>     convert_f32_to_f16;
NumericConverter<tfloat32_t, float> convert_f32_to_tf32;

half_t     x = convert_f32_to_f16(3.14159f);
tfloat32_t y = convert_f32_to_tf32(3.14159f);

Recent GPU architectures such as NVIDIA Turing and Ampere combine numeric conversion with efficient packing
into bit vectors. Consequently, CUTLASS defines conversion on both scalars and Array<> objects to implement
the optimal code sequence on all architectures.

c++
//
// Example: convert and pack 32b signed integers to a vector of packed signed 8-bit integers.
//
int const kN = 16;
Array<int8_t, kN> destination;
Array<int,    kN> source;

NumericConverter<descltype(destination), decltype(source)> convert;

destination = convert(source);

Coord

c++
template <
  int Rank,
  typename Index = int
>
class Coord;

Coord<Rank, class T = int> is a container used explicitly for defining logical coordinates in tensors of known rank. Traditional vector operators are defined such as +, -, and scalar multiplication * to simplify the creation of vector-valued expressions on tensor coordinates.

Example: Vector operations on coordinates.

c++
Coord<2> compute_offset(Coord<2> const & base) {
  
  Coord<2> stride = make_Coord(1, kM);

  return base + stride * make_Coord(threadIdx.x, threadIdx.y); 
}

Instances of Coord<> are used throughout CUTLASS to compute indices into tensors. Frequently, the dimensions of tensors of known layouts may be given names such as "rows" or "columns". To clarify the code, we have implemented several classes derived from Coord<> with accessors for each coordinate member.

Such classes include:

c++
struct MatrixCoord : public Coord<2> {
  Index & row();
  Index & column();
};

and

c++
struct Tensor4DCoord : public Coord<4> {
  Index & n();
  Index & h();
  Index & w();
  Index & c();
};

PredicateVector

PredicateVector contains a statically sized array of hardware predicates packed into registers to enable efficient access within unrolled loops.

This container is optimized for sequential access through iterators, though these are only efficient when used within fully unrolled loops.

Moreover, instances of PredicateVector<> are not guaranteed to be updated until any non-const iterator objects have gone out of scope. This is because iterators are effectively caches that update the PredicateVector<> instance's internal storage as a batch.

Example: Managing an array of predicates.

c++
unsigned mask;
PredicateVector<kBits> predicates;

// Nested scope to update predicates via an iterator
{
  auto pred_it = predicates.begin();

  CUTLASS_PRAGMA_UNROLL
  for (int bit = 0; bit < kBits; ++bit, ++pred_it) {
    bool guard = (mask & (1u << bit));
    pred_it.set(guard);
  }
}

// Efficient use of predicates to guard memory instructions
T *ptr;
Array<T, kAccesses> fragment;

auto pred_it = predicates.const_begin();
for (int access = 0; access < kAccesses; ++access, ++pred_it) {
  if (*pred_it) {
    fragment[access] = ptr[access];
  }
}

Note: PredicateVector<> is not efficient when accessed via dynamic random access. If an array of bits is needed with dynamic random access (in contrast with access via constexpr indices), then Array should be used instead.

Functional

CUTLASS defines function objects corresponding to basic arithmetic operations modeled after C++ Standard Library's <functional> header.

CUTLASS extends this by defining multiply_add<T> which computes d = a * b + c. The partial specialization multiply_add<complex<T>> computes complex-valued multiplication and addition using four real-valued multiply-add operations; these may correspond to native hardware instructions.

Example:

c++
complex<float> a;
complex<float> b;
complex<float> c;
complex<float> d;

multiply_add<complex<float>> mad_op;

d = mad_op(a, b, c);    // four single-precision multiply-add instructions

CUTLASS defines partial specializations for type Array<T, N>, performing elementwise operations on each element. A further partial specialization for Array<half_t, N> targets may target native SIMD instructions for compute capability SM60 and beyond.

Example: Fused multiply-add of arrays of half-precision elements.

c++
static int const kN = 8;

Array<half_t, kN> a;
Array<half_t, kN> b;
Array<half_t, kN> c;
Array<half_t, kN> d;

multiply_add<Array<half_t, kN>> mad_op;

d = mad_op(a, b, c);   // efficient multiply-add for Array of half-precision elements

Numeric Conversion

Operators are define to convert between numeric types in numeric_conversion.h. Conversion operators are defined in
terms of individual numeric elements and on arrays which enable the possibility of efficient hardware
support on current and future NVIDIA GPUs.

Example: Converting between 32-b and 8-b integers.

c++

Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Gemm Api (`media/docs/cpp/gemm_api.md`)

CUTLASS GEMM API

CUTLASS presents a uniform programming model for matrix multiply-accumulate operations at each level of the hierarchy. This document
focuses on device-level, threadblock-level GEMMs, warp-level GEMMs, thread-level GEMMs, and instruction-level GEMMs.

CUTLASS GEMM Model

CUTLASS implements the basic GEMM triple loop nest with a tiled structure mirroring the execution model hierarchy.

The following pseudocode describes the model for a GEMM kernel targeting a warp-synchronous matrix multiply instruction like
mma.sync. The entire operation is referred to as "Gemm," as it is assumed that an epilogue operation performs the general matrix
update similar to BLAS.

text
/* Detailed source-code truncated for AI context efficiency. */

The outer-most loops correspond to CTA-level hardware concurrency and are not explicitly written as loops in the code. These
are implied by CUDA grid launch semantics.

The comment cutlass::gemm::threadblock::Mma refers to the threadblock-scoped matrix multiply-accumulate concept. This is
the computation performed by one threadblock to compute a matrix product in registers. The "GEMM main loop" is listed.

The comment cutlass::gemm::warp::Mma refers to the computation performed by each warp. This is a nested loop executing a
sequence of accumulated outer products.

The inner-most operation corresponds directly to hardware support. In this example, the nested structure terminates with
warp-synchronous matrix multiply instructions targeting Tensor Cores.
Alternatively, GEMMs targeting single-thread instructions may have an additional series of nested loops corresponding to
thread-level concurrency.

CUTLASS GEMM Components

This loop nest is expressed in CUTLASS via the following components which are specialized for data type, layout, and
math instruction.

These components are described in the following sections.

Device-wide GEMM API

The device-level GEMM API is intended to streamline instantiation and execution of the standard
GEMM computation across the GPU. This operator is intended to be used in host-side .cu code and
has semantics similar to cuBLAS.

The device-wide GEMM API is embodied by the following operators:

Example: launch a mixed-precision GEMM targeting Volta Tensor Cores.

c++
using Gemm = cutlass::gemm::device::Gemm<
    cutlass::half_t,                           // ElementA
    cutlass::layout::ColumnMajor,              // LayoutA
    cutlass::half_t,                           // ElementB
    cutlass::layout::ColumnMajor,              // LayoutB
    cutlass::half_t,                           // ElementOutput
    cutlass::layout::ColumnMajor,              // LayoutOutput
    float,                                     // ElementAccumulator
    cutlass::arch::OpClassTensorOp,            // tag indicating Tensor Cores
    cutlass::arch::Sm70                        // tag indicating target GPU compute architecture
  >;

  Gemm gemm_op;
  cutlass::Status status;
 
  //
  // Launch GEMM on the device
  //
 
  status = gemm_op({
    {m, n, k},
    {ptrA, lda},
    {ptrB, ldb},
    {ptrC, ldc},
    {ptrD, ldd},
    {alpha, beta}
  });

  if (status != cutlass::Status::kSuccess) {
    return -1;
  }

Threadblock-level GEMM API

GEMMs at this scope are expected to efficiently load tiles of data from global memory into internal storage and then compute matrix
products with warp-level GEMM operators.

The threadblock-scoped matrix multiply operation is embodied by
cutlass::gemm::threadblock::MmaPipelined.
This is a class inspired by std::transform_reduce()
which computes the accumulated matrix product of a range of tiles defined by tile iterators.

In the case of GEMM, the tile iterators are
cutlass::transform::threadblock::PredicatedTileIterator
to traverse a sequence of tiles in global memory with appropriate predication to avoid out-of-bounds
memory accesses.

Concept. Threadblock-level matrix multiply accumulate operators are function objects satisfying the following concept.

text
/* Detailed source-code truncated for AI context efficiency. */

Warp-level Matrix Multiply API

Warp-level GEMM operators load tiles from shared memory into registers and then compute matrix multiplies using either
Tensor Cores or CUDA Cores. The result is accumulated in a register tile. Iterators are defined for each
operand A, B, and C.

The warp-level GEMM API is a generalization of CUDA's WMMA API to achieve the following objectives:

  • native matrix multiply sizes of Tensor Cores
  • permuted shared memory layouts to ensure conflict-free accesses
  • pointer initilization outside of the mainloop
  • efficient traversal

Defining a warp-level matrix multiply in CUTLASS is similar to WMMA as shown below.

The usage model is also similar. The following example computes a warp-level GEMM operation,
accumulating a series of matrix products in a register-backed array. The input to a warp-level
GEMM operation in CUTLASS must be data in shared memory loaded by iterators or on
register-backed fragments.

text
/* Detailed source-code truncated for AI context efficiency. */

Concept. Warp-level Mma operations are function objects satisfying the following concept.

text
/* Detailed source-code truncated for AI context efficiency. */

Tensor Core Operators. Warp-level matrix multiply operators targeting Tensor Cores
may be defined with the following template arguments. The Policy type specifies implementation-level details which may
be used to affect performance or internal implementation of the warp-level operator.

c++
namespace cutlass {
namespace gemm {
namespace warp {

/// Structure to compute the matrix product targeting CUDA cores and SIMT math instructions.
template <
  /// Size of the Gemm problem - concept: gemm::GemmShape<>
  typename Shape_,
  /// Data type of A elements
  typename ElementA_,
  /// Layout of A matrix (concept: MatrixLayout)
  typename LayoutA_,
  /// Data type of B elements
  typename ElementB_,
  /// Layout of B matrix (concept: MatrixLayout)
  typename LayoutB_,
  /// Element type of C matrix
  typename ElementC_,
  /// Layout of C matrix (concept: MatrixLayout)
  typename LayoutC_,
  /// Shape of the warp in units of thread (concept: MmaSimtPolicy)
  typename Policy_,
  /// Used for partial specialization
  typename Enable = bool
>
class MmaTensorOp {}

} // namespace warp
} // namespace gemm
} // namespace cutlass

SIMT Math Instructions. Warp-level matrix multiply operators targeting CUDA Cores
may be defined with the following template arguments. The Policy type specifies implementation-level details which may
be used to affect performance or internal implementation of the warp-level operator.

c++
/// Structure to compute the matrix product targeting CUDA cores and SIMT math instructions.
template <
  /// Size of the Gemm problem - concept: gemm::GemmShape<>
  typename Shape_,
  /// Data type of A elements
  typename ElementA_,
  /// Layout of A matrix (concept: MatrixLayout)
  typename LayoutA_,
  /// Data type of B elements
  typename ElementB_,
  /// Layout of B matrix (concept: MatrixLayout)
  typename LayoutB_,
  /// Element type of C matrix
  typename ElementC_,
  /// Layout of C matrix (concept: MatrixLayout)
  typename LayoutC_,
  /// Shape of the warp in units of thread (concept: MmaSimtPolicy)
  typename Policy_,
  /// Used for partial specialization
  typename Enable = bool
>
class MmaSimt;

Thread-level GEMM API

Thread-level GEMM operations perform matrix multiply-accumulate on data held in registers. These target CUDA Cores exclusively.

Concept. Thread-level matrix multiply operations are function objects satisfying the following concept.

c++
struct Mma {

  /// Shape of warp-level matrix operation (concept: GemmShape)
  struct Shape;

  /// Data type of multiplicand A (concept: numeric type)
  struct ElementA;

  /// Layout of multiplicand A (concept: Layout)
  struct LayoutA;

  /// Fragment object loaded from IteratorA (concept: Array<ElementA, ..>)
  struct FragmentA;

  /// Data type of multiplicand B (concept: numeric type)
  struct ElementB;

  /// Layout of multiplicand B (concept: Layout)
  struct LayoutB;

  /// Fragment object loaded from IteratorA (concept: Array<ElementB, ..>)
  struct FragmentB;

  /// Data type of accumulator matrix C (concept: numeric type)
  struct ElementC;

  /// Layout of accumulator matrix C (concept: Layout)
  struct LayoutC;

  /// Fragment object loaded from IteratorA (concept: Array<ElementC, ..>)
  struct FragmentC;

  //
  // Methods
  //

  /// Computes a matrix multiply-accumulate
  CUTLASS_DEVICE
  void operator()(
    FragmentC &D, 
    FragmentA const &A, 
    FragmentB const &B, 
    FragmentC const &C);
};

The CUTLASS thread-level GEMM template accepts the following template arguments.

c++
namespace cutlass {
namespace gemm {
namespace thread {

/// Structure to compute the matrix product
template <
  /// Size of the Gemm problem - concept: gemm::GemmShape<>
  typename Shape,
  /// Data type of A elements
  typename ElementA,
  /// Layout of A matrix (concept: MatrixLayout)
  typename LayoutA,
  /// Data type of B elements
  typename ElementB,
  /// Layout of B matrix (concept: MatrixLayout)
  typename LayoutB,
  /// Element type of C matrix
  typename ElementC,
  /// Layout of C matrix (concept: MatrixLayout)
  typename LayoutC,
  /// Concept: arch::OpMultiplyAdd or arch::Mma<>
  typename Operator = arch::OpMultiplyAdd,
  /// Used for partial specialization
  typename Enable = bool
>
struct Mma;

} // namespace thread
} // namespace gemm
} // namespace cutlass

Efficient Epilogue

CUTLASS GEMM operators perform mma followed by epilogue operation similar
to cuBLAS. CUTLASS implements an efficient row-major epilogue. Thus, to achieve
column-major GEMM, operands A & B are transposed and swapped.

To enable efficient row-major epilogue for both row-major and column-major output layout,
CUTLASS' device-level GEMM operators cutlass::device::Gemm and cutlass::device::GemmUniversal
provide two template definitions:

Efficient row-major epilogue for:

  • (i) GEMM operator on row-major source/output uses template (a). It runs row-major GEMM and
    an efficient row-major epilogue.
  • (ii) GEMM operator on column-major source/output uses template (b). It transposes and swaps
    operands A and B to enable efficient epilogue. A x B = C => Transpose(B) x Transpose(A) = Transpose(C).
    For column-major source (C) matrix, Transpose(C) is row-major, and efficient epilogue works on
    row-major.

Note that cuBLAS typically expects a column-major source (C) and output matrix (D). Thus,
CUTLASS library only instantiates and generates GEMM operatos with column-major layout. However,
CUTLASS by itself can run both row-major and column-major output layouts for all combinations
of input layouts. Thus, CUTLASS supports the following layout combinations for input and output layouts:

  • {N,T} x {N,T} => {N,T} - NN, TN, TN, TT GEMM for both row-major and column-major output

Instruction-level operations

CUTLASS defines a template-based interface to Tensor Core operations to avoid resorting
to inline PTX.

Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Gemm Api 3x (`media/docs/cpp/gemm_api_3x.md`)

CUTLASS 3.0 GEMM API

CUTLASS presents a uniform programming model
for matrix multiply-accumulate (MMA) operations
at different levels of the GPU system hierarchy.
CUTLASS 3.0 has GEMM APIs corresponding to the following levels
in order of highest to the lowest level.

  1. Device
  2. Kernel
  3. Collective
  4. Tiled MMA and Copy
  5. Atom

This document will cover the first three levels in detail:
Device, Kernel, and Collective.
It also briefly discusses the Tiled MMA/Copy and Atom level,
and then refers readers to CuTe's tutorial for more information.

CUTLASS GEMM Model

CUTLASS implements algorithms that express
the classical "triply nested loop" GEMM algorithm
with a tiled structure mirroring the above hierarchy.

The following pseudocode describes the model for a GEMM kernel
targeting a warp-synchronous matrix multiply instruction like mma.sync.
The entire operation is referred to as "Gemm,"
as it is assumed that an epilogue operation
performs the general matrix update similar to BLAS.
This is pseudocode and is only meant to illustrate which parts of the layers
correspond to the inner or outer loops of the GEMM.

c++
// cutlass::gemm::kernel::GemmUniversal: ClusterTileM and ClusterTileN loops
//   are either rasterized by the hardware or scheduled by the kernel in persistent kernels.
// Parallelism over thread block clusters
for (int cluster_m = 0; cluster_m < GemmM; cluster_m += ClusterTileM) {
  for (int cluster_n = 0; cluster_n < GemmN; cluster_n += ClusterTileN) {

    // cutlass::gemm::collective::CollectiveMma: mainloop that iterates over all k-tiles
    // No loop unrolling is performed at this stage
    for (int k_tile = 0; k_tile < size<2>(gmem_tensor_A); k_tile++) {

      // loops inside cute::gemm(tiled_mma, a, b, c); Dispatch 5: (V,M,K) x (V,N,K) => (V,M,N)
      // TiledMma uses the hardware instruction provided through its Mma_Atom
      // TiledMma's atom layout, value layout, and permutations define the iteration order
      for (int tiled_mma_k = 0; tiled_mma_k < size<2>(A); tiled_mma_k++) {
        for (int tiled_mma_m = 0; tiled_mma_m < size<1>(A); tiled_mma_m++) {
          for (int tiled_mma_n = 0; tiled_mma_n < size<1>(B); tiled_mma_n++) {

            // TiledMma's vector mode dispatches to the underlying instruction.
            mma.call(d, a, b, c);
          } // tiled_mma_n
        } // tiled_mma_m
      } // tiled_mma_k
    } // k_tile mainloop
  } // cluster_m
} // cluster_n

The first three nested for loops
correspond to parallelism over thread block clusters.
The code does not actually express them as explicit for loops.
Instead, the parallelization scheme over tiles
is implied by CUDA grid launch semantics.
However, for persistent kernels,
these three loops are expressed in the source code
as a single while loop that queries the
work tile scheduler
for problem tiles on which to compute.

Inside the three nested for loops,
one finds code that pulls matrix tiles
from global memory into more "local" memory
(like shared memory or registers)
and computes MMAs.
These tiled copy and tiled mma iterations are generally
fully static and get fully unrolled.

CUTLASS GEMM Components

CUTLASS expresses the above loop nest
with the following components which are specialized for
data type, layout, and math instruction.

API level API Class and/or function names
Device cutlass::gemm::device::GemmUniversalAdapter
Kernel cutlass::gemm::kernel::GemmUniversal
Collective cutlass::gemm::collective::CollectiveMma cutlass::epilogue::collective::DefaultEpilogue cutlass::epilogue::collective::Epilogue
Tiled (MMA and Copy) cute::TiledMma and cute::TiledCopy cute::gemm() and cute::copy()
Atom cute::Mma_Atom and cute::Copy_Atom

In CUTLASS 3.0, we assemble kernels
by first composing a collective mainloop and collective epilogue
together at the kernel layer,
and then wrapping them with a host-side adapter
to form a GEMM handle to that kernel.

The following sections describe these components
in the order a user should instantiate them
in order to assemble a kernel. This order is

  1. assemble the required collective mainloop and epilogues,

  2. compose them together to build a kernel type, and

  3. wrap up the kernel with a device layer adapter.

This order is also reflected in the CUTLASS 3.0 Hopper kernel examples as seen in the excerpt below.

c++
// Step 1: Generate the required collective layer mainloop specialization
using CollectiveMainloop = typename cutlass::gemm::collective::CollectiveBuilder<
    ArchTag, OperatorClass,
    ElementA, LayoutA, AlignmentA,
    ElementB, LayoutB, AlignmentB,
    ElementAccumulator,
    TilesShape, ClusterShape,
    cutlass::gemm::collective::StageCountAuto,
    cutlass::gemm::collective::KernelScheduleAuto
  >::CollectiveOp;

// Step 2: Specify the collective layer epilogue type
using CollectiveEpilogue = cutlass::epilogue::collective::DefaultEpilogue<
    ElementC,
    cutlass::gemm::TagToStrideC_t<LayoutC>,
    cutlass::gemm::TagToStrideC_t<LayoutC>,
    cutlass::epilogue::thread::LinearCombination<ElementC, 1, ElementAccumulator, ElementAccumulator>>;

// Step 3: Compose the mainloop and epilogue together at the kernel layer
using GemmKernel = cutlass::gemm::kernel::GemmUniversal<
    cute::Shape<int,int,int,int>, // ProblemShape [M,N,K,L]
    CollectiveMainloop,
    CollectiveEpilogue
>;

// Step 4: Wrap up the kernel::GemmUniversal kernel class
// with the device adapter to obtain a host-side handle to the kernel
using GemmHandle = cutlass::gemm::device::GemmUniversalAdapter<GemmKernel>;

Towards the end, we also briefly cover CuTe's tiled mma and copy as well as the atom layer APIs,
before redirecting users to CuTe-specific documentation for further details.

Collective API

A Collective is "the largest collection of threads
onto which mma atoms and copy atoms are tiled."
That is, it is the largest number of threads in a grid
that can cooperate by leveraging hardware features
for accelerated communication and synchronization.
These hardware features include

  • asynchronous array copy
    (e.g., from global memory to shared memory);

  • MMA instructions
    for small tiles that live in shared memory;

  • synchronization operations for clusters,
    thread blocks, and/or warps; and/or

  • hardware acceleration (such as barriers)
    for ensuring that data dependencies
    between asynchronous operations are met.

A Collective uses the TiledMma and TiledCopy API (see below)
to access operations that copy and perform MMA on tiles.

Different units of parallelism
(e.g., threads, warps, or thread blocks)
in a Collective might have different roles.
For example, in "warp-specialized" algorithms,
some warps may be responsible for copying data,
while others may be responsible for computation.
Nevertheless, the different units of parallelism
still need to share data and coordinate access
to the shared data. For example,
the producer warps in a warp-specialized algorithm
that copy input matrix tiles into shared memory
need to let the consumer MMA warp(s) know
that their MMA inputs are ready.
We contrast this with the kernel:: layer API,
which schedules the collectives over independent tiles in the grid.

The Collective API includes both the "mainloop"
of matrix multiply-accumulate, and the epilogue.
This API is the composition point for optimizations
such as mainloop fusions and epilogue fusions.
It is responsible for implementing
the k_tile loop in the above triply nested loop pseudocode.

Collective Mainloops

The cutlass::gemm::collective::CollectiveMma class
is the primary interface to the collective
matrix multiply-accumulate (MMA) mainloops.
"Mainloop" refers to the "main loop" over tiles --
the "cluster tile k" loop in the pseudocode
near the top of this document.
Any looping over multiple tiles that
the algorithm might need to do would happen here.

The CollectiveMma class is declared in the header
cutlass/gemm/collective/collective_mma.hpp.

c++
namespace cutlass::gemm::collective {

template <
  class DispatchPolicy,
  class TileShape,
  class ElementA,
  class StrideA,
  class ElementB,
  class StrideB,
  class TiledMma,
  class GmemTiledCopyA,
  class SmemLayoutAtomA,
  class SmemCopyAtomA,
  class TransformA,
  class GmemTiledCopyB,
  class SmemLayoutAtomB,
  class SmemCopyAtomB,
  class TransformB
>
struct CollectiveMma {
  static_assert(sizeof(ElementA) == 0, "Could not find a mainloop specialization.");
};

} // namespace cutlass::gemm::collective
  • DispatchPolicy is the most important type for a collective, and is
    covered in more detail below.

  • StrideA and StrideB are instances of type cute::Stride that represent the global memory layout of A and B tensors. These strides are required to be rank-3, representing the modes [outer, inner, batch]. Each of the 3 ranks can be a multi-modal hierarchical stride; this would apply if implementing a tensor contraction.

  • TiledMma is an instance of cute::TiledMma.

  • GmemTiledCopyA and GmemTiledCopyB are instances of cute::TiledCopy types. Both tiled operation types are covered in more detail below.

  • SmemLayoutAtomA and SmemLayoutAtomB are instances of type cute::Layout and represent the smallest
    layout that will get tiled over the entire collective's shared memory. This layout does not include the
    pipeline mode, and therefore, both are expected to be rank 2 layouts of shape [outer, inner].

  • SmemCopyAtomA and SmemCopyAtomB are Copy_Atoms to be used for moving data from shared memory
    into register memory.

Notice that CUTLASS 3.0 mainloops do not accept a dedicated accumulator element type.
We obtain the accumulator type from the typename TiledMma::ValTypeC. Note also that
top level API's ElementA and ElementB can differ from those of the MMA facing
typename TiledMma::ValTypeA and typename TiledMma::ValTypeB, allowing TMA or user
supplied transform operations to perform type conversions.

Collective Dispatch Policies

CollectiveMma implementations are not generic.
Instead, they must be specialized for each algorithm and GPU architecture.
Users can dispatch to a CollectiveMma specialization
by picking template arguments matching that specialization.
CUTLASS 3.0 adopts a tag-based dispatch policy type to specialize
mainloop implementations and add tuning options to them.

Below is an example of one of the dispatch policies that is used to dispatch to a Hopper TMA
warp-specialized mainloop implementation:

c++
// n-buffer in smem (Hopper TMA),
// pipelined with Hopper GMMA and TMA,
// warp-specialized dynamic schedule
template<
  int Stages_,
  class ClusterShape_ = Shape<_1,_1,_1>,
  class KernelSchedule = KernelTmaWarpSpecializedCooperative
>
struct MainloopSm90TmaGmmaWarpSpecialized {
  constexpr static int Stages = Stages_;
  using ClusterShape = ClusterShape_;
  using ArchTag = arch::Sm90;
  using Schedule = KernelSchedule;
};

The Stages_ template parameter lets the user freely vary the number of pipeline stages,
while the ClusterShape_ type allows for parameterization over the shape of the threadblock
cluster over which TMA multicast will take place.

The collective dispatch policy is also the primary point of composing various kernel schedules
freely with any mainloop. Each mainloop policy either prescribes a Schedule with which
it needs to be run, or exposes a template API that lets the user pick a subset of the following schedules:

c++
struct KernelCpAsyncWarpSpecialized { };
struct KernelCpAsyncWarpSpecializedPingpong { };
struct KernelCpAsyncWarpSpecializedCooperative { };
struct KernelTma { };
struct KernelTmaWarpSpecialized { };
struct KernelTmaWarpSpecializedPingpong { };
struct KernelTmaWarpSpecializedCooperative { };
  • A single kernel schedule can support multiple mainloop implementations. For example,
    KernelMultistage can be composed with many different mainloop implementations across GPU
    architectures such as MainloopSm70TwoStage, MainloopSm80CpAsyncUnpredicated, and many more.

  • A single mainloop can be composed with multiple
    possible kernel schedules. For example, the MainloopSm90TmaGmmaWarpSpecialized can be
    composed with any of the KernelTmaWarpSpecialized, KernelTmaWarpSpecializedPingpong or KernelTmaWarpSpecializedCooperative
    kernel schedules.

As discussed in the CUTLASS 3.0 design documentation, adopting tag
dispatch policies for our core vocabulary types allows us to maintain a single type name for
all operations that conceptually belong to the same class. This design has the following benefits.

  • It avoids code duplication in cases where mainloops can be composed with multiple kernels or vice versa.
  • It makes writing generic code easier, as the primary type name CollectiveMma does not change across any implementation.
  • It provides a clear, singular extension point for users to plug in new, custom mainloops implementations specialized on their own dispatch policies.

Collective Builder for `CollectiveMma`s

The primary CollectiveMma is intended to be an expert user interface that allows full control over
all the properties of the collective's GPU micro-kernel. However, often a user just wants an
off-the-shelf GEMM mainloop implementation parameterized on simple configuration parameters. CUTLASS 3.0
provides cutlass::gemm::collective::CollectiveBuilder for such scenarios.

c++
namespace cutlass::gemm::collective {
template <
  class ArchTag,
  class OpClass,
  class ElementA,
  class GmemLayoutA,
  int AlignmentA,
  class ElementB,
  class GmemLayoutB,
  int AlignmentB,
  class ElementAccumulator,
  class TileShape_MNK,
  class ClusterShape_MNK,
  class StageCountType,
  class KernelScheduleType,
  class Enable = void
>
struct CollectiveBuilder {
  static_assert(sizeof(ElementA) == 0, "Could not build a collective for given parameters.");
};
} // namespace cutlass::gemm::collective

CollectiveBuilder accepts CUTLASS 2.x equivalent input template arguments, and attempts to build
the best performing CollectiveMma from the given parameters.

  • ArchTag is one of the SM architectures tags from cutlass::arch::Sm*.
  • OpClass is one of the operator class tags from cutlass::arch::OpClass*.
  • ElementA and ElementB are the logical value types of the A resp. B tensors.
  • ElementAccumulator is the accumulator type to be used in the instruction.
  • GmemLayoutA and GmemLayoutB are CUTLASS 2.x layout tags, layout::RowMajor or layout::ColumnMajor.
  • AlignmentA and AlignmentB are global memory alignments of A and B tensors in terms of element count.
  • TileShape_MNK is an instance of cute::Shape that is rank-3, representing the MxNxK collective tile shape.
  • ClusterShape_MNK is an instance of cute::Shape that is rank-3, representing the MxNxK threadblock cluster tile shape.
  • StageCountType is either collective::StageCountAuto or an instance of collective::StageCount<N>.
  • KernelScheduleType is either collective::KernelScheduleAuto or one of the specific kernel schedule tags discussed in the dispatch policy section above.

StageCountAuto allows the collective builder to compute the size of a single stage's size in shared memory
and maximize the shared memory usage assuming 1 threadblock / multiprocessor occupancy.

KernelScheduleAuto allows the collective builder to pick the best kernel schedule available for the
given set of parameters, or let's the user override this with a specific kernel schedule type.

Note that collective builders are still in beta, and their functionality
does not map onto the full design space that the primary expert CollectiveMma API
allows for. We expect their supported mainloop types to expand in future releases, but
with 3.0, only SM90 tensorop kernels are supported through the builder API. The builder API
may also change in the future as we adopt user feedback.

If the builder is able to provide a collective mainloop type for the given set of parameters,
it will be aliased within as CollectiveOp. For more information on how to
parameterize kernels conveniently with the collective builder, please see example 49_hopper_gemm_with_collective_builder.

Epilogue

The collective epilogue implements element-wise operations
involving the output matrix. Users can provide a custom
epilogue, or use one of the standard epilogues.
These live in the directory
include/cutlass/epilogue/collective/,
and include classes like
cutlass::epilogue::collective::DefaultEpilogue
and
cutlass::epilogue::collective::Epilogue.
CUTLASS's provided collective epilogues
do not live under include/cutlass/gemm
or in the cutlass::gemm namespace,
because they can be used for computations
other than GEMM.

Kernel API

The kernel is "a collection of all clusters in the grid."
The kernel layer schedules have four main responsibilities.

  • Ordering the execution of collectives within the kernel, performing any synchronization between that may be necessary
  • Marshalling the threads of a warp specialized schedules into their respective roles
  • Performing any necessary grid swizzling logic
  • Tiling the input tensors with the threadblock cluster value tile before invoking the collectives on them

The Kernel API is the entry point for a grid of thread blocks
that may or may not be organized in a cluster.
It is the composition point for fusing back-to-back GEMMs,
epilogues, and/or other operations.

The entry point API for CUTLASS 3.0 kernel is the class
cutlass::gemm::kernel::GemmUniversal, found in the header file
include/cutlass/gemm/kernel/gemm_universal.hpp.
GemmUniversal is a stateless universal device kernel
that implements GEMM as the composition of two parts:

  • a collective mainloop, and
  • a collective epilogue
cpp
namespace cutlass::gemm::kernel {
/*
 * Stateless universal device GEMM kernel type that treats GEMM as
 * a composition of a collective mainloop and a collective epilogue.
 *
 * Supports both the 2.x and 3.x APIs based on whether the first type is
 * a cute::tuple<> or not.
 * 2.x API implementation: cutlass/gemm/kernel/gemm_universal.h
 * 3.x API implementation: cutlass/gemm/kernel/gemm_*.hpp
 *
 * In the following declaration, the name preceding the 'Or' refers to
 * 3.x API type argument order, and the name succeeding the 'Or' refers to
 * 2.x API type argument order. Template arguments without two names
 * belong to the 3.x API only.
**/
template <
  class ProblemShapeOrThreadblockMma_, // (m, n, k) or (m, n, k, l)
  class CollectiveMainloopOrEpilogue_,
  class CollectiveEpilogueOrThreadblockSwizzle_,
  class TileScheduler_ = void,
  class Enable = void
>
class GemmUniversal;
} // namespace cutlass::gemm::kernel

Stateless means that the caller --
for example, the Device API described above --
manages the kernel's state.
The kernel just takes input and output parameters (Params).

Universal means that GemmUniversal works
for both CUTLASS 3.0 and 2.x interfaces
and across a broad range of kernel schedules.
If GemmUniversal's first template argument is a cute::Shape,
then GemmUniversal assumes that the remaining template arguments
implement the 3.0 APIs. Otherwise, GemmUniversal assumes that
the remaining template arguments implement the 2.x APIs.
Starting with CUTLASS 3.0, the problem shape has been promoted
to a top-level template API for the GEMM kernel.
This supports fully static GEMM instantiations
where the user expects to know some or all
of the problem shapes at compile time
in order to extract even more performance.

The collective mainloop implements MMA on local tiles.
The collective epilogue addresses any operations after the MMA,
such as applying the beta * C part of C := beta * C + alpha * A * B.
We will explain collective in more detail below.

Specializations of kernel::GemmUniversal for 3.0 APIs live in
any of various gemm_*.hpp files in the directory
include/cutlass/gemm/kernel/.
Specializations for 2.x APIs can be found in the header file
include/cutlass/gemm/kernel/gemm_universal.h.

CUTLASS 3.x implements various embodiments of kernel::GemmUniversal.
Each kernel layer schedule is specialized
for a GEMM scheduling algorithm and GPU architecture.
Specializations of kernel::GemmUniversal for 3.0 APIs live in
any of various include/cutlass/gemm/kernel/{arch_tag}*.hpp files in the directory
include/cutlass/gemm/kernel/.
Which specialization to dispatch to is decided through the dispatch policy's Schedule type.

For example, the header file
include/cutlass/gemm/kernel/sm90_gemm_tma_warpspecialized_pingpong.hpp
has a specialization of kernel::GemmUniversal for Hopper
that uses a warp-specialized mainloop with a persistent scheduling algorithm,
while the header file
include/cutlass/gemm/kernel/sm90_gemm_tma_warpspecialized.hpp
has a specialization of GemmUniversal for Hopper
that uses a warp-specialized but non-persistent algorithm.

To support composition between supported kernel schedules and mainloop dispatch policies without having to
duplicate collective mainloop implementations, GEMM kernel layer schedules can be composed with
any mainloop that specifies their corresponding kernel schedule as their Schedule type in the policy.
This is discussed in detail in the collective dispatch policy section above.

c++
// An example of the SM90 KernelMultistage kernel's
// specialization logic that allows it to be composed
// with many mainloops such as `MainloopSm80CpAsync`
// and `MainloopSm70TwoStage`.
template <
  class ProblemShape_,
  class CollectiveMainloop_,
  class CollectiveEpilogue_,
  class TileScheduler_
>
class GemmUniversal<
  ProblemShape_,
  CollectiveMainloop_,
  CollectiveEpilogue_,
  TileScheduler_,
  std::enable_if_t<std::is_base_of_v<KernelMultistage, typename CollectiveMainloop_::DispatchPolicy::Schedule>>>

Device API

The Device API is a universal, kernel-agnostic host interface
for kernel launch and managing the lifetime of
reusable host-side parameters.

This API is how users' host-side .cu code
invokes CUTLASS's single-GPU GEMM kernels.
It serves the same purpose as cuBLAS and behaves similarly.

The entry point for the Device GEMM API is the class
cutlass::gemm::device::GemmUniversalAdapter.
This class lives in the header file
include/cutlass/gemm/device/gemm_universal_adapter.h.
GemmUniversalAdapter is a stateful, reusable handle,
which is parameterized on the cutlass::gemm::kernel type.

c++
/*! 
  GemmUniversalAdapter is a stateful, reusable GEMM handle built around a kernel
  of type cutlass::gemm::kernel::*

  It manages the lifetime of the underlying `kernel::Params` struct, and exposes APIs
  to create it from the host facing arguments. For power users, new static methods
  are exposed in 3.x APIs that bypass the stateful methods or args->params lowering.

  It supports kernel types that implement both the 2.x and 3.0 APIs,
  however, this is done by specializing the implementation of GemmUniversalAdapter
  on the two kernel API types, and thus, GemmUniversalAdapter's behavior might
  differ between the two specializations.
*/
template <class GemmKernel_, class Enable = void>
class GemmUniversalAdapter;

Stateful means that the handle instance contains state
that the kernel needs to run.
This means that the user must initialize the handle first,
then use the initialized handle instance to run the kernel.
Statefulness also means that the handle can manage the lifetime
of the kernel's Params -- the parameters of the kernel itself.
An important duty of GemmUniversalAdapter
is to map from the user's Arguments --
what the user sees as the kernel's parameters --
to the Params that the kernel actually sees.
For power users, the class exposes new static methods
in 3.0 APIs that can bypass stateful methods
or go directly to Params without intermediate Arguments.

Reusable means that the handle instance can be used
to call the kernel multiple times with different arguments
(e.g., different matrices).
Reusing the handle may be more efficient than just
creating a new handle for each kernel invocation.

Parameterized on the kernel type means that
the GemmUniversalAdapter class' behavior
depends on the GEMM kernel type (see the next section).
Specifically, GemmUniversalAdapter has a template parameter
GemmKernel, which is the GEMM kernel type.
Valid template arguments for GemmKernel are

  • cutlass::gemm::kernel::GemmUniversal,
    implementing CUTLASS 3.x API kernels;
  • cutlass::gemm::kernel::GemmUniversal,
    implementing CUTLASS 2.x API kernels; or
  • Any valid CUTLASS 2.x kernel layer GEMM that
    was previously composable with the device::GemmUniversalAdapter.

GemmUniversalAdapter presents a single
host-side interface to both 3.0 and 2.x kernels.
CUTLASS accomplishes this by
specializing GemmUniversalAdapter's implementation
on either the 2.x API implementing kernel layer GEMMs, or on the 3.x API
implementing kernel layer GEMMs. The metafunction cutlass::gemm::detail::IsCutlass3GemmKernel
is what GemmUniversalAdapter uses to distinguish between 2.x and 3.x kernels.

GemmUniversalAdapter sets up and launches the kernel, using the
CUDA extended launch API for threadblock cluster support if required.
Note, GemmUniversalAdapter does not specify the grid shape.
The kernel controls the grid shape
and other kernel-specific launch parameters.
This makes it possible for all 3.0 kernels
to use the same kernel launch code,
thus factoring out kernel launch from the actual kernel.

Tiled MMA and Copy

The Tiled MMA or Copy are tilings of MMA atoms resp. Copy atoms
across threads and data, with possible permutations applied to the
resulting tiling. This layer is most analogous to the warp level
tiling of MMA instructions in CUTLASS 2.x. However, it views the tiling
from the perspective of all threads participating in the operation
and generalizes the concept to copy operations as well. The purpose
of this layer is to build composable GPU micro-kernels out of a plethora
of hardware accelerated math and data movement operations, each with their
unit layouts in threads and data. The tiled MMA and Copy types present
all these various hardware accelerated CuTe Atoms with a single, consistent
API.

The resulting tiled operation acts as a single MMA or copy operation
that users can invoke in the "inner" loop
of the three-nested-loops pseudocode
at the top of this document using cute::gemm() or cute::copy().

We call this API "tiled" because it constructs
larger operations out of the Atoms provided by CuTe,
as if fitting together individual tiles
to build a reusable component of a mosaic.
For example, CuTe might provide an MMA Atom
that users can call on a single warp,
for fixed M, N, and K dimensions.
CUTLASS can then use CuTe operations like make_tiled_mma
to turn this Atom into an operation
that works on an entire thread block,
for larger M, N, and K dimensions.

Atom API

An "Atom" is the smallest collection of threads and data
that must participate in the execution of a hardware-accelerated
math or copy operation.

An Atom is "atomic" (indivisible) not in the sense of
concurrent memory operations like atomicAdd
(which are "indivisible in time (causality)"),
but in the sense of indivisibility in "space" --
the number of values and the groups of parallel workers
that must participate in the operation together.

An Atom uses CuTe Layouts to express the required
dimensions and strides of its input and output arrays.
Generally these are fixed at compile time.

The Atom API wraps calls to actual hardware instructions
that accelerate MMA or copy operations.
Users can ask for GPU architecture-specific implementations,
or just pick generic implementations and rely on
whatever GPU architectures were enabled.

For more information about Atoms,
please refer to CuTe's tutorial, e.g., the sections on

Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Gemm Performance Measurement Methodology Guidelines (`media/docs/cpp/gemm_performance_measurement_methodology_guidelines.md`)

GEMM Performance Measurement Methodology Guidelines

Introduction

As linear algebraic computation - particularly general matrix multiplication (GEMM) - forms the computational backbone of modern artificial intelligence and machine learning applications, the need for precise and robust performance measurement methodologies has never been greater. Rapid advances in NVIDIA GPU architectures continually push the boundaries of hardware and algorithmic efficiency, making it crucial to benchmark GEMM operations with rigorous, reproducible standards. Reliable measurement frameworks not only enable fair comparisons between devices and software stacks but also illuminate subtle bottlenecks, guide system optimizations, and ensure that performance gains reflect true progress rather than artifacts of measurement inconsistency. This document outlines the methodology for measuring GEMM performance to ensure results are accurate, reproducible, and consistent across different NVIDIA devices, firmware, and software.

Benchmark Requirements

When writing or using a benchmark to profile GEMM performance on a GPU, several techniques must be encoded into the benchmark to ensure stable and accurate measurements.

  1. Separate warmup and profiling iteration loops

A user should be able to separately specify the number of warmup and profiling iterations. The warmup and profiling loops must be separated by calls to cudaProfilerStart/Stop, for use with external profilers, and/or calls to cudaEventRecord, for self-profiling benchmarks. See Warm-ups and Iterations below for recommendations on configuring the number of warmup and profiling iterations.

  1. Minimal gaps between kernel launches

There should be minimal overhead between kernel launches in both the warmup and profiling loops. All data buffers must be allocated and initialized before entering either loop. There must be no additional GPU work between kernel launches, this includes data allocations, deallocations, and copies as well as extraneous kernel launches. CPU work must be minimized so the CPU does not limit the kernel launch rate.

  1. Tensor buffer rotation

Special care must be taken to ensure that all data caches are "cold" at the start of each kernel iteration. To achieve this without reallocating data between iterations and violating the "Minimal gaps between kernel launches" requirement, benchmarks must pre-allocate multiple buffers for every memory surface used by the kernel and change buffers each iteration. See Buffer Rotation below for more details.

Testing Methodology Overview

  1. Test Type Specification:

There are two main types of tests that are commonly run when profiling GPU GEMM performance: fixed frequency tests and fixed power tests.

Fixed frequency tests run on the GPU at a predefined frequency and are useful to measure architectural and software efficiency characteristics. Precautions must be taken to ensure the chosen frequency can be sustained. When workloads run on the GPU, the computation and data movement to process the work encapsulated result in energy consumption and hence power. It is essential to adhere to the electrical and thermal constraints prescribed for the product, so power and thermal controllers may induce clock variability even when the user specifies a locked clock frequency. To mitigate this risk, a user should use zero-fill data to reduce power consumption and monitor the clock frequencies during testing to watch for variation. If clock frequency variation is observed, the user should select a lower frequency that can be sustained for their testing. Note this is not a set up for measuring the maximum performance of the product.

Fixed power tests run on the GPU with a given power limit and mimic real-world scenarios.

GPU products are regulated by power limits with dynamic voltage and frequency scaling (DVFS) enabled in real-world usage. On reaching the power limit, a throttling mechanism enables the clocks to adjust to operating points within the allowable power budget. This is the standard configuration for GPU products when they are operated as received from the factory. Fixed power tests are the most realistic, but they are also subject to a higher degree of run-to-run variation, device-to-device variation, and sensitivity to firmware versions. As such, fixed power tests must be meticulously controlled to ensure a stable testing environment (refer to Matrix Fill Methodology, Warmups and Iterations, and Input Buffer Rotation below).

  1. Data Collection:

For all tests, it is recommended to collect kernel durations with nsight-systems or CUDA events to calculate the achieved FLOPS/s. The GPC frequency should be profiled with nvidia-smi or NVML to monitor for clock variation and to identify the settled frequency.

For fixed frequency tests, monitor the GPC frequency to ensure that the selected frequency is maintained throughout execution. It is also recommended to profile SM utilization, DRAM and L2 utilization and read/write bytes, and LRC multicast rate where applicable.

For fixed power tests, monitor the GPC frequency, GPU power, and GPU temperature and calculate the average frequency, power, and temperature during execution. Use the profiled GPC frequencies to ensure that the clocks reached a stable frequency before the profiling iterations (see Warm-ups and Iterations below).

  1. Data Analysis:

Before conducting any data analysis, please use profiled metrics to validate that the benchmark follows best practices: 1) Use the L2 hit rate per-iteration to ensure that buffers are rotated appropriately (with nsight-compute it is recommended to use --cache-control none to examine the cache behavior of the benchmark without additional cache flushes imposed by the profiler); 2) Examine the profiled GPC frequencies to ensure that clocks are stable during testing iterations; 3) Examine the kernel launch times to ensure that there are minimal gaps between kernel iterations.

Compare the measured average runtime against the SOL runtime at the locked frequency (for fixed frequency tests) or the settled average clock frequency (for power-constrained tests).

a. For fixed frequency tests, compare the hardware utilization metrics against the SOL for the given problem. Although increased DRAM and L2 utilization over SOL may not affect the runtime performance at fixed frequency, it will cause higher power consumption and lower performance at fixed power.

b. For fixed power tests, compare the achieved FLOPS/s against the SOL at the settled frequency. Also compare the achieved FLOPS/s, settled GPC frequency, and power consumption to the SOL expectations.

Matrix Fill Methodology

Values used to fill GEMM matrices can significantly influence the level of power consumed during computation and hence the final performance achieved. This is because the data patterns present in these matrices directly affect activation patterns within the underlying hardware, such as switching activity in arithmetic units and memory subsystems. For instance, all-zero or low-entropy data may lead to decreased dynamic power draw, as fewer bits transition during operations, while random or high-entropy values typically result in more widespread switching and, consequently, higher power consumption. As a result, the choice of fill values not only shapes performance characteristics but also determines the representativeness and repeatability of power measurements in GEMM benchmarks.

While matrix fills can generally be generated from a variety of distributions, we prefer to use the following values for consistency and accuracy in the GEMM measurements:

Uniform Distribution:

For all data types, the range used is [-1, 1] floating point numbers

Scale factor types are unsigned, so the range used is [0, 1]

Fixed power tests are highly sensitive to the data fill pattern, particularly for narrow precisions. If possible, it is recommended to manually examine the tensor data to ensure that it matches the expected distribution.

Fixed frequency tests are not sensitive to the data fill pattern. It is recommended to use zero-fill which uses less power and makes it easier to sustain a locked frequency.

Warm-ups and Profiling Iterations

Warm-up iterations play a crucial role in benchmarking by priming the system to a consistent runtime state before measurements commence. When a workload is executed, initial runs may encounter cold address caches, uninitialized architectural resources and thermal or power states that have not yet stabilized. This transience can result in inconsistent or artificially variable runtimes, as the hardware and software stack adjust from an idle to an active state. Warm-ups allow components such as caches to be populated, memory pages to be mapped, and device clocks to reach a stable operating point, thereby eliminating artifacts of system initialization. Only after these transient effects dissipate does the system reach a quiescent, stable operating point - ensuring that the measurements collected reflect real application performance under steady-state conditions rather than the idiosyncrasies of the initial ramp-up.

To ensure accurate and stable measurements, the following warm-up and iteration procedures should be applied:

  1. Warm-up Iterations: a) Warm-up iterations are used to ensure that caches and TLBs are warm and to allow the power reading to stabilize. b) For fixed power tests, clock stabilization takes longer, so the benchmark should run warm-ups until power draw stabilizes. When running GEMMs back-to-back, the GPC clocks were observed to oscillate for around the first 3 seconds of runtime before settling on a stable frequency.

  2. Profiling Iterations: a) Profiling iterations were designed to be large enough to minimize run-to-run variation. b) From previous GEMM studies, running greater than 1000 testing iterations provided stable results for large problem sizes (~4096x4096x4096). Smaller problems may have more variability, and the iteration count should be increased accordingly.

  3. Cool-down Cycles: After completing the testing iterations for each GEMM problem, the GPU should be returned to an idle, inactive state to allow the power draw, temperature, and clock frequency to return to their idle values. These cool-down cycles prevent interference between subsequent tests.

For large GEMMs on Blackwell (~8192x8192x16384), we use 10,000 warmups, 4,000 profiling iterations, and 1 second of cool-down time between tests.

Buffer Rotation

One critical step in designing reliable GEMM benchmarks is to address the influence of cache residency, particularly in the context of the L2 cache. If input matrices persist in cache across iterations, subsequent GEMM operations may benefit from artificially lowered memory access times. This distorts performance measurements, as the observed speed reflects cache effects rather than true DRAM memory access costs - a scenario that rarely matches real-world workloads, where data locality and reuse are not guaranteed to this extent.

To eliminate this anomalous observation, buffer rotation is employed. By allocating duplicate buffers for each input tensor, with a total size at least twice the L2 cache capacity (and using at least two buffers per tensor), we ensure that after each kernel iteration, the next computation accesses data not resident in the cache. The rotation of buffer pointers forces each iteration to read from new buffer regions, so that by the time a buffer is reused, its content has been evicted from the cache due to intervening memory traffic. This guarantees that GEMM computations consistently start with input matrices located in DRAM rather than cache, thereby reflecting realistic access latencies.

Through buffer rotation, we eliminate false performance gains that stem from prior cache residency, achieving greater determinism and accuracy in the measurements. This practice is essential for producing benchmarks that are representative, repeatable, and free from the confounding effects of temporal locality specific to tightly-looped microbenchmarks.

To ensure consistent, accurate results special care should be taken to prevent cross-iteration L2 hits:

  1. Calculate the footprint of all input tensors and compare it to the L2 cache capacity for the target device.
  2. Allocate duplicate buffers for all tensors such that the total footprint of all input buffers is >= 2x the L2 cache capacity.
    a. Some very large problems will have a total footprint that is >= 2x the L2 capacity without any buffer duplication. In such cases, 2 buffers should still be used to prevent data remaining resident in L2.
  3. After each profiling iteration, update the buffer pointers for all tensors.
    a. Rotate through the buffers in a circular manner, by the time a buffer is reused, enough data should have been read in intervening iterations to cause the buffer to be evicted from cache.

Conclusion

In summary, precise and reliable GEMM benchmarking requires careful attention to data fill strategies, system warm-up and iteration protocols, and memory management techniques such as buffer rotation. By adhering to consistent fill methodologies and evolving these recommendations based on real-world empirical distributions, we can ensure that performance measurements remain representative of actual workloads. Rigorous warm-up procedures are essential for eliminating transient effects, stabilizing system state, and achieving reproducible results. Buffer rotation, in turn, mitigates the confounding influence of L2 cache residency, guaranteeing that measured performance reflects realistic DRAM access latencies rather than idealized cache scenarios. Together, these practices create a robust framework for GEMM evaluation - one that balances accuracy, consistency, and relevance across diverse data types, problem sizes, and hardware configurations. As methodologies continue to evolve with ongoing research and observation, these principles provide a solid foundation for producing actionable and trustworthy benchmarking outcomes.


Media Docs Cpp Getting Started (`media/docs/cpp/getting_started.rst`)

.. _getting_started:

Getting Started

.. toctree::
:maxdepth: 2

Quickstart<quickstart.md>
IDE Setup
Build
Functionality<functionality.md>
Terminology<terminology.md>
Fundamental Types<fundamental_types.md>
Programming Guidelines
GEMM Heuristics <heuristics.md>


Media Docs Cpp Grouped Scheduler (`media/docs/cpp/grouped_scheduler.md`)

Grouped Kernel Schedulers

CUTLASS's grouped kernel is a persistent kernel which launches multiple problems (e.g., GEMMs, SYR2Ks) within a
single CUDA kernel launch.

Unlike a conventional GEMMs in CUTLASS, which launch a number of threadblocks equal to the number
of tiles in the GEMM, CUTLASS grouped kernels typically launch a number of threadblocks that is
fewer than the total number of tiles across all problems in the group. Each threadblock is then
responsible for computing one or more tiles among the problems in the group. The grouped kernel
scheduler (referred to as the problem visitor in code) is responsible for assigning each
threadblock the sequence of tiles that it will compute within the group.

This document provides background on the functionality of the grouped kernel scheduler, and describes
various optimizations to the grouped kernel scheduler.

Outline

Introduction to Grouped Kernel Schedulers

Given a group of problem sizes and a grid of threadblocks, the scheduler's job is to assign
tiles from problems in the group to threadblocks. Threadblocks in a grouped kernel persistently
execute a loop of querying the scheduler for the next tile to compute and performing the
kernel-level operations for that tile (e.g., MMA and epilogue). In pseudocode, this looks as
follows:

c++
ProblemVisitor problem_visitor;
 
while (problem_visitor.next_tile()) {
    //
    // Get next tile index from scheduler
    //
 
    //
    // Compute MMA and epilogue
    //
 
    // Inform the scheduler that we are done with the current tile
    problem_visitor.advance(gridDim.x);
}

The key functionality of the grouped kernel scheduler lies in the next_tile() method,
which determines which tile in the group the calling threadblock should compute next, if any.

Grouped GEMM Scheduler

The scheduler used by grouped GEMM assigns tiles in the group to threadblocks in a round-robin
fashion.

Consider, for example, the threadblock-to-tile mapping that occurs for a group of four GEMMs
each consisting of a grid of 2x2 tiles. Suppose that eight threadblocks are launched. The
figure below illustrates the threadblock ID assigned to each tile in each GEMM in the group.

A similar mapping for problems that do not have the same number of tiles
is shown below:

Computing the schedule for a given block

Each threadblock in the grouped GEMM computes its own schedule by calling
the next_tile() method described above.

To do this, the threadblock's ProblemVisitor maintains a thread_idx
member that is initialized to blockIdx.x and is incremented by
gridDim.x between each tile computed (only the x dimension is used)
in the launch configuration for grouped kernels). The scheduler must
then figure out which GEMM in the group tile_idx belongs to, and which tile
within that problem it maps to.

  1. Determining which GEMM tile_idx maps to: The scheduler determines
    the GEMM to which tile_idx belongs by iterating through GEMMs starting with
    the most-recently visited GEMM, and adding the number of tiles within that
    GEMM to a running variable problem_tile_start. The scheduler has found the
    correct problem for this tile when problem_tile_start <= tile_idx < problem_tile_start + tiles_in_problem.

  2. Determining the tile within a GEMM tile_idx maps to: Once the GEMM
    to which tile_idx maps has been located, the specific tile within that
    GEMM that this block should compute is given by tile_idx - problem_tile_start.
    Simple rasterization is then performed to map this one-dimensional tile ID
    into the two-dimensional coordinate of the tile to compute in the GEMM.

We describe how this search is accelerated in Scheduler Modes.

Grouped Rank2K Scheduler

The previous section described the operation of the scheduler used
for grouped GEMM kernels. While this scheduler is sufficient for
correctly implementing grouped Rank2K operations (i.e., SYR2K and HER2K), it leads to significant inefficiencies.

We next describe these inefficiencies as well as how the CUTLASS
grouped Rank2K scheduler overcomes them.

Inefficiency of grouped GEMM scheduler for grouped Rank2K problems

The grouped GEMM scheduler assumes that every tile in every GEMM in the group will
ultimately affect the output of the problem. This is not the case for Rank2K
problems, for which matrix C is either upper or lower triangular. Using the default
grouped GEMM scheduler for such problems will thus lead to threadblocks frequently
being assigned to tiles that exit early (e.g., due to being assigned to a tile in the
upper-triangular portion of a lower-triangular problem). This further leads to load
imbalance among threadblocks, as the grouped GEMM scheduler assigns nearly the same
number of tiles to all threadblocks, regardless of how many tiles are truly active.

Consider an example of a group of four SYR2K problems, each with matrix C consisting
of a grid of 2x2 tiles. Matrix C in each problem is lower triangular, indicated by
shaded tiles. Consider that eight threadblocks are launched to compute the grouped
problem. The default grouped GEMM scheduler will assign threadblocks to tiles in the following order:

In this case, threadblocks 1 and 5 are continuously assigned to inactive tiles. In
scenarios in which problems within the group have varying size, we have observed
this to still lead to significant load imbalance.

Specializing the scheduler for triangular problems

We seek to design a scheduler that more efficiently maps threadblocks to active tiles
for kernels that use triangular output matrices. The scheduler should ideally assign
threadblocks only to those tiles within lower-triangular portion of a
lower-triangular problem (and vice-versa for upper-triangular problems).

Using the example above, the resulting assignment of threadblocks to tiles from
such a scheduler might be:

Achieving this schedule requires mapping from a threadblock ID to tile coordinates
(i, j).

We will illustrate this by mapping a lower-triangular matrix with a 3x3 grid. We
first calculate row and column indices assuming one-indexed rows, tiles, and
threadblock IDs, and then subtract one to convert to zero-indexed versions. Our
description borrows heavily from the mapping described here.

Calculating row `i` given threadblock ID `t`

For a given row i, all threadblock IDs t in that row satisfy the following:

text
t <= 1 + 2 + 3 + ... + (i-1) + i

The closed-form equation for the right-hand side is: i(i+1)/2.
Using this, we can solve for i given t:

text
t  <= i(i+1)/2
2t <= i^2 + i
2t <= i^2 + i + 0.25 - 0.25
2t + 0.25 <= i^2 + i + 0.25
2t + 0.25 <= (i + 0.5)^2
sqrt(2t + 0.25) - 0.5 <= i

To account for fractional values, we set:

text
i = ceil(sqrt(2t + 0.25) - 0.5)

To turn this into a zero-indexed row and work with zero-indexed t, we perform:

text
i = ceil(sqrt(2(t+1) + 0.25) - 0.5) - 1
  = ceil(sqrt(2t + 2.25) - 0.5) - 1

Calculating column `j` given threadblock ID `t` and row `i`

For a given row i, all threadblock IDs t in that row also satisfy the following:

text
t > 1 + 2 + 3 + ... + (i-2) + (i-1)
--> t > i(i-1)/2

Threadblock IDs within a given row are sequential, so the one-indexed column ID
for one-indexed threadblock ID t and row i is:

text
j = t - (i(i-1)/2)

The zero-indexed version becomes:

text
j = (t+1) - (i(i+1)/2) -1
  = t - (i(i+1)/2)

Accounting for non-square grids

Though the overall output problem size for Rank2K problems is guaranteed to be square, the
grids used in computing may not be square due to using non-square threadblock shapes. For
example, a threadblock shape of 64x32 operating on a problem of output size 128x128 would
result in a grid of 2x4 tiles.

This case can be handled by noting that the output resembles a square grid of 2x2 "macro tiles"
each of which contains 2 "true tiles." We can thus first map a threadblock ID to its "macro tile"
using the equations above, and then map it to the "true tile" within its "macro tile." In the example
of a 2x4 grid, this mapping would look as follows:

A zero-indexed threadblock ID t is mapped to its "macro tile ID" t_macro as:

text
t_macro = t // r

Where r is the ratio of the maximum dimension of the grid to the
minimum dimension of the grid (i.e., r = 4 / 2 = 2 in the previous example).

One uses t_macro and the calculations above to find the row and column in the square matrix to
obtain i_macro and j_macro (zero-indexed). The mapping from (i_macro, j_macro) --> (i, j)
is simply the following:

text
if (ThreadblockShape::M > ThreadblockShape::N):
    r = ThreadblockShape::M / ThreadblockShape::N
    i = i_macro
    j = (j_macro * r) + (t % r)
elif (ThreadblockShape::M < ThreadblockShape::N):
    r = ThreadblockShape::N / ThreadblockShape::M
    i = (i_macro * r) + (t % r)
    j = j_macro
else:
    i = i_macro
    j = j_macro

Handling cases with grid dimensions that aren't multiples of each other

Even though threadblock shapes M and N are typically multiples of one another, the grid
for a given problem may not have dimensions of the same ratio as that of the threadblock.
For example, a problem of size 132x132 using a threadblock of shape 64x32 will result
in a grid of 3x5 tiles. In this case, there is not an integer number of "true tiles"
per "macro tile."

When this scenario arises, we simply pad the larger dimension of the grid such that
there are an integer number of "true tiles" per "macro tile." Thus, the 3x5 grid in
the example above will be treated as a 3x6 grid. Row and column positions for each
tile are calculated as above. Any threadblocks that map to tiles that are outside the
problem range or upper/lower triangular portion (e.g., (2, 5)) will exit early from
this problem and may proceed to the next problem in the group.

Handling upper-triangular matrices

The only modification needed for upper-triangular matrices is to swap i_macro and j_macro in the calculations above.

Scheduler modes

The grouped kernel schedulers come with two different modes for finding
the next tile for a block to compute. These techniques are controlled by
the cutlass::gemm::kernel::GroupScheduleMode enum.
We describe each mode in greater detail below.

`GroupScheduleMode::kDeviceOnly` (default)

This scheduler mode performs all scheduling work on the device. It parallelizes
the search for the problem that tile_idx maps to by having each thread "own"
a different problem and determine whether tile_idx falls within the range of
that problem.

GroupScheduleMode::kDeviceOnly performs this parallelization in a warp-wide
fashion. Each thread in the warp loads a problem size indexed by its lane id and
computes the number of tiles in that problem. A warp-wide prefix sum is used to find
the starting tiles for the set of problems the warp is looking at. At the end of the
prefix sum, each thread holds the starting tile index and tile count for a unique
problem in the group.

While tile_idx remains within the range of the problems currently hosted by the
warp, each thread will check whether tile_idx is in the range of its current
problem. The matching problem index and its starting tile are then broadcasted to all
threads in the warp.

Precomputing schedules on the host: `GroupScheduleMode::kHostPrecompute`

This scheduler attempts to reduce the amount of scheduling performed on the device
by precomputing on the host the sequence of problems that will
be accessed by each block. As described above, all that is needed to map tile_idx to
the specific tile within a problem to compute is the problem ID and the problem's
starting tile (among all of the tiles in the group). Thus, this scheduler precomputes
the problem index and problem starting tile for each tile computed by each block.

The schedule for an individual block is represented as an array of
(problem_idx, problem_starting_tile) tuples. There is one such array per block.
These arrays are produced on the host and copied over to the device. This
representation is optimized for the case in which blocks compute at most one
tile per problem. When a block computes multiple tiles per problem in the group,
the representation above will result in duplicate entries, and thus will be
suboptimal (e.g., [(3, 20), (3, 20)] for a block that computes two tiles in
problem 3, which has starting tile index 20).
We have chosen to use the representation described above because grouped kernels
themselves are typically most beneficial when problem sizes are small, and, thus,
blocks compute at most one tile per problem.

Which scheduler mode should I use?

Consider the following questions when deciding which scheduling mode to use:

How are the parameters used as input to the grouped kernel (e.g., ptrA, lda) set in my application?

If these are set by a previous kernel running on
the device (rather than by the host), you likely want to use kDeviceOnly,
as this will minimize additional host-device communication.

Can host-side work be overlapped with other device kernels in my application?

For example, if a grouped GEMM is used as the Nth layer in a neural network,
host-side precomputation for the grouped GEMM can potentially be overlapped
with device-side work for layer N-1. In this case kHostPrecompute is likely
a good fit.

How compute-intensive are the problems in my group?

The differences in performance between kHostPrecompute and kDeviceOnly are most
noticeable for grouped kernels with low computational intensity, for which time spent in
the scheduler accounts for a significant fraction of the grouped kernel's runtime.
Intuitively, as problems in a group decrease in computational intensity, a smaller
fraction of the overall runtime will be consumed in performing MMA operations, leading
to a larger fraction of the overall runtime being consumed by scheduling logic.

Since the scheduling modes affect only the scheduling logic of the grouped kernels,
one expects to see most benefit from kHostPrecompute for less computationally-intense
groups.

Improving Load Balance by Sorting Problems

The grouped kernel schedulers assign a nearly equal number
of tiles to each block participating in the grouped kernel. Every tile in the
group has the same M and N dimensions. However, the K dimension of each
tile depends on the K dimension of the problem, so tiles may have different
K dimensions. Thus, the K dimension of the
tile plays a significant role in determining how long it takes for a given
tile to be computed.

Potential problems with imbalanced K dimension

To ensure that compute load is balanced evenly across blocks, it is important
that the sum of the K dimensions among all tiles a block computes be similar
to that of other blocks; if one block computes far more tiles with a large
value of K than other blocks, it may take longer than the other blocks.

For example, consider the following group of GEMMs:

text
0 1152x768x128
1 1152x768x1024
2 768x1152x128
3 768x1152x1024

If a tile size of 128x128 is used, then each problem will have 54 tiles.
Thus, there are 216 tiles across the group.

Suppose this grouped GEMM is run on GA100, which has 108 SMs. Suppose that
the occupancy given the parameters of the grouped GEMM is one -- one threadblock
can be active at a time on an SM. The grouped GEMM will, thus, run with 108
persistent threadblocks, each of which computes (256 / 108) = 2 tiles.

Under the round-robin assignment of tiles to threadblocks employed by
the grouped GEMM scheduler, the assignment of tiles to threadblocks
in this GEMM will be as follows:

text
Threadblocks 0-53:     Tiles of size 128x128x128  from problem 0
Threadblocks 54-107:   Tiles of size 128x128x1024 from problem 1
Threadblocks 0-53:     Tiles of size 128x128x128  from problem 2
Threadblocks 54-107:   Tiles of size 128x128x1024 from problem 3

Following this assignment, threadblocks 54-107 perform significantly more
work than threadblocks 0-53 because they compute two tiles with a K
dimension of 1024, whereas threadblocks 0-53 compute two tiles with K
dimension of only 128.

Due to this imbalanced assignment, threadblocks 54-107 will run
significantly longer than threadblocks 0-53, leaving threadblocks
0-53 idle for a large fraction of time.

Clearly, a better assignment of tiles to threadblocks for this
example would involve all threadblocks computing one tile with
a K dimension of 1024 and one tile with a K dimension of 128.
This would better balance the workload among threadblocks.

Potential for sorting problems to reduce imbalance

A simple way to potentially reduce load imbalance is to sort the problems in a group in
descending order of their K dimension. This can help to improve load balance
because tiles in a group are assigned in a round-robin fashion to blocks
sequentially, so every block will always be assigned next the tile with
the highest K dimension available.

Considering the example described above, sorting the problem sizes before
executing grouped GEMM improves the runtime of this grouped GEMM on GA100 with each
scheduling mode by around 30%.

To ease the process of sorting groups and their associated metadata in this
manner, the device-level grouped kernels provide a sort_problems() method.
An example of how to use this may be found in the grouped GEMM example.

Finally, while sorting problems can be helpful in certain scenarios, it is
not guaranteed to improve performance. In some cases, performance can
decrease when sorting problems due to additional conflicting factors that
affect GEMM performance. We recommend profiling your grouped kernel with
and without sorting to see whether it helps in your case.

Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Heuristics (`media/docs/cpp/heuristics.md`)

GEMM Heuristics

Overview

Gemm heuristics in cutlass_library aim to reduce the search space for runtime autotuning, so that only a subset of valid kernels need to be built and profiled for a given set of GEMM problems. This implementation uses Nvidia's nvidia-matmul-heuristics, an analytical heuristic that ranks GEMM kernels by estimated performance given a problem size and hardware SKU. You can find more info in the docs.

Coverage

Gemm heuristics in cutlass_library is an experimental feature and exhaustive functional or performance coverage is not guaranteed. It currently supports the following.

Problem space:

  • Plain dense gemm for f8, f16, f32

Hardware:

  • Hopper (sm9x)
  • Blackwell (sm10x)

Usage / Quick Start

Install Dependencies

Using the wheel is recommended:

text
pip install nvidia-matmul-heuristics

Prepare Input File

Prepare a list of gemm problem definitions, in the form of a json list, to be evaluated by the heuristic. Here is a sample file with two problems:

text
[
{
     "m" : 4096,
     "n" : 4096,
     "k" : 4096,
     "batch_count" : 1,
     "layout" : "tnn",
     "dtype_a" : "f16",
     "dtype_b" : "f16",
     "dtype_c" : "f16",
     "dtype_acc" : "f32",
     "dtype_d" : "f16",
     "beta" : 0.0,
     "use_fast_acc": false
},
{
     "m" : 4096,
     "n" : 4096,
     "k" : 4096,
     "batch_count" : 1,
     "layout": "tnn",
     "dtype_a" : "e5m2",
     "dtype_b" : "e5m2",
     "dtype_c" : "f32",
     "dtype_acc" : "f32",
     "dtype_d" : "e5m2",
     "beta" : 0.0,
     "use_fast_acc": true
}
]

Note: use_fast_acc only needs to be specified for FP8 kernels on SM90. Otherwise, it is ignored.

Build

Build CUTLASS using CMake as normal, providing heuristics-specific options to CMake. Note that hardware details are detected automatically. For offline builds, use -DCUTLASS_LIBRARY_HEURISTICS_GPU.
For example, here is a minimal command for Nvidia's Hopper Architecture (sm90):

text
$ cmake .. \
    -DCUTLASS_NVCC_ARCHS=90a \
    -DCUTLASS_LIBRARY_HEURISTICS_PROBLEMS_FILE=<path_to_your_problem_list.json> \
    -DCUTLASS_LIBRARY_HEURISTICS_CONFIGS_PER_PROBLEM=<number of configurations to build per problem> 
...
...

$ make cutlass_profiler -j

This will produce a csv testlist which provides all testcases that need be run to perform autotuning over the built configurations, including kernel runtime options. The location of this file can be changed by the CMake option -DCUTLASS_LIBRARY_HEURISTICS_TESTLIST_FILE.

CUTLASS CMake currently supports the following for heuristics:

  • CUTLASS_LIBRARY_HEURISTICS_PROBLEMS_FILE: Path to the file containing a json list of GEMM problems
  • CUTLASS_LIBRARY_HEURISTICS_CONFIGS_PER_PROBLEM: Max number of configurations the heuristic will return for each GEMM problem. The same configuration or kernel can be suggested for multiple problems.
  • CUTLASS_LIBRARY_HEURISTICS_RESTRICT_KERNELS: Limits the build to only the set of kernels instantiated by the default CUTLASS CMake build flow, composing with other options such as CUTLASS_LIBRARY_INSTANTIATION_LEVEL. Set this to ON as a workaround if the heuristic suggests kernel configurations that do not build on your platform (possible for some unsupported or experimental use cases). This option is set to OFF by default, which builds all of the suggested configurations.
  • CUTLASS_LIBRARY_HEURISTICS_TESTLIST_FILE: Path to the output CSV which will contain the testcases to be used for autotuning, consumable by cutlass_profiler.
  • CUTLASS_LIBRARY_HEURISTICS_GPU: The GPU to use for heuristics; for instance, H100_SXM5. Used for offline builds. If unset, the hardware properties will be auto-detected using the Cuda Driver APIs. See generator.py for valid GPU strings

Profile

Use the emitted testlist CSV with cutlass_profiler to collect performance data, which can be used to determine the fastest built kernel configuration for each of the input problems. Example which profiles each testcase for a fixed 50ms:

text
cutlass_profiler --operation=Gemm --testlist-file=<path_to_your_testlist.csv> --profiling-iterations=0 --profiling-duration=50 --verification-enabled=false --output=<path_to_outfile>

Direct Usage in Python

If you have pre-built CUTLASS kernels or custom CUTLASS emitters, you can use the Python APIs directly to select kernels to build or profile. See filter_manifest_and_write_heuristics_file() in heuristics.py for example usage.


Media Docs Cpp Ide Setup (`media/docs/cpp/ide_setup.md`)

IDE Setup for CUTLASS Development

This document outlines instructions and tips for setting up a local editor for CUTLASS development, including support
for intellisense, go-to-definition, code formatting, and so on.

Overview

In order for any intellisense tool to work with CUTLASS, the following things need to be configured with it:

  • Include paths, i.e. where the compiler (or in this case, the intellisense tool) should look for header files
  • Compiler flags; especially the C++ standard (--std)
  • Preprocessor variables; especially CUDA-related ones

One usually needs to configure the above variables in a settings file. Below, two config approaches are described:
for VSCode, and for any editor that uses the clangd language server, which includes
Vim, Emacs, NeoVim, Sublime Text, and so on. Note that VSCode can also be configured to use clangd.
It might be worth setting up clangd for VSCode rather than the default intellisense,
and you might see faster responses and more stable performance with clangd.

VSCode Setup

  1. Install the Official C/C++ extension
  2. Open settings...
    1. Ctrl+Shift+P to open the command palette
    2. Enter "C/C++" to filter results
    3. Select "C/C++ Edit Configurations (UI)" (or "... (JSON)" if you feel like editing the raw JSON)
    4. View the documentation for these settings
      here
  3. Edit "Include Path" to set up include paths. For CUTLASS, this includes the following:
    • ${workspaceFolder}/include
    • ${workspaceFolder}/tools/util/include
    • ${workspaceFolder}/examples/common
    • ...others, depending on which files you edit
  4. Edit C++ standard to be c++17, gnu++17, or equivalent.
  5. Edit defines to define preprocessor variables. See
    Global Config below for examples. The important
    ones include __CUDACC_VER_MAJOR__, __CUDA_ARCH__, __CUDA_ARCH_FEAT_SM90_ALL__. But configure
    them according to your target architecture.
  6. ...and possible edit any other fields for your specific setup.

clangd Setup

clangd is a C++ language server that is part of the LLVM project. You must first set it up your specific IDE:

  • clangd official documentation for editor setup.
  • NeoVim setup is possible through lsp and either manually installing clangd or
    using an installation manager like Mason.

Then, one needs to edit the config (documentation). One typically has a
global and a per-project config.

Global Config

Here is one example for a global config.
On linux this is usually located at ~/.config/clangd/config.yaml . Here is one example config for CUDA projects on SM90.
The key settings here are the preprocessor vars (-D__CUDACC_VER_MAJOR__ , -D__CUDA_ARCH__)

text
CompileFlags:
  Compiler: /usr/local/cuda/bin/nvcc
  Add:
    - --cuda-path=/usr/local/cuda
    - --cuda-gpu-arch=sm_90a
    - -I/usr/local/cuda/include
    - "-xcuda"
    # report all errors
    - "-ferror-limit=0"
    - --cuda-gpu-arch=sm_90a
    - --std=c++17
    - "-D__INTELLISENSE__"
    - "-D__CLANGD__"
    - "-DCUDA_12_0_SM90_FEATURES_SUPPORTED"
    - "-DCUTLASS_ARCH_MMA_SM90_SUPPORTED=1"
    - "-D_LIBCUDACXX_STD_VER=12"
    - "-D__CUDACC_VER_MAJOR__=12"
    - "-D__CUDACC_VER_MINOR__=3"
    - "-D__CUDA_ARCH__=900"
    - "-D__CUDA_ARCH_FEAT_SM90_ALL"
    - "-Wno-invalid-constexpr"
  Remove:
    # strip CUDA fatbin args
    - "-Xfatbin*"
    # strip CUDA arch flags
    - "-gencode*"
    - "--generate-code*"
    # strip CUDA flags unknown to clang
    - "-ccbin*"
    - "--compiler-options*"
    - "--expt-extended-lambda"
    - "--expt-relaxed-constexpr"
    - "-forward-unknown-to-host-compiler"
    - "-Werror=cross-execution-space-call"
Hover:
  ShowAKA: No
InlayHints:
  Enabled: No
Diagnostics:
  Suppress:
    - "variadic_device_fn"
    - "attributes_not_allowed"

Local Config

Local config is needed to specify per-project settings, especially include paths. An example is:

text
CompileFlags:
  Add:
    - -I</absolute/path/to/cutlass>/include/
    - -I</absolute/path/to/cutlass>/tools/util/include/
    - -I</absolute/path/to/cutlass>/cutlass/examples/common/

Note that absolute paths are needed since clangd doesn't support relative paths.

Note on compile_commands.json

For typical C++ projects, clangd can automatically configure itself by parsing the compile_commands.json
generated by your CMake build. The path to such a file is by default build/compile_commands.json and is
configured by the CompilationDatabase config.

This is usually a convenient way to configure projects, but it's not as simple for CUDA/nvcc projects, since
clang doesn't understand many of the compiler flags used by nvcc. Hence, for now, we don't recommend using
compile_commands.json to configure your CUDA project.

Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Implicit Gemm Convolution (`media/docs/cpp/implicit_gemm_convolution.md`)

CUTLASS Convolution

Implicit GEMM is the formulation of a convolution operation as a GEMM (generalized matrix-matrix
product). Convolution takes an activation tensor and applies a sliding filter on it to produce an
output tensor.

Introduction

This release of CUTLASS contains several artifacts related to convolution.

Implicit GEMM Algorithm

2-D convolution may be mapped to matrix multiply
by first forming a convolution matrix containing elements of the activations tensor,
then multiplying this by a matrix formed from the filters tensor.
The earliest form of this algorithm constructs the convolution matrix explicitly via an operation
conventionally referred to as im2col. The resulting matrix replicates each activation element by a factor
equal to the filter size, consuming additional storage capacity and memory bandwidth.

The implicit GEMM algorithm is a variation on the blocked, hierarchical GEMM computation in CUDA.
Instead of constructing the convolution matrix explicitly,
it forms tiles of the convolution matrix on the fly
as data are loaded from global memory into Shared Memory
by carefully updating pointers and predicates.
Once the convolution matrix is formed in Shared Memory,
the existing warp-level GEMM components accumulate the result of
convolution and update the output tensor.

This section describes the structure of an efficient Implicit GEMM Convolution CUDA kernel
for Turing Tensor Cores.

Mapping Convolution to GEMM

The forward convolutional layer computes an output tensor y = conv(x, w) where x(NHWC), w(KRSC), and y(NPQK)
are 4-D tensors.

This computation may be described by the following analytic function.

text
y[n, p, q, k] = sum_c(sum_r(sum_s( x[n, f(p, r), g(q, s), c] * w[k, r, s, c] )))

where functions f and g are defined as follows.

text
f(p, r) = p * stride_h + R - r - 1 + pad_h
g(q, s) = q * stride_w + S - s - 1 + pad_w

A host and device
reference implementation are provided in the CUTLASS Utilities.

This computation may be mapped to the elements of a matrix product as follows.

text
C = gemm(A, B)

where

  • A is a row-major matrix of extent NHW-by-RSC containing activations
  • B is a column-major matrix of extent RSC-by-K containing filters
  • C is a row-major matrix of extent NPQ-by-K containing the output

Each element of the output matrix Cij corresponds to an element in the output tensor y[n, p, q, k] according to
the following relation.

text
y[n, p, q, k] = Cij

where

text
i = q + Q * (p + P * n)
j = k

These relations may be inverted as follows.

text
k = j

n = i / (PQ)
residual = i % (PQ)

p = residual / Q
q = residual % Q

The triple loop nest iterating over CRS to accumulate the result may also be linearized and mapped to the inner
GEMM K dimension (not to be confused with the filter tensor dimension K) by the following relations.

text
gemm_k = s + S * (r + R * c)

and inverse

text
c = gemm_k / (RS)
residual = gemm_k % (RS)

r = residual / S
s = residual % S

Given these equations, a GEMM triple loop nest could be augmented with tensor indexing as follows.

c++
int GEMM_M = N * P * Q;
int GEMM_N = K;
int GEMM_K = C * R * S;

for (int gemm_i = 0; gemm_i < GEMM_M; ++gemm_i) {
  for (int gemm_j = 0; gemm_j < GEMM_N; ++gemm_j) {

    int n = gemm_i / (PQ);
    int npq_residual = gemm_i % (PQ);

    int p = npq_residual / Q;
    int q = npq_residual % Q;

    Accumulator accum = 0;

    for (int gemm_k = 0; gemm_k < GEMM_K; ++gemm_k) {

      int k = gemm_j;

      int c = gemm_k / (RS);
      int crs_residual = gemm_k % (RS);

      int r = crs_residual / S;
      int s = crs_residual % S;

      int h = f(p, r);
      int w = g(q, s);

      ElementA a = tensor_A.at({n, h, w, c});
      ElementB b = tensor_B.at({k, r, s, c});

      accum += a * b;
    }

    C[gemm_i * K + gemm_j] = accum;
  }
}

The CUTLASS GEMM implementation explicitly iterates over tiles. Consequently,
a tile iterator could be implemented to compute these functions analytically and load the appropriate
elements. However, the resulting modulo arithmetic would be computationally intensive, and overhead would
limit performance of a GEMM kernel targeting Turing Tensor Cores.

The following section describes how an efficient implementation may be implemented within the structure of
a hierarchical GEMM kernel targeting Tensor Cores.

CUTLASS Convolution Implementation

To get the best performance, the following parameters are recommended.

  • All tensors are 128-bit aligned NHWC tensors
  • Channel count (C) is a multiple of 32 elements
  • Filter count (K) is a multiple of 32 elements

This enables 128-bit vector memory acceses which lead to efficient CUDA kernels. Smaller alignment is supported even on tensor cores by setting AlignmentA and AlignmentB in conv::kernel::DefaultConv2dFprop, but the performance is lower than 128-bit aligned tensors.

CUTLASS Device-level Convolution Operator

CUTLASS defines CUDA C++ templates accepting numerous template arguments to specialize the resulting
kernel by operation, data type, tile configuration, math instruction, and fused output operation.

In turing_tensorop_conv2dfprop.cu, a convolution
operation is defined as follows.

text
/* Detailed source-code truncated for AI context efficiency. */

This template is intended to be generic and cover all feasible configurations. The example specifies
the following concrete data types, layouts, and tile shapes.

text
/* Detailed source-code truncated for AI context efficiency. */

That is, this computes 2D convolutional forward propagation with 4-bit integer inputs and outputs (cutlass::int4b_t).
Internal accumulation is performed using 32-bit integers (int32_t), and an elementwise linear combination operation
is performed on the output in single-precision floating point (float).

The threadblock and warp-level tile shapes refer to the hierarchically blocked GEMM computation
described here. Larger tiles achieve greater reuse of data loaded through shared memory
but launch fewer CTAs and may not fully occupy the GPU for small problem sizes. Smaller tile configurations achieve
lower peak utilizations but may better match the number of SMs within the GPU for real-world workloads.

Launching the convolution

The following code collects the arguments for an implicit GEMM operation into a structure.

c++
//
// Define arguments for CUTLASS Convolution
//

// mode (kCrossCorrelation or kConvolution)
cutlass::conv::Mode mode = cutlass::conv::Mode::kCrossCorrelation;

// Split K dimension into 1 partitions
int split_k_slices = 1;

cutlass::conv::Conv2dProblemSize problem_size(      
    options.input_size,
    options.filter_size,
    options.padding,
    options.conv_stride,
    options.dilation,
    options.output_size(),
    mode,
    split_k_slices);

typename ImplicitGemm::Arguments arguments{
  problem_size,
  tensor_a.device_ref(),
  tensor_b.device_ref(),
  tensor_c.device_ref(),
  tensor_c.device_ref(),
  {options.alpha, options.beta},
};

The mode flag indicates whether to compute cross correlation or convolution. The arguments
input_size, filter_size, padding, conv_stride, and dilation specify the dimensions of the
input and output tensors and characterize the problem size.

The arguments tensor_a.device_ref(), tensor_b.device_ref(), and tensor_c.device_ref() are
CUTLASS TensorRef<> objects containing a pointer to the tensor data in GPU device memory and stride values.

The following code initializes and launches the Implicit GEMM operation on the device. After initializing
the arguments structure, it is used to query device-side workspace requirements and allocate them
in device memory if needed.

Then, the Implicit GEMM object is initialized with the arguments structure and the workspace in
device memory. This initialization step precomputes internal lookup tables used by the convolution kernel
and may also clear the device-side workspace if needed.

Finally, the initialized Implicit GEMM object is called, launching a kernel on the device. tensor_c now
contains the result of the implicit GEMM.

c++
ImplicitGemm implicit_gemm_op;

// Query workspace size
size_t workspace_size = implicit_gemm_op.get_workspace_size(arguments);

// Allocate workspace memory
cutlass::device_memory::allocation<uint8_t> workspace(workspace_size);

// Initialize the Implicit GEMM object
cutlass::Status status = implicit_gemm_op.initialize(arguments, workspace.get());

if (status != cutlass::Status::kSuccess) {
  /* error */
}

//
// Launch initialized CUTLASS kernel
//

status = implicit_gemm_op();

if (status != cutlass::Status::kSuccess) {
  /* error */
}

The example demonstrates how the input and output tensors may be written to a file as CSV using
cutlass::HostTensor<> defined in the CUTLASS Utilities.

c++
std::ofstream output_workspace(ss.str());

  output_workspace 
    << "Input = \n" << tensor_a.host_view() << "\n\n"
    << "Filters = \n" << tensor_b.host_view() << "\n\n";

  // Copy device memory to host backing store
  tensor_c.sync_host();

  output_workspace << "Computed = \n" << tensor_c.host_view() << std::endl;

CUTLASS Components

CUTLASS defines the following CUDA C++ templates to implement Implicit GEMM Convolution which are described in greater detail in subsequent sections.

Activations tile iterators load the activations tile into registers. Two implementations are provided:

Filter tile iterators load filters into registers. Similarly, two implementations are provided:

The improvements covered by optimized iterators are:

a. Precomputing kernel-invariant pointer deltas on the host
b. Computing cta-invariant mask predicates on device-side iterator ctors
c. Use of fast divmod to map GEMM dimensions to convolution tensors.

For example, an optimized activation iterator uses fast divmod to map GEMM M to NPQ.

Pipelined mainloop loads threadblock-scoped tiles from global memory into shared memory and then applies
CUTLASS warp-level GEMM operations to load from Shared Memory and issue instructions to Turing Tensor Cores.

Operations for storing to shared memory and performing warp-wide matrix multiply operations using
Turing Tensor Cores are applied directly from the CUTLASS GEMM components. These include the
following components.

Regular Tile Iterator implemented in
transform::threadblock::RegularTileIterator
stores register-backed fragments to Shared Memory in permuted layouts.

Warp-level GEMM defined in cutlass::gemm::warp::MmaTensorOp
defines tile iterators to load from Shared Memory and issue math instructions to Turing Tensor Cores.
Further details are described in here.

Epilogue reorders accumulator elements among threads within a threadblock to efficiently update
the output tensor. It is implemented in epilogue::threadblock::Epilogue.

Loading Activations and Filters

The Implicit GEMM Convolution algorithm partitions the GEMM K dimension (of extent CRS) into
threadblock tiles and assigning each threadblock tile to one filter position and an interval
of channels. After iterating over all filter positions, the convolution algorithm advances to the
next interval of channels and proceeds from filter r=0, s=0.

The matrix product of one threadblock tile is computed per iteration of
the mainloop as described in the CUTLASS GEMM implementation. To
summarize, the threadblock tile of activations and filters are loaded from tensors in global memory
and stored to shared memory. Each thread within the threadblock loads one or more vectors and
collectively span the entire tile.

The following figure illustrates one particular iteration of the Implicit GEMM mainloop. Each
thread within the threadblock is mapped to several vectors of elements in the Activations and
Filters tensors. Each index in the GEMM M dimension corresponds to a unique (N,P,Q)
index of the output tensor, and pointers may be computed based on this as well as
filter position (r,s).

The CUTLASS component that embodies this functionality is Conv2dFpropFilterTileAccessIteratorAnalytic.
Its constructor computes the mapping of GEMM M to (N, P, Q), the at() method maps the linear offset into the Activations
tensor for each memory access the thread is to perform. Additionally, the method valid() computes the valided of the access
for each filter position and for each memory access to indicate whether the memory access will be within the bounds of the
tensor or out of bounds.

operator++() iterates over memory accesses performed by a thread in both contiguous and strided dimension.

c++
// cutlass/conv/threadblock/conv2d_fprop_activation_tile_access_iterator_analytic.h

// Update iterator to thread's next contiguous, strided memory access
Conv2dFpropActivationTileAccessIteratorAnalytic &operator++() {
  ++iteration_contiguous_;
  if (iteration_contiguous_ < ThreadMap::Iterations::kContiguous) {
    return *this;
  }
  iteration_contiguous_ = 0;
  
  ++iteration_strided_;
  if (iteration_strided_ < ThreadMap::Iterations::kStrided) {
    return *this;
  }
  iteration_strided_ = 0;
 
  return *this;
}

After all accesses have been visited for the current threadblock tile, advance() updates the pointers to next tile.
Offsets added to each pointer follows the traversal of filter positions, performing one of the
following:

  • advance from filter position (r, s, c) to filter position (r, s+1, c)
  • advance from filter position (r, S-1, c) to filter position (r+1, 0, c)
  • advance from filter position (R-1, S-1, c) to filter position (0, 0, c+32)

This logic within method advance()'s body computes the above three updates for the activation GEMM-A tile.

c++
// cutlass/conv/threadblock/conv2d_fprop_activation_tile_access_iterator_analytic.h

// Advance to the next access
void advance() {
  // moves to the next tile
  ++filter_s_;
  if (filter_s_ < problem_size_.S) {
    return;
  }
  filter_s_ = 0;
  
  ++filter_r_;
  if (filter_r_ < problem_size_.R) {
    return;
  }
  filter_r_ = 0;
  
  filter_c_ += Shape::kRow * problem_size_.split_k_slices;
}

Similar logic holds for Conv2dFpropFilterTileAccessIteratorAnalytic.

To reduce computational overhead in the mainloop body, the pointer offsets may be precomputed
in host code and provided to the CUDA kernel as a lookup table in its Params structure.
As shown in Conv2dFpropFilterTileAccessIteratorOptimized,
the logic to compute offsets from filter position has been extracted to the Params constructor.

c++
// cutlass/conv/threadblock/conv2d_params.h
struct Conv2dFpropActivationIteratorOptimizedParams<layout::TensorNHWC> {
 ...
// next S
inc_next[0] = conv_sign * (int64_t(layout.stride()[0]) * problem_size.dilation_w) * element_size_bits / 8;

// next R
inc_next[1] = conv_sign * (
    int64_t(layout.stride()[1]) * problem_size.dilation_h
    - (problem_size.S - 1) * layout.stride()[0] * problem_size.dilation_w
  ) * element_size_bits / 8;

// next C
inc_next[2] = (
    threadblock_shape.column() * problem_size.split_k_slices
    - conv_sign * int64_t(problem_size.R - 1) * layout.stride()[1] * problem_size.dilation_h
    - conv_sign * int64_t(problem_size.S - 1) * layout.stride()[0] * problem_size.dilation_w
  ) * element_size_bits / 8;

 ...
}

This allows only a simple lookup from the delta table performed in device code in Conv2dFpropActivationTileAccessIteratorOptimized::advance().

c++
// cutlass/conv/threadblock/conv2d_fprop_activation_tile_access_iterator_optimized.h
CUTLASS_HOST_DEVICE
void advance() { 

  int next_idx = 0;
 
  // moves to the next tile
  ++filter_s_;
  if (filter_s_ == problem_size_.S) {
    filter_s_ = 0;
    ++filter_r_;
 
    if (filter_r_ < problem_size_.R) {
      next_idx = 1;
    }
    else {
      filter_r_ = 0;
      next_idx = 2;
    }
  }
  
  add_byte_offset_(params_.inc_next[next_idx]); // in addition to Conv2dFpropActivationTileAccessIteratorAnalytic::advance()

  if (next_idx == 2) {  
    filter_c_ += params_.filter_c_delta;
  }
}

Making use of Tensor Cores

Turing Tensor Cores compute matrix multiply-accumulate operations efficiently by sharing data among all
threads within a warp. The following operations are supported.

Shape A B C
8x8x32 int4b_t int4b_t int32_t
8x8x16 int8b_t int8b_t int32_t
16x8x8 half half half
16x8x8 half half float

Functionally, the Turing 8x8x32 matrix multiply operation distributes the A, B, and C matrix across 32
threads within a warp according to the following illustration.

This Tensor Core operation is accessible to the CUDA programmer via the PTX instruction
mma.sync.
CUTLASS wraps inline PTX with device-side intrinsics defined in cutlass/arch/mma_sm75.h
as in the following example.

c++
unsigned A;   // eight packed 4-bit integer elements
unsigned B;   // eight packed 4-bit integer elements

int C[2];     // two 32-bit integer elements
int D[2];     // two 32-bit integer elements

asm volatile(
  "mma.sync.aligned.m8n8k32.row.col.s32.s4.s4.s32 {%0,%1}, {%2}, {%3}, {%4,%5};\n"
  : "=r"(D[0]), "=r"(D[1])
  : "r"(A), "r"(B), "r"(C[0]), "r"(C[1]));

To load data efficiently from Shared Memory into registers with the distribution among
warps matching the above, the Turing GPU architecture introduces
ldmatrix.
ldmatrix is the ultimate warp-cooperative instruction, as all threads contribute addresses to up to 32 row vectors of
size 128-bits in length. These rows are fetched from Shared Memory and then distributed among groups of four threads
per row.

The arrangement of SMEM pointers and destination registers within threads is illustrated as follows. Thread 0 is highlighted
in the illustration to emphasize the mapping.

The size of the Turing Tensor Core operation computing matrix multiply-accumulate on INT4 data is 8-by-8-by-32
elements. ldmatrix fetches up to 32 rows (or columns) per operation. Sixteen Tensor Core operations may be issued
to implement a 32-by-32-by-32 matrix product and perfectly consume all data loaded by two ldmatrix instructions
as shown in the following figure. Larger tiles are possible by increasing the number of memory instructions
and issuing more Tensor Core operations, up to warp-level matrix operations of size 64-by-64-by-32. The limit is
the number of registers to hold the accumulator elements.

Shared Memory Layouts

In the previous two sections, we have described how data may be loaded from activations and filters tensors
in global memory to compute convolution, and we have described a composition of ldmatrix and mma.sync
to fetch data from Shared Memory and issue Tensor Core operations.

To ensure this data movement is efficient, care must be taken to ensure bank conflicts are avoided. CUTLASS
uses a permuted Shared Memory layout to avoid bank conflicts when storing to Shared Memory and to efficiently
load from Shared Memory using ldmatrix. The following figure illustrates the thread mapping used for
the loading the activations and filters threadblock tiles from global memory and the permuted layout in
Shared Memory.

In the illustration, one warp-wide memory access is highlighted in blue, with individual threads
loading one 128-bit vector. The tile in global memory could correspond either to the activations
or filters and is assumed to be 'strip-mined' with four threads loading consecutive channels.

Shared Memory is visualized as a 'row-major' matrix with eight columns representing
the eight 128-bit banks.
As described in the CUTLASS GTC 2019 presentation slides,
recording, an access to Shared Memory will be conflict-free if
the following conditions are satisfied across each warp:

  • {T0, T1, .., T7} do not access the same 128-bit bank
  • {T8, T9, .., T15} do not access the same 128-bit bank
  • {T16, T17, .., T23} do not access the same 128-bit bank
  • {T24, T25, .., T31} do not access the same 128-bit bank

To achieve conflict-free stores, the Shared Memory layout remaps the strip-mined arrangement to transpose
the vectors and applies an XOR operation on the column index of each thread's pointer. Specifically,

c++
int store_column = (lane_id % 8) ^ (lane_id / 8);

This transformation on the layout will be instrumental in reading slices of data from Shared Memory
to compute the warp-level matrix multiply using Tensor Cores.

The following figure shows how the first sixteen threads participating in an ldmatrix instruction
logically map to the c=0..31 slice of a matrix in Shared Memory. This slice is known as a "k-group"
within the code because it corresponds to the same K-index of a warp-level matrix multiply.

The lower half of the figure shows the physical arrangement in Shared Memory, with threads offset by row and column
according to the XOR function. By inspection, we can observe there are no bank conflicts, as T0 ... T7 each access unique
banks, as do T8 ... T15. and beyond.

To advance to the next "k-group" within Shared Memory, pointers are updated using an XOR operation according to
the following sequence:

  • ^1 advances from k=0 to k=1
  • ^3 advances from k=1 to k=2
  • ^1 advances from k=2 to k=3
  • ^3 advances from k=3 to k=0

The first of these transitions is shown below.

The CUTLASS warp-level GEMM API defines templates for
loading slices of data from permuted Shared Memory and issuing operations to Tensor Cores.

Updating the Output Tensor

After the mainloop terminates, the accumulator tile of the warp-level GEMM stores a warp's contribution to the output
tensor. However, the distribution of data among threads within the threadblock is specialized for efficient matrix multiply-accumulate
operations using Tensor Cores and is not conducive to efficient, coalesced operations to Global Memory. A data rearrangement is
needed.

The Epilogue is the component for exchanging accumulator elements through Shared Memory, loading slices of the output
matrix or tensor, applying an elementwise operation such as linear scaling or bias, and storing the result to the output tensor.
CUTLASS structures this as several components:

Unit Tests

Unit tests verify the functional behavior of each of the above components in a standalone CUDA kernel. This provides a
convenient environment to

a. inspect the template definition,
b. showcase instantiation of use of these templates in device code, and
c. assert functional correctness.

Convolution unit tests

GEMM unit tests

  • Warp-scoped matrix multiply for Turing Tensor Cores: gemm_sm75.cu

Epilogue unit tests

Convolution Example

This section describes the provided convolution example and is intended to orient the reader to the CUTLASS implementation
of Implicit GEMM Convolution.

Building and Running the Example

Example 09_turing_tensorop_conv2dfprop computes a forward convolutional layer in which inputs and
outputs are 4-b integers. The example source is visible in
examples/09_turing_tensorop_conv2dfprop/turing_tensorop_conv2dfprop.cu.

Before building the example, first perform the prerequisite steps for building any CUTLASS component described here.
Compute capability 7.5 refers to the Turing architecture, and this work requires CUDA 10.2 Toolkit or later to target
Turing Tensor Cores using the native mma PTX instruction.

bash
$ mkdir build && cd build

$ cmake .. -DCUTLASS_NVCC_ARCHS=75

To build the example, execute make 09_turing_tensorop_conv2dfprop from the build directory.

bash
$ make 09_turing_tensorop_conv2dfprop

$ ls examples/09_turing_tensorop_conv2dfprop 
examples/09_turing_tensorop_conv2dfprop

This example provides a simple command line interface to specify the extents of 4D tensors of 4-bit integer elements (cutlass::int4b_t),
initialize them to random values, and compute the result of a convolutional layer. Optionally, the input and output
tensors may be saved to .csv files, and the CUTLASS host-side reference check may be executed to verify correctness.

The complete usage statement is visible by running with --help:

text
$ ./examples/09_turing_tensorop_conv2dfprop/09_turing_tensorop_conv2dfprop --help
09_turing_tensorop_conv2dfprop example

  This example uses Turing's Tensor Core operators on int4 data types to compute
  forward convolution on tensors of layout NHWC.

Options:

  --help               If specified, displays this usage statement.

  --n <int>            Input tensor extent N
  --h <int>            Input tensor extent H
  --w <int>            Input tensor extent W
  --c <int>            Input tensor extent C
  --k <int>            Filter extent K
  --r <int>            Filter extent R
  --s <int>            Filter extent S

  --alpha <float>      Epilogue scalar alpha
  --beta <float>       Epilogue scalar beta

  --ref-check          If set (true), reference check on the host is computed
  --perf-check         If set (true), performance is measured.
  --benchmark          If set (true), performance benchmarking on several layers and batch-size.
  --iterations <int>   Number of profiling iterations to perform.
  --save-workspace     If set, workspace is written to a text file.
  --tag <string>       String to replicate across the first column in the results table



Examples:

$ ./examples/09_turing_tensorop_conv2dfprop/09_turing_tensorop_conv2dfprop  --n=32 --h=224 --w=224 --c=128 --k=256 --r=1 --s=1

$ ./examples/09_turing_tensorop_conv2dfprop/09_turing_tensorop_conv2dfprop  --n=1 --h=224 --w=224 --c=32 --k=32 --r=3 --s=3 --ref-check

Note, this example assumes all tensors are 128b aligned and in format NHWC. Consequently, dimension
C must be divisible by 32 for activations, filters, and output.

If the option --benchmark is passed, several layers from ResNet50 are profiled for various batch sizes.
This sample output was computed on an NVIDIA RTX 2080 compiled with CUDA 10.2.

bash
build$ ./examples/09_turing_tensorop_conv2dfprop/09_turing_tensorop_conv2dfprop --benchmark

Convolution can also be run by the CUTLASS Profiler.

Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Layout (`media/docs/cpp/layout.md`)

Note: This document talks about CUTLASS 2.x layout tag types.
CUTLASS 3.0 deprecates all legacy 2.x layout tags in favour of a single cute::Layout<Shape, Stride>
vocabulary type for all thread and data tensors. Please refer to the
documentation for cute layouts for more details about CUTLASS 3.0's definition of "layout".

Layouts and Tensors

Tensors are mathematical objects represented by a multidimensional array of numeric elements in memory.
These may define two dimensional matrices upon which classical linear algebra computations may be defined or
higher dimensional objects frequently used to structure data used by Deep Learning applications and frameworks.

This document describes design patterns used in CUTLASS to map logical index spaces onto memory (Layouts) and to
indirectly reference tensors in memory (TensorRef and TensorView objects).

As described, CUTLASS adheres to the following terminology which is consistent with the C++ Standard Library.

  • size (scalar): number of elements in a tensor
  • capacity (scalar): number of elements needed to represent tensor in memory (may be larger than size)
  • rank (scalar): number of logical dimensions describing tensor
  • extent (vector): size of each logical dimension in a tensor

CUTLASS Layout Concept

CUTLASS Layouts are a systematic design pattern for the following:

  • Mapping logical index space to physical offsets in memory
  • Storing the dynamic state needed in the above computation
  • Defining a type system for partial specialization of other CUTLASS components

Concept: layouts satisfy the following concept.

c++
/// CUTLASS Layout concept example
struct LayoutConcept {

  /// Logical rank of tensor
  static int const kRank;

  /// Rank of stride vector
  static int const kStrideRank;

  /// Index type used for coordinates
  struct Index;

  /// Long index type used for offsets
  struct LongIndex;

  /// Logical coordinate - satisfies Coord<kRank, ..>
  struct TensorCoord;

  /// Stride object - satisfies Coord<kStrideRank, ..>
  struct Stride

  //
  // Methods
  //

  /// Constructor
  CUTLASS_HOST_DEVICE
  LayoutConcept();

  /// Ctor
  CUTLASS_HOST_DEVICE
  LayoutConcept(Stride stride);

  /// Helper returns a layout to a tightly packed tensor
  CUTLASS_HOST_DEVICE
  static LayoutConcept packed(TensorCoord const &extent);

  /// Function call operator returns the offset of a coordinate in linear memory. 
  /// Assumes coordinate has convention (row, column)
  CUTLASS_HOST_DEVICE
  LongIndex operator()(TensorCoord const &coord) const;

  /// Inverse of layout function, mapping linear offset to logical coordinate
  CUTLASS_HOST_DEVICE
  TensorCoord inverse(LongIndex offset) const;

  /// Returns the stride of the layout
  CUTLASS_HOST_DEVICE
  Stride stride() const;

  /// Returns the stride of the layout
  CUTLASS_HOST_DEVICE
  Stride & stride();

  /// Compute the number of contiguous elements needed to store a tensor with the given size
  CUTLASS_HOST_DEVICE
  LongIndex capacity(TensorCoord const &extent) const;
};

Layout objects generalize leading dimensions of matrices typical in BLAS implementations. For example, cuBLAS assumes
Fortran-style column-major layouts of matrices and refers to this as the matrix's "leading dimension."

c++
cublasGemmEx(
  ...
  ptr_A,      // pointer to first element of matrix A
  lda,        // leading dimension
  ...
);

This implies an element at coordinate (row, column) has offset row + lda * column.

This is equivalently represented by CUTLASS's layout::ColumnMajor type as follows.

c++
layout::ColumnMajor layout(lda); 

int offset = layout({row, column});     // returns row  + lda * column

Other layout functions are possible such as row-major:

c++
layout::RowMajor layout(lda); 

int offset = layout({row, column});     // returns lda * row + column

In both cases, the logical coordinate (row, column) is represented by the same object. This enables an algorithm to be
implemented as generic template, with locations within tensors always specified in logical space. Layout objects map this to
physical offsets in memory.

The layout's ::packed() static method may be used to construct a layout object given the extent of a densely packed tensor.
This method is needed when an algorithm must define a buffer of arbitrary layout.

Example:

c++
typename ArbitraryLayout::TensorCoord extent = make_Coord(...);
typename ArbitraryLayout::TensorCoord coord;

ArbitraryLayout layout = ArbitraryLayout::packed(extent);

int offset = layout({coord});

The layout's ::capacity() method computes the number of locations in memory needed to represent a tensor. This is
useful when allocating memory, as more storage may be needed than what is strictly necessary for a fully packed
tensor.

Example:

c++
int lda = columns + padding;
MatrixCoord extent{rows, columns};

layout::RowMajor layout(lda);

auto capacity = layout.capacity(extent);    // returns rows * (columns + padding)

Accessing elements within a tensor

TensorRef

TensorRef<class T, class Layout> is a structure containing both a pointer to the start of a
tensor and a layout object to access its elements. This is a convenient object which may be
passed to functions to limit an explosion of arguments when the number of stride elements is
numerous.

Example:

c++
int4_t *ptr = ...;
int ldm = ...;

int row = ...;
int column = ...;

layout::ColumnMajor layout(ldm);
TensorRef<int4_t, layout::ColumnMajor> ref(ptr, layout);

int4_t x = ref.at({row, column});     // loads a 4-bit signed integer from the tensor

ref.at({row, column}) = x * 2_s4;     // transforms this quantity and stores it back

TensorView

Matrices and tensors used in linear algebra computations are invariably finite. TensorView<class T, class Layout> extends TensorRef<> by
adding an extent vector to describe the logical extent of the tensor or matrix.

Example:

c++
int4_t *ptr = ...;
int ldm = ...;
MatrixCoord extent = ...;

int row = ...;
int column = ...;

layout::ColumnMajor layout(ldm);
TensorView<int4_t, layout::ColumnMajor> view(ptr, layout, extent);

MatrixCoord coord = {row, column};

if (view.contains(coord)) {     // verify coordinate is in bounds before performing access
  
  int4_t x = ref.at(coord);  
  ref.at({row, column}) = x * 2_s4;
}

A TensorView<> may be constructed from a TensorRef<> succinctly as follows:

c++
layout::ColumnMajor layout(ldm);
TensorRef<int4_t, layout::ColumnMajor> ref(ptr, layout);

TensorView<int4_t, layout::ColumnMajor> view(ref, extent);    // construct TensorView from TensorRef and extent

Note, computations avoid becoming overdetermined by accepting a single problem size component
and TensorRef objects for each of the operands whose extents are implied as a precondition of the operation. By avoiding
redundant storage of extent quantities, CUTLASS minimizes capacity utilization of precious resources such as constant memory.
This is consistent with BLAS conventions.

Summary:

The design patterns described in this document form a hierarchy:

  • T *ptr; is a pointer to a contiguous sequence of elements of type T
  • Layout layout; is an object mapping an index space to a linear offset
  • TensorRef<T, Layout> ref(ptr, layout); is an object pointing to an unbounded tensor containing elements of type T and a layout of type Layout
  • TensorView<T, Layout> view(ref, extent); is an object pointing to a bounded tensor containing elements of type T and a layout of type Layout

Appendix: Existing Layouts

This section enumerates several existing Layout types defined in CUTLASS.

Matrix layouts:

  • PitchLinear: data layout defined by contiguous and strided dimensions. contiguous refers to consecutive elements in memory, where as strided refers to data separated by a uniform stride
    -- Rank: 2
    -- TensorCoord type: PitchLinearCoord
    -- Shape type: PitchLinearShape
    -- Stride rank: 1

  • ColumnMajor: data layout defined by rows and columns dimensions. Can be mapped to PitchLinear by: (contiguous = rows, strided = columns)
    -- Rank: 2
    -- TensorCoord type: MatrixCoord
    -- Shape type: MatrixShape
    -- Stride rank: 1

  • RowMajor: data layout defined by rows and columns dimensions. Can be mapped to PitchLinear by: (contiguous = columns, strided = rows)
    -- Rank: 2
    -- TensorCoord type: MatrixCoord
    -- Shape type: MatrixShape
    -- Stride rank: 1

  • ColumnMajorInterleaved<k>: data layout defined by rows and columns dimensions. Data is packed into a 'column-major' arrangement of row vectors of fixed length.
    -- Rank: 2
    -- TensorCoord type: MatrixCoord
    -- Shape type: MatrixShape
    -- Stride rank: 1

  • RowMajorInterleaved<k>: data layout defined by rows and columns dimensions. Data is packed into a 'row-major' arrangement of column vectors of fixed length.
    -- Rank: 2
    -- TensorCoord type: MatrixCoord
    -- Shape type: MatrixShape
    -- Stride rank: 1

Tensor layouts:

  • TensorNHWC:

Permuted Shared Memory Layouts:

  • TensorOpCongruous<ElementSize>
  • TensorOpCrosswise<ElementSize>

Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Pipeline (`media/docs/cpp/pipeline.md`)

Synchronization primitives

Overview of CUDA's synchronization methods

The CUDA programming model provides 3 abstractions:

  • hierarchical parallelism -- that is, parallel threads
    grouped into hierarchical units such as blocks and clusters;

  • shared memory, through which parallel threads that are
    in the same hierarchical unit can communicate; and

  • synchronization methods for threads.

These abstractions help developers extract
both fine-grained and coarse-grained parallelism,
by making it possible for them to subdivide problems
into independent components,
and to insert synchronization at appropriate points.

Over the years CUDA has introduced several synchronization primitives
that operate at different levels of the hierarchy.
These include

As an extension to this, starting with the Hopper architecture, CUDA added the following improvements:

  • thread block clusters --
    a new level in the thread hierarchy representing
    a group of thread blocks that can coordinate and share data;

  • synchronization instructions for a thread block cluster and threads within a cluster scope.

CUTLASS's abstractions for Hopper features

CUTLASS now includes abstractions
for the following features introduced in Hopper.

  1. Thread block cluster - level synchronization and query
    APIs

  2. Abstractions for new
    barrier instructions
    which help with efficient synchronization
    of threads within a thread block cluster.

Asynchronous pipelines

In order to write a performant GEMM Kernel,
software pipelining is critical to hide the latency of global memory loads.
(Please refer to the
Efficient GEMM document.)
Different threads or groups of threads
may have different roles in the pipeline.
Some are "producers" that load data or perform computations
to satisfy other threads' input data dependencies.
The same or different threads may be "consumers"
that do other work with those input data dependencies,
once they are satisfied.
Starting with the Hopper architecture,
the presence of hardware-accelerated synchronization instructions
make it possible for "producer" and "consumer" threads
to communicate with each other efficiently
about their data dependencies.

Implementing a persistent GEMM algorithm calls for managing
dozens of different kinds of asynchronously executing operations
that synchronize using multiple barriers organized as a circular list.
This complexity is too much for human programmers to manage by hand.
As a result, we have developed
asynchronous Pipeline classes.
These classes help developers orchestrate a pipeline
of asynchronous producer and consumer threads,
without needing to worry about lower-level hardware details.
These classes serve a similar function as the various
pipeline abstractions
in libcu++.

Pipeline methods

Producer acquire

The producer_acquire method is to be used by asynchronous producer threads
before issuing other instructions associated with a particular pipeline stage
(e.g., copy or write).

This is a blocking instruction
which blocks further execution of producer threads
unless the particular stage waiting to be acquired
is released by a consumer.

We say that a pipeline at its start is "empty" if producer threads are free to produce and do not need to wait for a consumer release -- that is, if an acquire operation is expected to succeed. If the pipeline at its start is empty, then we can either skip performing producer acquire operations during the first pass through the pipeline stages, or use the make_producer_start_state method. The latter ensures that the acquire operation will succeed at the start of a pipeline.

Producer commit

The producer_commit method is to be issued by asynchronous producer threads
after the instructions associated with a particular stage
(e.g., shared memory writes) have completed,
in order to notify the waiting asynchronous consumer threads.
This is a nonblocking instruction.

This API may result in a No-Op in some cases,
if the producer instructions also update the barrier stage associated automatically
(e.g., TMA_based producer threads using the PipelineTmaAsync class).

Consumer wait

The consumer_wait method is to be used by consumer threads
before consuming data from a particular pipeline stage
which is expected to be produced by producer threads.

This is a blocking instruction. That is,
until the producer threads have committed to a particular stage,
this instruction is expected to block further execution of consumer threads.

Consumer release

The consumer_release method is to be used by consumer threads
to signal waiting producer threads that they have finished consuming data
associated with a particular stage of the pipeline.
This is a nonblocking instruction.

Pipeline example

c++
// 4-stage Pipeline
static constexpr int NumStages = 4;
using MainloopPipeline = typename cutlass::PipelineAsync<NumStages>;
using PipelineState = typename cutlass::PipelineState<NumStages>;

// 2 producer threads and 1 consumer thread 
typename MainloopPipeline::Params params;
params.producer_arv_count = 2;
params.consumer_arv_count = 1;
MainloopPipeline pipeline(shared_storage.storage, params);
  
// Producer threads
if (thread_idx == 0 or thread_idx == 1) {
  PipelineState smem_pipe_write = cutlass::make_producer_start_state<MainloopPipeline>();
  for ( ; iter > 0; --iter) {
    pipeline.producer_acquire(smem_pipe_write);

    // Producer ops
    // If any memory operations are involved, then we also need
    // to guarantee that writes are completed and visible to consumer(s).

    pipeline.producer_commit(smem_pipe_write);
    ++smem_pipe_write;
  }
}
else if (thread_idx == 2) {
  PipelineState smem_pipe_read;
  for (; iter > 0; --iter) {
    pipeline.consumer_wait(smem_pipe_read);

    // Consumer ops

    pipeline.consumer_release(smem_pipe_read);
    ++smem_pipe_read;
  }
}

In this example, we create an instance of the asynchronous pipeline class PipelineSync,
and then synchronize among 3 asynchronously executing threads:
2 producer threads and 1 consumer thread.

Please note that this is a basic example.
There are different versions possible,
depending on what the producer and consumer threads are doing.
Please refer to our unit tests
and the other pipeline classes
for more details.

Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Profiler (`media/docs/cpp/profiler.md`)

CUTLASS Profiler

The CUTLASS Profiler is a command-line driven test and profiling environment for CUTLASS computations
defined in the CUTLASS Instance Library. The CUTLASS Profiler is capable of executing each GEMM, Sparse Gemm,
Conv2d, and Conv3d kernel.

The CUTLASS Profiler may be compiled with:

bash
$ make cutlass_profiler -j

To limit compilation time, only one tile size (typically 128x128) and threadblock cluster size (typically 2x1x1) is instantiated for each data type,
math instruction, and layout. To instantiate all sizes, set the following environment variable when running CMake from an
empty build/ directory.

bash
$ cmake .. -DCUTLASS_NVCC_ARCHS="70;75;80" -DCUTLASS_LIBRARY_KERNELS=all  -DCUTLASS_UNITY_BUILD_ENABLED=ON
...
$ make cutlass_profiler -j

Enabling the unity build places multiple kernel instances in one compilation unit, thereby reducing size of the compiled
binary and avoiding linker limitations on some platforms.

The CUTLASS Profiler sources are stored in:

bash
tools/
  profiler/

Emitting kernels via `emit_kernel_listing.py`

We provide a Python script emit_kernel_listing.py that allows a user to selectively test a subset of profiler-based kernels stamped out in generator.py. A unique benefit to generate kernels and test via this script is that it can feed a series of runtime arguments, such as different M/N/K and alpha/beta, to each kernel, instead of relying on a single default value. It also properly generates runtime datatype and cluster shapes for certain kernels to help reduce the generated kernel count and accordingly the total compilation time. An interested user may refer to emit_kernel_listing.py for details. To enable this new feature, a user should add -DCUTLASS_BUILD_FOR_PROFILER_REGRESSIONS=ON when building CUTLASS profiler.

Instantiating more kernels with Hopper

With Hopper (SM90), you will need to use an additional flag,
CUTLASS_LIBRARY_INSTANTIATION_LEVEL, in order to instantiate all possible combinations,
which unlike previous architectures, will be in the order of millions of kernels.
Due to this, CUTLASS_LIBRARY_KERNELS must be non-empty, since generating and filtering these
kernels alone can take hours.
You must also exercise caution, because not all of these configs are tested, and some may fail to
compile or fail to launch at runtime.

bash
$ cmake .. \
  -DCUTLASS_NVCC_ARCHS="90a" \
  -DCUTLASS_LIBRARY_KERNELS="cutlass3x_sm90_tensorop_gemm_f16_f16_f32_void_f32_*" \
  -DCUTLASS_LIBRARY_INSTANTIATION_LEVEL="max" \
  -DCUTLASS_UNITY_BUILD_ENABLED=ON

The CUTLASS profiler employs a four-digit integer level (global instantiation level) mechanism to manage the generation of kernel configurations. This global instantiation level decides the behavior of multiple "generators" by defining how many and which combinations of configurations are produced. If a global instantiation level contains fewer than four digits, it can be padded with leading zeros to ensure it is four digits long. Each of the four digits in the global level corresponds to a specific category that influences kernel generation, from right to left:

  1. Instruction Shape
  2. MMA Shape Multiplier
  3. Cluster Shape
  4. Schedule Pruning

Cluster shape levels define the number of CTAs (Cooperative Thread Arrays) included in the kernel generation:

  • Level 0: Only (1, 2, 1) cluster shape.
  • Level 1: Clusters with 2 CTAs.
  • Level 2: Clusters with 1 or 2 CTAs.
  • Level 3: Clusters with 1, 2, or 4 CTAs.
  • Level 4: Clusters with 1, 2, 4, or 8 CTAs.
  • Level 5: Clusters with 1, 2, 4, 8, or 16 CTAs.

The MMA multipliers are combined with MMA instruction shapes (WGMMA shapes) to form CTA shapes. The levels for MMA multipliers determine the configurations generated for different data types.

  • Levels [0, 3]: Control the specific configurations generated for various data types.
  • Level 9: Activates exhaustive mode, generating all possible configurations.

Higher levels encompass a broader range of CTA configurations, resulting in more comprehensive kernel generation.

Instruction shape levels control the selection of WGMMA shapes used in kernel generation:

  • Level 0: Generates the "default" shape only.
  • Level 1: Includes additional shapes for unpruned cases, specifically for TF32 data type.
  • Level 2: Includes shapes that are powers of 2.
  • Level 3: Includes all other shapes.

The detailed definition of the three instantiation levels controlling cluster shape, MMA shape multiplier, and instruction shape can be found in sm90_shapes.py.

Schedule pruning levels decide the epilogue schedule and mainloop schedule to stamp out a kernel instance. As defined in get_valid_schedules in sm90_utils.py,

  • Level >= 1: Indicates that no pruning is being applied.
  • Level 0: Indicates pruning according to existing generator.py behavior.

An instantiation level 500, which is padded to 0500, thus indicates:

  • Instruction Shapes: At level 0, generating only the "default" shape.
  • MMA Multipliers: At level 0, generating only one multiplier, (2, 1, 4).
  • Cluster Sizes: At level 5, allowing for clusters with 1, 2, 4, 8, or 16 CTAs.
  • Schedule Pruning: At level 0, where pruning is applied according to the existing generator.py behavior.

Instantiating more MMA shapes with Hopper

When instantiating more tile shapes, specially non-power-of-2 Tile-N shapes, make sure to enable CUTLASS_ENABLE_SM90_EXTENDED_MMA_SHAPES.
This may lead to some increase in per-kernel compilation times.
When CUTLASS_LIBRARY_INSTANTIATION_LEVEL is set, then CUTLASS_ENABLE_SM90_EXTENDED_MMA_SHAPES is enabled by default.

Mixed input data type kernels for Hopper

With Hopper (SM90), the kernel generator will generate the following combinations of mixed input data types ("mixed dtype"):

dtype(A) dtype(B)
e4m3 f16, bf16
e5m2 f16, bf16
int8 f16, bf16
uint8 f16, bf16
int4 f16, bf16
int4 e4m3, e5m2
uint4 f16, bf16
int2 f16, bf16
uint2 f16, bf16

For each mixed dtype kernel, the kernel generator will generate combinations of three different running modes:

  • Convert-only
  • Scale-only
  • Scale-with-zero-point-shifting

For {4-bits-dtype, 8-bits-dtype} x 16-bits-dtype, the kernel generator will further generate kernels using shuffled layouts for the narrow data type matrix, which may have a better performance compared to its non-shuffle counter parts.

Instantiating more kernels with Blackwell

Blackwell (SM100) and Blackwell Ultra similarly support
CUTLASS_LIBRARY_INSTANTIATION_LEVEL, in order to instantiate all possible combinations.
Due to this, CUTLASS_LIBRARY_KERNELS must be non-empty, since generating and filtering these
kernels alone can take hours.
You must also exercise caution, because not all of these configs are tested, and some may fail to
compile or fail to launch at runtime.

bash
$ cmake .. \
  -DCUTLASS_NVCC_ARCHS="100f" \
  -DCUTLASS_LIBRARY_KERNELS="cutlass3x_sm100_tensorop_gemm_f16_f16_f32_void_f32_*" \
  -DCUTLASS_LIBRARY_INSTANTIATION_LEVEL="max" \
  -DCUTLASS_UNITY_BUILD_ENABLED=ON

The CUTLASS profiler uses the same four-digit integer level (global instantiation level) mechanism to manage the generation of kernel configurations for Blackwell as well:

  1. Instruction Shape
  2. MMA Shape Multiplier
  3. Cluster Shape
  4. Data Type and Schedule Pruning

Note for Blackwell kernels an MMA shape multiplier is no longer necessary since Blackwell kernels do not have a different
ping pong or cooperative schedule. The profiler ignores this digit when instantiating.

Cluster shape levels define the number of CTAs (Cooperative Thread Arrays) included in the kernel generation:

  • Level 0: Only dynamic cluster shapes.
  • Level 1: For 1SM kernels (1, 1, 1) and (2, 1, 1) for 2SM kernels.
  • Level 2: For 1SM kernels we also have (1, 2, 1) and for 2SM we have (2, 2, 1) and (4, 1, 1).
  • Level 3: For 1SM kernels we have (1, 4, 1) and for 2SM we have (2, 4, 1) and (4, 2, 1).
  • Level 4: For 1SM kernels we have (4, 4, 1) and for 2SM we have (4, 4, 1).
  • Level 5: For 1SM kernels we have (2, 1, 1).
  • Level 6: For 1SM kernels we have (2, 2, 1) and (4, 1, 1) and for 2SM kernels we have (8, 1, 1).
  • Level 7: For 1SM kernels we have (2, 4, 1) and (4, 2, 1)
  • Level 8: For 1SM kernels we have (1, 8, 1) and (8, 1, 1)

Instruction shape levels control the selection of MMA shapes used in kernel generation:

  • Level 0: Generates the "default" shape only.
  • Level 1: Includes additional shapes for FP8, FP6, and FP4 as well as MX and NVFP4.
  • Level 2: Includes small tile shapes.
  • Level 3: Includes some non-power of 2 shapes.
  • Level 4: Includes further small tile shapes and non-power of 2 shapes.
  • Level 5: Includes all shapes.

The detailed definition of the three instantiation levels controlling cluster shape and instruction shape can be found in sm100_shapes.py.

CUTLASS Profiler usage

The CUTLASS Profiler usage statement may be obtained by executing cutlass_profiler --help and appears as follows.

text
/* Detailed source-code truncated for AI context efficiency. */

GEMM

The CUTLASS Profiler is capable of executing GEMM and Sparse GEMM problems.

The CUTLASS Profiler can be built with cuBLAS enabled to use as a reference implementation. If CMake detects
the cuBLAS library available in the system, it is included as a dependency. This may be explicitly overridden
with CMake flag CUTLASS_ENABLE_CUBLAS.

GEMM Arguments

The complete set of arguments available to each operation may be viewed by specifying the operation name
in addition to --help. The argument flags and their aliases usable for GEMM appear as follows.

text
/* Detailed source-code truncated for AI context efficiency. */

The format of tensor argument is followed by <type>:<layout>. The type could be f32 as 32-bit floating point, s8 as 8-bit signed integer, etc. The available types can be referred to the NumericTypeID_enumerants in util.cu. The layout could be row or column. If --enable_sm90_mixed_dtype_shuffle_test=true is used, the actual layout of the narrow data type matrix is a shuffled layout, neither row nor column.

In addition to encoded data types, CUTLASS profiler allows non-encoded generic data types, namely f8, f6, and f4, with corresponding encoding specified through GEMM input argument: --runtime_input_datatype_a and --runtime_input_datatype_b. Currently, six encoding schemes are supported: e4m3, e5m2, e3m2, e2m3, and e2m1.

Cluster shapes can be statically set to Shape; and specified via runtime arguments: cluster_m, cluster_n and cluster_k in CUTLASS profiler. In addition to preferred cluster shapes, a user can also specify fallback cluster shapes via runtime arguments: cluster_m_fallback, cluster_n_fallback and cluster_k_fallback in CUTLASS profiler. Those fallback cluster shapes are smaller shapes than the preferred ones for the hardware to assign when there is no chance to issue a larger preferred cluster to the GPU. There are several rules for using a dynamic cluster: 1) Preferred cluster size should be divisible by fallback cluster size. 2) Grid dim should be divisible by preferred cluster size. 3) Preferred cluster and fallback cluster must have the same depth (cluster_dim.z must be equal). One may refer to our CUTLASS Example 73_blackwell_gemm_dynamic_cluster for more details of the this feature.
Please be noted that this feature (dynamic cluster shapes within a single grid) is only applicable to sm100a kernels. The hardware will rasterize into a single cluster shape for those kernels that do not support this feature even with preferred or fallback cluster shapes assigned.

CUTLASS 3.x kernels for Hopper and Blackwell also support a new feature called programatic dependent launch (PDL). This can be enabled with --use-pdl, and can overlap the epilogue of the prior kernel with the prologue of the next kernel. This can effectively hide kernel prologues. Using PDL can improve performance for back to back GEMMs. See dependent kernel launch for more information. CUDA graphs can also be used (--use-cuda-graphs) with PDL to ensure that smaller kernels are enqueued back-to-back on a stream.

Exhaustive search mode and top-k output ranking according to performance in GFLOPS/s

CUTLASS also allows a few options to enable searching best performing kernel in a broader parameter space.

  1. Sorting Performance Results by GFLOPs/second
    A new option enables users to sort the final performance report based on GFLOPs/second, making it easier to identify the most efficient kernels.

  2. Exhaustive Search for Best Kernel Performance in GFLOPs/second
    This feature allows the profiler to search for the best-performing kernel across a range of problem sizes, swizzle sizes, rasterization orders, and dynamic cluster configurations. It ensures that all viable configurations are considered to maximize performance.

  3. Performance Search Under a Fixed GEMM Shape
    This option enables exhaustive performance tuning for a specific problem size. Unlike the previous feature, this restricts the search to a fixed GEMM shape while still exploring various kernel parameters to find the best configuration.

Usage Examples

1. Finding the Best Performing Kernel

Use the following command to conduct an exhaustive search and sort results by GFLOPs/second:

bash
cutlass_profiler --kernels=*gemm* --enable-kernel-performance-search --sort-results-flops-per-sec

2. Performance Optimization for a Fixed GEMM Shape

To optimize kernel performance for a specific GEMM problem size:

bash
cutlass_profiler --kernels=*gemm* --enable-best-kernel-for-fixed-shape --m=6144 --n=6144 --k=6144 --sort-results-flops-per-sec

To search optimized kernel performance for a series of GEMM shapes (m, n, k = 1024, 2048):

bash
cutlass_profiler --kernels=*gemm* --enable-best-kernel-for-fixed-shape --m=1024,2048 --n=1024,2048 --k=1024,2048 --sort-results-flops-per-sec

It is worth noting that by enabling exhaustive performance search via --enable-kernel-performance-search, a user is still able and responsible to decide parameters like data distribution in argument list, for which a user can choose --dist=uniform,min:-1,max:1,scale:-1 to initialize a tensor with floating point numbers in uniform distribution. Otherwise, those parameters will be initialized to their default values.

For examples above, one can change the kernel filtering regex according to their own use cases.

Example CUDA Core GEMM Operation

Example command line for profiling SGEMM kernels is as follows:

bash
$ ./tools/profiler/cutlass_profiler --kernels=sgemm --m=3456 --n=4096 --k=4096



=============================
  Problem ID: 1

        Provider: CUTLASS
   OperationKind: gemm
       Operation: cutlass_simt_sgemm_128x128_8x2_nn_align1

          Status: Success
    Verification: ON
     Disposition: Passed

          cuBLAS: Passed

       Arguments: --m=3456 --n=4096 --k=4096 --A=f32:column --B=f32:column --C=f32:column --alpha=1 --beta=0 --split_k_slices=1  \
                  --batch_count=1 --op_class=simt --accum=f32 --cta_m=128 --cta_n=128 --cta_k=8 --stages=2 --warps_m=4  \
                  --warps_n=2 --warps_k=1 --inst_m=1 --inst_n=1 --inst_k=1 --min_cc=50 --max_cc=1024

           Bytes: 180355072  bytes
           FLOPs: 115992428544  flops

         Runtime: 6.73655  ms
          Memory: 24.934 GiB/s

            Math: 17218.4 GFLOP/s

Note, the arguments which appear in the output may be used as command line parameters for subsequent invocations.

Example Tensor Core GEMM Operations

To execute kernels targeting Tensor Core operations, supply the flag --op_class=tensorop in the command line.

bash
$ ./tools/profiler/cutlass_profiler --op_class=tensorop --m=3456 --n=4096 --k=8192



=============================
  Problem ID: 1

        Provider: CUTLASS
   OperationKind: gemm
       Operation: cutlass_tensorop_s16816gemm_f16_256x128_32x3_nn_align8

          Status: Success
    Verification: ON
     Disposition: Passed

          cuBLAS: Passed

       Arguments: --m=3456 --n=4096 --k=8192 --A=f16:column --B=f16:column --C=f32:column --alpha=1 --beta=0 --split_k_slices=1  \
                  --batch_count=1 --op_class=tensorop --accum=f32 --cta_m=256 --cta_n=128 --cta_k=32 --stages=3 --warps_m=4  \
                  --warps_n=2 --warps_k=1 --inst_m=16 --inst_n=8 --inst_k=16 --min_cc=80 --max_cc=1024

           Bytes: 180355072  bytes
           FLOPs: 231956545536  flops

         Runtime: 0.98647  ms
          Memory: 170.272 GiB/s

            Math: 235138 GFLOP/s

Covering the problem space

All arguments may have single values or comma-delimited set of values. Integers may also be specified
as an inclusive range with the following syntax start:end:increment or simply start:end.

For example, the following sweeps over the range of the GEMM K dimension from 8 to 4096 in increments
of 8 elements.

bash
$ ./tools/profiler/cutlass_profiler --kernels=cutlass_simt_sgemm_128x128_nn --m=4352 --n=4096 --k=8:4096:8

Output

By default, runtime and computed GFLOP/s are reported for each operation and problem size. Additionally,
a table of comma separated values are reported at the end of the execution. This may be output to a file
with the --output=<filename.csv> command line option as shown:

bash
$ ./tools/profiler/cutlass_profiler --kernels=cutlass_simt_sgemm_128x128_nn            \
                                    --m=3456 --n=4096 --k=8:4096:8 --output=report.csv

To faclitate generation of pivot tables and charts, additional columns may be prepended with the
--tags=<column>:<value> option. One or more tags may be specified using a comma-delimited list.

bash
$ ./tools/profiler/cutlass_profiler --kernels=cutlass_simt_sgemm_128x128_nn            \
                                    --m=3456 --n=4096 --k=8:4096:8 --output=report.csv \
                                    --tags=cutlass:2.2,date:2020-06-08

CUTLASS 3.0 GEMM procedural names

CUTLASS 3.0 introduces a new naming convention for GEMMs used by the profiler targeting the NVIDIA
Hopper architecture and beyond so as to indicate new features of the kernel within the name
(e.g., the cluster shape).

To best illustrate this naming convention, we will walk through the meaning of each of the components
in a GEMM kernel used by the profiler:

text
cutlass3x_sm90_tensorop_gemm_f16_f16_f32_f16_f32_{optional-mixed-dtype-config}_128x128x64_2x1x1_0_ntn_align8

The components within this name are as follows:

  • cutlass3x: indicates that the kernel was generated through the CUTLASS 3.0 API
  • sm90: indicates that the kernel targets NVIDIA GPUs with compute capability 90
  • tensorop: indicates that the kernel makes use of NVIDIA Tensor Cores
    (as opposed to simt, which indicates the use of "CUDA cores")
  • s: indicates that the Tensor Core instruction being used accumulates in single precision
    (as opposed to h, which indicates half precision)
  • 64x128x16gemm: indicates that the shape of the Tensor Core instruction being used (MxNxK) is 64x128x16
  • f16_f16_f32_f16_f16: indicates that the data types for operands A, B, Accumulator, C and D (in that order).
  • optional-mixed-dtype-config: optional, will be empty if this is not a mixed dtype kernel. For mixed dtype kernels, it contains _cvt, _scl, _sclzr, respectively, for convert-only, scale-only, scale-with-zero-point running modes. It further contains _shfl if the kernel uses a shuffled layout for the narrow data type input matrix.
  • 128x128x64: indicates that the thread block shape used in the GEMM (MxNxK) is 128x128x64
  • 2x1x1: indicates that the cluster shape being used is 2x1x1
  • 0: indicates that the kernel uses the CollectiveBuilder's automatic stage calculation to determine the
    number of pipeline stages in the kernel. Note that 0 does not mean that no stages are used. A nonzero value indicates that automatic stage calculation is not performed and indicates the number of pipeline stages to be used.
    This 0 is only added to the kernel's procedural name, the profiler will still report the actual stage count
    when printing the kernel argument details (--stages=N) and kernel discovery will still support filtering through the --stages argument.
  • ntn: indicates that the layouts for operands A, B, and C are column major ("n"; non-transposed),
    row major ("t"; transposed), and column major, respectively.
  • align8: indicates that the maximum alignment between operands A and B is 8.

Note that in some special cases where the input A/B types do not match that of the MMA
instruction's, the MMA facing input type is added to the instruction string as well.

text
cutlass3x_sm90_tensorop_tf32gemm_f32_f32_f32_f32_f32_128x128x32_2x1x1_0_tnn_align4
  • s64x128x8tf32gemm: indicates that the MMA consumes inputs in tf32 format, and therefore
    the kernel performs rounding of the f32 values in global memory while loading them into shared memory.

For custom mainloop or epilogue schedules, details of the opted-in schedule are appended to the end of the
kernel name. For example,

text
cutlass3x_sm90_tensorop_gemm_f16_f16_f16_void_f16_128x128x64_1x1x1_0_nnn_align8_warpspecialized_cooperative_epi_tma
  • warpspecialized_cooperative: Mainloop employs a persistent warp-specialized mainloop and kernel schedule.
  • epi_tma: Kernel epilogue employs TMA based vectorization.
  • f16_f16_f16_void_f16: In this case, C type is set to void, indicating that residual matrix support
    is disabled.

Further Documentation

For documentation on profiling blockwise and groupwise (software scaled) GEMMs see the example 81 README.

Convolution

The CUTLASS Profiler is capable of executing 2-D and 3-D convolution problems for forwards and backwards
operator variants.

The CUTLASS Profiler can be built with cuDNN enabled to use as a reference implementation. If CMake detects
the cuDNN library available in the system, it is included as a dependency. This may be explicitly overridden
with CMake flag CUTLASS_ENABLE_CUDNN.

bash
$ cmake .. -DCUTLASS_LIBRARY_OPERATIONS=conv2d -DCUTLASS_ENABLE_CUDNN=OFF
...
$ make -j16 cutlass_profiler

Convolution Arguments

text
/* Detailed source-code truncated for AI context efficiency. */

Example CUDA Core Convolution Operation

Example command line for profiling forward propagation convolution kernels on CUDA cores is as follows:

bash
$ ./tools/profiler/cutlass_profiler --kernels=simt_sfprop  --verification-providers=device --n=8 --h=224 --w=224 --c=128 --k=128 --r=3 --s=3


=============================
  Problem ID: 1

        Provider: CUTLASS
   OperationKind: conv2d
       Operation: cutlass_simt_sfprop_optimized_128x128_8x2_nhwc

          Status: Success
    Verification: ON
     Disposition: Passed

reference_device: Passed

       Arguments: --conv_kind=fprop --n=8 --h=224 --w=224 --c=128 --k=128 --r=3 --s=3 --p=224 --q=224 --pad_h=1 --pad_w=1  \
                  --stride_h=1 --stride_w=1 --dilation_h=1 --dilation_w=1 --Activation=f32:nhwc --Filter=f32:nhwc --Output=f32:nhwc  \
                  --conv_mode=cross --iterator_algorithm=optimized --alpha=1 --beta=0 --split_k_mode=serial --split_k_slices=1  \
                  --eq_gemm_provider=none --op_class=simt --accum=f32 --cta_m=128 --cta_n=128 --cta_k=8 --stages=2 --warps_m=4  \
                  --warps_n=2 --warps_k=1 --inst_m=1 --inst_n=1 --inst_k=1 --min_cc=50 --max_cc=1024

           Bytes: 2055798784  bytes
           FLOPs: 118482796544  flops

         Runtime: 8.13237  ms
          Memory: 235.431 GiB/s

            Math: 14569.3 GFLOP/s

Example Tensor Core Convolution Operation

Example command line for profiling forward propagation convolution kernels runing on Tensor Cores is as follows:

bash
$ ./tools/profiler/cutlass_profiler --kernels=tensorop*fprop  --verification-providers=device --n=8 --h=224 --w=224 --c=128 --k=128 --r=3 --s=3



=============================
  Problem ID: 1

        Provider: CUTLASS
   OperationKind: conv2d
       Operation: cutlass_tensorop_s16816fprop_optimized_f16_128x128_64x4_nhwc

          Status: Success
    Verification: ON
     Disposition: Passed

reference_device: Passed

       Arguments: --conv_kind=fprop --n=8 --h=224 --w=224 --c=128 --k=128 --r=3 --s=3 --p=224 --q=224 --pad_h=1 --pad_w=1  \
                  --stride_h=1 --stride_w=1 --dilation_h=1 --dilation_w=1 --Activation=f16:nhwc --Filter=f16:nhwc --Output=f32:nhwc  \
                  --conv_mode=cross --iterator_algorithm=optimized --alpha=1 --beta=0 --split_k_mode=serial --split_k_slices=1  \
                  --eq_gemm_provider=none --op_class=tensorop --accum=f32 --cta_m=128 --cta_n=128 --cta_k=64 --stages=4  \
                  --warps_m=2 --warps_n=2 --warps_k=1 --inst_m=16 --inst_n=8 --inst_k=16 --min_cc=80 --max_cc=1024

           Bytes: 1130659840  bytes
           FLOPs: 118482796544  flops

         Runtime: 0.945071  ms
          Memory: 1114.21 GiB/s

            Math: 125369 GFLOP/s

Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Programming Guidelines (`media/docs/cpp/programming_guidelines.md`)

Programming Guidelines

Hierarchical Organization

The CUTLASS 3.0 GEMM API document
explains CUTLASS 3.0's hierarchical organization,
based conceptually on parallelization strategy.
This differs from CUTLASS 2.x's approach,
which more closely mirrors the GPU hardware hierarchy
of thread blocks, warps, and threads.

Design Patterns

CUTLASS aims for the highest performance possible on NVIDIA GPUs.
It also offers flexible components that can be assembled and customized
to solve new problems related to deep learning and linear algebra.
Given a tradeoff between simplicity and performance,
CUTLASS chooses performance.
Consequently, several design patterns are necessary
to yield a composable structure
while also satisfying these performance objectives.

Templates

CUDA C++ templates and modern generic programming techniques enable CUTLASS device code to span a large design space.

This design space includes:

  • Mixed precision arithmetic and data storage
  • Kernels specialized for layout and problem size
  • Support for kernel fusion

Moreover, templates provided a structured approach to collecting compile-time constants such as tile dimensions. These
must be template arguments to target static array allocation and take advantage of loop unrolling, constant folding,
and function inlining.

Constant Memory

Several CUTLASS template classes exhibit a pattern in which problem-specific internal state is known at kernel
launch time and remains invariant throughout the execution of a kernel. For example, tile iterators compute several
offsets based on the strides of the input tensor that is added to an internal pointer when loading the elements
of a tile. These are computed from the tensor stride and never updated; the per-thread internal state consists
only of the internal global memory pointer.

CUTLASS can take advantage of this CUDA grid-invariant property by constructing the object in host code and passing
a composed parameters structure to the kernel. This confers two benefits: (1.) invariant state is held in constant
memory, and (2.) there is no overhead to compute the initial state by each thread.

The design pattern in CUTLASS is for classes with nontrivial constructors to define struct Params as an inner class
which contains grid-invariant state. These should define a constructor and an initialize() method. The Params
structure should also include a data member corresponding to each data member in the parent class, so these too can
be properly constructed in host code. The parent class should define a constructor which accepts Params const & as
its first argument.

Composable Shared Memory

Shared memory requires explicit effort by the programmer to allocate and de-allocate. CUTLASS follows the paradigm
introduced by CUB to define composed structures for storing data intended to be held
in shared memory. Any object requiring shared memory storage for itself or its data members should define a child
structure called SharedStorage. This holds data needed by the class and also instantiates SharedStorage
objects for each data member.

To be consistent, this pattern defines a convention in which classes define internal shared memory storage requirements.
Classes should consider all SharedStorage structures to be opaque other than their own child class. When the lifetimes
of child objects are known to be non-overlapping, unions may be used to alias multiple SharedStorage objects to the same
shared memory region and reduce overall shared memory capacity. Developers should carefully note that C++ union rules
require that they only access the most recently written ("active") member of the union; this differs from C rules.

For host to device ABI compatibility, inheritance from a class is only permitted if the superclass is unique to the
child class. This is most easily achieved by templating the parent class by the child class (CRTP).

Loop Unrolling

CUTLASS requires tiles of data to be stored in registers for high-bandwidth access. Simultaneously, high-throughput math instructions
must be issued concurrently with memory instructions to hide latency with relatively few concurrent threads. These objectives are
achieved by unrolling loops whose iteration counts are known at compile time.

Consequently, most loops within the CUTLASS GEMM implementation are specified by constant values and template arguments. The CUDA compiler
is able to unroll the loop bodies, map array elements to registers, and construct an efficient instruction schedule.

All loops expected to be unrolled should be annotated with CUTLASS_PRAGMA_UNROLL to explicitly direct the compiler
to unroll them.

c++
int const kN = 8;
Array<float, kN> x;                       // Array we would like to store in registers

CUTLASS_PRAGMA_UNROLL                     // Directs the CUDA compiler to unroll this loop.
for (int idx = 0; idx < kN; ++idx) {      // Loop has constant number of iterations.

  x[i] = float(idx);                      // Indirect access by induction variable results in
                                          // direct register access.
}

Style

If you see an issue in code formatting, fix it

You are empowered to reformat code.
Please, however, consider making reformatting changes separately from content-related changes.

No automatic code formatting

Do not use any kind of automatic code formatting,
like clang-format, on CUTLASS code.

C++ style

CUTLASS is a C++ project

CUTLASS is a C++ project. CUDA C++ is a C++ dialect.
Therefore, we write using standard C++ idioms as much as possible.
We aim for portability to as many compilers as possible,
by writing host code in Standard C++
and device code in CUDA C++
that resembles Standard C++ as much as possible.
This improves usability
for the general community of C++ developers,
and makes it easier for new staff to join the project.

Follow Standard C++ idioms where possible

Regarding "standard C++ idioms,"
CUTLASS source code follows the following guidelines,
with deviations only because of compiler limitations
or where performance absolutely requires it.
"Performance requires it" implies measurement.
Deviations should be limited in scope
and we should always strive to eliminate them.

C is not a subset of C++

C is not a subset of C++.
Some valid C is not valid C++, and some valid "C-looking" C++ is not valid C.
See e.g., the informative C++ Standard Committee (WG21) document
P2735R0,
which explains ways in which the same code has different behavior in C vs. C++.
In some cases, code that compiles in both C and C++,
and is correct in C, has undefined behavior (can crash or worse) in C++.
The "type.punning" section of P2735R0 specifically relates to unions.

Spacing and line length

  • Use spaces, not tabs.

  • Use 2 spaces to indent.

  • Use at most 100 characters per line.

(Right-align tensor shape layout comments at column 120.
Please see below.)
Lines longer than 100 characters typically wrap unfavorably
when viewed in Github's pretty printer.

Formatting function declarations and definitions

Short function headers can go on one line.

Do not insert a newline between the parenthesis
that closes the function's parameters and
the curly bracket that opens the function's body.

c++
int short_name(int x, int y) {
  return x + y;
}

If the function name and its parameters are too long to fit on one line,
break the line immediately after the opening parenthesis
that starts the parameter list. Then, double-indent the parameters
to distinguish them from the body of the function.

c++
void indeed_my_fellowbeings_this_function_name_is_unusually_long(
    std::uint32_t foo, // parameters are double-indented
    std::uint32_t const* bar,
    TypeA a,
    TypeB b,
    TypeC c) { // the ) and { go on the same line still
  auto d = body_of_the_function(a, b, c); // body is single-indented
  // ... more code ...
}

For a constructor with a long parameter list,
break the line after the parentheses, just as with other functions.
Align the colon that starts the constructor's initializer list
flush with the comma on the next line.

As with functions, double-indent the parameters
to distinguish them from the constructor body.
Here is an example.

c++
class YesTheCommunityAgreesThatTheNameOfThisClassIsIndeedExtremelyLong {
public:
  CUTLASS_HOST_DEVICE
  YesTheCommunityAgreesThatTheNameOfThisClassIsIndeedExtremelyLong(
      int this_is_the_first_parameter_and_its_name_is_long,
      int this_is_the_second_parameter_and_its_name_is_also_long,
      int this_is_the_third_parameter_and_its_name_is_long_too)
  : x_(this_is_the_first_parameter_and_its_name_is_long)
  , y_(this_is_the_second_parameter_and_its_name_is_also_long)
  , z_(this_is_the_third_parameter_and_its_name_is_long_too) {
    // constructor body
    // more of the constructor body
  }

private:
  int x_ = 0;
  int y_ = 0;
  int z_ = 0;
};

Formatting function calls

When calling a function or function object with a long name,
break the line right after the invoking open parenthesis.
Here are some examples.

c++
detail::very_long_function_object_name<TemplateArgument>{}(
  params.long_parameter_name, some_operator.another_long_function_name());

detail::an_even_longer_function_object_name<TemplateArgument1, TemplateArgument2>{}(
  params.long_parameter_name, some_operator.long_member_function_name(),
  another_operator.another_long_member_function_name(x, y, z));

If-else brackets and spacing

  • Always use braces with conditionals such as if,
    even if the body is a single line.

  • Use a space after control flow keywords
    such as if, for, and while.

  • Use a space after the parenthesis closing a conditional
    such as if, and the curly bracket opening a scope.

  • Use a new line between the closing brace
    of an if branch, and the else keyword.

c++
if (condition) { // space after if, and between ) and {
  // ... code ...
} // newline after }
else {
  // ... other code ...
}

// space after keyword for
for (int k = 0; k < num_iters; ++k) {
  // ... still more code ...
}

East const

CUTLASS uses the
"East const"
convention.
That is, the const or constexpr keyword
goes after the type, not before.
The general rule is that const or constexpr
modifies the type to the left of it.
Here are some examples.

c++
float constexpr compile_time_constant = 42.3f;

float const const_float = /* whatever */;
float const& reference_to_const_float = const_float;
float const* pointer_to_const_float = &const_float;
float const* const const_pointer_to_const_float = &const_float;

float nonconst_float;
float& reference_to_nonconst_float = nonconst_float;
float* pointer_to_nonconst_float = &nonconst_float;
float* const pointer_to_nonconst_float = &nonconst_float;

Contrast this with "West const" style, e.g.,

c++
const float const_float = /* whatever */;
const float* pointer_to_const_float = &const_float;

Alignment of reference and pointer types

For reference and pointer types,
align the & resp. * flush against the type
that it modifies. This is called "left alignment."

For example, do this:

c++
int const& var;
int const* var;

and not this.

c++
int const &var;
int const *var;

Avoid calling functions "fast" or "optimized"

Putting words like "fast" or "optimized"
in the name of a function
assumes that the "fast" path is actually faster.
That might be true now, but later changes
(in the code, compilers, or GPU hardware)
might make it false. In that case,
your name could be unintentionally misleading.
Consider instead a name that briefly describes
the algorithm or feature that is relevant for optimization.
For example, compute_on_host is more meaningful
than compute_slowly, and computing on host
might be faster in some cases
(e.g., if the data are already on host
and the algorithm is not GPU-friendly).

CUTLASS code has not always followed this rule in the past.
Some functions and classes might have words like "fast" in their name.
New code should follow this rule, however.

Avoid creating unconstrained templated functions with common names

See C++ Core Guidelines T.47:
"Avoid highly visible unconstrained templates
with common names."
Argument-dependent lookup (ADL) means that
if users call a function name without specifying the namespace,
the compiler can find overloads
of that function in any namespace.
This can lead to ambiguous overloads in users' code,
just because they happened to include one of your header files
that exposes an unconstrained function template.
The following illustrates this
with an unconstrained swap overload in the cutlass namespace.

text
/* Detailed source-code truncated for AI context efficiency. */

Function return values and in-out parameters

Prefer return values to output parameters

In general, avoid in-out mutable references to return a value.
If you need to return multiple values,
you can return them by struct or tuple,
rather than by output references.
This includes the special case of error reporting
by returning either a value or an error code.
Please see the next section for details.

c++
// Instead of passing in-out mutable references ...
void not_preferred(float& input_and_output); // not preferred

// keep functions pure and return value types instead
float preferred(float input); // preferred

Return multiple values by struct or tuple

Sometimes a function needs to return multiple values. In that case, consider the following, in decreasing order of preference.

  1. Return a struct. This lets you name the fields
    (for more self-documenting code),
    yet still permits use of structured binding.

  2. Return a tuple. If you need a tuple type
    that works on device, use cute::tuple.
    (Please note that cute::tuple does not work
    for all the types that work in std::tuple.
    CuTe's documentation explains.)

  3. Resort to "returning" multiple values by output references
    only if performance requires it.

Here is an example of the struct approach for named values.
For a comparable example in the C++ Standard,
please see std::allocate_at_least,
which returns std::allocation_result.

c++
struct my_computation_result {
  float value = 0.0f;
  float relative_error = 0.0f;
  bool success = false;
};

my_computation_result my_computation(float tolerance);

void foo(float tolerance) {
  // Approach 1: Use structured binding.  The names
  // you choose on the left-hand side have nothing
  // to do with the struct, so it's up to you
  // to get the order right.  On the other hand,
  // this code works whether my_computation returns
  // a struct or a tuple.
  auto [val, rel_err, ok] = my_computation(tolerance);

  // Approach 2: Keep the struct and use its named fields.
  // This approach prevents errors like mixing the order of return types.
  // However, it only works for structs, not for tuples.

  auto result = my_computation(tolerance);
  if (not result.success) {
    // computation did not succeed
  }
  else if (result.relative_error > tolerance) {
    // successful but relative error too large
  }
  else {
    // successful and relative error is in bounds
  }
}

Reporting errors from a function that returns one or more values

We may want to return one or more values
from a function that could fail
or otherwise report errors.
That is, the function either

  • returns one or more valid values, or

  • does not return any values and reports an error,

but NOT BOTH. We contrast this with cases
when it's meaningful to report both a result
and whether the result is satisfactory.
For example, when solving
a system of nonlinear equations iteratively,
users may want the approximate computed solution,
even if the iteration did not succeed
by converging to the desired tolerance
in the desired number of steps.
(Users may want to invest more steps,
or use the current approximation
to jump-start a different algorithm.)

We're talking here about the "either valid value(s),
or error, but not both" case.
For this case, C++ offers a few options.

  1. Return the value(s), or throw an exception on error

  2. std::expected (requiring C++23) or something like it

  3. std::optional (for a Boolean error state)
    or something like it

  4. std::variant (a C++17 fall-back for std::expected)
    or something like it

  5. C-style interface: return an error code,
    and "return" the values as output parameters

We usually cannot or do not want to
throw exceptions on device.
Some code projects forbid exceptions entirely
(on host or device)
and tell the compiler to disable them.
If we exclude a C-style interface (the last option)
as not idiomatic C++, then for host-only code,
std::expected, std::optional, and std::variant
all work.
For code that needs to build and run on device,
we can fall back to libcu++ equivalents
in the cuda::std:: namespace, when they exist.
Otherwise, we must resort to returning a struct or tuple
with the value and the error information,
and ask users not to use the value on error.
This is acceptable if the value can be constructed
cheaply with a reasonable default.

Performance of different value-or-error reporting methods

P1886R0
(Ben Craig, "Error speed benchmarking")
surveys different ways in Standard C++
to report errors from a function
that returns one or more values,
and compares their (host-only) performance
with different compilers.

Use aggregate initialization when returning a struct or tuple

Use aggregate initialization when returning a struct or tuple.
This avoids duplication of the return type name.

c++
struct foo_result {
  float value = 0.0f;
  float error = 0.0f;
  bool success = false;
};

foo_result foo(std::span<const float> input) {
  // ... code  ...

  // Prefer this.  We know what type the function returns.
  return {val, err, ok}; // prefer this

  // Naming foo_result again here is unnecessary.
  // return foo_result{val, err, ok};
}

However, note that this won't work if the function returns auto.
The general rule is to avoid code duplication.

c++
auto foo(std::span<const float> input) {
  // ... code  ...

  if constexpr (some_condition) {
    return foo_result{val, err, ok};
  }
  else {
    return bar_result{val, err, ok};
  }
}

Prefer using the actual return type to auto, if you know the type

C++ lets you use auto to deduce the type returned from a function.

  • If you know the actual type, prefer using the type instead of auto.

  • Use Constructor Type Argument Deduction
    (CTAD) if you know that a function returns some type
    (e.g., Tensor), but don't know the type's template arguments.

  • Use auto in structured bindings (where you have to use it anyway). This also makes your code agnostic of whether the return type is a struct, tuple, pair, or other tuple-like type.

  • Be careful using auto with types that provide expression templates.

Contrast this with "Almost Always Auto" (AAA) style.
We deliberately choose not to follow AAA style,
for the following reasons.

  • Using the actual type when we know it can help prevent common loss-of-precision errors in mixed-precision computations, an important use case for CUTLASS.

  • CTAD gives us much of the brevity of AAA, with more clarity.

  • Using the actual type instead of auto can prevent common dangling errors with expression templates.

Classes and structs

Type names use CamelCase.
That is, words start with capital letters.
The remaining letters in the word are lower case,
and words are joined with no intervening underscores.
The only exception is when implementations are
a drop-in replacement for C++ Standard Library components.

Follow the
C++ Core Guidelines
to decide whether to use class or struct.

  • Use class when the object must maintain an invariant.
    Data members related to the invariant should be private.

  • Use struct when the class has no invariant to maintain,
    and data members may vary arbitrarily with respect to each other.

Prefer nonmember functions and statelessness where possible.
Member functions imply invariants.
More invariants make code maintenance and testing harder.

Class members

Methods and members are written using snake_case.

Private data and function members have suffix _.

Class Member Order

Members within classes and structures should be organized as follows:

  1. Type and constant definitions

  2. Data members

  3. Constructors

  4. Other methods

This convention follows the
CUB library
and is also described by
Howard Hinnant.
It also approximates the usual ordering of chapters
in a typical Systems and Controls textbook.
That is, it

  1. identifies relevant constants,

  2. defines a state-space representation
    of the dynamical system under study
    (the class's data members), and then

  3. devotes the remaining "chapters" to defining
    the system's dynamical behavior
    (the class's methods).

Here is an example class.

c++
class A {
public:
  // type definitions
protected:
  // protected type definitions
private:
  // private type definitions

public:
  // data members
protected:
  // protected data members
  // STRONGLY TO BE AVOIDED;
  // please see C++ Core Guidelines
private:
  // private data members

public:
  // methods
protected:
  // protected methods
private:
  // private methods
};

For code reuse, prefer composition over inheritance

Suppose that a class hierarchy exists entirely for implementation convenience, so that implementers can reuse code and "program by difference" (changing or adding only what's different from the base class). In the example below, both PipelineA and PipelineB are used by themselves. PipelineB inherits from PipelineA just to avoid duplicating code. There are no virtual member functions, and users don't expect to rely on run-time polymorphism.

c++
class PipelineA {
public:
  PipelineA(Arg0 arg0, Arg1 arg1)
    : arg0_(arg0), arg1_(arg1)
  {}

  void producer_acquire(uint32_t stage, uint32_t phase, uint32_t skip_wait) {
    // ... implementation ... 
  }

  void consumer_release(uint32_t stage, uint32_t skip) {
    // ... implementation ...
  }

private:
  Arg0 arg0_;
  Arg1 arg1_;
};

class PipelineB : public PipelineA {
public:
  PipelineB(Arg0 arg0, Arg1 arg1, Arg2 arg2) :
    PipelineA(arg0, arg1), arg2_(arg2)
  {}

  // Reuse PipelineA::producer_acquire via inheritance

  // Override PipelineA::consumer_release
  void consumer_release(uint32_t stage, uint32_t skip) {
    // ... some other implementation, not invoking parent ...
  }

private:
  Arg2 arg2_;
};

The problem with public inheritance here is that PipelineB is NOT a (versus "is-a," i.e., substitutable-as) PipelineA. In particular, the following code would be incorrect.

c++
void consume_and_release_pipeline(PipelineA* parent) {
  // ... code ...
  parent->consumer_release(stage, skip);
  // ... code ...
}

void use_pipeline( /* other args */ ) {
  // ... code ...
  PipelineB child{arg0, arg1, arg2};
  // ... code ...

  // WRONG!!! SLICES CHILD TO PARENT!!!
  consume_and_release_pipeline(&child); // BAD

  // ... code ...
}

PipelineA::consumer_release is not a virtual member function, so consume_and_release_pipeline would not actually be polymorphic, as callers might have expected from an interface that takes a base class pointer. What's worse is that the resulting slicing could violate PipelineB's invariants, thus putting it in an incorrect state.

The most straightforward way to reuse code would be by changing from inheritance (is-a) to composition (has-a).

text
/* Detailed source-code truncated for AI context efficiency. */

This design prevents users at compile time from incorrectly assuming that PipelineB is a PipelineA. Implementers continue to get compile-time polymorphism, as long as PipelineA and PipelineB implement the same compile-time interface.

Behavioral subtyping

Another reason to avoid public inheritance would be if the public member functions of PipelineA and PipelineB have different behavior, such that the invariants satisfied by the member functions of the base class PipelineA are not satisfied by the correspondingly named member functions of the subclass PipelineB. For example, suppose that both classes have a public producer_arrive member function. However, for PipelineA, this issues a producer arrival only for its own block, whereas for PipelineB, this issues a producer arrival for all blocks in the cluster. Again, PipelineB "is-not-a" PipelineA. The child class doesn't just add behavior onto the parent class; it has completely different behavior. Thus, it fails to satisfy behavioral subtyping: invariants of the parent class's member functions are not satisfied by the child class. Behavioral subtyping is especially important when reasoning about already difficult things like parallel synchronization. The inheritance design would give developers the false impression that PipelineB just adds behavior atop PipelineA, whereas in fact, developers would need to understand both pipeline classes completely to build a correct mental model about their behavior.

The fix is the same: Use composition, not inheritance. As C++ Core Guidelines C.120 explains: "Use class hierarchies to represent concepts with inherent hierarchical structure (only)."

  1. "Make sure the idea represented in the base class exactly matches all derived types and there is not a better way to express it than using the tight coupling of inheritance."
  2. "Do not use inheritance when simply having a data member will do."

Use scoped enums

Use scoped enums (a C++11 feature) for enumerated types.
Use capital letters for the enumerated type name
and prefix k for enumerators like other constants.

c++
enum class MatrixOperation {
  kNone,
  kTranspose,
  kConjugate,
  kHermitian
};

Namespaces

Namespaces are all lower case.
The top-level namespace is cutlass::.
The second nested namespace refers to
the general category of operation
performed by its members: e.g., gemm::.
The third nested namespace refers to
the operations' position in the conceptual hierarchy:
e.g., device::, kernel::, or collective::.

The bodies of namespace definitions should not be indented.
Comments on the closing brace to indicate
the namespace being closed are welcome.

c++
namespace cutlass {
namespace gemm {
namespace kernel {

struct AnotherGemmKernel {
  // ... contents ...
};

} // namespace kernel
} // namespace gemm
} // namespace cutlass

File Names

New files should be named using snake_case
with extension .hpp for header files,
.cu for CUDA sources,
and .cpp for C++ host-only source files.

Header files with extension .h
are CUTLASS 2.x legacy headers.

Macros

Only use macros when the preprocessor
is the only way to accomplish the task.
Do not use macros for literal constants.
Instead, if inside the body of a function,
use constexpr values,
and if at namespace scope, use
inline constexpr variables
(a C++17 feature).

"Namespace" macros by starting them with the module name, e.g., CUTLASS_.
Macros and ONLY MACROS use all capital letters with underscores between words.
For example:

c++
#define CUTLASS_MACROS_USE_ALL_CAPS inline __host__ __device__

Header files such as
cutlass/cutlass.h
and
cute/config.hpp
offer macros for expressing compiler-dependent behavior.
These include

  • replacements for __device__ and/or __host__
    annotations:

    • CUTLASS_HOST_DEVICE or CUTE_HOST_DEVICE
      for functions that run on the host and the device,

    • CUTLASS_DEVICE or CUTE_DEVICE
      for functions that run on the device only,

    • CUTE_HOST
      for functions that run on the host only, and

    • CUTE_HOST_RTC
      for functions that run on the host only,
      but occur as unevaluated operands (of e.g., decltype or sizeof;
      see C++ Standard, [expr.context] 1) in device code; and

  • annotations to loop unrolling:

    • CUTLASS_PRAGMA_UNROLL or CUTE_UNROLL
      for full unrolling of loops with constant trip counts, and

    • CUTLASS_PRAGMA_NO_UNROLL or CUTE_NO_UNROLL to prevent unrolling.

Guard all headers with `#pragma once`

Use #pragma once to guard all headers.

CuTe Layout Comments

  • Right-align tensor shape layout comments at column 120.
  • If layout comment is too long do your best to align it.
  • If layout comment is too long and there are many related tensors
    that the reader should read together,
    try to align the layout comments of related tensors.

Here are a couple examples.

c++
Tensor mC = make_tensor(make_gmem_ptr(params.ptr_C), make_shape(M,N), params.dC);                              // (M,N)
Tensor mD = make_tensor(make_gmem_ptr(params.ptr_D), make_shape(M,N), params.dD);                              // (M,N)
Tensor mAux = make_tensor(make_gmem_ptr(params.ptr_Aux), make_shape(M,N), params.dAux);                        // (M,N)

auto thr_mma = tiled_mma.get_thread_slice(thread_idx);
Tensor tCgD = thr_mma.partition_C(gD);                                                             // (VEC,THR_M,THR_N)
Tensor tCgC = thr_mma.partition_C(gC);                                                             // (VEC,THR_M,THR_N)
Tensor tCgAux = thr_mma.partition_C(gAux);                                                         // (VEC,THR_M,THR_N)
c++
Tensor my_tensor = make_tensor<Type>(Layout<Shape<_2,_2>{}, Stride<_1,_2>>{});                           // (2,2):(1,2)
    
// Related tensors
Tensor my_tensor1 = make_tensor<Type>(ThisIsAVeryComplicatedLayoutWithAVeryLongName);         // ((Mode0_0,Mode0_1,Mode0_2),Mode1,Mode2,Mode3)
Tensor my_tensor2_related = make_tensor<Type>(ThisIsAVeryComplicatedLayoutWithAVeryLongName); // ((Mode0_0,Mode0_1,Mode0_2),Mode1,Mode2,Mode3)

Warnings

CUTLASS code aims to build free of warnings.

Spurious warnings

Some compilers, or some versions of a compiler, emit spurious warnings, that is, "false positives" for perfectly fine code. While such code is correct, the warnings can obscure errors. Users also may report warnings as bugs, and processing those bugs takes developer time away from other tasks. Thus, it's good to try to "fix" the warnings, if doing so wouldn't make the code worse.

Missing return statement

GCC 10 (but not 7.5, 9.4.0, or 11) has trouble deducing that a function with auto return type and all of its returns in an if constexpr ... else statement must actually return. As a result, GCC emits spurious "missing return statement" build warnings. Such functions have one of two forms: if constexpr ... else where else returns, and if constexpr ... else where else is meant to fail at compile time. Here is an example of the first form.

c++
template<class T>
constexpr auto first_form(T t) {
  if constexpr (some_condition_v<T>) {
    return some_function(t);
  }
  else if constexpr (another_condition_v<T>) {
    return another_function(t);
  }
  else {
    return yet_another_function(t);
  }
}

In this form, the if constexpr ... else sequence of branches covers all possibilities. Here is an example of the second form.

c++
template<class T>
constexpr auto second_form(T t) {
  if constexpr (some_condition_v<T>) {
    return some_function(t);
  }
  else if constexpr (another_condition_v<T>) {
    return another_function(t);
  }
  else {
    static_assert(sizeof(T) < 0, "This branch always fails");
  }
}

In this form, the else branch had a static_assert that was meant always to fail if the else branch were taken, such as static_assert(sizeof(T) < 0). (Note that we cannot use static_assert(false) here, because it will ALWAYS fail at compile time, even if the else branch is not taken. C++23 fixes this behavior, but CUTLASS currently requires that its code be compatible with C++17. As a result, CUTLASS includes a dependent_false<T> library function that you can use in place of the always-false test sizeof(T) < 0.)

One can suppress "missing return statement" warnings for both forms by invoking CUTLASS' function-like macro CUTE_GCC_UNREACHABLE. When building with GCC, this invokes the GCC-specific built-in function __builtin_unreachable(). Actually calling this function is undefined behavior, so using this lets the programmer declare that the code path calling that function will never be taken. (C++23 introduces the std::unreachable() function, which achieves the same goal. Again, though, CUTLASS cannot currently use C++23 library functions.) Here is an example of how to use CUTE_GCC_UNREACHABLE.

c++
template<class T>
constexpr auto second_form(T t) {
  if constexpr (some_condition_v<T>) {
    return some_function(t);
  }
  else if constexpr (another_condition_v<T>) {
    return another_function(t);
  }
  else {
    static_assert(sizeof(T) < 0, "This branch always fails");
  }
  CUTE_GCC_UNREACHABLE;
}

This macro should only be used if it is needed to suppress spurious warnings. Also, this function should not be used if the developer is not sure whether the code exhaustively tests all possibilities. For example, some functions may look like this.

c++
template<class T>
constexpr auto possibly_nonexhaustive(T t) {
  if constexpr (some_condition_v<T>) {
    return some_function(t);
  }
  else if constexpr (another_condition_v<T>) {
    return another_function(t);
  }
 
  // NOTE lack of unadorned "else" here
}

This is a good opportunity to review the function. If the branches are obviously meant to be exhaustive, you can add an else branch with a static_assert (see above for how to express this). If you're not sure, leave it alone and let the compiler issue warnings.

Unused variable

Some compilers may emit spurious unused warnings for some variable declarations, where the variable was only being used inside a decltype in an if constexpr test. Marking the variables as [[maybe_unused]] (a standard C++17 attribute) suppresses these warnings. Again, please only do this if you're sure that the code is right.

CUDA C++ style

CUDA Built-in Variables

Avoid direct access to CUDA built-in variables threadIdx, blockIdx, blockDim, and gridDim within
CUTLASS components except in special circumstances.

Using built-in global variables directly within resuable components necessitates that all components
use them consistently which may not be possible if CUTLASS components are used in other contexts.

Instead, components should accept a linear ID identifying threads, warps, and threadblocks from calling
code. The top-level kernel may then decide how to map threads, warps, and blocks to the problem it is
solving.

Use CUTLASS's and CuTe's fundamental types and operations

Use the
fundamental types and operations
defined in CUTLASS consistently.
This contributes to a framework of interoperable, consistent components.
It reduces code duplication, which reduces build and test times.
It also saves developer effort.

CUTLASS's fundamental types and operations include

  • Numeric types to represent numeric data in host and device code, and

  • functional.h to perform numeric operations in generic code.

CUTLASS 3.0 uses CuTe components to represent data layouts and multidimensional arrays.
Please refer to the CuTe Tutorial for details.
CuTe has replaced CUTLASS 2.x components such as
Containers,
Layouts, and
TensorRef and TensorView.

CUTLASS idioms

Detecting major mode

Developers sometimes need to detect whether a tensor is MN-major or K-major.
(For definitions, see the CuTe GEMM tutorial.)

  • Correct: cutlass::detail::is_major<0, Stride>() or
    cutlass::detail::is_k_major() from include/cutlass/gemm/gemm.h

  • Incorrect: get<0>(stride) == 1

The second point is incorrect because it assumes that the mode
is a single integer, not a multimode.
This means that the code will fail to compile for tensor contractions.
For example, suppose that a tensor A
has shape ((X, Y), K) and stride ((1, X), X*Y).
get<0>(stride) is the tuple (1, X), not a single integer.
However, A is certainly M major if interpreted as a matrix.

Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Terminology (`media/docs/cpp/terminology.md`)

CUTLASS Terminology

cute::Layout: A cute::Layout vocabulary type composed of the hierarchical cute::Shape and cute::Stride
tuples that is used throughout CUTLASS 3.0 to represent and manipulate thread and data layouts. More details are included in the CuTe specific tensor type documentation.

cute::Tensor: A pointer backed by a cute::Layout used to represent a tensor. More details are included in the CuTe specific tensor type documentation.

Capacity: (scalar) physical number of elements in memory required to store a multidimensional object; expressed as the type's LongIndex type

  • example: the capacity of a column-major matrix is lda * N

Element: data type describing one item in a multidimensional tensor, array, or matrix

Extent: (vector-valued quantity) the logical size of each dimension of a multidimensional index space. Consistent with the C++ Standard Library.

  • Coord<N> extent()
  • Index extent(int dim)

Fragment: a register-backed array of elements used to store a thread's part of a tile

Index: signed integer representing quantities aligned with a logical dimension

Layout: functor mapping logical coordinates of a tensor to linear offset (as LongIndex); owns stride vectors, if any.

LongIndex: signed integer representing offsets in memory; typically wider than Index type

Numeric Type: a CUTLASS data type used to represent real-valued quantities; is trivially copyable.

Pitch Linear: linear memory allocation obtained from a user-defined 2-D size, which specifies the
contiguous and strided dimensions of a tile.

Planar Complex: representation of complex tensors as two real-valued tensors, with real elements in one part and imaginary elements in another part of identical layout, separated by an offset

Policy: additional details extending the interface of a template guiding internal implementation;
typically used to target specific design points known to be efficient

Rank: number of dimensions in a multidimensional index space, array, tensor, or matrix. Consistent with
C++ Standard Library

Register: in device code, registers are the most efficient storage for statically sized arrays of elements.
Arrays may be expected to be stored in registers if all accesses are made via constexpr indices or within
fully unrolled loops.

Residue: partial tile or matrix computation which may require special accommodation for functional correctness or performance

Size: (scalar) number of logical elements in a tensor; equal to the product of each member of extent()

  • LongIndex size()

sizeof_bits<T>::value - template pattern returning the size of a numeric type or array in units of bits

Storage: when appropriate, refers to some alternative type used to store a packed collection of elements;
may be used to handle bit-level packing or make types safe for use in unions

TensorRef: contains base pointer and Layout object for referencing infinitely-sized tensor object

TensorView: contains TensorRef and extent of a finite mathematical object

Tile: partitions of a tensor that have constant extents and layout known at compile time

Trait: characteristics of a fully-specialized type, typically used in metaprogramming reflection

View: an object containing references to a data structure that it does not own; typically, construction of views is lightweight

Warp: a collection of hardware threads executing in lock-step; warp-level operations typically rely on cooperation among the threads within the warp

AlignedBuffer<T, N>: statically sized array type; union-safe, no construction guarantee for elements

Array<T, N>: container for holding numeric types - handles bit packing for small numeric types (e.g. int4_t, uint4_t, bin1_t)
sizeof(Array<T, N>) - gives expected value in units of bytes with minimum storage of 1 B: (sizeof_bits::value * N) / 8

Operator: an object performing a computation on matrix or tensor objects. May be further refined by scope within the execution model hierarchy. Deprecated starting CUTLASS 3.0,
replaced by MMA and Copy atoms from CuTe.

Tile Iterator: abstraction for accessing and traversing a sequence of tiles in a tensor; CUTLASS specifies
formal concepts for tile iterators. Deprecated starting CUTLASS 3.0.
Replaced by cute::Layout in equivalent usage scenarios to represent data tensors.

Thread Map: abstraction for defining how threads are mapped to a given tile. Deprecated starting CUTLASS 3.0.
Replaced by cute::Layout in equivalent usage scenarios to represent thread tensors.

Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Tile Iterator Concept (`media/docs/cpp/tile_iterator_concept.md`)

Tile Iterator Concepts

Note: CUTLASS 3.0 deprecates all tile access iterators in favour of CuTe's single
vocabulary type cute::Tensor, which is parameterized on cute::Layout.
cute::Tensors can therefore be manipulated with the same layout algebra as all CuTe layouts.
This removes the need for bespoke types that encapsulate iterator properties.
The following text thus only applies to legacy CUTLASS 2.x API and related types.

CUTLASS 2.x implements generic algorithms on tiles of matrix or tensors of constant size. These may
be considered as partitions of tensors of infinite size, with a range of partitions accessible
by tile iterators.

Various data structures may make operations such as random access to tiles inexpensive,
while data structures may not offer random access at all. For example, iterating over a linked
list of matrices requires sequential traversal. Algorithms implemented in terms of sequences of tiles
should require only the minimum set of operators be defined for tile iterators.

This document describes a set of C++ concepts which may be used to define tile iterators used
by CUTLASS algorithms. ("Concept" here does not refer to a C++20 concept that uses the concept keyword.
Rather, it refers to a set of requirements on a type.)
Each concept specifies members and type definitions that a tile iterator
must implement. Frequently, a tile iterator implements several concepts, and its members are
the union of the members from each individual concept. These definitions were inspired by
Boost "New style" iterator concepts.

The set of all possible combinations of these concepts is quite large, however most tile iterator
templates can be described by one of several combinations. The section
Frequently Used Tile Iterator Concepts describes several common interfaces used throughout CUTLASS.

Definitions

Base Tile Iterator Concept. All tile iterators must describe an Element type as well as a Shape.

c++
/// Base concept for all tile iterators
struct TileIteratorConcept {
  using Element;           ///< Element type composing tile (concept: numeric type or Array<>)
  using Shape;             ///< Shape type describing extent of tile. The shape concept depends 
                           ///  on iterator implementation.
};

Contiguous Memory Tile Iterator Concept. Iterators over tiles stored arbitrarily within
a continuous block of data in memory. Linear offset in units of Element may be added to
internally held pointers to 'move' the iterator in memory.

c++
/// Tile iterator over partitions of a tensor in contiguous memory which may be referenced via a
/// TensorRef object.
struct ContiguousMemoryTileIterator : public TileIteratorConcept {

  using Index;            ///< index type used to add pointer offsets

  /// Adds a linear offset in units of Element to internal pointer(s) into tensor
  CUTLASS_DEVICE 
  void add_pointer_offset(Index pointer_offset);
};

Readable Tile Iterator Concept. Iterators that may be read from define a Fragment type holding
each thread's part of the data to be loaded. An explicit load() method reads the tile from memory,
and places each thread's part in its Fragment object.

c++
/// Tile iterator capable of loading tiles from memory into fragments
struct ReadableTileIteratorConcept {
  
  using Fragment;              ///< fragment object derived from cutlass::Array<Element, N>

  CUTLASS_DEVICE
  void load(Fragment &frag);   ///< loads a fragment from memory
};

Readable Contiguous Tile Iterator Concept. Iterators reading from contiguous memory
support an optional pointer offset that is added to any internally managed pointers before
performing the load. This provides a convenient method to fold an offset in with load
operations.

c++
/// Union of the following tile iterator concepts:
///
///   - ReadableTileIteratorConcept
///   - ContiguousMemoryTileIterator
///
struct ReadableContiguousTileIteratorConcept : 
  public ReadableTileIteratorConcept, 
  public ContiguousMemoryTileIterator {

  /// Loads a fragment from memory with additional logical offset
  CUTLASS_DEVICE
  void load_with_pointer_offset(
    Fragment &frag,                             ///< fragment to load from the tensor
    Index pointer_offset);                      ///< loads a tile with a linear offset
};

Writeable Tile Iterator Concept. Iterators that may write to memory define a Fragment type holding
each thread's part of the data to be written. An explicit store() method writes the tile to memory.

c++
/// Tile iterator capable of storing tiles from memory
struct WriteableTileIteratorConcept {

  using Fragment;                     ///< fragment object derived from cutlass::Array<Element, N>

  /// Stores a fragment to memory
  CUTLASS_DEVICE
  void store(Fragment const &frag);             ///< stores a fragment to memory
};

Writeable Contiguous Tile Iterator Concept. Iterators writing to contiguous memory
support an optional pointer offset that is added to any internally managed pointers before
performing the store operation. This provides a convenient method to fold an offset into the
store.

c++
/// Union of the following tile iterator concepts:
///
///   - WriteableTileIteratorConcept
///   - ContiguousMemoryTileIterator
///
struct WriteableContiguousTileIteratorConcept : 
  public WriteableTileIteratorConcept, 
  public ContiguousMemoryTileIterator {

  /// Loads a fragment from memory with additional logical offset
  CUTLASS_DEVICE
  void store_with_pointer_offset(
    Fragment const &frag,                       ///< fragment to store to the tensor
    Index pointer_offset);                      ///< stores a tile with a linear offset
};

Forward Tile Iterator Concept. This concept offers traversal "forward" by one tile in
a pre-defined sequence. Often, this sequence is relevant to the context in which the iterator
was defined, such as along the K dimension of a GEMM operation. Equality operators are defined
to determine whether two iterators point to the same tile.

c++
/// Tile iterator that may be incremented along a traversal sequence.
struct ForwardTileIteratorConcept {

  CUTLASS_DEVICE bool operator==(TileIterator const &it);        ///< true if iterators point to same tile, false if otherwise
  CUTLASS_DEVICE bool operator!=(TileIterator const &it);        ///< false if iterators point to same tile, true if otherwise

  CUTLASS_DEVICE ForwardTileIteratorConcept & operator++();      ///< pre-increment - advance to next tile in sequence
  CUTLASS_DEVICE ForwardTileIteratorConcept operator++(int);     ///< post-increment - advance to next tile in sequence
};

Bidirectional Tile Iterator Concept. This concept permits traversal both forward and backward.

c++
/// Tile iterator which may be traverse in both directions along a defined sequence.
struct BidirectionalTileIteratorConcept : public ForwardTileIteratorConcept {

  CUTLASS_DEVICE
  BidirectionalTileIteratorConcept & operator--();      ///< pre-decrement - traverse to previous tile in sequence

  CUTLASS_DEVICE
  BidirectionalTileIteratorConcept operator--(int);     ///< post-decrement - traverse to previous tile in sequence
};

Random Access Tile Iterator Concept. This iterator defines random access operations in the logical
coordinate system of the underlying tensor. Thus, tensors must have a defined Layout with associated
TensorCoord coordinate describing logical position within the tensor and TensorRef reference type.
It may be advanced forward or backwards by an offset specified as units of whole tiles along each dimension.

c++
/// Tile iterator offering random access to tiles in contiguous memory.
struct RandomAccessTileIteratorConcept : 
  public BidirectionalTileIteratorConcept, 
  public ContiguousMemoryTileIterator {

  using Layout;           ///< Layout object mapping 
  using TensorRef;        ///< Tensor Reference object
  using TensorCoord;      ///< Logical coordinate in referenced tensor

  ///< advances in units of whole tiles along the logical coordinate space of the tensor
  CUTLASS_DEVICE
  RandomAccessTileIteratorConcept & add_tile_offset(TensorCoord const &tile_offset);

  ///< advances in units of whole tiles along the logical coordinate space of the tensor
  CUTLASS_DEVICE
  RandomAccessTileIteratorConcept & operator+=(TensorCoord const &tile_offset);

  ///< advances in units of whole tiles along the logical coordinate space of the tensor
  CUTLASS_DEVICE
  RandomAccessTileIteratorConcept & operator-=(TensorCoord const &tile_offset);
};

Readable Random Access Tile Iterator Concept. Readable random access iterators
accept an additional tile offset in logical coordinate space when loading fragments.

c++
/// Loads a fragment with a logical coordinate offset in units of whole tiles.
struct ReadableRandomAccessTileIteratorConcept : 
  public RandomAccessTileIteratorConcept, 
  public ReadableTileIteratorConcept {

  /// Loads a fragment from memory with logical offset in units of whole tiles.
  CUTLASS_DEVICE
  void load(
    Fragment &frag,                             ///< fragment to load from the tensor
    TensorCoord const &tile_offset);            ///< loads a tile with a logical offset in units of whole tiles
};

Readable Random Access Contiguous Tile Iterator Concept. Readable random access iterators
accept an additional tile offset in logical coordinate space when loading fragments.

c++
/// Loads a fragment with a logical coordinate offset in units of whole tiles.
struct ReadableRandomAccessContiguousTileIteratorConcept : 
  public ReadableRandomAccessTileIteratorConcept, 
  ReadableContiguousTileIteratorConcept {

  /// Loads a fragment from memory with logical offset in units of whole tiles.
  CUTLASS_DEVICE
  void load(
    Fragment &frag,                             ///< fragment to load from the tensor
    TensorCoord const &tile_offset,             ///< loads a tile with a logical offset in units of whole tiles
    Index pointer_offset);                      ///< loads a tile with a logical offset AND a pointer offset
};

Writeable Random Access Tile Iterator Concept. Writeable random access iterators
accept an additional tile offset in logical coordinate space when storing fragments.

c++
/// Stores a fragment with a logical coordinate offset in units of whole tiles.
struct WriteableRandomAccessTileIteratorConcept : 
  public RandomAccessTileIteratorConcept, 
  public WriteableContiguousTileIteratorConcept {
  
  /// Stores a fragment from memory with logical offset in units of whole tiles.
  CUTLASS_DEVICE 
  void store(
    Fragment const &frag,                       ///< fragment to store to the location pointed to by the tensor
    TensorCoord const &tile_offset);            ///< stores a tile with a given offset from the current iterator
};

Writeable Random Access Contiguous Tile Iterator Concept. Writeable random access iterators
accept an additional tile offset in logical coordinate space when storing fragments.

c++
/// Stores a fragment with a logical coordinate offset in units of whole tiles.
struct WriteableRandomAccessContiguousTileIteratorConcept : 
  public WriteableRandomAccessTileIteratorConcept, 
  public WriteableContiguousTileIteratorConcept {

  /// Stores a fragment from memory with logical offset in units of whole tiles.
  CUTLASS_DEVICE 
  void store(
    Fragment const &frag,                       ///< fragment to store to the location pointed to by the tensor
    TensorCoord const &tile_offset,             ///< stores a tile with a logical offset in units of whole tiles
    Index pointer_offset);                      ///< stores a tile witha logical offset AND a pointer offset
};

Masked Tile Iterator Concept. Matrix and tensors may not always be multiples of whole tiles.
Masked tile iterators define a Mask type which may be used to guard accesses to memory. The
semantics and interface of this Mask are implementation-defined details of each tile iterator,
but several convenience methods are defined for interacting with the mask such as efficiently
clearing or enabling all guarded memory accesses.

c++
/// Supports iterating over tiles that are not 'whole' in memory. Iterator maintains a mask object
/// which guards against out-of-bounds access.
///
/// Note, this concept definition does not formally define operations on the mask or methods it
/// supports. These remain implementation-dependent details of iterators implementing this concept.
struct MaskedTileIteratorConcept {

  using Mask;                                        ///< mask object used to guard against acceses.

  CUTLASS_DEVICE void clear_mask();                  ///< efficiently disables all accesses guarded by mask
  CUTLASS_DEVICE void enable_mask();                 ///< efficiently enables all accesses guarded by mask

  CUTLASS_DEVICE void get_mask(Mask &mask);          ///< gets the mask
  CUTLASS_DEVICE void set_mask(Mask const &mask);    ///< sets the mask
};

Frequently Used Tile Iterator Concepts

This section describes several frequently used compositions of the basic tile iterator concepts. They are
listed here as complete type declarations for convenience of the reader.

Writeable, Readable, Forward, Contiguous Memory Tile Iterator Concept.
This combines several of the basic iterator concepts to
yield a tile iterator capable of loading and storing tiles as well as advancing forward along a traversal sequence.

text
/* Detailed source-code truncated for AI context efficiency. */

Writeable, Readable, Random Access, Contiguous Memory Tile Iterator Concept.
This combines several of the basic iterator concepts to
yield a tile iterator with random access suitable for loading matrix operands for GEMM.

text
/* Detailed source-code truncated for AI context efficiency. */

Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause

text
Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Media Docs Cpp Utilities (`media/docs/cpp/utilities.md`)

Note: This document discusses utilities commonly used with code that targets CUTLASS 2.x.
Although CUTLASS 3.0's primary entry point APIs do not transact in these cutlass::* tensor types anymore,
users can still find them convenient for managing allocations with trivial affine layouts.
For more adv