用户指南¶
术语表¶
术语“张量”指的是 n 阶(又名 n 维)数组。可以将张量视为矩阵向更高阶的推广。例如,标量、向量和矩阵分别是 0 阶、1 阶和 2 阶张量。
一个 n 阶张量有 \(n\) 个模式。每个模式都有一个维度(又名大小)。对于每个模式,您可以指定一个步长 \(s > 0\)。此步长描述了沿该模式的两个逻辑上连续的元素在物理内存中相隔多远。它们具有类似于 BLAS 中前导维度的功能,并允许例如对子张量进行操作。
默认情况下,cuTENSOR 遵循广义的列优先数据布局。例如:\(A_{a,b,c} \in {R}^{4\times 8 \times 12}\) 是一个 3 阶张量,其中 a 模式、b 模式和 c 模式的维度分别为 4、8 和 12。如果未明确指定,则假定步长为
\(stride(a) = 1\)
\(stride(b) = extent(a)\)
\(stride(c) = extent(a) * extent(b)\).
如果张量沿所有模式连续存储在内存中,则认为该张量是紧凑的。也就是说,\(stride(i_1) = 1\) 和 \(stride(i_l) = stride(i_{l-1}) * extent(i_{l-1})\)。
爱因斯坦求和约定¶
cuTENSOR 遵循“爱因斯坦求和约定”:出现在输入张量中但未出现在输出张量中的模式被隐式缩并。
性能指南¶
本节假设广义的列优先数据布局(即,左侧模式具有最小的步长)。以下大多数性能指南旨在促进更规则的内存访问模式
尝试在所有张量中以类似的方式排列张量的模式(相对于步长增加)。例如,\(C_{a,b,c} = A_{a,k,c} B_{k,b}\) 优于 \(C_{a,b,c} = A_{c,k,a} B_{k,b}\)。
尝试将批处理模式保持为变化最慢的模式(即,具有最大步长的模式)。例如 \(C_{a,b,c,l} = A_{a,k,c,l} B_{k,b,l}\) 优于 \(C_{a,l,b,c} = A_{l,a,k,c} B_{k,l,b}\)。
尝试使变化最快的模式(又名步长为 1 的模式)的维度尽可能大。
软件管理的计划缓存¶
本节介绍软件管理的计划缓存。其主要特点是
最大限度地减少与启动相关的开销(例如,由于内核选择)
无开销自动调优(又名增量自动调优)
此功能使用户能够自动找到给定问题的最佳实现,从而提高达到的性能
缓存以线程安全的方式实现,并在使用相同 cutensorHandle_t 的所有线程之间共享。
存储/从文件读取/写入
允许用户将缓存状态存储到磁盘,并在稍后阶段重用它
本质上,计划缓存可以被视为从特定问题实例(例如,cutensorOperationDescriptor_t)到实际实现(由 cutensorPlan_t 编码)的查找表。
计划缓存目前是一项实验性功能 - 未来可能会对 API 进行更改。
有关详细说明,请参阅 计划缓存。
精度保证¶
cuTENSOR 使用其自己的计算类型来设置张量运算的浮点精度。cutensorComputeDescriptor_t 编码了在整个计算过程中保证的最小精度。由于它只是最小精度的保证,因此库可能会选择比用户要求的更高的精度(例如,如果给定问题不支持该计算类型,或者有更多内核可供选择)。
例如,让我们考虑一个张量缩并,其中所有张量的类型均为 CUTENSOR_R_32F
,但计算描述符为 CUTENSOR_COMPUTE_DESC_16F
:在这种情况下,cuTENSOR 可以使用 Nvidia 的 Tensor Cores,其累积类型为 CUTENSOR_R_32F
(即,提供比用户要求的更高的精度)。
另一个说明性示例是一个张量缩并,其中所有张量的类型均为 CUTENSOR_R_16F
,计算描述符为 CUTENSOR_COMPUTE_DESC_32F
:在这种情况下,并行归约(如果性能需要)将必须在 CUTENSOR_R_32F
中执行,因此需要辅助工作区。准确地说,在这种情况下,cuTENSOR 不会选择通过输出张量进行串行归约(通过原子操作),因为最终归约的一部分将在 CUTENSOR_R_16F
中执行,这低于用户通过计算描述符请求的精度。
cuTENSOR 遵循 BLAS 关于 NaN 传播的约定:每当标量 (alpha
、beta
、gamma
) 设置为零时,缩放张量表达式中的 NaN 将被忽略,即来自标量的零优先于来自张量的 NaN。但是,来自张量的 NaN 遵循正常的 IEEE 754 行为。
为了说明,令 \(\alpha = 1; \beta = 0; A_{i, j} = 1; A'_{i, j} = 0; B_{i, j} = \textrm{NaN}\),则 \(\alpha A_{i,j} B_{i, j} = \textrm{NaN}\), \(\beta A_{i, j} B_{i, j} = 0\), \(\alpha A'_{i,j} B_{i, j} = \textrm{NaN}\),并且 \(\beta A'_{i, j} B_{i, j} = 0\)。
标量类型¶
许多操作支持将参数乘以标量。该标量的类型是输出类型和计算类型的函数。下表列出了相应的类型
输出类型 |
标量类型 |
|
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
支持的一元运算符¶
cuTENSOR 支持 逐元素操作 的一元运算符。下表列出了相应的计算类型和运算符
一元运算符 |
|
---|---|
|
|
|
|
|
|
支持的 GPU¶
cuTENSOR 支持任何计算能力大于或等于 6.0 的 Nvidia GPU。
CUDA 图形支持¶
cuTENSOR 中的所有操作都可以使用 CUDA 图形捕获,但以下两种情况除外
如果启用了即时编译(即,cutensorJitMode_t 不是
CUTENSOR_JIT_MODE_NONE
),则 cutensorCreatePlan() 不得通过 CUDA 图形捕获。对此的解决方法是确保使用cudaStreamNonBlocking
标志创建捕获流。正在积极进行自动调优的操作(请参阅 软件管理的计划缓存)不应通过 CUDA 图形捕获。存在此限制的原因是,在自动调优期间,cuTENSOR 会迭代不同的内核。虽然在这种情况下图形捕获仍然有效,但不建议这样做,因为它可能会捕获到次优内核。
日志记录¶
可以通过在启动目标应用程序之前设置以下环境变量来启用 cuTENSOR 中的日志记录机制
CUTENSOR_LOG_LEVEL=<level> - 其中 level 是以下级别之一
“0” - 关闭 - 日志记录已禁用(默认)
“1” - 错误 - 仅记录错误
“2” - 跟踪 - 启动 CUDA 内核的 API 调用将记录其参数和重要信息
“3” - 提示 - 可能提高应用程序性能的提示
“4” - 信息 - 提供有关库执行的常规信息,可能包含有关启发式状态的详细信息
“5” - API 跟踪 - API 调用将记录其参数和重要信息
CUTENSOR_LOG_MASK=<mask> - 其中 mask 是以下掩码的组合
“0” - 关闭
“1” - 错误
“2” - 跟踪
“4” - 提示
“8” - 信息
“16” - API 跟踪
CUTENSOR_LOG_FILE=<file_name> - 其中 file name 是日志文件的路径。文件名可能包含 %i,它将被进程 ID 替换。例如 “<file_name>_%i.log”。
如果未定义 CUTENSOR_LOG_FILE,则日志消息将打印到 stdout。
cuTENSOR 可以通过将 CUTENSOR_NVTX_LEVEL=<level> 设置为以下值之一来自动添加 NVTX 标记以进行性能分析
“0” - 关闭 - NVTX 标记已禁用(默认)
“1” - 开启 - 选定的 API 调用添加 NVTX 标记
环境变量¶
本节中的环境变量修改 cuTENSOR 的运行时行为。请注意,这些环境变量仅在初始化句柄时读取(即,在 cutensorCreate
期间);因此,对环境变量的更改仅对新初始化的句柄生效。
NVIDIA_TF32_OVERRIDE
,当设置为 0
时,将覆盖 NVIDIA 库的任何默认设置或程序化配置,并且永远不会使用 TF32 Tensor Core 加速 FP32 计算。这仅用作调试工具,NVIDIA 库之外的任何代码都不应根据此环境变量更改行为。除 0
之外的任何其他设置均保留供将来使用。
export NVIDIA_TF32_OVERRIDE=0
CUTENSOR_DISABLE_PLAN_CACHE
,当设置为 1
时,禁用计划缓存(请参阅 软件管理的计划缓存)
export CUTENSOR_DISABLE_PLAN_CACHE=1