运算符#

运算符用于描述属性并配置我们要解决问题的执行。它们分为描述运算符执行运算符


描述运算符#

运算符

默认值

描述

Size<M, N, K>

未设置

定义问题大小。

Function<function>

未设置

BLAS 函数。对于 GEMM,使用 function::MM

Arrangement<arrangement, arrangement, arrangement>

row_major, col_major, col_major

全局内存中每个矩阵的排列以及共享内存中的默认排列。

TransposeMode<transpose_mode, transpose_mode>

transposed, non-transposed

每个矩阵的转置模式(自 0.2.0 版本起已弃用)。

Precision<PA, PB, PC>

float, float, float

GEMM ABC 的计算精度 - 要么都是浮点型,要么都是整型。

Type<type>

type::real

输入和输出数据类型(type::realtype::complex)。

LeadingDimension<LDA, LDB, LDC>

SizeArrangementTransposeMode 运算符定义

矩阵 ABC 的主导维度。

Alignment<a_align, b_align, c_align>

alignof(BLAS::a_value_type), alignof(BLAS::b_value_type), alignof(BLAS::c_value_type)

矩阵 ABC 的对齐方式(以字节为单位)。

SM<CC>

未设置

应为其生成 BLAS 函数的目标 CUDA 架构。

描述运算符定义了我们要解决的问题。与执行运算符结合使用,它们形成了一个完整的函数描述符,可以在 GPU 上执行。

添加运算符(以任意顺序)以构造操作描述符类型。例如,要描述用于非转置矩阵 A (m x k)、B (k x n)、C (m x n) 的矩阵乘法,其中复双精度值,其中 m = 8n = 16k = 32 用于在 Volta 架构上执行,可以编写

#include <cublasdx.hpp>

using GEMM = decltype(cublasdx::Size<8, 16, 32>()
              + cublasdx::Precision<double>()
              + cublasdx::Type<cublasdx::type::complex>()
              + cublasdx::Arrangement<cublasdx::col_major, cublasdx::col_major>()
              + cublasdx::Function<cublasdx::function::MM>()
              + cublasdx::SM<700>());

为了使函数描述符完整,需要以下内容

大小运算符#

cublasdx::Size<unsigned int M, unsigned int N, unsigned int K>()

设置要执行函数的的问题大小。

对于 GEMM

  • M - 矩阵 op(A)C 中的逻辑行数。

  • N - 矩阵 op(B)C 中的逻辑列数。

  • K - 矩阵 op(A) 中的逻辑列数和 C 中的行数。

例如,对于 GEMM,MNK 指定 A (M x K) 矩阵乘以 B (K x N) 矩阵,结果为 C (M x N) 矩阵(假设 AB 未转置)。请参阅排列转置模式GEMM

类型运算符#

cubladx::Type<cublasdx::type T>;

namespace cublasdx {
  enum class type
  {
    real,
    complex
  };
}

设置计算中使用的输入和输出数据类型。对于实数据类型,请使用 type::real,对于复数据类型,请使用 type::complex

精度运算符#

cublasdx::Precision<PA, PB=PA, PC=PA>;

设置 ABC 的计算精度。

精度类型可以是浮点型

  • __half

  • float

  • double

  • __nv_fp8_e5m2

  • __nv_fp8_e4m3

  • __nv_bfloat16

  • cublasdx::tfloat32_t

或整型

  • int8_t

  • uint8_t

  • int16_t

  • uint16_t

  • int32_t

  • uint32_t

  • int64_t

  • uint64_t

它描述了用于输入和输出值的计算精度。这意味着这是输入值将在执行乘法指令之前转换成的类型。

注意

从 cuBLASDx 0.3.0 开始,计算精度已与数据精度分离,即每个矩阵的输入/输出数据可以是任意类型(甚至浮点 GEMM 的整型输入),前提是设置了对齐运算符并且满足以下条件之一

  1. 它可以隐式转换为使用精度运算符类型运算符选择的数据类型。

  2. 对于输入:提供适当的转换加载操作作为参数之一。它采用输入类型值。其结果必须至少可以隐式转换为计算类型。

  3. 对于输出:提供适当的转换存储操作作为参数之一。它采用结果计算类型(通常是 精度运算符类型运算符定义的 C 类型)。其结果必须至少可以隐式转换为输出类型。

警告

如果使用与输入类型分离的计算精度,则必须显式设置对齐运算符

排列运算符#

cublasdx::Arrangement<cublasdx::arrangement A, cublasdx::arrangement B = cublasdx::col_major, cublasdx::arrangement C = cublasdx::col_major>;

namespace cublasdx {
  enum class arrangement
  {
    col_major,
    row_major
  };

  inline constexpr auto col_major   = arrangement::col_major;
  inline constexpr auto left_layout = arrangement::col_major;
  inline constexpr auto row_major   = arrangement::row_major;
  inline constexpr auto right_major = arrangement::row_major;
}

设置函数中使用的全局 ABC 矩阵的顺序。顺序可以是列优先或行优先。排列运算符直接影响获取内存布局建议的共享内存布局方法,通过显式设置默认的获取内存布局值并选择建议的共享内存布局以优化全局共享传输。

为矩阵选择特定顺序并不意味着该函数不接受具有不同布局的矩阵,但它可能会影响整体性能。

警告

ArrangementTransposeMode 运算符不能同时定义。

转置模式运算符#

警告

TransposeMode 运算符自 0.2.0 版本起已弃用,并且可能会在未来版本中删除。

cublasdx::TransposeMode<cublasdx::transpose_mode ATransposeMode, cublasdx::transpose_mode BTransposeMode>;

namespace cublasdx {
  enum class transpose_mode
  {
    non_transposed,
    transposed,
    conj_transposed,
  };

  inline constexpr auto N = transpose_mode::non_transposed;
  inline constexpr auto T = transpose_mode::transposed;
  inline constexpr auto C = transpose_mode::conj_transposed;
}

设置函数中使用的 AB 矩阵的转置模式。例如,TransposeMode<N, N>()AB 矩阵的转置模式设置为 GEMM 的 non-transposed。转置模式的可能值是

  • transpose_mode::non_transposed,

  • transpose_mode::transposed, 和

  • transpose_mode::conj_transposed (共轭转置)。

警告

  • 排列TransposeMode 运算符不能同时定义。

  • 对于 TransposeMode 运算符中的矩阵使用 transpose_mode::non_transposed 对应于 排列运算符中的 arrangement::col_major

  • 对于 TransposeMode 运算符中的矩阵使用 transpose_mode::transposed 对应于 排列运算符中的 arrangement::row_major

  • 对于 TransposeMode 运算符中的矩阵使用 transpose_mode::conj_transposed 对应于 排列运算符中的 arrangement::row_major 和在 execute() 方法中作为该矩阵的变换运算符传递的 cublasdx::conjugate

主导维度运算符#

cublasdx::LeadingDimension<unsigned int LDA, unsigned int LDB, unsigned int LDC>()

定义矩阵 ABC 的主导维度。矩阵的主导维度是列优先矩阵到下一列开头或行优先矩阵到下一行开头的步幅(以元素为单位)。

如果在通用矩阵乘法运算的描述中使用了排列,则可以用以下方式描述 ABC 矩阵

  • 矩阵 A 的实际维度是 \(LDA\times K\),其中如果 A 是列优先矩阵,则 LDA >= M,否则为 \(LDA\times M\),其中 LDA >= K

  • 矩阵 B 的实际维度是 \(LDB\times N\),其中如果 B 是列优先矩阵,则 LDA >= K,否则为 \(LDB\times K\),其中 LDB >= N

  • 矩阵 C 的实际维度是 \(LDC\times N\),其中如果 C 是列优先矩阵,则 LDC >= M,否则为 \(LDC\times M\),其中 LDB >= N

矩阵也可以使用布局来描述(请参阅 CuTe:布局),即整数元组对:形状和步幅(元素之间的距离)。简而言之,形状表示每个维度中元素的数量,而步幅表示每个维度中元素之间的距离。

  • A - 形状为 \(M\times K\) 的矩阵,如果 A 是列优先矩阵,则在第 1 维度中步幅为 1,在第 2 维度中步幅为 LDA,否则在第 1 维度中为 LDA,在第 2 维度中为 1

  • B - 形状为 \(K\times N\) 的矩阵,如果 B 是列优先矩阵,则在第 1 维度中步幅为 1,在第 2 维度中步幅为 LDB,否则在第 1 维度中为 LDB,在第 2 维度中为 1

  • C - 形状为 \(M\times N\) 的矩阵,如果 C 是列优先矩阵,则在第 1 维度中步幅为 1,在第 2 维度中步幅为 LDC,否则在第 1 维度中为 LDC,在第 2 维度中为 1

警告

TransposeMode 运算符自 0.2.0 版本起已弃用,并且可能会在未来版本中删除。

如果在通用矩阵乘法运算的描述中使用了转置模式(已弃用),则在 BLAS 命名法中,可以用以下方式描述 ABC 矩阵的维度

  • 矩阵 A 的实际维度是 \(LDA\times K\),其中如果 A 未转置,则 LDA >= M,否则为 \(LDA\times M\),其中 LDA >= K

  • 矩阵 B 的实际维度是 \(LDB\times N\),其中如果 B 未转置,则 LDA >= K,否则为 \(LDB\times K\),其中 LDB >= N

  • 矩阵 C 的实际维度是 \(LDC\times N\),其中 LDC >= M

另请参阅 suggested_leading_dimension_of

对齐运算符#

cublasdx::Alignment<unsigned int AAlignment, unsigned int BAlignment, unsigned int CAlignment>()

cublasdx::MaxAlignment = Alignment<16, 16, 16> // alias of maximum supported alignements

定义传递给 execute(...) 方法的矩阵 ABC 的指针(原始指针或封装在 CuTe 张量中)的对齐方式(以字节为单位)。

请注意,对于给定的描述运算符,对齐方式直接影响所需的共享内存量。此外,它还可能影响性能。

要求

  • AAlignmentsBAlignmentsCAlignments 应该是 2 的幂,并且小于或等于最大支持的对齐方式,即 16 字节。

  • AAlignmentsBAlignmentsCAlignments 是所选计算值类型或输入值类型(如果使用分离精度)的对齐方式的倍数。

警告

如果使用与输入类型分离的计算精度,则必须显式设置对齐运算符

另请参阅 Suggested Alignment Trait

函数运算符#

cublasdx::Function<cublasdx::function F>()

namespace cublasdx {
  enum class function
  {
    MM
  };
}

设置要执行的 BLAS 函数。

通用矩阵乘法#

Function<function::MM> 将运算设置为通用矩阵乘法,定义为以下之一

  1. \(\mathbf{C}_{m\times n} = {\alpha} \times \mathbf{A}_{m\times k} \times \mathbf{B}_{k\times n} + {\beta} \times \mathbf{C}_{m\times n}\)

  2. \(\mathbf{C}_{m\times n} = \mathbf{A}_{m\times k} \times \mathbf{B}_{k\times n} + \mathbf{C}_{m\times n}\)

  3. \(\mathbf{C}_{m\times n} = \mathbf{A}_{m\times k} \times \mathbf{B}_{k\times n}\)

其中 \({\alpha}\)\({\beta}\) 是标量(实数或复数),ABC 是维度分别为 \(A: m\times k\)\(B: k\times n\)\(C: m\times n\) 的矩阵。

矩阵可以是列优先、行优先或具有自定义布局。请参阅执行方法获取内存布局建议的共享内存布局排列

SM 运算符#

cublasdx::SM<unsigned int CC>()

设置底层 BLAS 函数要使用的目标架构 CC。支持的架构有

  • Volta:700720 (sm_70, sm_72)。

  • Turing:750 (sm_75)。

  • Ampere: 800, 860870 (sm_80, sm_86, sm_87)。

  • Ada: 890 (sm_89)。

  • Hopper: 900 (sm_90, sm_90a)。

注意

当为 9.0a 计算能力编译 cuBLASDx 时,在 SM 运算符中使用 900(另请参阅 CUDA C++ 编程指南:功能可用性)。

警告

不保证在不同 CUDA 架构的 GPU 上使用完全相同的输入执行完全相同的 BLAS 函数会产生位相同的结果。


执行运算符#

执行运算符配置函数在 GPU 上的运行方式。与 描述运算符 结合使用,它们构成了一个完整的函数描述符,可以在 GPU 上执行。

运算符

描述

Block(块)

创建块执行对象。请参阅 块配置运算符

Block 运算符#

cublasdx::Block()

生成在单个 CUDA 块中运行的集体操作。线程将协同工作以计算集体操作。可以使用 块配置运算符 配置布局和参与执行的线程数。

例如,以下代码示例为 GEMM 函数创建一个函数描述符,该函数将在单个 CUDA 块中运行

#include <cublasdx.hpp>

using GEMM = decltype(cublasdx::Size<32, 32, 64>()
              + cublasdx::Precision<double, __half, double>()
              + cublasdx::Type<cublasdx::type::real>()
              + cublasdx::TransposeMode<cublasdx::T, cublasdx::N>()
              + cublasdx::Function<cublasdx::function::MM>()
              + cublasdx::SM<700>()
              + cublasdx::Block());

块配置运算符#

块配置运算符允许用户配置单个 CUDA 块的块大小。

运算符

默认值

描述

BlockDim<X, Y, Z>

基于启发式方法

用于执行 BLAS 函数的线程数。

注意

块配置运算符只能与 Block 运算符 一起使用。

警告

不保证使用完全相同的输入,但在不同

的情况下执行完全相同的 BLAS 函数会产生位相同的结果。

BlockDim 运算符#

struct cublasdx::BlockDim<unsigned int X, unsigned int Y, unsigned int Z>()

将 CUDA 块大小设置为 (X, Y, Z) 以配置执行,这意味着它设置参与执行的线程数及其布局。使用此运算符,用户可以在具有不同线程数的 1D、2D 或 3D 块中运行 BLAS 函数。可以通过 BLAS::block_dim 特征访问设置的块维度。

BlockDim<X, Y, Z> 添加到描述中对 BLAS 函数的执行提出了以下要求

  • 内核必须使用 3D 块维度 dim3(X1, Y1, Z1) 启动,其中 X1 >= XY1 >= Y,并且 Z1 >= Z,并且

    • 对于 1D BlockDim<X>,内核必须使用 dim3(X1, Y1, Z1) 启动,其中 X1 >= X

    • 对于 2D BlockDim<X, Y>,内核必须使用 dim3(X, Y1, Z1) 启动,其中 Y1 >= Y

    • 对于 3D BlockDim<X, Y, Z>,内核必须使用 dim3(X, Y, Z1) 启动,其中 Z1 >= Z

  • X * Y * Z 个线程必须参与执行。

  • 参与线程必须是连续(相邻)的线程。

cuBLASDx 的未来版本可能会解除或放宽列出的要求。

注意

cuBLASDx 无法在运行时验证所有内核启动配置并检查是否满足所有要求,因此用户有责任遵守上面列出的规则。违反这些规则被认为是未定义的行为,并可能导致不正确的结果和/或失败。

示例

BlockDim<64>,内核使用块维度 dim3(128, 1, 1) 启动 - OK(正确)
BlockDim<64>,内核使用块维度 dim3(64, 4, 1) 启动 - OK(正确)
BlockDim<64>,内核使用块维度 dim3(64, 2, 2) 启动 - OK(正确)
BlockDim<16, 16>,内核使用块维度 dim3(16, 32, 1) 启动 - OK(正确)
BlockDim<16, 16>,内核使用块维度 dim3(16, 16, 2) 启动 - OK(正确)
BlockDim<8, 8, 8>,内核使用块维度 dim3(8, 8, 16) 启动 - OK(正确)

BlockDim<64>,内核使用块维度 dim3(32, 1, 1) 启动 - INCORRECT(不正确)
BlockDim<64>,内核使用块维度 dim3(32, 2, 1) 启动 - INCORRECT(不正确)
BlockDim<16, 16>,内核使用块维度 dim3(256, 1, 1) 启动 - INCORRECT(不正确)
BlockDim<8, 8, 8>,内核使用块维度 dim3(512, 2, 1) 启动 - INCORRECT(不正确)

BlockDim 的值可以从 BLAS 描述中通过 BLAS::block_dim 特征访问。当未设置 BlockDim 时,将使用默认块维度(默认值为 BLAS::suggested_block_dim)。

如果 cuBLASDx 提供的默认块维度小于内核的最佳维度,那么在增加参与计算的线程数之前,尝试默认值仍然可能是最佳选择。

限制

  • X * Y * Z 必须大于或等于 32。

注意

  • 建议 X * Y * Z 为 32、64、128、256、512 或 1024。

  • 建议 X * Y * Z 是 32 的倍数。