主机软件

CuTeCuTe

CUDA 模板库 (CUDA Templates, CuTe) 是 CUTLASS 内部的一个仅包含头文件(Header-only)的 CUDA C++ 库,用于描述和操作由 数据线程 构成的张量。

顾名思义,CuTe 充分利用了 CUDA C++ 的模板 templates 机制。模板是 C++ 对参数多态性 parametric polymorphism 的实现,您在其他语言中可能以泛型 generics 的形式接触过这一概念。多态函数只需编写一次,便可操作不同输入类型的数据。请注意,CuTe 不要与 CuTe DSL 混淆,后者是在 Python 中通过领域特定语言(DSL)来封装并提供 CuTe/CUTLASS 的功能。

CuTe 类型系统的核心是 LayoutsLayouts 描述了对 CuTe Tensors 进行常规访问的模式。而 Tensors 则将 Layouts 与指向 内存 的指针结合在了一起。至关重要的是,这些 Layouts 具有可组合性 (Composable)——它们构成了一个范畴 category,并拥有丰富的代数结构 rich algebra,从而将强大的表达能力与结构化完美结合。需要说明的是,Layout 本身是由 ShapeStride 元组构成的,用于描述内存的维度范围以及如何遍历它们。

CuTe 利用类型系统来编码关键的程序元数据(如内存组织形式、步长访问和分块等),使得 编译器 在应用优化时,能够检查正确性的方方面面并维持不变性。这使得开发者可以在不牺牲性能的前提下,对核函数 kernels 进行非常高级的元编程。例如,同一个模板可以被编译成跨越多种流式多处理器架构 Streaming Multiprocessor architectures 的高性能优化核函数。由于布局在编译期就已经解析完毕,内存访问带来了“零”额外的运行期开销,否则这种开销可能会直接摧毁内存受限型 memory-bound 工作负载的性能 performance。

欲了解更多细节,请参阅 NVIDIA 的 CuTe 官方文档

下方展示了一个基于 CuTe 的矩阵转置核函数(该代码基于 Colfax International 的这篇文章中的初始“朴素”实现)。它演示了 CuTe 的核心特性与类型——模板、形状、布局和张量。您可以使用 这个 Modal Notebook 在 H100 GPU 上运行它。

// one CuTe trick: transpose a row-major matrix just using Layouts
template <typename T>
__global__ void transpose_kernel(const T* __restrict__ d_S,
                                 T* __restrict__ d_D,
                                 int M, int N)
{
    // define the Shape of tiles worked on by thread blocks
    using b = Int<32>;
    auto block_shape = make_shape(b{}, b{});

    // define the Shape of input/output Tensors
    auto tensor_shape = make_shape(M, N);

    // define the Layout of the input and output Tensors in global memory
    auto gmemLayoutS  = make_layout(tensor_shape, GenRowMajor{}); // input:  row-major
    auto gmemLayoutDT = make_layout(tensor_shape, GenColMajor{}); // output: col-major

    // construct the Tensors
    auto tensor_S  = make_tensor(make_gmem_ptr(d_S), gmemLayoutS);
    auto tensor_DT = make_tensor(make_gmem_ptr(d_D), gmemLayoutDT);

    // define a tile-ing of the Tensors (as a "Tensor of Tensors")
    auto tiled_tensor_S  = tiled_divide(tensor_S,  block_shape);
    auto tiled_tensor_DT = tiled_divide(tensor_DT, block_shape);

    // pull out the tiles this thread block will be working on
    auto tile_S  = tiled_tensor_S (make_coord(_, _), blockIdx.x, blockIdx.y);
    auto tile_DT = tiled_tensor_DT(make_coord(_, _), blockIdx.x, blockIdx.y);

    // create a Layout for threads in the thread block
    auto thr_layout = make_layout(
        make_shape(Int<8>{}, Int<32>{}),
        GenRowMajor{}
    );

    // pull out the tile this thread will work on
    auto thr_tile_S  = local_partition(tile_S,  thr_layout, threadIdx.x);
    auto thr_tile_DT = local_partition(tile_DT, thr_layout, threadIdx.x);

    // define a "Tensor" in register memory
    auto rmem = make_tensor_like<T>(thr_tile_S);

    // copy tile into registers
    copy(thr_tile_S, rmem);
    // copy tile out of registers as though it were column-major
    copy(rmem, thr_tile_DT);
}
英文原文(Modal GPU Glossary)

CUDA Templates (CuTe) is a header-only CUDA C++ library within CUTLASS for describing and manipulating tensors of data and threads.

As the name implies, CuTe uses CUDA C++ templates. Templates are the C++ implementation of parametric polymorphism, which you may have encountered in the form of generics in other languages. Polymorphic functions are written once but can operate on inputs with different types. CuTe is not to be confused with CuTe DSL, which exposes CuTe/CUTLASS via a Domain-Specific Language (DSL) in Python.

At the core of CuTe's type system are Layouts. Layouts describe regular patterns of access to CuTe Tensors. Tensors combine a Layout with a pointer to memory. These Layouts are, critically, composable -- they form a category with a rich algebra and so combine both expressiveness and structure. Note that Layouts are themselves composed of Shape and Stride tuples used to describe memory extents and how to traverse them.

CuTe uses the type system to encode key program metadata like memory organization, strided accesses, and tiling such that the compiler can check many aspects of correctness and preserve invariants while applying optimizations. This allows for very high-level metaprogramming of kernels without sacrificing performance. For instance, the same template can be compiled into highly-optimized kernels across several Streaming Multiprocessor architectures. Because layouts are resolved at compile time, memory accesses carry zero additional runtime overhead, which might otherwise kill performance for memory-bound workloads.

For additional details, see NVIDIA's CuTe documentation.

The CuTe-based matrix transpose kernel below, based on the initial "naive" implementation from this article by Colfax International, demonstrates the core features and types of CuTe -- templating, shapes, layouts, and tensors. You can run it on an H100 via this Modal Notebook.

// one CuTe trick: transpose a row-major matrix just using Layouts
template <typename T>
__global__ void transpose_kernel(const T* __restrict__ d_S,
                                 T* __restrict__ d_D,
                                 int M, int N)
{
    // define the Shape of tiles worked on by thread blocks
    using b = Int<32>;
    auto block_shape = make_shape(b{}, b{});

    // define the Shape of input/output Tensors
    auto tensor_shape = make_shape(M, N);

    // define the Layout of the input and output Tensors in global memory
    auto gmemLayoutS  = make_layout(tensor_shape, GenRowMajor{}); // input:  row-major
    auto gmemLayoutDT = make_layout(tensor_shape, GenColMajor{}); // output: col-major

    // construct the Tensors
    auto tensor_S  = make_tensor(make_gmem_ptr(d_S), gmemLayoutS);
    auto tensor_DT = make_tensor(make_gmem_ptr(d_D), gmemLayoutDT);

    // define a tile-ing of the Tensors (as a "Tensor of Tensors")
    auto tiled_tensor_S  = tiled_divide(tensor_S,  block_shape);
    auto tiled_tensor_DT = tiled_divide(tensor_DT, block_shape);

    // pull out the tiles this thread block will be working on
    auto tile_S  = tiled_tensor_S (make_coord(_, _), blockIdx.x, blockIdx.y);
    auto tile_DT = tiled_tensor_DT(make_coord(_, _), blockIdx.x, blockIdx.y);

    // create a Layout for threads in the thread block
    auto thr_layout = make_layout(
        make_shape(Int<8>{}, Int<32>{}),
        GenRowMajor{}
    );

    // pull out the tile this thread will work on
    auto thr_tile_S  = local_partition(tile_S,  thr_layout, threadIdx.x);
    auto thr_tile_DT = local_partition(tile_DT, thr_layout, threadIdx.x);

    // define a "Tensor" in register memory
    auto rmem = make_tensor_like<T>(thr_tile_S);

    // copy tile into registers
    copy(thr_tile_S, rmem);
    // copy tile out of registers as though it were column-major
    copy(rmem, thr_tile_DT);
}

相关词条

本词条改编自 Modal GPU Glossary(CC BY 4.0)· 中文翻译 miter6/gpu-glossary-zh,MAE 整理排版。