并行线程执行指令集架构版本8.7

使用PTX(并行线程执行)和ISA(指令集架构)的编程指南。

1. 简介

本文档描述了PTX,一种底层的并行线程执行虚拟机和指令集架构(ISA)。PTX将GPU暴露为一个数据并行计算设备

1.1. 使用GPU实现可扩展的数据并行计算

在实时高清3D图形永不满足的市场需求推动下,可编程GPU已发展成为一种高度并行、多线程的多核处理器,拥有巨大的计算能力和极高的内存带宽。GPU特别适合解决可表示为数据并行计算的问题——相同的程序在多个数据元素上并行执行——且具有高算术强度(算术操作与内存操作的比率)。由于对每个数据元素执行相同的程序,因此对复杂流程控制的要求较低;又因为它在众多数据元素上执行且算术强度高,内存访问延迟可以通过计算而非大数据缓存来隐藏。

数据并行处理将数据元素映射到并行处理线程。许多处理大型数据集的应用可以利用数据并行编程模型来加速计算。在3D渲染中,大量像素和顶点被映射到并行线程。同样地,图像和媒体处理应用(如渲染图像的后处理、视频编码解码、图像缩放、立体视觉和模式识别)也可以将图像块和像素映射到并行处理线程。事实上,从通用信号处理、物理模拟到计算金融或计算生物学,图像渲染和处理领域之外的许多算法都能通过数据并行处理获得加速。

PTX defines a virtual machine and ISA for general purpose parallel thread execution. PTX programs are translated at install time to the target hardware instruction set. The PTX-to-GPU translator and driver enable NVIDIA GPUs to be used as programmable parallel computers.

1.2. PTX的目标

PTX 为通用并行编程提供了一个稳定的编程模型和指令集。它专为支持NVIDIA Tesla架构定义的计算特性的NVIDIA GPU进行了高效设计。针对CUDA和C/C++等语言的高级语言编译器会生成PTX指令,这些指令经过优化并转换为目标架构的原生指令。

PTX的目标包括以下内容:

  • 提供跨越多代GPU的稳定ISA架构。

  • 在编译应用程序中实现与原生GPU性能相当的表现。

  • 为C/C++及其他编译器提供一个与机器无关的指令集架构(ISA)。

  • 为应用程序和中间件开发者提供代码分发ISA。

  • Provide a common source-level ISA for optimizing code generators and translators, which map PTX to specific target machines.

  • 便于手动编写库、性能内核和架构测试。

  • 提供一个可扩展的编程模型,能够覆盖从单个GPU单元到多个并行GPU单元的不同规模。

1.3. PTX ISA 版本 8.7

PTX ISA 8.7版本引入了以下新特性:

  • 新增对sm_120目标架构的支持。

  • 新增对支持专用加速特性的目标sm_120a的支持。

  • 扩展tcgen05.mma指令以添加对.kind::mxf4nvf4.scale_vec::4X限定符的支持。

  • 扩展mma指令以支持.f16类型的累加器和.m16n8k16形状,同时支持FP8类型.e4m3.e5m2

  • 扩展cvt指令以添加对.rs舍入模式及目标类型 .e2m1x4.e4m3x4.e5m2x4.e3m2x4.e2m3x4的支持。

  • 扩展了对st.asyncred.async指令的支持,新增了对.mmio.release.global.scope限定符的支持。

  • 扩展tensormap.replace指令,为.elemtype限定符添加对值1315的支持。

  • 扩展了mmamma.sp::ordered_metadata指令,新增支持类型.e3m2/.e2m3/.e2m1以及限定符.kind.block_scale.scale_vec_size

1.4. 文档结构

本文档中的信息按以下章节组织:

参考资料

2. 编程模型

2.1. 高度多线程的协处理器

GPU是一种能够并行执行大量线程的计算设备。它作为主CPU(即主机)的协处理器运行:换句话说,运行在主机上的应用程序中数据并行、计算密集的部分会被卸载到该设备上执行。

更准确地说,应用程序中需要多次执行但能独立处理不同数据的部分,可以被隔离成一个内核函数,该函数将在GPU上以多个不同线程的形式执行。为此,这类函数会被编译为PTX指令集,生成的内核在安装时会被转换为目标GPU的指令集。

2.2. 线程层次结构

执行内核的线程批次被组织成一个网格。网格由协作线程数组(CTAs)或协作线程数组集群组成,如本节所述并在图1图2中展示。协作线程数组(CTAs)实现了CUDA线程块,而集群实现了CUDA线程块集群。

2.2.1. 协作线程阵列

并行线程执行(PTX)编程模型是显式并行的:PTX程序指定了并行线程数组中给定线程的执行。一个协作线程数组(CTA)是指并发或并行执行内核的线程数组。

CTA内的线程可以相互通信。为了协调CTA内线程之间的通信,可以指定同步点,线程在这些同步点等待,直到CTA中的所有线程都到达。

每个线程在CTA内部拥有唯一的线程标识符。程序采用数据并行分解的方式,将输入、工作和结果分配到CTA的各个线程中。每个CTA线程利用其线程标识符来确定分配的角色、指定输入输出位置、计算地址并选择要执行的工作。线程标识符是一个三维向量tid(包含元素tid.xtid.ytid.z),用于指定线程在一维、二维或三维CTA中的位置。每个线程标识符分量的取值范围是从零到该CTA维度中的线程ID数量。

每个CTA都有一个由三元素向量ntid(包含元素ntid.xntid.yntid.z)指定的1D、2D或3D形状。向量ntid指定了每个CTA维度中的线程数量。

CTA内的线程以SIMT(单指令多线程)方式分组执行,这些分组称为warpWarp是单个CTA中线程的最大子集,这些线程在同一时间执行相同的指令。Warp内的线程按顺序编号。Warp大小是与机器相关的常量。通常,一个warp包含32个线程。某些应用程序如果了解warp大小可能能够最大化性能,因此PTX包含一个运行时立即常量WARP_SZ,可以在允许立即操作数的任何指令中使用。

2.2.2. 协作线程阵列集群

集群是一组并发或并行运行的CTA,它们可以通过共享内存相互同步和通信。执行中的CTA必须确保在通过共享内存与对等CTA通信之前,对等CTA的共享内存已存在,并且对等CTA在完成共享内存操作之前尚未退出。

集群中不同CTA内的线程可以通过共享内存进行同步和相互通信。集群范围的屏障可用于同步集群内的所有线程。每个CTA在其集群中具有唯一的CTA标识符(cluster_ctaid)。每个CTA集群具有由参数cluster_nctaid指定的一维、二维或三维形状。集群中的每个CTA在所有维度上还具有唯一的CTA标识符(cluster_ctarank)。集群中所有维度的CTA总数由cluster_nctarank指定。线程可以通过预定义的只读特殊寄存器%cluster_ctaid%cluster_nctaid%cluster_ctarank%cluster_nctarank读取和使用这些值。

集群级别仅适用于目标架构sm_90或更高版本。在启动时指定集群级别是可选的。如果用户在启动时指定了集群维度,则将被视为显式集群启动,否则将被视为默认维度为1x1x1的隐式集群启动。PTX提供了只读特殊寄存器%is_explicit_cluster来区分显式和隐式集群启动。

2.2.3. 集群网格

CTA可以包含的线程数量以及集群可以包含的CTA数量都存在上限。不过,执行相同内核的CTA集群可以批量组合成集群网格,这样单个内核调用中能够启动的线程总数就非常庞大。但这是以减少线程通信和同步为代价的,因为不同集群中的线程无法相互通信和同步。

每个集群在集群网格中拥有唯一的集群标识符(clusterid)。每个集群网格通过参数nclusterid指定为一维、二维或三维形状。每个网格还具有唯一的时间网格标识符(gridid)。线程可以通过预定义的只读特殊寄存器%tid%ntid%clusterid%nclusterid%gridid来读取和使用这些值。

每个CTA在网格中都有一个唯一标识符(ctaid)。每个CTA网格具有由参数nctaid指定的1D、2D或3D形状。线程可以通过预定义的只读特殊寄存器%ctaid%nctaid来使用和读取这些值。

每个内核作为一批线程执行,这些线程组织为由CTA组成的集群网格,其中集群是可选的层级,仅适用于目标架构sm_90及更高版本。图1展示了一个由CTA组成的网格,而图2则展示了一个由集群组成的网格。

网格之间可以设置依赖关系启动 - 一个网格既可以作为依赖网格,也可以作为前置条件网格。要了解如何定义网格依赖关系,请参阅Cuda编程指南中的CUDA Graphs章节。

Grid with CTAs

图1 带CTA按钮的网格

Grid with clusters

图2 带集群的网格

集群是一组协作线程数组(CTA)的集合,其中CTA是执行相同内核程序的一组并发线程。网格是由独立执行的CTA组成的集群集合。

2.3. 内存层次结构

PTX线程在执行过程中可以从多个状态空间访问数据,如图3所示,其中从目标架构sm_90开始引入了集群层级。每个线程拥有私有的本地内存。每个线程块(CTA)具有共享内存,该内存对块内所有线程以及集群中所有活动块可见,且生命周期与块相同。最后,所有线程都可以访问相同的全局内存。

所有线程都可以访问额外的状态空间:常量、参数、纹理和表面状态空间。常量和纹理内存是只读的;表面内存可读可写。全局、常量、参数、纹理和表面状态空间针对不同的内存使用场景进行了优化。例如,纹理内存为特定数据格式提供了不同的寻址模式和数据过滤功能。请注意,纹理和表面内存会被缓存,在同一内核调用期间,缓存不会与全局内存写入和表面内存写入保持一致性,因此任何对已通过全局或表面写入地址的纹理获取或表面读取操作都会返回未定义数据。换句话说,线程只有在内存位置由先前内核调用或内存复制更新的情况下才能安全读取某些纹理或表面内存位置,但如果该内存位置是由同一线程或同一内核调用中的其他线程先前更新的,则无法安全读取。

全局、常量和纹理状态空间在同一应用程序的内核启动之间是持久存在的。

主机和设备各自维护自己的本地内存,分别称为主机内存设备内存。设备内存可以被主机映射并读取或写入,或者为了更高效的传输,可以通过利用设备高性能直接内存访问(DMA)引擎的优化API调用从主机内存复制数据。

Memory Hierarchy

图3 内存层次结构

3. PTX机器模型

3.1. 一组SIMT多处理器

NVIDIA GPU架构围绕可扩展的多线程流式多处理器(SMs)阵列构建。当主机程序调用内核网格时,网格中的块会被枚举并分配到具有可用执行能力的多处理器上。线程块中的线程在一个多处理器上并发执行。当线程块终止时,新的块会在空闲的多处理器上启动。

一个多处理器由多个标量处理器(SP)核心、一个多线程指令单元以及片上共享内存组成。该多处理器在硬件层面以零调度开销创建、管理和执行并发线程。它实现了单指令屏障同步机制。快速的屏障同步结合轻量级线程创建与零开销线程调度,能高效支持极细粒度的并行计算,例如通过为每个数据元素(如图像中的像素、体素数据中的体素、基于网格计算中的单元格)分配一个线程,实现问题的低粒度分解。

为了管理运行多个不同程序的数百个线程,多处理器采用了一种我们称之为SIMT(单指令多线程)的架构。多处理器将每个线程映射到一个标量处理器核心,每个标量线程独立执行,拥有自己的指令地址和寄存器状态。多处理器的SIMT单元以称为warp的并行线程组为单位创建、管理、调度和执行线程。(这个术语源自纺织业,这是最早的并行线程技术。)组成SIMT warp的各个线程从相同的程序地址开始执行,但可以自由分支并独立执行。

当多处理器获得一个或多个线程块执行时,它会将这些线程块分割成由SIMT单元调度的线程束。线程块分割成线程束的方式始终相同;每个线程束包含连续递增的线程ID,第一个线程束包含线程0。

在每条指令发出时,SIMT单元会选择一个准备就绪的warp,并向该warp的活动线程发出下一条指令。一个warp每次执行一条公共指令,因此当warp中的所有线程在它们的执行路径上达成一致时,才能实现完全效率。如果warp中的线程通过数据相关的条件分支发生分歧,该warp会串行执行每个被采用的分支路径,同时禁用不在该路径上的线程;当所有路径执行完毕后,这些线程会重新收敛到相同的执行路径。分支分歧仅发生在同一个warp内部;不同的warp会独立执行,无论它们执行的是相同还是不同的代码路径。

SIMT架构类似于SIMD(单指令多数据)向量组织,因为单个指令控制多个处理单元。一个关键区别在于,SIMD向量组织向软件暴露了SIMD宽度,而SIMT指令则规定了单个线程的执行和分支行为。与SIMD向量机相比,SIMT使程序员能够为独立的标量线程编写线程级并行代码,也能为协同线程编写数据并行代码。就正确性而言,程序员基本上可以忽略SIMT行为;但通过确保代码很少需要线程束中的线程发生分支,可以实现显著的性能提升。实际上,这类似于传统代码中缓存线的作用:在设计正确性时可以安全地忽略缓存线大小,但在设计峰值性能时必须考虑代码结构。另一方面,向量架构要求软件将加载合并到向量中并手动管理分支。

一个多处理器能同时处理多少个块取决于给定内核所需的每个线程寄存器数量和每个块共享内存大小,因为多处理器的寄存器和共享内存会在批处理块的所有线程之间分配。如果每个多处理器没有足够的寄存器或共享内存来处理至少一个块,内核将无法启动。

_images/hardware-model.png

图4 硬件模型

一组带有片上共享内存的SIMT多处理器。

3.2. 独立线程调度

在Volta之前的架构中,warp使用一个由32个线程共享的程序计数器,并通过活动掩码指定warp中的活动线程。因此,来自同一warp但处于不同分支区域或执行状态的线程无法相互发送信号或交换数据。依赖锁或互斥量实现细粒度数据共享的算法很容易导致死锁,具体取决于竞争线程来自哪个warp。

从Volta架构开始,独立线程调度允许线程之间实现完全并发,不受束的限制。通过独立线程调度,GPU可以维护每个线程的执行状态,包括程序计数器和调用栈,并且能够以线程级粒度暂停执行,以便更好地利用执行资源或让一个线程等待另一个线程生成数据。调度优化器会决定如何将来自同一束的活动线程分组到SIMT单元中。这保留了与之前NVIDIA GPU相同的SIMT执行高吞吐量,但提供了更大的灵活性:现在线程可以在子束粒度上分叉和重新汇聚。

独立线程调度可能导致实际执行代码的线程组与开发者预期的不同,如果他们基于之前硬件架构的warp同步性做出了假设。特别是,任何warp同步代码(例如无需同步的warp内归约操作)都应重新检查,以确保与Volta及后续架构的兼容性。更多详情请参阅Cuda编程指南中关于计算能力7.x的章节。

3.3. 片上共享内存

图4所示,每个多处理器拥有以下四种类型的片上内存:

  • 每个处理器配备一组本地32位寄存器

  • 一个并行数据缓存或共享内存,由所有标量处理器核心共享,是共享内存空间的所在位置,

  • 一个只读的常量缓存,由所有标量处理器核心共享,可加速从常量内存空间的读取。常量内存空间是设备内存中的一个只读区域。

  • 一个只读的纹理缓存,由所有标量处理器核心共享,可加速从纹理内存空间(设备内存中的只读区域)的读取;每个多处理器通过纹理单元访问纹理缓存,该单元实现了各种寻址模式和数据过滤功能。

本地和全局内存空间是设备内存的可读写区域。

4. 语法

PTX程序是文本源模块(文件)的集合。PTX源模块采用汇编语言风格的语法,包含指令操作码和操作数。伪操作(pseudo-operations)用于指定符号和地址管理。ptxas优化后端编译器会对PTX源模块进行优化和汇编,生成相应的二进制目标文件。

4.1. 源格式

源模块为ASCII文本。行与行之间通过换行符(\n)分隔。

所有空白字符都是等效的;除了用于分隔语言中的标记外,空白字符会被忽略。

C预处理器cpp可用于处理PTX源模块。以#开头的行是预处理指令。以下是常见的预处理指令:

#include, #define, #if, #ifdef, #else, #endif, #line, #file

Harbison和Steele所著的C: A Reference Manual对C预处理器进行了很好的描述。

PTX区分大小写,关键字使用小写字母。

每个PTX模块必须以指定PTX语言版本的.version指令开头,后跟指定目标架构的.target指令。有关这些指令的更多信息,请参阅PTX模块指令

4.2. Comments

PTX中的注释遵循C/C++语法,使用非嵌套的/**/来标记可能跨越多行的注释,使用//来开始一个延伸到下一个换行符的注释(换行符会终止当前行)。注释不能出现在字符常量、字符串字面值或其他注释内部。

PTX中的注释被视为空白字符。

4.3. 语句

PTX语句可以是指令或指示。语句以可选的标签开头,并以分号结尾。

示例

        .reg     .b32 r1, r2;
        .global  .f32  array[N];

start:  mov.b32   r1, %tid.x;
        shl.b32   r1, r1, 2;          // shift thread id by 2 bits
        ld.global.b32 r2, array[r1];  // thread[tid] gets array[tid]
        add.f32   r2, r2, 0.5;        // add 1/2

4.3.1. 指令语句

指令关键字以点号开头,因此不会与用户自定义标识符产生冲突。PTX中的指令在表1中列出,并在状态空间、类型和变量以及指令章节中详细描述。

表 1 PTX指令

.address_size

.explicitcluster

.maxnreg

.section

.alias

.extern

.maxntid

.shared

.align

.file

.minnctapersm

.sreg

.branchtargets

.func

.noreturn

.target

.callprototype

.global

.param

.tex

.calltargets

.loc

.pragma

.version

.common

.local

.reg

.visible

.const

.maxclusterrank

.reqnctapercluster

.weak

.entry

.maxnctapersm

.reqntid

4.3.2. 指令语句

指令由操作码后跟零个或多个以逗号分隔的操作数组成,并以分号结尾。操作数可以是寄存器变量、常量表达式、地址表达式或标签名称。指令具有可选的条件执行保护谓词。该保护谓词位于可选标签之后、操作码之前,写作@p,其中p为谓词寄存器。保护谓词可选择性地取反,写作@!p

目标操作数在前,源操作数在后。

指令关键字列于 表2中。所有指令关键字都是 PTX中的保留标记。

表 2 保留指令关键字

abs

cvta

membar

setp

vabsdiff4

activemask

discard

min

shf

vadd

add

div

mma

shfl

vadd2

addc

dp2a

mov

shl

vadd4

alloca

dp4a

movmatrix

shr

vavrg2

and

elect

mul

sin

vavrg4

applypriority

ex2

mul24

slct

vmad

atom

exit

multimem

sqrt

vmax

bar

fence

nanosleep

st

vmax2

barrier

fma

neg

stackrestore

vmax4

bfe

fns

not

stacksave

vmin

bfi

getctarank

stmatrix

vmin2

bfind

griddepcontrol

pmevent

sub

vmin4

bmsk

isspacep

popc

subc

vote

bra

istypep

prefetch

suld

vset

brev

ld

prefetchu

suq

vset2

brkpt

ldmatrix

prmt

sured

vset4

brx

ldu

rcp

sust

vshl

call

lg2

red

szext

vshr

clz

lop3

redux

tanh

vsub

cnot

mad

rem

testp

vsub2

copysign

mad24

ret

tex

vsub4

cos

madc

rsqrt

tld4

wgmma

clusterlaunchcontrol

mapa

sad

trap

wmma

cp

match

selp

txq

xor

createpolicy

max

set

vabsdiff

cvt

mbarrier

setmaxnreg

vabsdiff2

4.4. 标识符

用户自定义标识符遵循扩展的C++规则:它们可以以字母开头,后跟零个或多个字母、数字、下划线或美元符号;或者以下划线、美元符号或百分号开头,后跟一个或多个字母、数字、下划线或美元符号:

followsym:   [a-zA-Z0-9_$]
identifier:  [a-zA-Z]{followsym}* | {[_$%]{followsym}+

PTX未规定标识符的最大长度,并建议所有实现至少支持1024个字符的最小长度。

许多高级语言如C和C++对标识符名称遵循类似的规则,但不允许使用百分号。PTX允许将百分号作为标识符的首字符。百分号可用于避免命名冲突,例如用户定义的变量名与编译器生成的名称之间的冲突。

PTX预定义了一个常量以及少量以百分号开头的特殊寄存器,这些内容列在表3中。

表 3 预定义标识符

%clock

%laneid

%lanemask_gt

%pm0, ..., %pm7

%clock64

%lanemask_eq

%nctaid

%smid

%ctaid

%lanemask_le

%ntid

%tid

%envreg<32>

%lanemask_lt

%nsmid

%warpid

%gridid

%lanemask_ge

%nwarpid

WARP_SZ

4.5. 常量

PTX支持整数和浮点常量以及常量表达式。这些常量可用于数据初始化以及作为指令的操作数。对于整数、浮点和位宽类型,类型检查规则保持不变。对于谓词类型的数据和指令,允许使用整数常量,并按照C语言的规则进行解释,即零值表示False,非零值表示True

4.5.1. 整数常量

整数常量的大小为64位,可以是有符号或无符号的,即每个整数常量具有.s64.u64类型。整数常量的有符号/无符号特性对于正确评估包含除法运算和有序比较等操作的常量表达式是必需的,因为这些操作的行为取决于操作数类型。当用于指令或数据初始化时,每个整数常量会根据其使用处的数据或指令类型转换为适当的大小。

整数字面量可以用十进制、十六进制、八进制或二进制表示法书写。其语法遵循C语言的规范。整数字面量可以紧跟着字母U来表示该字面量是无符号的。

hexadecimal literal:  0[xX]{hexdigit}+U?
octal literal:        0{octal digit}+U?
binary literal:       0[bB]{bit}+U?
decimal literal       {nonzero-digit}{digit}*U?

整数字面量是非负的,其类型由数值大小和可选类型后缀决定:除非数值无法完全用.s64表示或指定了无符号后缀,否则字面量默认为有符号类型(.s64),这种情况下字面量为无符号类型(.u64)。

预定义的整数常量WARP_SZ指定了目标平台上每个warp的线程数量;迄今为止,所有目标架构的WARP_SZ值均为32。

4.5.2. Floating-Point Constants

浮点常量以64位双精度值表示,所有浮点常量表达式均使用64位双精度算术进行计算。唯一的例外是用于表示精确32位单精度浮点值的十六进制表示法;此类值保留其精确的32位单精度值,且不可用于常量表达式。每个64位浮点常量会根据其使用场景的数据类型或指令类型,转换为适当的浮点精度。

浮点数字面量可以包含可选的小数点和可选的带符号指数。与C和C++不同,这里没有后缀字母来指定大小;字面量始终以64位双精度格式表示。

PTX包含第二种浮点常量的表示方式,用于通过十六进制常量指定精确的机器表示。要指定IEEE 754双精度浮点值,常量以0d0D开头,后跟16个十六进制数字。要指定IEEE 754单精度浮点值,常量以0f0F开头,后跟8个十六进制数字。

0[fF]{hexdigit}{8}      // single-precision floating point
0[dD]{hexdigit}{16}     // double-precision floating point

示例

mov.f32  $f3, 0F3f800000;       //  1.0

4.5.3. 谓词常量

在PTX中,整型常量可用作谓词。对于谓词类型的数据初始化器和指令操作数,整型常量的解释方式与C语言相同,即零值表示False,非零值表示True

4.5.4. 常量表达式

在PTX中,常量表达式采用类似C语言的运算符构成,并遵循与C语言相似的求值规则,但通过限制类型和大小、移除大多数类型转换以及明确定义完整语义进行了简化,从而消除了C语言中表达式求值依赖具体实现的情况。

常量表达式由常量字面量、一元加减运算符、基本算术运算符(加、减、乘、除)、比较运算符、条件三元运算符(?:)以及括号组成。整型常量表达式还允许使用一元逻辑非(!)、按位取反(~)、取模(%)、移位运算符(<<>>)、位类型运算符(&|^)以及逻辑运算符(&&||)。

PTX中的常量表达式不支持整数与浮点数之间的类型转换。

常量表达式的求值采用与C语言相同的运算符优先级。表4列出了运算符优先级和结合性。一元运算符的优先级最高,并随着图表中的每一行递减。同一行的运算符具有相同的优先级,一元运算符从右到左求值,二元运算符从左到右求值。

表 4 运算符优先级

类型

运算符符号

运算符名称

结合性

主要

()

括号

不适用

一元

+- ! ~

加、减、取反、补码

正确

(.s64)(.u64)

类型转换

正确

二进制

*/ %

乘法、除法、取余

左侧

+-

加法,减法

>> <<

班次

< > <= >=

有序比较

== !=

等于,不等于

&

按位与

^

按位异或

|

按位或

&&

逻辑与

||

逻辑或

三元

?:

条件性的

正确

4.5.5. 整数常量表达式求值

整型常量表达式在编译时根据一组规则进行评估,这些规则决定了每个子表达式的类型(有符号.s64 对比 无符号.u64)。这些规则基于C语言的规则,但已被简化为仅适用于64位整数,并且在所有情况下行为都有明确定义(特别是对于取余和移位运算符)。

  • 字面量默认是有符号的,除非需要防止溢出或使用了U后缀的无符号表示。例如:

    • 42, 0x1234, 0123 是有符号数。

    • 0xfabc123400000000, 42U, 0x1234U 是无符号数。

  • 一元加号和减号会保留输入操作数的类型。例如:

    • +123, -1, -(-42) 是带符号的。

    • -1U, -0xfabc123400000000 是无符号数。

  • 一元逻辑非运算 (!) 会产生一个有符号结果,其值为 01

  • 一元按位补码运算符 (~) 将源操作数视为无符号数并生成无符号结果。

  • 某些二元运算符需要对源操作数进行规范化处理。这种规范化被称为常规算术转换,其基本规则是:只要任一操作数为无符号类型,就将两个操作数都转换为无符号类型。

  • 加法、减法、乘法和除法执行常规的算术转换,并生成与转换后的操作数类型相同的结果。也就是说,如果任一源操作数是无符号的,则操作数和结果也是无符号的;否则就是有符号的。

  • 取余运算符 (%) 将操作数视为无符号数。请注意这与C语言不同,C语言允许负除数但定义其行为依赖于具体实现。

  • 左移和右移操作将第二个操作数视为无符号数,并产生与第一个操作数类型相同的结果。请注意,右移的行为由第一个操作数的类型决定:有符号值的右移是算术移位并保留符号位,而无符号值的右移是逻辑移位并在高位补零。

  • AND (&)、OR (|) 和 XOR (^) 执行常规的算术转换,并生成与转换后的操作数类型相同的结果。

  • AND_OP (&&), OR_OP (||), Equal (==), 和 Not_Equal (!=) 会产生一个有符号结果。结果值为0或1。

  • 有序比较运算符(<, <=, >, >=)会对源操作数执行常规算术转换,并生成一个有符号结果。结果值为01

  • 支持使用(.s64)和(.u64)强制转换将表达式转换为有符号或无符号类型。

  • 对于条件运算符(? :),第一个操作数必须是整数,而第二个和第三个操作数必须同为整数或同为浮点数。系统会对第二和第三个操作数执行常规算术转换,结果类型与转换后的类型相同。

4.5.6. 常量表达式求值规则总结

表5 包含了常量表达式求值规则的摘要。

表5 常量表达式求值规则

类型

运算符

操作数类型

操作数解释

结果类型

主要

()

任意类型

与源相同

与源相同

常量字面量

不适用

不适用

.u64, .s64, 或 .f64

一元

+-

任意类型

与源相同

与源相同

!

整数

零或非零

.s64

~

整数

.u64

.u64

转换

(.u64)

整数

.u64

.u64

(.s64)

整数

.s64

.s64

二进制

+- * /

.f64

.f64

.f64

integer

使用常规转换

转换后的类型

< > <= >=

.f64

.f64

.s64

整数

使用常规转换

.s64

== !=

.f64

.f64

.s64

整数

使用常规转换

.s64

%

整数

.u64

.s64

>> <<

整数

第1个未改变,第2个是.u64

与第一个操作数相同

& | ^

整数

.u64

.u64

&& ||

整数

零或非零

.s64

三元

?:

int ? .f64 : .f64

与来源相同

.f64

int ? int : int

使用常规转换

转换后的类型

5. 状态空间、类型与变量

虽然特定目标GPU中可用的具体资源会有所不同,但资源类型在各平台间是通用的,这些资源在PTX中通过状态空间和数据类型进行了抽象。

5.1. 状态空间

状态空间是具有特定特性的存储区域。所有变量都存在于某个状态空间中。状态空间的特性包括其大小、可寻址性、访问速度、访问权限以及线程间的共享级别。

PTX中定义的状态空间是并行编程和图形编程的副产品。状态空间列表如表6所示,状态空间的属性如表7所示。

表 6 状态空间

名称

描述

.reg

快速注册。

.sreg

特殊寄存器。只读;预定义;平台相关。

.const

共享的只读内存。

.global

全局内存,由所有线程共享。

.local

本地内存,每个线程私有。

.param

内核参数,按网格定义;或

每个线程定义的函数或局部参数。

.shared

可寻址内存,按CTA定义,在定义它的CTA的整个生命周期内,集群中的所有线程均可访问。

.tex

全局纹理内存(已弃用)。

表 7 状态空间的属性

名称

可寻址

可初始化

访问权限

共享方式

.reg

读/写

每线程

.sreg

RO

每CTA

.const

1

RO

每网格

.global

1

读/写

上下文

.local

读/写

每线程

.param (作为内核的输入)

2

RO

每网格

.param (用于函数中)

受限3

读/写

每线程

.shared

读/写

每集群5

.tex

编号4

是的,通过驱动程序

RO

上下文

注意:

1 .const.global 状态空间中的变量默认初始化为零。

2 仅可通过ld.param{::entry}指令访问。地址可通过mov指令获取。

3 可通过ld.param{::func}st.param{::func}指令访问。设备函数的输入和返回参数可以通过mov指令获取其地址;此时参数位于栈帧上,其地址处于.local状态空间。

4 仅能通过tex指令访问。

5 对所属CTA和集群中其他活动CTA可见。

5.1.1. 注册状态空间

寄存器(.reg状态空间)是高速存储位置。寄存器数量有限,且因平台而异。当超出限制时,寄存器变量将被溢出到内存中,从而导致性能变化。针对每种架构,都有一个建议的最大寄存器使用数量(详见CUDA编程指南)。

寄存器可以是有类型的(有符号整数、无符号整数、浮点数、谓词)或无类型的。寄存器大小有限制;除了1位的谓词寄存器外,标量寄存器的宽度为8位、16位、32位、64位或128位,向量寄存器的宽度为16位、32位、64位或128位。8位寄存器最常见的用途是与ldstcvt指令一起使用,或作为向量元组的元素。

寄存器与其他状态空间的不同之处在于它们不是完全可寻址的,即无法引用寄存器的地址。当编译为使用应用程序二进制接口(ABI)时,寄存器变量被限制在函数作用域内,不能在模块作用域中声明。当编译包含模块作用域.reg变量的旧版PTX代码(3.0之前的ISA版本)时,编译器会静默禁用ABI的使用。寄存器可能具有多字加载和存储所需的对齐边界。

5.1.2. 特殊寄存器状态空间

特殊寄存器(.sreg)状态空间包含预定义的平台特定寄存器,例如网格、集群、CTA和线程参数、时钟计数器以及性能监控寄存器。所有特殊寄存器都是预定义的。

5.1.3. 恒定状态空间

常量(.const)状态空间是由主机初始化的只读内存。常量内存通过ld.const指令访问。常量内存的大小受到限制,目前上限为64 KB,可用于存储静态大小的常量变量。此外还有640 KB的常量内存,组织为十个独立的64 KB区域。驱动程序可以在这些区域中分配和初始化常量缓冲区,并将缓冲区指针作为内核函数参数传递。由于这十个区域不是连续的,驱动程序必须确保分配的常量缓冲区完全位于一个64 KB区域内,不跨越区域边界。

静态大小的常量变量可以有一个可选的变量初始化器;没有显式初始化器的常量变量默认初始化为零。由驱动程序分配的常量缓冲区由主机初始化,指向这些缓冲区的指针作为参数传递给内核。有关将指向常量缓冲区的指针作为内核参数传递的更多详细信息,请参阅内核函数参数属性中的内核参数属性描述。

5.1.3.1. Banked Constant State Space (已弃用)

PTX的早期版本将常量内存暴露为一组11个64 KB的存储区,变量声明和访问时都需要明确指定存储区编号。

在PTX ISA 2.2版本之前,常量内存被组织成固定大小的存储体。共有11个64 KB的存储体,使用.const[bank]修饰符指定存储体,其中bank的取值范围为0到10。如果未指定存储体编号,则默认为存储体0。

按照惯例,银行零用于所有静态大小的常量变量。剩余的银行用于声明不完整的常量数组(例如在C语言中),这些数组的大小在编译时未知。例如,声明

.extern .const[2] .b32 const_buffer[];

导致const_buffer指向常量存储区二的起始地址。该指针随后可用于访问整个64 KB的常量存储区。在同一存储区中声明的多个不完整数组变量会被别名化,每个变量都指向指定常量存储区的起始地址。

要访问常量存储区1到10中的数据,需要在加载指令的状态空间中指定存储区编号。例如,访问存储区2中的一个不完整数组的方式如下:

.extern .const[2] .b32 const_buffer[];
ld.const[2].b32  %r1, [const_buffer+4]; // load second word

在PTX ISA 2.2版本中,我们取消了显式的存储体(banks),并用内核参数属性取代了驱动程序分配常量缓冲区的不完整数组表示方式,这些属性允许将指向常量缓冲区的指针作为内核参数传递。

5.1.4. 全局状态空间

全局(.global)状态空间是所有线程在上下文中均可访问的内存区域。它实现了不同CTA、集群和网格中线程间的通信机制。使用ld.globalst.globalatom.global来访问全局变量。

全局变量可以有一个可选的变量初始化器;没有显式初始化器的全局变量默认会被初始化为零。

5.1.5. 本地状态空间

本地状态空间(.local)是每个线程用于存储自身数据的私有内存。它通常是带有缓存的标准内存。由于必须按线程分配,其大小有限。使用ld.localst.local来访问本地变量。

当编译以使用应用程序二进制接口(ABI)时,.local状态空间变量必须在函数作用域内声明,并分配在栈上。在不支持栈的实现中,所有本地内存变量都存储在固定地址,不支持递归函数调用,且.local变量可以在模块作用域声明。当编译包含模块作用域.local变量的旧版PTX代码(ISA 3.0之前的版本)时,编译器会静默禁用ABI的使用。

5.1.6. 参数状态空间

参数状态空间(.param)用于:(1)将输入参数从主机传递到内核;(2a)声明从内核执行中调用的设备函数的形式输入和返回参数;(2b)声明作为函数调用参数的局部作用域字节数组变量,通常用于通过值传递大型结构到函数。内核函数参数与设备函数参数在访问和共享方面有所不同(只读与读写,每个内核与每个线程)。请注意,PTX ISA 1.x版本仅支持内核函数参数在.param空间;设备函数参数以前仅限于寄存器状态空间。在PTX ISA 2.0版本中引入了使用参数状态空间作为设备函数参数的功能,需要目标架构sm_20或更高版本。可以在带有.param状态空间的指令上指定额外的子限定符::entry::func,以指示地址是引用内核函数参数还是设备函数参数。如果没有为.param状态空间指定子限定符,则默认子限定符是特定于并依赖于确切指令的。例如,st.param等同于st.param::func,而isspacep.param等同于isspacep.param::entry。有关默认子限定符假设的更多详细信息,请参阅指令描述。

注意

参数空间的位置由具体实现决定。例如,在某些实现中,内核参数位于全局内存中。这种情况下,参数空间与全局空间之间不提供访问保护。虽然内核参数空间的确切位置取决于具体实现,但内核参数空间窗口始终包含在全局空间窗口内。类似地,函数参数会根据应用程序二进制接口(ABI)的函数调用约定,映射到参数传递寄存器和/或堆栈位置。因此,PTX代码不应假设.param空间变量的相对位置或顺序。

5.1.6.1. Kernel Function Parameters

每个内核函数定义包含一个可选的参数列表。这些参数是在.param状态空间中声明的可寻址、只读变量。从主机传递到内核的值通过ld.param指令访问这些参数变量。内核参数变量在网格内所有集群的所有CTA之间共享。

内核参数的地址可以通过mov指令移入寄存器。生成的地址位于.param状态空间,并使用ld.param指令进行访问。

示例

.entry foo ( .param .b32 N, .param .align 8 .b8 buffer[64] )
{
    .reg .u32 %n;
    .reg .f64 %d;

    ld.param.u32 %n, [N];
    ld.param.f64 %d, [buffer];
    ...

示例

.entry bar ( .param .b32 len )
{
    .reg .u32 %ptr, %n;

    mov.u32      %ptr, len;
    ld.param.u32 %n, [%ptr];
    ...

内核函数参数可以表示普通的数据值,也可以保存指向常量、全局、局部或共享状态空间中对象的地址。对于指针类型,编译器和运行时系统需要了解哪些参数是指针以及它们指向哪个状态空间。内核参数属性指令用于在PTX层面提供这些信息。有关内核参数属性指令的详细说明,请参阅Kernel Function Parameter Attributes

注意

当前实现不允许在将常量缓冲区指针作为内核参数传递的程序中创建指向常量变量的通用指针(cvta.const)。

5.1.6.2. Kernel Function Parameter Attributes

内核函数参数可以通过可选的.ptr属性来声明,以表明该参数是一个指向内存的指针,同时指明所指向内存的状态空间和对齐方式。Kernel Parameter Attribute: .ptr描述了.ptr内核参数属性。

5.1.6.3. 内核参数属性:.ptr

.ptr

内核参数对齐属性。

语法

.param .type .ptr .space .align N  varname
.param .type .ptr        .align N  varname

.space = { .const, .global, .local, .shared };

描述

用于指定状态空间,并可选择性地指定由指针类型内核参数指向的内存的对齐方式。对齐值N(如果存在)必须是2的幂次方。如果未指定状态空间,则假定该指针为指向常量、全局、局部或共享内存中之一的通用地址。如果未指定对齐方式,则假定所指向的内存按4字节边界对齐。

为了提高可读性,可以移除.ptr.space.align之间的空格。

PTX ISA 说明

  • 在PTX ISA版本2.2中引入。

  • 在PTX ISA版本3.1中增加了对.const空间的通用寻址支持。

目标ISA注意事项

  • 支持所有目标架构。

示例

.entry foo ( .param .u32 param1,
             .param .u32 .ptr.global.align 16 param2,
             .param .u32 .ptr.const.align 8 param3,
             .param .u32 .ptr.align 16 param4  // generic address
                                               // pointer
) { .. }

5.1.6.4. 设备函数参数

PTX ISA 2.0版本扩展了参数空间在设备函数参数中的应用。最常见的用途是通过值传递不适合PTX寄存器的对象,例如大于8字节的C结构体。在这种情况下,会使用参数空间中的字节数组。通常,调用者会声明一个局部作用域的.param字节数组变量,用于表示扁平化的C结构体或联合体。该变量将通过值传递给被调用者,被调用者会声明一个与传入参数具有相同大小和对齐方式的.param形式参数。

示例

// pass object of type struct { double d; int y; };
.func foo ( .reg .b32 N, .param .align 8 .b8 buffer[12] )
{
    .reg .f64 %d;
    .reg .s32 %y;

    ld.param.f64 %d, [buffer];
    ld.param.s32 %y, [buffer+8];
    ...
}

// code snippet from the caller
// struct { double d; int y; } mystruct; is flattened, passed to foo
    ...
    .reg .f64 dbl;
    .reg .s32 x;
    .param .align 8 .b8 mystruct;
    ...
    st.param.f64 [mystruct+0], dbl;
    st.param.s32 [mystruct+8], x;
    call foo, (4, mystruct);
    ...

有关更多详细信息,请参阅函数调用语法部分。

函数输入参数可以通过ld.param读取,函数返回参数可以通过st.param写入;对输入参数进行写入或从返回参数读取都是非法的。

除了通过值传递结构体外,当被调用函数中获取了形式参数的地址时,也需要使用.param空间。在PTX中,可以使用mov指令将函数输入参数的地址移动到寄存器中。请注意,必要时该参数会被复制到堆栈中,因此地址将位于.local状态空间,并通过ld.localst.local指令访问。无法使用mov获取局部作用域的.param空间变量的地址。从PTX ISA 6.0版本开始,可以使用mov指令获取设备函数返回参数的地址。

示例

// pass array of up to eight floating-point values in buffer
.func foo ( .param .b32 N, .param .b32 buffer[32] )
{
    .reg .u32  %n, %r;
    .reg .f32  %f;
    .reg .pred %p;

    ld.param.u32 %n, [N];
    mov.u32      %r, buffer;  // forces buffer to .local state space
Loop:
    setp.eq.u32  %p, %n, 0;
@%p bra         Done;
    ld.local.f32 %f, [%r];
    ...
    add.u32      %r, %r, 4;
    sub.u32      %n, %n, 1;
    bra          Loop;
Done:
    ...
}

5.1.7. 共享状态空间

共享(.shared)状态空间是由正在执行的CTA拥有的一块内存,集群内所有CTA的线程均可访问。共享内存中的地址可以被CTA集群中的任意线程读写。

可以在带有.shared状态空间的指令上指定额外的子限定符::cta::cluster,分别表示该地址属于执行CTA或集群中任何CTA的共享内存窗口。.shared::cta窗口中的地址也属于.shared::cluster窗口。如果未为.shared状态空间指定子限定符,则默认为::cta。例如,ld.shared等同于ld.shared::cta

.shared状态空间中声明的变量指向当前CTA中的内存地址。指令mapa提供了集群中另一个CTA内对应变量的.shared::cluster地址。

共享内存通常有一些优化来支持数据共享。一个例子是广播模式,即所有线程从同一地址读取数据。另一个例子是顺序线程的连续访问。

5.1.8. 纹理状态空间(已弃用)

纹理(.tex)状态空间是通过纹理指令访问的全局内存。它由上下文中的所有线程共享。纹理内存是只读且缓存的,因此对纹理内存的访问与对纹理图像的全局内存存储不一致。

GPU硬件在单个内核中可访问的纹理绑定数量是固定的(通常为128个)。.tex指令会将命名的纹理内存变量绑定到硬件纹理标识符,纹理标识符从零开始按顺序分配。多个名称可以绑定到同一个物理纹理标识符。如果超过最大物理资源数量,则会生成错误。纹理名称的类型必须为.u32.u64

物理纹理资源是按每个内核粒度分配的,.tex变量需要在全局作用域中定义。

纹理内存是只读的。纹理的基地址假定对齐到16字节边界。

示例

.tex .u32 tex_a;         // bound to physical texture 0
.tex .u32 tex_c, tex_d;  // both bound to physical texture 1
.tex .u32 tex_d;         // bound to physical texture 2
.tex .u32 tex_f;         // bound to physical texture 3

注意

纹理状态空间中变量的显式声明已被弃用,程序应改为通过.texref类型的变量引用纹理内存。为保持向后兼容性,.tex指令仍被保留,在.tex状态空间中声明的变量等同于.global状态空间中的模块作用域.texref变量。

例如,传统的PTX定义如

.tex .u32 tex_a;

等同于:

.global .texref tex_a;

有关.texref类型的描述,请参阅Texture Sampler and Surface Types;关于其在纹理指令中的使用,请参见Texture Instructions

5.2. 类型

5.2.1. 基础类型

在PTX中,基础类型反映了目标架构支持的本地数据类型。基础类型同时指定了基本类型和大小。寄存器变量始终属于基础类型,而指令则对这些类型进行操作。变量定义和指令类型化使用相同的类型-大小说明符,因此它们的名称被有意设计得很简短。

表8列出了每种基本类型的基础类型说明符:

表 8 基础类型说明符

基础类型

基本类型说明符

有符号整数

.s8, .s16, .s32, .s64

无符号整数

.u8, .u16, .u32, .u64

浮点数

.f16, .f16x2, .f32, .f64

位(无类型)

.b8, .b16, .b32, .b64, .b128

谓词

.pred

大多数指令都有一个或多个类型说明符,用于完整指定指令行为。操作数类型和大小会根据指令类型进行兼容性检查。

如果两种基本类型具有相同的基础类型和大小,则它们是兼容的。有符号和无符号整数类型如果大小相同,则它们是兼容的。位大小类型与任何具有相同大小的基本类型兼容。

原则上,所有变量(除谓词外)都可以仅使用位大小类型来声明,但类型化变量能提高程序可读性,并支持更好的操作数类型检查。

5.2.2. 子字大小的限制使用

.u8.s8.b8指令类型仅限于ldstcvt指令。.f16浮点类型仅允许在与.f32.f64类型之间的转换、半精度浮点指令和纹理获取指令中使用。.f16x2浮点类型仅允许在半精度浮点算术指令和纹理获取指令中使用。

为了方便起见,ldstcvt指令允许源数据和目标数据操作数宽度超过指令类型大小,这样可以使用常规宽度寄存器来加载、存储和转换较窄的值。例如,8位或16位值在加载、存储或转换为其他类型和尺寸时,可以直接保存在32位或64位寄存器中。

5.2.3. 替代浮点数据格式

PTX支持的基本浮点类型具有隐式的位表示方式,用于指示存储指数和尾数所使用的位数。例如,.f16类型表示保留5位用于指数,10位用于尾数。除了基本类型假定的浮点表示形式外,PTX还允许以下替代浮点数据格式:

bf16 data format:

该数据格式是一种16位浮点格式,其中8位用于指数,7位用于尾数。包含bf16数据的寄存器变量必须使用.b16类型声明。

e4m3 data format:

该数据格式是一种8位浮点格式,其中4位用于指数,3位用于尾数。e4m3编码不支持无穷大,且NaN值仅限于0x7f0xff。包含e4m3值的寄存器变量必须使用位大小类型声明。

e5m2 data format:

该数据格式是一种8位浮点格式,其中5位用于指数,2位用于尾数。包含e5m2值的寄存器变量必须使用位大小类型声明。

tf32 data format:

这种数据格式是一种特殊的32位浮点格式,支持矩阵乘加指令,其数值范围与.f32相同但精度降低(≥10位)。tf32格式的内部布局由具体实现定义。PTX支持从单精度.f32类型到tf32格式的转换。包含tf32数据的寄存器变量必须声明为.b32类型。

e2m1 data format:

该数据格式是一种4位浮点格式,其中2位用于指数,1位用于尾数。 e2m1编码不支持无穷大和NaNe2m1值必须使用 指定的打包格式e2m1x2。包含两个e2m1值的寄存器变量必须 声明为.b8类型。

e2m3 data format:

该数据格式是一种6位浮点格式,其中2位用于指数,3位用于尾数。 e2m3编码不支持无穷大和NaNe2m3值必须使用 指定的打包格式e2m3x2。包含两个e2m3值的寄存器变量必须 声明为.b16类型,其中每个.b8元素包含6位浮点值,最高有效2位用零填充。

e3m2 data format:

该数据格式是一种6位浮点格式,其中3位用于指数,2位用于尾数。 e3m2编码不支持无穷大和NaNe3m2值必须使用 指定的打包格式e3m2x2。包含两个e3m2值的寄存器变量必须 声明为.b16类型,其中每个.b8元素包含6位浮点值,且最高有效2位用零填充。

ue8m0 data format:

该数据格式是一种8位无符号浮点格式,其中8位用于指数,0位用于尾数。ue8m0编码不支持无穷大。NaN值限制为0xffue8m0值必须使用ue8m0x2指定的打包格式。包含两个ue8m0值的寄存器变量必须声明为.b16类型。

ue4m3 data format:

该数据格式是一种7位无符号浮点格式,其中4位用于指数,3位用于尾数。ue4m3编码不支持无穷大。NaN值限制为0x7f。包含单个ue4m3值的寄存器变量必须声明为.b8类型,且最高有效位(MSB)需补零。

替代数据格式不能用作基础类型。某些指令支持将其作为源或目标格式。

5.2.4. Packed Data Types

某些PTX指令可以并行操作两组或更多输入集,并产生两个或多个输出。这类指令可以利用打包格式存储的数据。PTX支持将两个或四个相同标量数据类型的值打包成一个更大的值。打包后的值被视为打包数据类型的值。本节我们将介绍PTX支持的打包数据类型。

5.2.4.1. Packed Floating Point Data Types

PTX支持多种打包浮点数据类型的变体。其中,只有.f16x2被支持作为基础类型,而其他类型不能用作基础类型——它们仅在某些指令上作为指令类型被支持。当使用带有此类非基础类型的指令时,操作数数据变量必须是适当大小的位类型。例如,对于指令类型为.bf16x2的指令,所有操作数变量都必须是.b32类型。表9描述了PTX中各种打包浮点数据类型的变体。

表 9 打包浮点指令类型的操作数类型。

打包浮点类型

打包格式中包含的元素数量

每个元素的类型

声明时应使用的寄存器变量类型

.f16x2

.f16

.f16x2.b32

.f32x2

.f32

.b64

.bf16x2

.bf16

.b32

.e4m3x2

.e4m3

.b16

.e5m2x2

.e5m2

.e2m3x2

.e2m3

.e3m2x2

.e3m2

.ue8m0x2

.ue8m0

.e2m1x2

.e2m1

.b8

.e4m3x4

.e4m3

.b32

.e5m2x4

.e5m2

.e2m3x4

.e2m3

.e3m2x4

.e3m2

.e2m1x4

.e2m1

5.2.4.2. Packed Integer Data Types

PTX支持两种打包整数数据类型的变体:.u16x2.s16x2。打包数据类型由两个.u16.s16值组成。包含.u16x2.s16x2数据的寄存器变量必须声明为.b32类型。打包整数数据类型不能用作基本类型,它们仅作为特定指令的指令类型被支持。

5.3. 纹理采样器与表面类型

PTX包含内置的不透明类型,用于定义纹理、采样器和表面描述符变量。这些类型具有类似结构的命名字段,但关于布局、字段顺序、基地址和总大小的所有信息对PTX程序都是隐藏的,因此称为不透明。这些不透明类型的使用仅限于:

  • 在全局(模块)作用域和内核入口参数列表中的变量定义。

  • 使用逗号分隔的静态赋值表达式对类型的命名成员进行模块作用域变量的静态初始化。

  • 通过纹理和表面加载/存储指令(texsuldsustsured)引用纹理、采样器或表面。

  • 通过查询指令(txq, suq)检索命名成员的值。

  • 使用mov指令创建指向不透明变量的指针,例如mov.u64 reg, opaque_var;。生成的指针可以存储到内存中、从内存加载、作为函数参数传递,并通过纹理和表面加载、存储及查询指令进行解引用,但不能将该指针作为地址处理(例如使用ldst指令访问指针,或执行指针算术运算将导致未定义结果)。

  • 不透明变量不能出现在初始化器中,例如,用于初始化指向不透明变量的指针。

注意

从PTX ISA版本3.1开始支持通过指向不透明变量的指针间接访问纹理和表面,且需要目标架构为sm_20或更高版本。

间接访问纹理仅在统一纹理模式下支持(见下文)。

三种内置类型是.texref.samplerref.surfref。对于纹理和采样器的操作,PTX有两种工作模式。在统一模式下,纹理和采样器信息通过单一的.texref句柄访问。在独立模式下,纹理和采样器信息各自拥有独立的句柄,允许它们分别定义并在程序使用点进行组合。在独立模式下,.texref类型中描述采样器属性的字段将被忽略,因为这些属性由.samplerref变量定义。

表10表11列出了统一和独立纹理模式下每种类型的命名成员。这些成员及其值与纹理HW类中定义的方法和值以及通过API公开的值具有精确的映射关系。

表 10 统一纹理模式下的不透明类型字段

成员

.texref 值

.surfref 值

width

在元素中

height

在元素中

depth

在元素中

channel_data_type

enum 类型对应源语言API

channel_order

enum 类型对应源语言API

normalized_coords

0, 1

不适用

filter_mode

nearest, linear

不适用

addr_mode_0, addr_mode_1, addr_mode_2

wrap,mirror, clamp_ogl, clamp_to_edge, clamp_to_border

不适用

array_size

作为纹理数组中的纹理数量

作为表面数组中的表面数量

num_mipmap_levels

作为mipmapped纹理中的层级数量

不适用

num_samples

作为多采样纹理中的样本数量

不适用

memory_layout

不适用

1 表示线性内存布局;0 表示其他情况

5.3.1. 纹理与表面属性

字段 widthheightdepth 分别指定纹理或表面在每个维度上的元素数量大小。

channel_data_typechannel_order 字段使用与源语言API对应的枚举类型来指定纹理或表面的这些属性。例如,有关PTX当前支持的OpenCL枚举类型,请参阅Channel Data Type and Channel Order Fields

5.3.2. 采样器属性

normalized_coords字段表示纹理或表面是否使用[0.0, 1.0)范围内的归一化坐标,而非[0, N)范围内的非归一化坐标。如果未指定值,则默认由运行时系统根据源语言设置。

filter_mode 字段指定了如何根据输入的纹理坐标计算纹理读取返回的值。

addr_mode_{0,1,2} 字段定义了每个维度的寻址模式,用于确定如何处理超出范围的坐标。

有关这些属性的更多详细信息,请参阅CUDA C++编程指南

表 11 独立纹理模式下的不透明类型字段

成员

.samplerref 值

.texref 值

.surfref 值

width

不适用

在元素中

height

不适用

在元素中

depth

不适用

在元素中

channel_data_type

不适用

enum 类型对应源语言API

channel_order

不适用

enum 类型对应源语言AP

normalized_coords

不适用

0, 1

不适用

force_unnormalized_coords

0, 1

不适用

不适用

filter_mode

nearest, linear

已忽略

不适用

addr_mode_0, addr_mode_1, addr_mode_2

wrap,mirror, clamp_ogl, clamp_to_edge, clamp_to_border

不适用

不适用

array_size

不适用

作为纹理数组中的纹理数量

作为表面数组中的表面数量

num_mipmap_levels

不适用

作为mipmapped纹理中的层级数量

不适用

num_samples

不适用

作为多采样纹理中的样本数量

不适用

memory_layout

不适用

不适用

1 表示线性内存布局;0 表示其他情况

在独立纹理模式下,采样器属性保存在独立的.samplerref变量中,这些字段在.texref变量中被禁用。独立纹理模式下还额外提供了一个采样器属性force_unnormalized_coords

force_unnormalized_coords字段是.samplerref变量的一个属性,它允许采样器覆盖纹理头normalized_coords属性。该字段仅在独立纹理模式下定义。当值为True时,将覆盖纹理头设置并使用非归一化坐标;当值为False时,则使用纹理头设置。

force_unnormalized_coords属性用于编译OpenCL;在OpenCL中,归一化坐标的属性包含在采样器头中。要将OpenCL编译为PTX,纹理头总是初始化为normalized_coords设为True,而基于OpenCL采样器的normalized_coords标志会(取反后)映射到PTX级别的force_unnormalized_coords标志。

使用这些类型的变量可以在模块作用域或内核入口参数列表中声明。在模块作用域中,这些变量必须位于.global状态空间。作为内核参数时,这些变量则在.param状态空间中声明。

示例

.global .texref     my_texture_name;
.global .samplerref my_sampler_name;
.global .surfref    my_surface_name;

在模块作用域声明时,可以通过静态表达式列表为命名成员赋值来初始化这些类型。

示例

.global .texref tex1;
.global .samplerref tsamp1 = { addr_mode_0 = clamp_to_border,
                               filter_mode = nearest
                             };

5.3.3. 通道数据类型与通道顺序字段

channel_data_typechannel_order 字段具有与源语言API对应的枚举类型。目前,OpenCL是唯一定义了这些字段的源语言。表13表12展示了OpenCL 1.0版本中为通道数据类型和通道顺序定义的枚举值。

表 12 OpenCL 1.0通道数据类型定义

CL_SNORM_INT8

0x10D0

CL_SNORM_INT16

0x10D1

CL_UNORM_INT8

0x10D2

CL_UNORM_INT16

0x10D3

CL_UNORM_SHORT_565

0x10D4

CL_UNORM_SHORT_555

0x10D5

CL_UNORM_INT_101010

0x10D6

CL_SIGNED_INT8

0x10D7

CL_SIGNED_INT16

0x10D8

CL_SIGNED_INT32

0x10D9

CL_UNSIGNED_INT8

0x10DA

CL_UNSIGNED_INT16

0x10DB

CL_UNSIGNED_INT32

0x10DC

CL_HALF_FLOAT

0x10DD

CL_FLOAT

0x10DE

表13OpenCL 1.0 通道顺序定义

CL_R

0x10B0

CL_A

0x10B1

CL_RG

0x10B2

CL_RA

0x10B3

CL_RGB

0x10B4

CL_RGBA

0x10B5

CL_BGRA

0x10B6

CL_ARGB

0x10B7

CL_INTENSITY

0x10B8

CL_LUMINANCE

0x10B9

5.4. 变量

在PTX中,变量声明同时描述了变量的类型及其状态空间。除了基本类型外,PTX还支持简单聚合对象(如向量和数组)的类型。

5.4.1. 变量声明

所有数据存储均通过变量声明指定。每个变量必须位于前一节列举的状态空间之一。

变量声明定义了变量所在的空间名称、类型和大小、变量名,可选的数组大小,可选的初始化器,以及变量的可选固定地址。

谓词变量只能在寄存器状态空间中声明。

示例

.global .u32 loc;
.reg    .s32 i;
.const  .f32 bias[] = {-1.0, 1.0};
.global .u8  bg[4] = {0, 0, 0, 0};
.reg    .v4 .f32 accel;
.reg    .pred p, q, r;

5.4.2. 向量

支持有限长度的向量类型。任何非谓词基本类型的2维和4维向量可以通过在类型前添加.v2.v4前缀来声明。向量必须基于基本类型,并且可以驻留在寄存器空间中。向量的长度不能超过128位;例如,不允许使用.v4 .f64。三维向量可以通过使用.v4向量来处理,其中第四个元素提供填充。这是三维网格、纹理等常见情况。

示例

.global .v4 .f32 V;   // a length-4 vector of floats
.shared .v2 .u16 uv;  // a length-2 vector of unsigned ints
.global .v4 .b8  v;   // a length-4 vector of bytes

默认情况下,向量变量会按照其整体大小(向量长度乘以基类型大小)的倍数进行对齐,以便支持需要地址按访问大小倍数对齐的向量加载和存储指令。

5.4.3. 数组声明

数组声明用于允许程序员预留空间。要声明一个数组,变量名后需跟随维度声明,类似于C语言中的固定大小数组声明。每个维度的大小是一个常量表达式。

示例

.local  .u16 kernel[19][19];
.shared .u8  mailbox[128];

数组的大小指定了应保留多少个元素。对于上述kernel数组的声明,保留了19*19=361个半字,总共722字节。

当使用初始化器声明时,可以省略数组的第一个维度。数组第一个维度的大小由数组初始化器中的元素数量决定。

示例

.global .u32 index[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
.global .s32 offset[][2] = { {-1, 0}, {0, -1}, {1, 0}, {0, 1} };

数组index包含八个元素,数组offset是一个4x2的数组。

5.4.4. Initializers

声明的变量可以使用类似C/C++的语法指定初始值,即在变量名后跟一个等号和变量的初始值。标量采用单个值,而向量和数组则使用花括号内的嵌套值列表(嵌套层级与声明的维度相匹配)。

与C语言类似,数组初始化器可以是不完整的,即初始化元素的数量可能少于对应数组维度的范围,剩余的数组位置将被初始化为指定数组类型的默认值。

示例

.const  .f32 vals[8] = { 0.33, 0.25, 0.125 };
.global .s32 x[3][2] = { {1,2}, {3} };

等同于

.const  .f32 vals[8] = { 0.33, 0.25, 0.125, 0.0, 0.0, 0.0, 0.0, 0.0 };
.global .s32 x[3][2] = { {1,2}, {3,0}, {0,0} };

目前,变量初始化仅支持常量和全局状态空间。默认情况下,未显式初始化的常量和全局状态空间中的变量会被初始化为零。外部变量声明中不允许使用初始化器。

出现在初始化器中的变量名代表该变量的地址;这可用于静态初始化指向变量的指针。初始化器还可以包含变量+偏移量表达式,其中偏移量是添加到变量地址的字节偏移量。只有位于.global.const状态空间的变量才能用于初始化器。默认情况下,生成的地址是变量在其状态空间中的偏移量(就像使用mov指令获取变量地址时的情况)。提供了一个操作符generic()来为初始化器中使用的变量创建通用地址。

从PTX ISA 7.1版本开始,提供了一个mask()运算符,其中mask是一个整数立即数。在mask()运算符中唯一允许的表达式是整数常量表达式和表示变量地址的符号表达式。mask()运算符从初始化器中使用的表达式中提取n个连续位,并将这些位插入到被初始化变量的最低位。要提取的位数n和起始位置由整数立即数mask指定。PTX ISA 7.1版本仅支持从变量地址的字节边界开始提取单个字节。PTX ISA 7.3版本支持将整数常量表达式作为mask()运算符的操作数。

mask 支持的值包括: 0xFF, 0xFF00, 0XFF0000, 0xFF000000, 0xFF00000000, 0xFF0000000000, 0xFF000000000000, 0xFF00000000000000.

示例

.const  .u32 foo = 42;
.global .u32 bar[] = { 2, 3, 5 };
.global .u32 p1 = foo;          // offset of foo in .const space
.global .u32 p2 = generic(foo); // generic address of foo

// array of generic-address pointers to elements of bar
.global .u32 parr[] = { generic(bar), generic(bar)+4,
generic(bar)+8 };

// examples using mask() operator are pruned for brevity
.global .u8 addr[] = {0xff(foo), 0xff00(foo), 0xff0000(foo), ...};

.global .u8 addr2[] = {0xff(foo+4), 0xff00(foo+4), 0xff0000(foo+4),...}

.global .u8 addr3[] = {0xff(generic(foo)), 0xff00(generic(foo)),...}

.global .u8 addr4[] = {0xff(generic(foo)+4), 0xff00(generic(foo)+4),...}

// mask() operator with integer const expression
.global .u8 addr5[] = { 0xFF(1000 + 546), 0xFF00(131187), ...};

注意

PTX 3.1重新定义了初始化器中全局变量的默认寻址方式,从通用地址改为全局状态空间中的偏移量。传统PTX代码会被视为在每个初始化器使用的全局变量前隐式添加了generic()运算符。PTX 3.1代码应当在初始化器中包含显式的generic()运算符,使用cvta.global在运行时生成通用地址,或者通过ld.global从非通用地址加载数据。

出现在初始化器中的设备函数名称代表该函数第一条指令的地址;这可用于初始化一个函数指针表,以便用于间接调用。从PTX ISA版本3.1开始,内核函数名称也可用作初始化器,例如用于初始化内核函数指针表,以便通过CUDA动态并行机制从GPU启动内核。详情请参阅CUDA动态并行编程指南

标签不能用于初始化器中。

存储变量或函数地址的变量类型应为.u8.u32.u64

仅当使用mask()运算符时,才允许类型.u8

.f16.f16x2.pred类型外,所有类型都支持初始化器。

示例

.global .s32 n = 10;
.global .f32 blur_kernel[][3]
               = {{.05,.1,.05},{.1,.4,.1},{.05,.1,.05}};

.global .u32 foo[] = { 2, 3, 5, 7, 9, 11 };
.global .u64 ptr = generic(foo);   // generic address of foo[0]
.global .u64 ptr = generic(foo)+8; // generic address of foo[2]

5.4.5. Alignment

所有可寻址变量的存储字节对齐可以在变量声明中指定。对齐是通过在状态空间说明符后立即添加一个可选的.align字节数说明符来指定的。该变量将被对齐到一个地址,该地址是字节数的整数倍。对齐值字节数必须是2的幂次。对于数组,对齐指定的是整个数组起始地址的对齐方式,而非单个元素的对齐方式。

标量和数组变量的默认对齐方式是基类型大小的倍数。向量变量的默认对齐方式是整个向量大小的倍数。

示例

 // allocate array at 4-byte aligned address.  Elements are bytes.
.const .align 4 .b8 bar[8] = {0,0,0,0,2,0,0,0};

请注意,所有访问内存的PTX指令都要求地址按访问大小的整数倍对齐。内存指令的访问大小是指该指令在内存中访问的总字节数。例如,ld.v4.b32的访问大小为16字节,而atom.f16x2的访问大小为4字节。

5.4.6. 参数化变量名

由于PTX支持虚拟寄存器,编译器前端通常会生成大量寄存器名称。PTX不需要显式声明每个名称,而是支持一种语法来创建一组具有共同前缀字符串并附加整数后缀的变量。

例如,假设一个程序使用了大量变量,比如一百个.b32变量,分别命名为 %r0, %r1, ..., %r99。这100个寄存器变量可以按如下方式声明:

.reg .b32 %r<100>;    // declare %r0, %r1, ..., %r99

这种简写语法可用于任何基本类型和任何状态空间,并且可以前置对齐说明符。数组变量不能以这种方式声明,也不允许使用初始化器。

5.4.7. 变量属性

变量可以通过可选的.attribute指令进行声明,该指令允许指定变量的特殊属性。关键字.attribute后跟括号内的属性说明。多个属性之间用逗号分隔。

Variable and Function Attribute Directive: .attribute 描述了 .attribute 指令。

5.4.8. 变量与函数属性指令:.attribute

.属性

变量与函数属性

描述

用于指定变量或函数的特殊属性。

支持以下属性。

.managed

.managed 属性指定变量将被分配在统一虚拟内存环境中,主机和系统中的其他设备可以直接引用该变量。此属性仅能与.global状态空间中的变量一起使用。详情请参阅CUDA UVM-Lite编程指南

.unified

.unified 属性指定函数在主机和系统其他设备上具有相同的内存地址。整型常量 uuid1uuid2 分别指定与函数或变量关联的唯一标识符的高64位和低64位。该属性只能用于设备函数或处于 .global 状态空间的变量。带有 .unified 属性的变量是只读的,必须通过在 ld 指令的地址操作数上指定 .unified 限定符来加载,否则行为是未定义的。

PTX ISA 说明

  • 在PTX ISA版本4.0中引入。

  • 支持PTX ISA 8.0版本中引入的函数属性。

目标ISA注意事项

  • .managed 属性需要 sm_30 或更高版本。

  • .unified 属性需要 sm_90 或更高版本。

示例

.global .attribute(.managed) .s32 g;
.global .attribute(.managed) .u64 x;

.global .attribute(.unified(19,95)) .f32 f;

.func .attribute(.unified(0xAB, 0xCD)) bar() { ... }

5.5. 张量

张量是内存中的一种多维矩阵结构。张量由以下属性定义:

  • 维度

  • 每个维度的尺寸大小

  • 单个元素类型

  • 张量在每个维度上的步长

PTX支持可对张量数据进行操作的指令。PTX张量指令包括:

  • 在全局内存和共享内存之间复制数据

  • 使用源数据减少目标张量数据。

张量数据可以通过各种wmma.mmammawgmma.mma_async指令进行操作。

PTX张量指令将全局内存中的张量数据视为多维结构,而将共享内存中的数据视为线性数据。

5.5.1. 张量维度、大小与格式

张量可以具有以下维度:1D、2D、3D、4D或5D。

每个维度都有一个大小,表示该维度上的元素数量。元素可以具有以下类型之一:

  • 位大小类型: .b32, .b64

  • 子字节类型:.b4x16, .b4x16_p64, .b6x16_p32, .b6p2x16

  • 整数类型: .u8, .u16, .u32, .s32, .u64, .s64

  • 浮点数和替代浮点数格式:.f16, .bf16, .tf32, .f32, .f64 (四舍五入到最接近的偶数)。

张量可以在每个维度的末尾添加填充(padding),以便为后续维度的数据提供对齐。张量步长(stride)可用于指定每个维度的填充量。

5.5.1.1. Sub-byte Types

5.5.1.1.1. 子字节类型的填充与对齐

子字节类型预计在全局内存中连续打包,Tensor复制指令将通过附加空位来扩展它们,如下所示:

  1. 类型 .b4x16: 使用此类型时不会进行填充操作,64位容器中打包的16个.b4元素会在共享内存与全局内存之间直接复制。

  2. 类型 .b4x16_p64: 使用此类型时,十六个连续的4位数据会从全局内存复制到共享内存,并附加64位的填充,如 图5所示

    _images/tensor-dimension-size-format-sub-bytes-padding-align-b4-16-p64.png

    图5 .b4x16_p64的布局

    添加的填充区域是未初始化的。

  3. 类型 .b6x16_p32: 使用此类型时,16个6位数据会从全局内存复制到共享内存,并附加32位填充,如 图6所示

    _images/tensor-dimension-size-format-sub-bytes-padding-align-b6-16-p32.png

    图6 .b6x16_p32的布局

    添加的填充区域是未初始化的。

  4. 类型 .b6p2x16: 使用此类型时,十六个元素(每个元素包含6位数据位于最低有效位和2位填充位于最高有效位)会从共享内存复制到全局内存中,丢弃2位填充数据并将6位数据连续打包,如图7所示

    _images/tensor-dimension-size-format-sub-bytes-padding-align-b6-p2-16.png

    图7 .b6p2x16的布局

对于.b6x16_p32.b4x16_p64的情况,所添加的填充区域是未初始化的。

类型 .b6x16_p32.b6p2x16 在描述符中共享相同的编码值(值15),因为这两种类型适用于不同种类的张量复制操作:

类型

有效的张量复制方向

.b6x16_p32

.shared::cluster.global, .shared::cta.global

.b6p2x16

.global.shared::cta

5.5.2. 张量访问模式

张量数据可以通过两种模式访问:

  • 平铺模式:

    在平铺模式下,源多维张量的布局会在目标位置保持不变。

  • Im2col模式:

    在im2col模式下,源张量边界框中的元素会被重新排列为目标矩阵的列。更多详情请参考此处

5.5.3. 平铺模式

本节讨论张量(Tensor)及其在平铺模式下的访问方式。

5.5.3.1. 边界框

张量可以通过称为边界框(Bounding Box)的块来访问。边界框与其访问的张量具有相同的维度。每个边界框的大小必须是16字节的倍数。边界框的地址也必须对齐到16字节。

边界框具有以下访问属性:

  • 边界框尺寸大小

  • 越界访问模式

  • 遍历步长

PTX张量指令中指定的张量坐标定义了边界框的起始偏移量。边界框的起始偏移量与其他边界框信息共同用于确定需要访问的元素。

5.5.3.2. Traversal-Stride

当边界框(Bounding Box)沿着某个维度迭代张量时,遍历步长(traversal stride)指定了需要跳过的确切元素数量。若无需跳过元素,则必须指定默认值1。

维度0中的遍历步长可用于交错布局。对于非交错布局,维度0中的遍历步长必须始终为1。

图8展示了张量、张量尺寸、张量步长、边界框尺寸和遍历步长。

_images/tensor-tiled-mode-bounding-box-example.png

图8 平铺模式下的边界框、张量尺寸和遍历步长

5.5.3.3. 越界访问

PTX张量操作能够检测并处理边界框在任何维度上跨越张量边界的情况。共有两种模式:

  • 零填充模式:

    边界框内超出张量边界的元素将被设置为0。

  • OOB-NaN 填充模式:

    边界框内超出张量边界的元素会被设置为一种特殊的NaN值,称为OOB-NaN

图9展示了一个越界访问的示例。

_images/tensor-oob-access.png

图9 越界访问

5.5.3.4. Tile::scatter4 和 Tile::gather4 模式

这些模式与平铺模式类似,但限制条件是这些模式仅适用于二维张量数据。 Tile::scatter4Tile::gather4 模式用于访问张量数据的多个非连续行。

Tile::scatter4模式下,单个2D源张量会被分割成目标2D张量中的四行。 在Tile::gather4模式下,源2D张量中的四行会被合并形成单个2D目标张量。

这些模式作用于四行数据,因此指令将需要:

  1. 维度0上的四个张量坐标

  2. 跨维度1的一个张量坐标

.tile::scatter4.tile::gather4 模式不支持交错布局。

所有其他瓦片模式的约束和规则同样适用于这些模式。

5.5.3.4.1. 边界框

对于Tile::scatter4Tile::gather4模式,四个请求坐标将在张量空间中形成四个边界框。

图10展示了起始坐标为(1, 2)、(1, 5)、(1, 0)和(1, 9)的相同示例。

边界框在维度0上的大小代表行的长度。 边界框在维度1上的大小必须为1。

_images/tiled-scatter4-gather4-bounding-box.png

图10 tiled::scatter4/tiled::gather4模式边界框示例

5.5.4. Im2col模式

Im2col模式支持以下张量维度:3D、4D和5D。在此模式下,张量数据被视为具有以下属性的图像批次:

  • N : 批次中的图像数量

  • D, H, W : 3D图像的尺寸(深度、高度和宽度)

  • C: 每个图像元素的通道数

上述属性与3D、4D和5D张量的关联如下:

维度

N/D/H/W/C适用性

3D

NWC

4D

NHWC

5D

NDHWC

5.5.4.1. Bounding Box

在im2col模式下,边界框(Bounding Box)是在DHW空间中定义的。其他维度的边界由如下所述的每列像素数(Pixels-per-Column)和每像素通道数(Channels-per-Pixel)参数指定。

边界框的维度比张量的维度少两个。

以下属性描述了如何在im2col模式下访问元素:

  • 边界框左下角

  • 边界框右上角

  • 每列像素数

  • 每像素通道数

边界框左下角边界框右上角指定了DHW空间中边界框的两个对角顶点。边界框左下角指定坐标值最小的角点,而边界框右上角指定坐标值最大的角点。

边界框上角下角是16位有符号值,其限制因维度而异,如下所示:

3D

4D

5D

上/下角尺寸

[-215, 215-1]

[-27, 27-1]

[-24, 24-1]

图11图12 展示了上角点和下角点。

_images/tensor-im2col-mode-bounding-box1.png

图11 im2col模式边界框示例1

_images/tensor-im2col-mode-bounding-box2.png

图12 im2col模式边界框示例2

边界框的上角下角仅指定边界,而不指定要访问的元素数量。每列像素数指定在NDHW空间中要访问的元素数量。

每像素通道数指定了在C维度上要访问的元素数量。

在PTX张量指令中指定的张量坐标在不同维度上的行为表现不同:

  • 跨N和C维度:指定沿维度的起始偏移量,类似于平铺模式。

  • 在DHW维度上:指定卷积滤波器基在张量空间中的位置。滤波器角点位置必须在边界框内。

im2col偏移量,在im2col模式的PTX张量指令中指定,会被添加到过滤器基础坐标中,用于确定从张量空间中访问元素的起始位置。

im2col偏移量的大小在各个维度上有所不同,其有效范围如下所示:

3D

4D

5D

im2col偏移量范围

[0, 216-1]

[0, 28-1]

[0, 25-1]

以下是im2col模式访问的一些示例:

  • 示例1 (图13):

    Tensor Size[0] = 64
    Tensor Size[1] = 9
    Tensor Size[2] = 14
    Tensor Size[3] = 64
    每列像素数 = 64
    每像素通道数 = 8
    边界框左下角W坐标 = -1
    边界框左下角H坐标 = -1
    边界框右上角W坐标 = -1
    边界框右上角H坐标 = -1.
    
    张量坐标 = (7, 7, 4, 0)
    im2col偏移量 : (0, 0)
    
    _images/tensor-im2col-mode-example1.png

    图13 im2col模式示例1

  • 示例2 (图14):

    Tensor Size[0] = 64
    Tensor Size[1] = 9
    Tensor Size[2] = 14
    Tensor Size[3] = 64
    每列像素数 = 64
    每像素通道数 = 8
    边界框左下角W坐标 = 0
    边界框左下角H坐标 = 0
    边界框右上角W坐标 = -2
    边界框右上角H坐标 = -2
    
    张量坐标 = (7, 7, 4, 0)
    im2col偏移量: (2, 2)
    
    _images/tensor-im2col-mode-example2.png

    图14 im2col模式示例2

5.5.4.2. 遍历步长

在im2col模式下,遍历步幅不会像平铺模式那样影响被访问元素(或像素)的总数。每列像素数决定了在im2col模式下被访问元素的总数。

沿D、H和W维度遍历的元素数量由该维度的遍历步幅决定。

以下示例配合图15展示了使用遍历步幅进行访问的情况:

Tensor Size[0] = 64
Tensor Size[1] = 8
Tensor Size[2] = 14
Tensor Size[3] = 64
Traversal Stride = 2
Pixels-per-Column = 32
channels-per-pixel = 16
Bounding-Box Lower-Corner W = -1
Bounding-Box Lower-Corner H = -1
Bounding-Box Upper-Corner W = -1
Bounding-Box Upper-Corner H = -1.
Tensor coordinates in the instruction = (7, 7, 5, 0)
Im2col offsets in the instruction : (1, 1)
_images/tensor-im2col-mode-example3.png

图15 im2col模式遍历步幅示例

5.5.4.3. 越界访问

在im2col模式下,当每列像素数指定的NDHW空间请求像素数超过图像批次中可用像素数时,将执行越界访问。

类似于平铺模式,可以根据指定的填充模式执行零填充或OOB-NaN填充。

5.5.5. Im2col::w 和 Im2col::w::128 模式

这些模式与im2col模式类似,但有限制条件:元素仅沿W维度访问,同时保持HD维度不变。

im2col模式的所有约束和规则同样适用于这些模式。

im2col::w::128模式下访问的元素数量是固定的,等于128。 在im2col::w模式下访问的元素数量取决于TensorMap中的Pixels-per-Column字段。

5.5.5.1. 边界框

在这些模式下,边界框在DH维度上的大小为1。

PTX指令中张量坐标参数的DH维度指定了边界框在张量空间中的位置。

边界框的Lower-Corner-W和边界框的Upper-Corner-W指定了边界框在W维度上的两个对角顶点。

张量坐标参数中的W维度在PTX指令中指定了边界框内待访问第一个元素的位置。

im2col::w模式下加载的像素数量由TensorMap中的每列像素数指定。 在im2col::w::128模式下加载的像素数量始终为128。因此,在im2col::w::128模式下会忽略每列像素数。

图16展示了im2col::wim2col::w:128模式的示例。

_images/tensor-im2col-w-w128-modes-example.png

图16 im2col::w和im2col::w::128模式示例

第一个元素可以仅位于W维度的边界框之外,并且只能在边界框的左侧。图17展示了这种情况的示例。

_images/tensor-im2col-w-w128-modes-example2.png

图17 im2col::w和im2col::w::128模式下边界框外第一个元素的示例

5.5.5.2. 遍历步长

这与im2col模式类似,不同之处在于仅沿W维度遍历的元素数量会根据TensorMap中指定的遍历步长进行跨步。

5.5.5.3. wHalo

im2col::w模式下,PTX指令中的wHalo参数指定了需要在图像末尾加载的滤波器光晕元素数量。

im2col::w::128模式下,沿W维度的边界框中每32个元素后会加载halo元素。PTX指令中的wHalo参数指定了每32个元素后必须加载的halo元素数量。

以下是.im2col::w模式访问的示例:

Tensor Size [0] = 128
Tensor Size [1] = 9
Tensor Size [2] = 7
Tensor Size [3] = 64
Pixels-per-column = 128
Channels-per-pixel = 64
Bounding Box Lower Corner W = 0
Bounding Box Upper Corner W = 0

Tensor Coordinates in the instruction = (7, 2, 3, 0)
wHalo in the instruction = 2 (as 3x3 convolution filter is used)

使用上述参数的张量复制操作会加载128个像素和两个边缘像素,如图18所示。

_images/tensor-im2col-w-w128-modes-example3.png

图18 使用im2col::w模式进行张量复制操作的示例

光晕像素总是加载在共享内存中,紧邻主行像素,如图18所示。

以下是.im2col::w::128模式访问的示例:

Tensor Size [0] = 128
Tensor Size [1] = 9
Tensor Size [2] = 7
Tensor Size [3] = 64
Channels-per-pixel = 64
Bounding Box Lower Corner W = 0
Bounding Box Upper Corner W = 0

Tensor Coordinates in the instruction = (7, 2, 3, 0)
wHalo in the instruction = 2 (as 3x3 convolution filter is used)

使用上述参数进行张量复制操作时,会加载128个元素,其中每加载32个元素后,会额外加载wHalo数量的元素,如图19所示。

_images/tensor-im2col-w-w128-modes-example4.png

图19 使用im2col::w::128模式进行张量复制操作的示例

5.5.5.4. wOffset

在卷积计算中,沿W维度的相同元素会在卷积滤波器覆盖范围内的不同位置被重复使用。根据像素被使用的次数,这些像素可能会被加载到不同的共享内存缓冲区中。每个缓冲区可以通过单独的张量复制操作进行加载。

张量复制和预取指令中的wOffset参数用于调整每个缓冲区的源像素位置。缓冲区的精确位置会沿着W维度通过以下公式进行调整:

Bounding Box Lower Corner W += wOffset
Bounding Box Upper Corner W += wOffset
W += wOffset

以下是使用不同wHalowOffset值将张量复制到多个缓冲区的示例:

示例 1:

Tensor Size [0] = 128
Tensor Size [1] = 9
Tensor Size [2] = 67
Tensor Size [3] = 64
Pixels-per-Column = 128
Channels-per-pixel = 64
Bounding Box Lower Corner W = -1
Bounding Box Upper Corner W = 0
Traversal Stride = 2

Tensor Coordinates in the instruction = (7, 2, -1, 0)

Shared memory buffer 1:
   wHalo = 1
   wOffset = 0

Shared memory buffer 2:
   wHalo = 0
   wOffset = 1
_images/tensor-im2col-w-w128-modes-example5.png

图20 示例1中向缓冲区1的张量复制操作

_images/tensor-im2col-w-w128-modes-example6.png

图21 示例1中向缓冲区2的张量复制操作

示例 2:

Tensor Size [0] = 128
Tensor Size [1] = 7
Tensor Size [2] = 7
Tensor Size [3] = 64
Pixels-per-Column = 128
Channels-per-pixel = 64
Bounding Box Lower Corner W = -1
Bounding Box Upper Corner W = -1
Traversal Stride = 3

Tensor Coordinates in the instruction = (7, 2, -1, 0)

Shared memory buffer 1:
   wHalo = 0
   wOffset = 0

Shared memory buffer 2:
   wHalo = 0
   wOffset = 1

Shared memory buffer 3:
   wHalo = 0
   wOffset = 2
_images/tensor-im2col-w-w128-modes-example7.png

图22 示例2中向缓冲区1的张量复制操作

_images/tensor-im2col-w-w128-modes-example8.png

图23 示例2中向缓冲区2的张量复制操作

_images/tensor-im2col-w-w128-modes-example9.png

图24 示例2中向缓冲区3的张量复制操作

5.5.6. 交错布局

张量可以交错排列,支持以下交错布局:

  • 无交错 (NDHWC)

  • 8字节交错(NC/8DHWC8):C8假设每个通道2字节,在内存中占用16字节。

  • 16字节交错(NC/16HWC16):假设每个通道4字节,C16在内存中占用32字节。

C信息被组织成切片,其中连续的C元素被分组为16字节或32字节的量。

如果通道总数不是每个切片通道数的整数倍,则最后一个切片必须用零填充以使其成为完整的16B或32B切片。

交错布局仅支持以下维度:3D、4D和5D。

交错布局不支持 .im2col::w.im2col::w::128 模式。

5.5.7. Swizzling Modes

出于访问性能的考虑,共享内存中的数据布局可能与全局内存不同。以下描述了各种数据重排模式:

  • 无交换模式:

    此模式下不进行数据交换,目标数据布局与源数据布局完全一致。

    0

    1

    2

    3

    4

    5

    6

    7

    0

    1

    2

    3

    4

    5

    6

    7

    ... 模式重复 ...

  • 32字节交换模式:

    下表展示了目标数据布局的模式,其中每个元素(编号单元格)为16字节,起始地址按256字节对齐:

    0

    1

    2

    3

    4

    5

    6

    7

    1

    0

    3

    2

    5

    4

    7

    6

    ... 模式重复 ...

    对于维度为1x2x10x10xC16的NC/(32B)HWC(32B)张量,其最内层维度包含16个通道的切片(每通道2字节),图25展示了32字节交换模式的示例。

    _images/tensor-32B-swizzle-dst.png

    图25 32字节交换模式示例

    图26展示了张量的两个片段:一个对应C/(32B) = 0,另一个对应C/(32B) = 1。

    _images/tensor-32B-swizzle-dst.png

    图26 32字节交换模式片段

    图27展示了经过32字节交换后的目标数据布局。

    _images/tensor-32B-swizzle-dst.png

    图27 32字节交换模式目标数据布局

  • 64字节交换模式:

    下表展示了目标数据布局的模式,其中每个元素(编号单元格)为16字节,起始地址按512字节对齐:

    0

    1

    2

    3

    4

    5

    6

    7

    1

    0

    3

    2

    5

    4

    7

    6

    2

    3

    0

    1

    6

    7

    4

    5

    3

    2

    1

    0

    7

    6

    5

    4

    ... 模式重复 ...

    对于1x10x10x64维度的NHWC张量,2字节/通道和32通道的64字节交换模式示例见图28

    _images/tensor-64B-swizzle-dst.png

    图28 64字节交换模式示例

    每个彩色单元格代表8个通道。图29展示了源数据布局。

    _images/tensor-64B-swizzle-dst.png

    图29 64字节交换模式源数据布局

    图30展示了经过64字节交换后的目标数据布局。

    _images/tensor-64B-swizzle-dst.png

    图30 64字节交换模式目标数据布局

  • 128字节交换模式:

    128字节交换模式支持以下子模式:

    • 16字节原子性子模式:

      在此子模式下,16字节数据在交换时保持完整。

    下表展示了目标数据布局的模式,其中每个元素(编号单元格)为16字节,起始地址按1024字节对齐:

    0

    1

    2

    3

    4

    5

    6

    7

    1

    0

    3

    2

    5

    4

    7

    6

    2

    3

    0

    1

    6

    7

    4

    5

    3

    2

    1

    0

    7

    6

    5

    4

    4

    5

    6

    7

    0

    1

    2

    3

    5

    4

    7

    6

    1

    0

    3

    2

    6

    7

    4

    5

    2

    3

    0

    1

    … 模式重复 …

    一个针对1x10x10x64维度的NHWC张量采用128字节交换模式的示例,其中每个通道2字节,共64个通道,如图31所示。

    _images/tensor-128B-swizzle.png

    图31 128字节交换模式示例

    每个彩色单元格代表8个通道。图32展示了源数据布局。

    _images/tensor-128B-swizzle-src.png

    图32 128字节交换模式源数据布局

    图33展示了采用128字节交织的目标数据布局。

    _images/tensor-128B-swizzle-dst.png

    图33 128字节交换模式目标数据布局

    • 32字节原子性子模式:

      在该子模式下,32字节数据在重排时保持完整。

      下表展示了目标数据布局中的重排模式,其中每个元素(编号单元格)为16字节:

      0 1

      2 3

      4 5

      6 7

      2 3

      0 1

      6 7

      4 5

      4 5

      6 7

      0 1

      2 3

      6 7

      4 5

      2 3

      0 1

      ... 模式重复 ...

      该子模式要求共享内存中的32字节对齐。

      图34展示了全局内存中的数据布局及其在共享内存中的重排数据布局示例,其中每个元素(彩色单元格)为16字节

      _images/tensor-128B-swizzle-32B-atom.png

      图34 具有32字节原子性的128字节重排模式示例

    • 32字节原子性与8字节翻转子模式:

      该子模式的交错模式与32字节原子性子模式类似,区别在于每间隔一个共享内存行时,16字节数据内的相邻8字节会发生翻转。

      图35展示了全局内存中的数据布局及其在共享内存中的交错数据布局示例(每个彩色单元格表示16字节元素,其中显示两个8字节子元素以展示翻转效果)

      _images/tensor-128B-swizzle-32B-atom-8B-flip.png

      图35 128字节交错模式示例(具备32字节原子性与8字节翻转特性)

    • 64字节原子性子模式:

      在该子模式下,数据在重排时保持64字节的完整性。

      下表展示了目标数据布局中的重排模式,其中每个元素(编号单元格)为16字节:

      0 1 2 3

      4 5 6 7

      4 5 6 7

      0 1 2 3

      ... 模式重复 ...

      该子模式要求共享内存中的64字节对齐。

      图36展示了全局内存中的数据布局及其在共享内存中的重排数据布局示例,其中每个元素(彩色单元格)为16字节。

      _images/tensor-128B-swizzle-64B-atom.png

      图36 具有64字节原子性的128字节重排模式示例

表14 列出了swizzle-atomicity与swizzling-mode的有效组合。

表14有效的swizzle原子性与swizzling模式的组合

交换模式

交换原子性

无交换

32B 交换模式

16B

64B 交换模式

16B

128B 交换模式

  • 16B

  • 32B

  • 32B + 8B翻转

  • 64B

dstMem共享内存地址位于以下边界时,swizzle基偏移量的值为0:

Swizzling模式

重复模式的起始地址

128字节交换

1024字节边界

64字节交换

512字节边界

32字节交换

256字节边界

否则,swizzle基础偏移量将是一个非零值,通过以下公式计算得出:

通道混排模式

公式

128字节交换

基础偏移量 = (dstMem / 128) % 8

64字节交换

基础偏移量 = (dstMem / 64) % 4

32字节交换

基础偏移量 = (dstMem / 32) % 2

5.5.8. Tensor-map

tensor-map是一个128字节的不透明对象,可以位于.const空间、.param空间(内核函数参数)或.global空间中,它描述了前几节中提到的张量属性及其数据访问属性。

Tensor-Map可以通过CUDA API创建。更多详情请参阅CUDA编程指南

6. 指令操作数

6.1. 操作数类型信息

指令中的所有操作数都根据其声明具有已知类型。每个操作数类型必须与指令模板和指令类型确定的类型兼容。类型之间没有自动转换。

位大小类型与所有具有相同大小的类型兼容。具有相同大小的整数类型彼此兼容。操作数类型与指令类型不同但兼容时,会被静默转换为指令类型。

6.2. 源操作数

指令描述中的源操作数用名称abc表示。PTX描述的是加载-存储架构机器,因此ALU指令的操作数必须全部位于.reg寄存器状态空间中声明的变量内。对于大多数操作,操作数的大小必须保持一致。

cvt(转换)指令支持多种操作数类型和大小,其作用是将几乎任何数据类型转换为其他任何数据类型(及大小)。

ldstmovcvt指令用于将数据从一个位置复制到另一个位置。其中ldst指令在可寻址状态空间与寄存器之间传输数据,而mov指令则在寄存器之间进行数据拷贝。

大多数指令都有一个可选的条件谓词守卫用于控制条件执行,少数指令还包含额外的谓词源操作数。谓词操作数用名称pqrs表示。

6.3. 目标操作数

生成单个结果的PTX指令会将结果存储在指令描述中由d(表示目标位置)表示的字段中。结果操作数是寄存器状态空间中的标量或向量变量。

6.4. 使用地址、数组和向量

使用标量变量作为操作数非常简单。有趣的功能始于地址、数组和向量。

6.4.1. 地址作为操作数

所有内存指令都接受一个地址操作数,用于指定要访问的内存位置。该可寻址操作数属于以下类型之一:

[var]

可寻址变量的名称 var

[reg]

一个整数或位宽类型寄存器 reg,其中包含一个字节地址。

[reg+immOff]

寄存器 reg 中包含的字节地址加上一个常量整数字节偏移量(有符号,32位)的总和。

[var+immOff]

可寻址变量var的字节地址加上一个常量整数字节偏移量(有符号,32位)的总和。

[immAddr]

一个直接的绝对字节地址(无符号,32位)。

var[immOff]

数组作为操作数中所述的数组元素。

包含地址的寄存器可以声明为位宽类型或整数类型。

内存指令的访问大小是指内存中访问的总字节数。例如,ld.v4.b32的访问大小为16字节,而atom.f16x2的访问大小为4字节。

地址必须自然对齐到访问大小的整数倍。如果地址未正确对齐,将导致未定义行为。例如,访问可能会通过静默屏蔽低位地址位来实现正确舍入,或者指令可能会引发错误。

地址大小可以是32位或64位。不支持128位地址。地址会根据需要零扩展到指定宽度,如果寄存器宽度超过目标架构的状态空间地址宽度,则会被截断。

地址运算使用整数算术和逻辑指令执行。示例包括指针算术和指针比较。所有地址和地址计算均基于字节;不支持C风格的指针算术。

mov指令可用于将变量的地址移动到指针中。该地址是变量声明所在状态空间中的偏移量。加载和存储操作在寄存器和可寻址状态空间中的位置之间移动数据。其语法类似于许多汇编语言中使用的语法,其中标量变量只需命名,而地址则通过将地址表达式括在方括号中进行解引用。地址表达式包括变量名、地址寄存器、地址寄存器加字节偏移量,以及在编译时计算为常量地址的立即地址表达式。

以下是一些示例:

.shared .u16 x;
.reg    .u16 r0;
.global .v4 .f32 V;
.reg    .v4 .f32 W;
.const  .s32 tbl[256];
.reg    .b32 p;
.reg    .s32 q;

ld.shared.u16   r0,[x];
ld.global.v4.f32 W, [V];
ld.const.s32    q, [tbl+12];
mov.u32         p, tbl;

6.4.1.1. 通用寻址

如果内存指令未指定状态空间,则使用通用寻址执行操作。状态空间.constKernel Function Parameters (.param)、.local.shared被建模为通用地址空间内的窗口。每个窗口由窗口基址和等于相应状态空间大小的窗口大小定义。通用地址映射到global内存,除非它落在constlocalshared内存的窗口内。Kernel Function Parameters (.param)窗口包含在.global窗口内。在每个窗口内,通过从通用地址中减去窗口基址,将通用地址映射到底层状态空间中的地址。

6.4.2. 数组作为操作数

可以声明所有类型的数组,标识符将成为数组声明所在空间中的地址常量。数组的大小是程序中的一个常量。

数组元素可以通过显式计算的字节地址访问,也可以通过方括号表示法进行索引访问。方括号内的表达式可以是常量整数、寄存器变量,或者简单的带常量偏移的寄存器表达式,其中偏移量是一个常量表达式,可以与寄存器变量相加或相减。如果需要更复杂的索引计算,必须先进行地址计算再使用。示例如下:

ld.global.u32  s, a[0];
ld.global.u32  s, a[N-1];
mov.u32        s, a[1];  // move address of a[1] into s

6.4.3. 向量作为操作数

向量操作数仅支持有限的指令子集,包括movldstatomredtex。向量也可以作为参数传递给被调用的函数。

可以通过后缀.x.y.z.w从向量中提取元素,也可以使用常见的颜色字段.r.g.b.a

大括号包围的列表用于模式匹配以分解向量。

.reg .v4 .f32 V;
.reg .f32     a, b, c, d;

mov.v4.f32 {a,b,c,d}, V;

向量加载和存储操作可用于实现宽位加载和存储,这可能会提升内存性能。在加载/存储操作中使用的寄存器可以是向量,也可以是用大括号括起来的同类标量列表。示例如下:

ld.global.v4.f32  {a,b,c,d}, [addr+16];
ld.global.v2.u32  V2, [addr+8];

大括号包围的向量中的元素,例如 {Ra, Rb, Rc, Rd},对应的提取元素如下:

Ra = V.x = V.r
Rb = V.y = V.g
Rc = V.z = V.b
Rd = V.w = V.a

6.4.4. 标签与函数名作为操作数

标签和函数名称只能分别在bra/brx.idxcall指令中使用。函数名称可以在mov指令中使用,以将函数的地址存入寄存器,用于间接调用。

从PTX ISA 3.1版本开始,mov指令可用于获取内核函数的地址,传递给从GPU发起内核启动的系统调用。此功能是CUDA动态并行支持的一部分。详情请参阅CUDA动态并行编程指南

6.5. 类型转换

所有算术、逻辑和数据移动指令的操作数必须具有相同的类型和大小,除非指令定义中明确包含改变大小和/或类型的操作。不同大小或类型的操作数必须在操作前进行转换。

6.5.1. 标量转换

表15展示了cvt指令在不同类型操作数下使用的精度和格式。例如,如果一条cvt.s32.u16指令给定一个u16源操作数和s32作为目标操作数,该u16会被零扩展为s32

超出浮点数范围的转换值将用最大浮点数值表示(IEEE 754标准中f32f64对应Inf,而f16对应约131,000)。

表15转换指令精度与格式

目标格式

s8

s16

s32

s64

u8

u16

u32

u64

f16

f32

f64

源格式

s8

sext

sext

sext

sext

sext

sext

s2f

s2f

s2f

s16

截断1

符号扩展

符号扩展

截断1

符号扩展

符号扩展

s2f

s2f

s2f

s32

chop1

chop1

sext

chop1

chop1

sext

s2f

s2f

s2f

s64

chop1

chop1

chop

chop1

chop1

chop

s2f

s2f

s2f

u8

zext

zext

zext

zext

zext

zext

u2f

u2f

u2f

u16

chop1

zext

zext

chop1

zext

zext

u2f

u2f

u2f

u32

chop1

chop1

zext

chop1

chop1

zext

u2f

u2f

u2f

u64

chop1

chop1

chop

chop1

chop1

chop

u2f

u2f

u2f

f16

f2s

f2s

f2s

f2s

f2u

f2u

f2u

f2u

f2f

f2f

f32

f2s

f2s

f2s

f2s

f2u

f2u

f2u

f2u

f2f

f2f

f64

f2s

f2s

f2s

f2s

f2u

f2u

f2u

f2u

f2f

f2f

说明

sext = 符号扩展;zext = 零扩展;chop = 仅保留低位有效位;

s2f = 有符号转浮点;f2s = 浮点转有符号;u2f = 无符号转浮点;

f2u = 浮点转无符号;f2f = 浮点转浮点。

1 如果目标寄存器比目标格式宽,结果在截断后会扩展至目标寄存器宽度。扩展类型(符号或零)基于目标格式。例如,针对32位寄存器的cvt.s16.u32会先截断至16位,然后符号扩展至32位。

6.5.2. Rounding Modifiers

转换指令可以指定舍入修饰符。在PTX中,有四种整数舍入修饰符和四种浮点舍入修饰符。表16表17总结了这些舍入修饰符。

表 16 浮点数舍入修饰符

修饰符

描述

.rn

尾数最低有效位向最近的偶数舍入

.rna

尾数最低有效位向最接近的值舍入,中间值远离零

.rz

尾数最低有效位向零舍入

.rm

尾数最低有效位向负无穷方向舍入

.rp

尾数最低有效位向正无穷方向舍入

表 17 整数舍入修饰符

修饰符

描述

.rni

四舍五入到最接近的整数,如果源数值与两个整数距离相等,则选择偶数整数。

.rzi

向零方向四舍五入到最接近的整数

.rmi

向负无穷方向四舍五入到最接近的整数

.rpi

向正无穷方向四舍五入到最接近的整数

6.6. 操作数成本

来自不同状态空间的操作数会影响运算速度。寄存器最快,而全局内存最慢。内存延迟可以通过多种方式被掩盖。第一种方式是拥有多个执行线程,这样硬件可以发出内存操作,然后切换到其他执行。另一种隐藏延迟的方式是尽早发出加载指令,因为直到后续指令中使用所需结果时,执行才会被阻塞。存储操作中的寄存器可用速度要快得多。表18给出了使用不同类型内存的成本估算。

表 18 访问状态空间的成本估算

空间

时间

备注

注册

0

共享

0

常量

0

摊销成本低,首次访问成本高

本地

> 100 时钟周期

参数

0

立即

0

全局

> 100个时钟周期

纹理

> 100时钟周期

表面

> 100个时钟周期

7. 抽象化ABI

PTX并非直接暴露特定调用约定、堆栈布局和应用程序二进制接口(ABI)的细节,而是提供了稍高层次的抽象并支持多种ABI实现。本节将描述PTX为实现这种ABI隐藏所需的功能特性,包括函数定义语法、函数调用、参数传递、可变参数函数(varargs)支持,以及堆栈内存分配(alloca)。

有关生成符合CUDA®架构应用二进制接口(ABI)的PTX代码的详细信息,请参阅PTX互操作性编写指南

7.1. 函数声明与定义

在PTX中,函数使用.func指令进行声明和定义。函数声明会指定一个可选的返回参数列表、函数名称以及可选的输入参数列表;这些共同构成了函数的接口或原型。函数定义则同时指定了接口和函数体。函数在被调用前必须先进行声明或定义。

最简单的函数没有参数或返回值,在PTX中表示如下:

.func foo
{
    ...
    ret;
}

    ...
    call foo;
    ...

在这里,执行call指令会将控制权转移到foo,同时隐式保存返回地址。在foo内部执行ret指令会将控制权转移到调用指令之后的下一条指令。

标量和向量基础类型的输入及返回参数可以简单地表示为寄存器变量。在调用时,参数可以是寄存器变量或常量,返回值可以直接放入寄存器变量中。调用处的参数和返回变量必须与被调用方对应的形式参数在类型和大小上匹配。

示例

.func (.reg .u32 %res) inc_ptr ( .reg .u32 %ptr, .reg .u32 %inc )
{
    add.u32 %res, %ptr, %inc;
    ret;
}

    ...
    call (%r1), inc_ptr, (%r1,4);
    ...

使用ABI时,.reg状态空间参数的大小必须至少为32位。源语言中的子字标量对象应提升为PTX中的32位寄存器,或使用接下来描述的.param状态空间字节数组。

诸如C语言中的结构和联合体等对象在PTX中被扁平化为寄存器或字节数组,并使用.param空间内存表示。例如,考虑以下通过值传递给函数的C结构体:

struct {
    double dbl;
    char   c[4];
};

在PTX中,该结构将被展平为字节数组。由于内存访问需要按照访问大小的整数倍对齐,本例中的结构将是一个12字节数组,采用8字节对齐方式,以确保对.f64字段的访问是对齐的。.param状态空间用于按值传递该结构:

示例

.func (.reg .s32 out) bar (.reg .s32 x, .param .align 8 .b8 y[12])
{
    .reg .f64 f1;
    .reg .b32 c1, c2, c3, c4;
    ...
    ld.param.f64 f1, [y+0];
    ld.param.b8  c1, [y+8];
    ld.param.b8  c2, [y+9];
    ld.param.b8  c3, [y+10];
    ld.param.b8  c4, [y+11];
    ...
    ... // computation using x,f1,c1,c2,c3,c4;
}

{
     .param .b8 .align 8 py[12];
     ...
     st.param.b64 [py+ 0], %rd;
     st.param.b8  [py+ 8], %rc1;
     st.param.b8  [py+ 9], %rc2;
     st.param.b8  [py+10], %rc1;
     st.param.b8  [py+11], %rc2;
     // scalar args in .reg space, byte array in .param space
     call (%out), bar, (%x, py);
     ...

在这个示例中,请注意.param空间变量以两种方式被使用。首先,函数定义bar中使用了一个.param变量y来表示形式参数。其次,在调用函数体内声明了一个.param变量py,用于设置传递给bar的结构体。

以下是思考设备函数中.param状态空间使用的概念性方法。

对于调用者来说,

  • .param状态空间用于设置将传递给被调用函数的值和/或接收来自被调用函数的返回值。通常,.param字节数组用于收集按值传递的结构体字段。

对于被调用方,

  • .param 状态空间用于接收参数值和/或将返回值传递回调用者。

以下限制适用于参数传递。

对于调用者来说,

  • 参数可以是.param变量、.reg变量或常量。

  • 对于.param空间中的字节数组形式参数,实参也必须是具有匹配类型、大小和对齐方式的.param空间字节数组。.param参数必须在调用者的局部作用域内声明。

  • 对于.param空间中的基本类型标量或向量形式参数,对应的实参可以是具有匹配类型和大小的.param.reg空间变量,也可以是能够用形式参数类型表示的常量。

  • 对于.reg空间的形式参数,对应的实参可以是匹配类型和大小的.param.reg空间变量,也可以是能够用形式参数类型表示的常量。

  • 对于.reg空间的形式参数,寄存器的大小必须至少为32位。

  • 所有用于向函数调用传递参数的st.param指令必须紧接在对应的call指令之前,而用于收集返回值的ld.param指令必须紧接在call指令之后,期间不能有任何控制流改变。用于参数传递的st.paramld.param指令不能被谓词化。这使得编译器能够进行优化,并确保.param变量不会在调用者帧中占用超出ABI所需的额外空间。.param变量只是在调用点建立一种映射关系,将可能位于多个位置的数据(例如调用者正在操作的结构体可能存放在寄存器和内存中)转换为可以作为参数或返回值传递给被调用者的形式。

对于被调用方,

  • 输入和返回参数可以是.param变量或.reg变量。

  • .param 内存中的参数必须按1、2、4、8或16字节的倍数对齐。

  • .reg 状态空间中的参数大小必须至少为32位。

  • .reg 状态空间可用于接收和返回基本类型的标量及向量值,包括在非ABI模式下编译时的子字长对象。支持 .reg 状态空间提供了向后兼容性。

请注意,选择.reg.param状态空间进行参数传递,并不会影响参数最终是通过物理寄存器还是栈传递。参数到物理寄存器和栈位置的映射取决于ABI定义以及参数的顺序、大小和对齐方式。

7.1.1. PTX ISA 1.x版本的变更

在PTX ISA 1.x版本中,形式参数仅限于.reg状态空间,且不支持数组参数。像C结构体这样的对象会被扁平化,并通过多个寄存器传递或返回。为此,PTX ISA 1.x版本支持多返回值。

从PTX ISA版本2.0开始,形式参数可以位于.reg.param状态空间中,并且.param空间参数支持数组。对于sm_20或更高版本的目标,PTX限制函数只能返回单个值,对于无法放入寄存器的对象应使用.param字节数组返回。PTX继续支持sm_1x目标的多个返回寄存器。

注意

PTX仅为目标sm_20或更高版本实现了基于栈的ABI。

PTX ISA 3.0之前的版本允许在.reg.local状态空间中定义模块作用域的变量。当编译使用ABI时,PTX ISA 3.0及后续版本禁止模块作用域的.reg.local变量,并将它们的使用限制在函数作用域内。当编译时不使用ABI时,模块作用域的.reg.local变量仍像以前一样受支持。当编译包含模块作用域.reg.local变量的旧版PTX代码(ISA 3.0之前的版本)时,编译器会静默禁用ABI的使用。

7.2. 可变参数函数

注意

规范中已移除对尚未实现的变参函数的支持。

PTX 6.0版本支持将未指定大小的数组参数传递给函数,这可用于实现可变参数函数。

详情请参阅Kernel and Function Directives: .func

7.3. Alloca

PTX提供了alloca指令,用于在运行时为每个线程的本地内存栈分配存储空间。通过alloca返回的指针,可以使用ld.localst.local指令访问分配的栈内存。

为了便于释放通过alloca分配的内存,PTX提供了两条额外指令:stacksave允许将栈指针值读取到局部变量中,以及stackrestore可通过保存的值恢复栈指针。

allocastacksavestackrestore指令在栈操作指令中有详细说明。

Preview Feature:

堆栈操作指令 allocastacksavestackrestore 是 PTX ISA 7.3 版本中的预览功能。所有细节都可能发生变化,不保证在未来的 PTX ISA 版本或 SM 架构中保持向后兼容性。

8. 内存一致性模型

在多线程执行过程中,每个线程执行的内存操作产生的副作用会以部分且不一致的顺序对其他线程可见。这意味着任何两个操作在不同线程看来可能以无顺序或不同顺序发生。内存一致性模型引入的公理精确规定了不同线程观察到的顺序之间哪些矛盾是被禁止的。

在没有任何约束的情况下,每次读取操作会返回由某个写入操作提交到同一内存位置的值,包括对该内存位置的初始写入。内存一致性模型有效地限制了读取操作可以返回值的候选写入操作集合。

8.1. 模型的范围与适用性

该模型下指定的约束适用于具有任何PTX ISA版本号的PTX程序,运行在sm_70或更高架构上。

内存一致性模型不适用于纹理(包括ld.global.nc)和表面访问。

8.1.1. 系统范围内的原子性限制

当与主机CPU通信时,某些具有系统范围的强操作可能无法在某些系统上原子性地执行。有关主机内存原子性保证的更多详情,请参阅CUDA原子性要求

8.2. 内存操作

PTX内存模型中的基本存储单元是字节,由8位组成。PTX程序可用的每个状态空间都是内存中连续的字节序列。PTX状态空间中的每个字节都有一个相对于所有可以访问同一状态空间的线程而言唯一的地址。

每条PTX内存指令都指定一个地址操作数和数据类型。地址操作数包含一个虚拟地址,该地址在内存访问期间会被转换为物理地址。物理地址与数据类型的大小共同定义一个物理内存位置,即从物理地址开始并扩展到数据类型大小(以字节为单位)的字节范围。

内存一致性模型规范使用术语"地址"或"内存地址"表示虚拟地址,使用术语"内存位置"表示物理内存位置。

每条PTX内存指令还指定了要在对应内存位置的所有字节上执行的操作——读取、写入或原子性的读取-修改-写入。

8.2.1. 重叠

当两个内存位置的起始地址位于另一个位置所构成的字节范围内时,称这两个内存位置存在重叠。当两个内存操作指定相同的虚拟地址且对应的内存位置重叠时,称这两个内存操作存在重叠。若两个内存位置完全相同,则称为完全重叠;否则称为部分重叠。

8.2.2. Aliases

如果两个不同的虚拟地址映射到同一个内存位置,则称它们为别名。

8.2.3. Multimem Addresses

多内存地址是一种虚拟地址,它指向跨设备的多个不同内存位置。

只有multimem.*操作对multimem地址有效。也就是说,在任何其他内存操作中访问multimem地址的行为是未定义的。

8.2.4. 向量数据类型的内存操作

内存一致性模型涉及对具有标量数据类型的内存位置执行的操作,这些数据类型的大小和对齐方式最大为64位。具有向量数据类型的内存操作被建模为一组等效的标量数据类型内存操作,这些操作以未指定的顺序对向量中的元素执行。

8.2.5. 打包数据类型的内存操作

打包数据类型由两个相同标量数据类型的值组成,如Packed Data Types中所述。这些值在相邻的内存位置被访问。对打包数据类型的内存操作被建模为一对标量数据类型的等效内存操作,以未指定的顺序在打包数据的每个元素上执行。

8.2.6. 初始化

内存中的每个字节都由一个假设的写操作W0初始化,该操作在程序启动任何线程之前执行。如果该字节包含在程序变量中,并且该变量具有初始值,则W0会写入该字节对应的初始值;否则假定W0向该字节写入了一个未知但恒定的值。

8.3. 状态空间

内存一致性模型中定义的关系与状态空间无关。具体来说,因果顺序会跨越所有状态空间闭合所有内存操作。但某个状态空间中内存操作的副作用,只能由也能访问同一状态空间的操作直接观察到。这除了作用域之外,进一步约束了内存操作的同步效果。例如,PTX指令ld.relaxed.shared.sys的同步效果与ld.relaxed.shared.cluster完全相同,因为同一集群外的线程无法执行访问相同内存位置的操作。

8.4. 操作类型

为简化起见,本文档后续部分将统一使用以下操作类型进行说明,而不再提及产生这些操作的具体指令。

表 19 操作类型

操作类型

指令/操作

原子操作

atomred 指令。

读取操作

ld 指令的所有变体和 atom 指令(但不包括 red 指令)。

写入操作

st指令的所有变体,以及会导致写入操作的原子操作。

内存操作

一个读取写入操作。

易失性操作

带有.volatile限定符的指令。

获取操作

带有.acquire.acq_rel限定符的内存操作。

释放操作

一个带有.release.acq_rel限定符的内存操作。

mmio操作

带有.mmio限定符的ldst指令。

内存屏障操作

一条membarfence.scfence.acq_rel指令。

代理围栏操作

一条 fence.proxymembar.proxy 指令。

强力操作

一个内存屏障操作,或带有.relaxed.acquire.release.acq_rel.volatile.mmio 限定符的内存操作。

弱操作

带有 .weak 限定符的 ldst 指令。

同步操作

一个barrier指令、fence操作、release操作或acquire操作。

8.4.1. mmio操作

一个mmio操作是指带有.mmio限定符的内存操作。这种操作通常在对等I/O设备控制寄存器映射的内存位置上执行。它也可用于线程间通信,但相比非mmio操作性能较差。

mmio操作的具体语义无法精确定义,因为它是由底层I/O设备决定的。从内存一致性模型的角度来看,mmio操作的正式语义规范等同于strong操作的语义。但如果它在指定范围内满足CUDA原子性要求,则会遵循一些特定于实现的属性:

  • 在指定范围内始终执行写入操作,且不会合并写入。

  • 读取操作始终会执行,且不会转发、预取、合并,也不允许命中指定范围内的任何缓存。

    • 作为例外情况,在某些实现中,相邻内存位置的数据也可能被加载。这种情况下加载的数据量取决于具体实现,大小在32到128字节之间变化。

8.4.2. volatile操作

一个易失性操作是指带有.volatile限定符的内存操作。易失性操作的语义等同于具有系统作用域的宽松内存操作,但具有以下额外的实现特定约束:

  • 易失性指令不会被PTX编译器拆分、合并或复制,也就是说,程序中易失性指令(而非操作)的数量保持不变。硬件可能会合并由多个不同易失性指令发出的易失性操作,也就是说,程序中易失性操作的数量不会被保留。

  • volatile指令不会围绕其他volatile指令重新排序,但这些指令的内存操作可能会相互重新排序。

注意

PTX volatile操作旨在让编译器将CUDA C++及其他共享CUDA C++ volatile语义的编程语言中的volatile读写操作转换为PTX指令。

由于易失性操作在系统范围内受到额外约束而放宽限制,建议优先使用其他读/写操作(如ld.relaxed.sysst.relaxed.sys)来实现线程间同步,这样可能获得更好的性能表现。

PTX volatile操作不适用于内存映射IO (MMIO),因为volatile操作无法保证执行的内存操作次数,可能会以非确定性的方式执行比请求更多或更少的操作。 请改用.mmio操作,它能严格保持执行的操作次数。

8.5. Scope

每个操作必须指定一个作用域,这是可能直接与该操作交互并建立内存一致性模型中描述的任何关系的线程集合。共有四种作用域:

表 20 作用域

范围

描述

.cta

与当前线程在同一CTA中执行的所有线程集合。

.cluster

与当前线程在同一集群中执行的所有线程的集合。

.gpu

当前程序中所有在与当前线程相同的计算设备上执行的线程集合。这还包括由主机程序在同一计算设备上调用的其他内核网格。

.sys

当前程序中的所有线程集合,包括主机程序在所有计算设备上调用的所有内核网格,以及构成主机程序本身的所有线程。

请注意,warp并不是一个作用域;在内存一致性模型中,CTA才是符合作用域定义的最小线程集合。

8.6. 代理

一个内存代理,或称代理,是应用于内存访问方法的抽象标签。当两个内存操作使用不同的内存访问方法时,它们被称为不同的代理

操作类型中定义的内存操作使用通用内存访问方法,即通用代理。其他操作如纹理和表面都使用不同的内存访问方法,这些方法也与通用方法不同。

需要代理栅栏来同步不同代理之间的内存操作。虽然虚拟别名使用通用的内存访问方法,但由于使用不同的虚拟地址就像使用不同的代理一样,因此需要代理栅栏来建立内存顺序。

8.7. 道德强操作

如果两个操作满足以下所有条件,则称它们彼此之间具有道德强关系:

  1. 这些操作在程序顺序中相关联(即它们都由同一个线程执行),或者每个操作都是的,并指定一个包含执行另一个操作的线程的作用域

  2. 这两个操作都是通过同一个代理执行的。

  3. 如果两者都是内存操作,则它们完全重叠。

内存一致性模型中的大多数(但非全部)公理依赖于道德强操作之间的关系。

8.7.1. 冲突与数据竞争

当两个重叠的内存操作中至少有一个是写入操作时,就称它们发生了冲突

如果两个冲突的内存操作既不在因果顺序中相关,也不具备道德强度,则称它们处于数据竞争状态。

8.7.2. 混合尺寸数据竞争的限制

完全重叠的操作之间发生的数据竞争称为统一大小数据竞争, 而部分重叠的操作之间发生的数据竞争称为混合大小数据竞争

如果PTX程序包含一个或多个混合大小数据竞争,则内存一致性模型中的公理不适用。但这些公理足以描述仅包含统一大小数据竞争的PTX程序行为。

混合尺寸RMW操作的原子性

在任何包含或不包含混合大小数据竞争的程序中,以下属性对每对重叠原子操作A1和A2都成立(假设每个操作指定的作用域都包含另一个操作):要么由A1指定的读取-修改-写入操作在A2开始之前完全执行完毕,反之亦然。无论两个操作A1和A2是部分重叠还是完全重叠,此属性都成立。

8.8. 释放与获取模式

某些指令序列会产生参与内存同步的模式,具体将在后文描述。release(释放)模式使当前线程1的先前操作对其他线程的某些操作可见。acquire(获取)模式使其他线程的某些操作对当前线程的后续操作可见。

位置M上的发布模式包含以下其中一种情况:

  1. 对M执行释放操作

    例如:st.release [M];atom.acq_rel [M];mbarrier.arrive.release [M];

  2. 或者在程序顺序中对M执行释放操作后紧接着执行对M的写入

    例如:st.release [M]; st.relaxed [M];

  3. 或者按照程序顺序,在M上执行内存屏障后接一个写入操作

    例如:fence; st.relaxed [M];

释放模式建立的任何内存同步仅影响在该模式第一条指令之前按程序顺序发生的操作。

在位置M上的获取模式包含以下其中一种情况:

  1. 对M执行获取操作

    例如:ld.acquire [M];atom.acq_rel [M];mbarrier.test_wait.acquire [M];

  2. 或者按照程序顺序对M进行读取后,再对M执行acquire操作

    例如:ld.relaxed [M]; ld.acquire [M];

  3. 或者按照程序顺序对M进行强读取后接内存屏障

    例如:ld.relaxed [M]; fence;

获取模式建立的任何内存同步仅影响在该模式最后一条指令之后按程序顺序发生的操作。

1 对于释放获取模式,这种效果通过因果顺序的传递性进一步扩展到其他线程中的操作。

8.9. 内存操作顺序

每个线程执行的操作序列被记录为程序顺序,而线程间的内存同步则被记录为因果顺序。内存操作产生的副作用对其他内存操作的可见性被记录为通信顺序。内存一致性模型定义了通信顺序与因果顺序程序顺序之间不允许出现的矛盾关系。

8.9.1. 程序顺序

程序顺序将线程执行的所有操作与顺序处理器执行相应PTX源代码指令的顺序关联起来。它是一种传递关系,在线程执行的操作上形成全序,但不关联不同线程之间的操作。

8.9.1.1. 异步操作

某些PTX指令(包括cp.asynccp.async.bulkcp.reduce.async.bulkwgmma.mma_async的所有变体)执行的操作相对于执行该指令的线程是异步的。这些异步操作会与同一线程中先前的指令保持顺序(wgmma.mma_async除外),但它们不属于该线程的程序顺序。相反,如指令描述文档所示,它们提供了较弱的顺序保证。

例如,作为cp.async一部分执行的加载和存储操作彼此之间是有序的,但与同一线程发起的任何其他cp.async指令无关,也与线程随后发出的任何其他指令无关,除了cp.async.commit_groupcp.async.mbarrier.arrive。由cp.async.mbarrier.arrive指令执行的异步mbarrier arrive-on操作与同一线程发起的所有先前cp.async操作执行的内存操作是有序的,但与线程发出的任何其他指令无关。作为所有cp.async.bulkcp.reduce.async.bulk变体一部分的隐式mbarrier complete-tx操作仅与同一异步指令执行的内存操作有序,特别是它不会传递性地建立与发起线程先前指令的有序关系。

8.9.2. Observation Order

观察顺序通过一系列可选的原子读-修改-写操作,将写操作W与读操作R关联起来。

观察顺序中,写操作W先于读操作R的条件是:

  1. R和W是道德上强的,并且R读取了W写入的值,或者

  2. 对于某些原子操作Z,在观察顺序中,W先于Z,Z先于R。

8.9.3. Fence-SC 顺序

Fence-SC顺序是一种在运行时确定的非循环偏序关系,它关联每一对道德强fence.sc操作。

8.9.4. 内存同步

不同线程执行的同步操作在运行时相互同步,如这里所述。这种同步的效果是在线程间建立因果关系顺序

  1. 如果操作X在Fence-SC顺序中先于操作Y,那么fence.sc操作X将与fence.sc操作Y同步

  2. bar{.cta}.syncbar{.cta}.redbar{.cta}.arrive操作会与在同一屏障上执行的bar{.cta}.syncbar{.cta}.red操作同步

  3. barrier.cluster.arrive 操作与 barrier.cluster.wait 操作同步。

  4. 当X模式中的操作在观察顺序中先于Y模式中的操作,且X模式中的第一个操作与Y模式中的最后一个操作具有道德强关系时,释放模式X将与获取模式Y同步

API同步

某些CUDA API也可以建立同步关系。

  1. 在CUDA流中排队的任务完成时,会与同一流中下一个任务的开始同步(如果存在下一个任务)。

  2. 就上述目的而言,在流中记录或等待CUDA事件,或由于cudaStreamLegacy导致插入跨流屏障时,即使没有直接副作用,也会在相关流中排队任务。事件记录任务会与匹配的事件等待任务同步,屏障到达任务会与匹配的屏障等待任务同步

  3. CUDA内核的启动会与内核中所有线程的启动同步。内核中所有线程的结束会与内核的结束同步

  4. CUDA图的开始会与图中所有源节点的开始同步。图中所有汇节点的完成会与整个图的完成同步。图节点的完成会与其具有直接依赖关系的所有节点的开始同步

  5. 开始一个CUDA API调用以排队一个任务同步于任务的开始。

  6. 如果存在,队列中最后一个任务的完成将与从cudaStreamSynchronize返回同步。如果存在,最近队列中匹配事件记录任务的完成将与从cudaEventSynchronize返回同步。同步CUDA设备或上下文的行为就像同步上下文中的所有流一样,包括已被销毁的流。

  7. 从查询CUDA句柄(如流或事件)的API返回cudaSuccess的行为,与从匹配的同步API返回的行为相同。

除了建立同步关系外,上述CUDA API同步机制还参与代理保留的基础因果顺序

8.9.5. 因果顺序

因果顺序描述了内存操作如何通过同步操作在不同线程间变得可见。"因果性"公理利用这一顺序来约束读取操作可能从中获取值的写入操作集合。

因果关系中的关系主要包括基础因果关系1中的关系,这是一种在运行时确定的传递性顺序。

基础因果顺序

基本因果顺序中,操作X优先于操作Y的条件是:

  1. 程序顺序中X先于Y,或者

  2. X 同步 于 Y,或者

  3. 对于某些操作Z,

    1. 程序顺序中X先于Z,且在基本因果顺序中Z先于Y,或者

    2. 基本因果顺序中X先于Z,且在程序顺序中Z先于Y,或者

    3. 基础因果顺序中X先于Z,且在基础因果顺序中Z先于Y。

代理保留的基本因果顺序

代理保留的基础因果顺序中,内存操作X先于内存操作Y的条件是:X在基础因果顺序中先于Y,并且满足以下条件:

  1. X和Y通过通用代理对同一地址执行,或者

  2. X和Y操作针对同一地址,使用相同的代理,并由同一线程块执行, 或者

  3. X和Y是别名,并且沿着从X到Y的基础因果路径存在一个名为proxy fence的别名。

因果顺序

因果顺序基础因果顺序与一些非传递性关系结合如下:

如果满足以下条件,则操作X在因果顺序中先于操作Y:

  1. X在代理保留的基础因果顺序中先于Y,或者

  2. 对于某些操作Z,X在观察顺序中先于Z,且Z在代理保留的基本因果顺序中先于Y。

1 基础因果顺序的传递性解释了同步操作的"累积性"。

8.9.6. 一致性顺序

存在一种部分传递序,用于关联重叠的写操作,这种关系在运行时确定,称为一致性顺序1。当两个重叠的写操作具有道德强关系或存在因果顺序关系时,它们会在一致性顺序中相关联。若两个重叠写操作处于数据竞争状态,则它们在一致性顺序中互不关联,这体现了一致性顺序的部分性特征。

1 一致性顺序无法直接观测,因为它完全由写入操作组成。但可以通过其对读取操作可读取候选写入集合的限制作用进行间接观测。

8.9.7. 通信顺序

通信顺序是一种在运行时确定的非传递性顺序,用于关联写操作与其他重叠的内存操作。

  1. 通信顺序中,如果读取操作R返回了写入操作W所写的任何字节值,那么写入操作W就重叠地先于读取操作R。

  2. 通信顺序中,如果写操作W在一致性顺序中先于写操作W',则称W先于W'。

  3. 通信顺序中,读取操作R优先于重叠写入操作W的条件是:对于R和W共同访问的任何字节,R返回的值必须是由某个在一致性顺序中先于W的写入操作W'所写入的值。

通信顺序定义了内存操作的可见性——当一个内存操作X1在通信顺序中先于内存操作X2时,就称X1对X2可见。

8.10. 公理

8.10.1. 一致性

如果一个写入操作 W 在因果顺序上先于一个重叠的写入操作 W',那么 W 必须在一致性顺序上先于 W'。

8.10.2. Fence-SC

Fence-SC顺序不能与因果顺序相矛盾。对于一对道德上强fence.sc操作F1和F2,如果F1在因果顺序中先于F2,那么F1在Fence-SC顺序中也必须先于F2。

8.10.3. 原子性

单拷贝原子性

具有道德强约束的冲突操作以单副本原子性方式执行。当读操作R与写操作W具有道德强约束关系时,对于R和W共同访问的字节集,以下两种通信情况不可能在同一执行过程中同时存在:

  1. R从W读取任意字节。

  2. R从任何在一致性顺序中先于W的写操作W'读取任意字节。

读取-修改-写入(RMW)操作的原子性

当一个原子操作A和写入操作W重叠且具有强一致性时,对于被A和W同时访问的字节集,以下两种通信不可能在同一执行过程中同时存在:

  1. A 从在一致性顺序中先于 W 的写操作 W' 中读取任意字节。

  2. 一致性顺序中,A跟随W。

初步测试 1:

.global .u32 x = 0;

T1

T2

A1: atom.sys.inc.u32 %r0, [x];
A2: atom.sys.inc.u32 %r0, [x];
最终状态 x == 2

当操作在道德上强有力时,原子性得到保证。

关键测试2:

.global .u32 x = 0;

T1

T2 (位于不同的CTA中)

A1: atom.cta.inc.u32 %r0, [x];
A2: atom.gpu.inc.u32 %r0, [x];
最终状态: x == 1  x == 2

如果操作不具备道德强一致性,则无法保证原子性。

8.10.4. 无凭空产生

数值不会凭空出现:执行过程不能以某种方式推测性地产生一个值,使得这种推测通过指令依赖链和线程间通信自我满足。这既符合程序员的直觉,也符合硬件实际情况,但在进行形式化分析时必须明确说明。

关键测试:负载缓冲

.global .u32 x = 0;
.global .u32 y = 0;

T1

T2

A1: ld.global.u32 %r0, [x];
B1: st.global.u32 [y], %r0;
A2: ld.global.u32 %r1, [y];
B2: st.global.u32 [x], %r1;
最终状态 x == 0  y == 0

被称为"LB"(负载缓冲)的基准测试检查那些可能凭空出现的禁止值。两个线程T1和T2各自从第一个变量读取数据,并将观察到的结果复制到第二个变量中,其中第一个和第二个变量在线程之间交换。如果每个变量初始值为零,最终结果也应为零。如果A1从B2读取,A2从B1读取,那么本例中通过内存操作的值会形成一个循环:A1->B1->A2->B2->A1。只有x == 0和y == 0的值被允许满足这个循环。如果本例中任何内存操作推测性地将不同值与对应的内存位置关联,那么这种推测将成为自我实现的,因此是被禁止的。

8.10.5. 每个位置的顺序一致性

在任何一组重叠的内存操作中,如果这些操作两两之间具有道德强关系,那么通信顺序不能与程序顺序相矛盾。也就是说,在重叠操作之间的程序顺序通信顺序中的道德强关系进行串联时,不能形成循环。这确保了每组重叠且两两具有道德强关系的操作的程序片段都是严格顺序一致的。

试金石测试:CoRR

.global .u32 x = 0;

T1

T2

W1: st.global.relaxed.sys.u32 [x], 1;
R1: ld.global.relaxed.sys.u32 %r0, [x];
R2: ld.global.relaxed.sys.u32 %r1, [x];
IF %r0 == 1 THEN %r1 == 1

基准测试"CoRR"(一致性读-读)展示了这一保证的一个结果。线程T1对位置x执行写操作W1,线程T2对同一位置x执行两次(或无限次)读操作R1和R2。除模拟初始值的写操作外,没有其他对x的写操作。操作W1、R1和R2两两之间具有道德强关系。如果R1读取自W1,那么后续读操作R2也必须观察到相同的值。如果R2观察到的是x的初始值,这将形成一个在通信顺序中与线程T2的程序顺序R1->R2相矛盾的道德强关系序列R2->W1->R1。因此,在这样的执行中,R2不能读取x的初始值。

8.10.6. 因果关系

通信顺序中的关系不能与因果顺序相矛盾。这限制了读操作可能从中读取的候选写操作集合:

  1. 如果在因果顺序中,读取操作R先于重叠的写入操作W,那么R不能从W读取数据。

  2. 如果在因果顺序中写操作W先于重叠的读操作R,那么对于R和W都访问的任何字节,R不能从在一致性顺序中先于W的任何写操作W'读取数据。

关键测试:消息传递

.global .u32 data = 0;
.global .u32 flag = 0;

T1

T2

W1: st.global.u32 [data], 1;
F1: fence.sys;
W2: st.global.relaxed.sys.u32 [flag], 1;
R1: ld.global.relaxed.sys.u32 %r0, [flag];
F2: fence.sys;
R2: ld.global.u32 %r1, [data];
如果 %r0 等于 1 那么 %r1 等于 1

被称为"MP"(消息传递)的基准测试代表了典型同步算法的精髓。绝大多数有用的程序都可以简化为这种模式的序列化应用。

线程T1首先写入数据变量,然后写入标志变量,而第二个线程T2首先从标志变量读取,然后从数据变量读取。对标志变量的操作是道德上强的,每个线程中的内存操作都由栅栏分隔,这些栅栏也是道德上强的。

如果R1观察到W2,那么释放模式"F1; W2"与获取模式"R1; F2"同步。这建立了因果顺序 W1 -> F1 -> W2 -> R1 -> F2 -> R2。然后因果性公理保证R2不能从任何在一致性顺序中先于W1的写入读取数据。在本例中没有其他写入操作的情况下,R2必须从W1读取。

关键测试:CoWR

// 这些地址是别名
.global .u32 data_alias_1;
.global .u32 data_alias_2;

T1

W1: st.global.u32 [data_alias_1], 1;
F1: fence.proxy.alias;
R1: ld.global.u32 %r1, [data_alias_2];
%r1 == 1

虚拟别名需要沿着同步路径设置一个别名代理隔离区

基准测试:存储缓冲

被称为"SB"(存储缓冲)的基准测试展示了fence.sc所强制的顺序一致性。线程T1先写入第一个变量,然后读取第二个变量的值;同时线程T2先写入第二个变量,然后读取第一个变量的值。每个线程中的内存操作都由fence.sc指令分隔,这些内存屏障具有极强的约束力

.global .u32 x = 0;
.global .u32 y = 0;

T1

T2

W1: st.global.u32 [x], 1;
F1: fence.sc.sys;
R1: ld.global.u32 %r0, [y];
W2: st.global.u32 [y], 1;
F2: fence.sc.sys;
R2: ld.global.u32 %r1], [x];
%r0 == 1 OR %r1 == 1

在任何执行过程中,F1和F2在Fence-SC顺序中要么前者先于后者,要么反之。如果F1在Fence-SC顺序中先于F2,那么F1将与F2同步。这就在W1 -> F1 -> F2 -> R2之间建立了因果顺序因果性公理确保R2不能读取任何在一致性顺序中先于W1的写入操作。在没有其他对该变量的写入操作时,R2必须从W1读取数据。类似地,当F2在Fence-SC顺序中先于F1时,R1必须从W2读取数据。如果本例中的每个fence.sc都被替换为fence.acq_rel指令,那么这一结果就无法得到保证。可能会出现每个线程的写入操作对另一个线程不可见的情况,也就是说,存在R1和R2分别返回变量y和x初始值"0"的执行情况。

9. 指令集

9.1. 指令描述格式与语义

本节介绍每条PTX指令。除了指令的名称和格式外,还描述了其语义,随后提供了一些示例,试图展示该指令的几种可能实例化形式。

9.2. PTX指令集

PTX指令通常包含零到四个操作数,以及一个可选的保护谓词,该谓词出现在@符号之后、opcode的左侧:

  • @p   opcode;

  • @p   opcode a;

  • @p   opcode d, a;

  • @p opcode d, a, b;

  • @p opcode d, a, b, c;

对于生成结果值的指令,d操作数是目标操作数,而abc是源操作数。

setp指令会写入两个目标寄存器。我们使用|符号来分隔多个目标寄存器。

setp.lt.s32  p|q, a, b;  // p = (a < b); q = !(a < b);

对于某些指令,目标操作数是可选的。可以使用下划线(_)表示的位桶操作数来代替目标寄存器。

9.3. 谓词执行

在PTX中,谓词寄存器是虚拟的,其类型标识符为.pred。因此,谓词寄存器可以声明为

.reg .pred p, q, r;

所有指令都有一个可选的守卫谓词,用于控制指令的条件执行。指定条件执行的语法是在指令前加上@{!}p前缀,其中p是一个谓词变量,可选择性地取反。没有守卫谓词的指令将无条件执行。

谓词(predicate)通常是通过setp指令执行比较操作后设置的结果。

例如,考虑以下高级代码

if (i < n)
    j = j + 1;

这可以用PTX编写为

      setp.lt.s32  p, i, n;    // p = (i < n)
@p    add.s32      j, j, 1;    // if i < n, add 1 to j

要获取条件分支或条件函数调用,可使用谓词来控制分支或调用指令的执行。要将上述示例实现为真正的条件分支,可使用以下PTX指令序列:

      setp.lt.s32  p, i, n;    // compare i to n
@!p   bra  L1;                 // if False, branch over
      add.s32      j, j, 1;
L1:     ...

9.3.1. 比较

9.3.1.1. 整数与位宽比较

有符号整数比较包括传统的eq(等于)、ne(不等于)、lt(小于)、le(小于等于)、gt(大于)和ge(大于等于)。无符号比较包括eqnelo(低于)、ls(低于或等于)、hi(高于)和hs(高于或等于)。位大小比较仅包含eqne;位大小类型未定义排序比较。

表21 展示了有符号整数、无符号整数和位宽类型对应的运算符。

表21 有符号整数、无符号整数及位宽类型的运算符

含义

有符号运算符

无符号运算符

位宽运算符

a == b

eq

eq

eq

a != b

ne

ne

ne

a < b

lt

lo

不适用

a <= b

le

ls

不适用

a > b

gt

hi

不适用

a >= b

ge

hs

不适用

9.3.1.2. 浮点数比较

有序浮点数比较运算符包括eqneltlegtge。如果任一操作数为NaN,则结果为False表22列出了浮点数比较运算符。

表 22 浮点数比较运算符

含义

浮点运算符

a == b && !isNaN(a) && !isNaN(b)

eq

a != b && !isNaN(a) && !isNaN(b)

ne

a < b && !isNaN(a) && !isNaN(b)

lt

a <= b && !isNaN(a) && !isNaN(b)

le

a > b && !isNaN(a) && !isNaN(b)

gt

a >= b && !isNaN(a) && !isNaN(b)

ge

为了在存在NaN值时辅助比较操作,提供了无序浮点数比较方法:equneultuleugtugeu。如果两个操作数都是数值(非NaN),那么比较结果与其有序对应方法相同。如果任一操作数是NaN,则比较结果为True

表23列出了接受NaN值的浮点数比较运算符。

表23 接受NaN的浮点数比较运算符

含义

浮点运算符

a == b || isNaN(a) || isNaN(b)

equ

a != b || isNaN(a) || isNaN(b)

neu

a < b || isNaN(a) || isNaN(b)

ltu

a <= b || isNaN(a) || isNaN(b)

leu

a > b || isNaN(a) || isNaN(b)

gtu

a >= b || isNaN(a) || isNaN(b)

geu

要检测NaN值,系统提供了两个运算符:numnumeric)和nanisNaN)。当两个操作数都是数值(非NaN)时,num返回True;而只要任一操作数是NaNnan就会返回TrueTable 24列出了用于检测NaN值的浮点数比较运算符。

表 24 浮点数比较运算符测试NaN

含义

浮点运算符

!isNaN(a) && !isNaN(b)

num

isNaN(a) || isNaN(b)

nan

9.3.2. 操作谓词

谓词值可以通过以下指令进行计算和操作:andorxornotmov

谓词(predicate)与整数值之间没有直接转换方式,也没有直接加载或存储谓词寄存器值的方法。不过,可以通过setp指令从整数生成谓词,而基于谓词的选择指令(selp)则可以根据谓词值生成整数值。例如:

selp.u32 %r1,1,0,%p;    // convert predicate to 32-bit value

9.4. 指令与操作数的类型信息

类型化指令必须包含类型大小修饰符。例如,add指令需要类型和大小信息才能正确执行加法操作(有符号、无符号、浮点、不同大小),这些信息必须作为操作码的后缀指定。

示例

.reg .u16 d, a, b;

add.u16 d, a, b;    // perform a 16-bit unsigned add

某些指令需要多个类型大小修饰符,最典型的是数据转换指令cvt。它需要为结果和源分别指定类型大小修饰符,这些修饰符按照操作数的顺序排列。例如:

.reg .u16 a;
.reg .f32 d;

cvt.f32.u16 d, a;   // convert 16-bit unsigned to 32-bit float

通常,操作数的类型必须与对应的指令类型修饰符一致。操作数与指令类型一致性的规则如下:

  • 位大小类型与任何相同大小的类型兼容。

  • 有符号和无符号整数类型在大小相同的情况下是兼容的,整数操作数会根据需要隐式转换为指令类型。例如,在有符号整数指令中使用的无符号整数操作数将被该指令视为有符号整数。

  • 浮点类型仅在大小相同时才一致;也就是说,它们必须完全匹配。

表25总结了这些类型检查规则。

表 25 类型检查规则

操作数类型

.bX

.sX

.uX

.fX

指令类型

.bX

正常

正常

正常

正常

.sX

正常

正常

正常

无效

.uX

正常

正常

正常

无效

.fX

正常

无效

无效

正常

注意

某些操作数的类型和大小是独立于指令类型大小定义的。例如,左移和右移指令的移位量操作数始终具有.u32类型,而其余操作数的类型和大小则由指令类型决定。

示例

// 64-bit arithmetic right shift; shift amount 'b' is .u32
    shr.s64 d,a,b;

9.4.1. 操作数大小超出指令类型限制

为了方便起见,ldstcvt指令允许源数据和目标数据操作数的宽度超过指令类型大小,这样可以使用常规宽度的寄存器来加载、存储和转换较窄的值。例如,8位或16位值在加载、存储或转换为其他类型和大小时,可以直接保存在32位或64位寄存器中。对于位宽和整数(有符号和无符号)指令类型,操作数类型检查规则有所放宽;而浮点指令类型仍要求操作数类型大小完全匹配,除非操作数是位宽类型。

当源操作数的大小超过指令类型大小时,源数据会被截断(截取)为指令类型大小指定的适当位数。

表26 总结了源操作数的宽松类型检查规则。请注意,某些组合可能 对于特定指令仍然无效;例如,cvt指令不支持 .bX指令类型,因此这些行对cvt无效。

表 26 源操作数的宽松类型检查规则

源操作数类型

b8

b16

b32

b64

b128

s8

s16

s32

s64

u8

u16

u32

u64

f16

f32

f64

指令类型

b8

chop

chop

chop

chop

chop

chop

chop

chop

chop

chop

chop

chop

chop

b16

inv

chop

chop

chop

inv

chop

chop

inv

chop

chop

chop

chop

b32

inv

inv

chop

chop

inv

inv

chop

inv

inv

chop

inv

chop

b64

inv

inv

inv

chop

inv

inv

inv

inv

inv

inv

inv

inv

b128

inv

inv

inv

inv

inv

inv

inv

inv

inv

inv

inv

inv

inv

inv

inv

s8

chop

chop

chop

chop

chop

chop

chop

chop

chop

chop

inv

inv

inv

s16

inv

chop

chop

chop

inv

chop

chop

inv

chop

chop

inv

inv

inv

s32

inv

inv

chop

chop

inv

inv

chop

inv

inv

chop

inv

inv

inv

s64

inv

inv

inv

chop

inv

inv

inv

inv

inv

inv

inv

inv

inv

u8

chop

chop

chop

chop

chop

chop

chop

chop

chop

chop

inv

inv

inv

u16

inv

chop

chop

chop

inv

chop

chop

inv

chop

chop

inv

inv

inv

u32

inv

inv

chop

chop

inv

inv

chop

inv

inv

chop

inv

inv

inv

u64

inv

inv

inv

chop

inv

inv

inv

inv

inv

inv

inv

inv

inv

f16

inv

chop

chop

chop

inv

inv

inv

inv

inv

inv

inv

inv

inv

inv

f32

inv

inv

chop

chop

inv

inv

inv

inv

inv

inv

inv

inv

inv

inv

f64

inv

inv

inv

chop

inv

inv

inv

inv

inv

inv

inv

inv

inv

inv

注意事项

chop = 仅保留适合的低位;"--" = 允许,但无需转换;

inv = 无效,解析错误。

  1. 源寄存器大小必须等于或大于指令类型大小。

  2. 位宽源寄存器可用于任何适当大小的指令类型。数据会被截断("chopped")至指令类型大小,并按指令类型解释。

  3. 整型源寄存器可用于任何适当大小的位宽或整型指令类型。数据会被截断至指令类型大小,并按指令类型解释。

  4. 浮点源寄存器只能用于位宽或浮点指令类型。当用于较窄的位宽指令类型时,数据会被截断。当用于浮点指令类型时,大小必须完全匹配。

当目标操作数的大小超过指令类型大小时,目标数据会进行零扩展或符号扩展以适应目标寄存器的大小。如果对应的指令类型为有符号整数,则数据进行符号扩展;否则,数据进行零扩展。

表27 总结了目标操作数的宽松类型检查规则。

表 27 目标操作数的宽松类型检查规则

目标操作数类型

b8

b16

b32

b64

b128

s8

s16

s32

s64

u8

u16

u32

u64

f16

f32

f64

指令类型

b8

zext

zext

zext

zext

zext

zext

zext

zext

zext

zext

zext

zext

zext

b16

inv

zext

zext

zext

inv

zext

zext

inv

zext

zext

zext

zext

b32

inv

inv

zext

zext

inv

inv

zext

inv

inv

zext

inv

zext

b64

inv

inv

inv

zext

inv

inv

inv

inv

inv

inv

inv

inv

b128

inv

inv

inv

inv

inv

inv

inv

inv

inv

inv

inv

inv

inv

inv

inv

s8

sext

sext

sext

sext

sext

sext

sext

sext

sext

sext

inv

inv

inv

s16

inv

sext

sext

sext

inv

sext

sext

inv

sext

sext

inv

inv

inv

s32

inv

inv

sext

sext

inv

inv

sext

inv

inv

sext

inv

inv

inv

s64

inv

inv

inv

sext

inv

inv

inv

inv

inv

inv

inv

inv

inv

u8

zext

zext

zext

zext

zext

zext

zext

zext

zext

zext

inv

inv

inv

u16

inv

zext

zext

zext

inv

zext

zext

inv

zext

zext

inv

inv

inv

u32

inv

inv

zext

zext

inv

inv

zext

inv

inv

zext

inv

inv

inv

u64

inv

inv

inv

zext

inv

inv

inv

inv

inv

inv

inv

inv

inv

f16

inv

zext

zext

zext

inv

inv

inv

inv

inv

inv

inv

inv

inv

inv

f32

inv

inv

zext

zext

inv

inv

inv

inv

inv

inv

inv

inv

inv

inv

f64

inv

inv

inv

zext

inv

inv

inv

inv

inv

inv

inv

inv

inv

inv

注意事项

sext = 符号扩展;zext = 零扩展;"--" = 允许但不需转换;

inv = 无效,解析错误。

  1. 目标寄存器大小必须等于或大于指令类型大小。

  2. 位宽目标寄存器可与任何适当大小的指令类型配合使用。对于有符号整数指令类型,数据会符号扩展到目标寄存器宽度;其他情况下则进行零扩展。

  3. 整数目标寄存器可与任何适当大小的位宽或整数指令类型配合使用。对于有符号整数指令类型,数据会符号扩展到目标寄存器宽度;对于位宽和无符号整数指令类型则进行零扩展。

  4. 浮点目标寄存器只能与位宽或浮点指令类型配合使用。当与较窄的位宽指令类型配合时,数据会进行零扩展。当与浮点指令类型配合时,大小必须完全匹配。

9.5. 控制结构中线程的分歧

CTA中的线程会一起执行(至少表面上看是这样),直到遇到条件控制结构,例如条件分支、条件函数调用或条件返回。如果线程沿着不同的控制流路径执行,这些线程被称为发散的。如果所有线程同步行动并遵循单一控制流路径,则这些线程被称为统一的。这两种情况在程序中都很常见。

一个线程执行路径存在分叉的CTA(线程块)可能比线程执行路径统一的CTA性能更低,因此让分叉线程尽快重新收敛非常重要。所有控制结构默认被视为分叉点,除非使用.uni后缀将控制流指令标记为统一。对于分叉控制流,优化代码生成器会自动确定重新收敛点。因此,针对PTX的编译器或代码编写者可以忽略线程分叉问题,但当编译器或编写者能确保分支点不存在分叉时,可以通过将分支点标记为统一来提升性能。

9.6. 语义

指令语义描述的目标是尽可能用简单的语言描述所有情况下的结果。语义描述使用C语言,直到C语言无法充分表达为止。

9.6.1. 16位代码的机器特定语义

PTX程序可以在具有16位或32位数据路径的GPU上执行。当在32位数据路径上运行时,PTX中的16位寄存器会被映射到32位物理寄存器,16位计算会被提升为32位计算。这可能导致在16位机器上运行的代码与在32位机器上运行的相同代码之间存在计算差异,因为提升后的计算可能在寄存器的高半字中包含16位物理寄存器中不存在的位。这些额外的精度位可能会在应用层面变得可见,例如通过右移指令。

在PTX语言层面,一种解决方案是为16位代码定义与16位数据路径执行相一致的语义。这种方法会导致在32位数据路径上执行16位代码时产生性能损失,因为转换后的代码需要大量额外的掩码指令来抑制32位寄存器高半字中的额外精度位。

为了避免在32位GPU上运行16位代码时引入性能损失,PTX中16位指令的语义是特定于机器的。编译器或程序员可以选择通过在程序中适当位置添加对16位值的显式转换来强制执行可移植的、与机器无关的16位语义,以保证代码的可移植性。然而,对于许多性能关键型应用来说,这是不可取的,并且对于许多应用来说,执行差异比限制性能更可取。

9.7. Instructions

所有PTX指令都可以被条件执行。在以下描述中,语法中省略了可选的条件谓词。

9.7.1. 整数算术指令

整数算术指令对寄存器和常量立即数形式的整数类型进行操作。整数算术指令包括:

  • add

  • sub

  • mul

  • mad

  • mul24

  • mad24

  • sad

  • div

  • rem

  • abs

  • neg

  • min

  • max

  • popc

  • clz

  • bfind

  • fns

  • brev

  • bfe

  • bfi

  • bmsk

  • szext

  • dp4a

  • dp2a

9.7.1.1. 整数算术指令:加法

add

将两个值相加。

语法

add.type       d, a, b;
add{.sat}.s32  d, a, b;     // .sat applies only to .s32

.type = { .u16, .u32, .u64,
          .s16, .s32, .s64,
          .u16x2, .s16x2 };

描述

执行加法运算并将结果值写入目标寄存器。

对于.u16x2.s16x2指令类型,通过从源操作数中提取半字值来形成输入向量。然后并行相加半字操作数以在目标中产生.u16x2.s16x2结果。

操作数 dab 的类型为 .type。对于指令类型 .u16x2.s16x2, 操作数 dab 的类型为 .b32

语义

if (type == u16x2 || type == s16x2) {
    iA[0] = a[0:15];
    iA[1] = a[16:31];
    iB[0] = b[0:15];
    iB[1] = b[16:31];
    for (i = 0; i < 2; i++) {
         d[i] = iA[i] + iB[i];
    }
} else {
    d = a + b;
}

注意事项

饱和度调节器:

.sat

将操作结果的大小限制在MININT..MAXINT范围内(无溢出)。仅适用于.s32类型。

PTX ISA 说明

在PTX ISA版本1.0中引入。

add.u16x2add.s16x2 在PTX ISA 8.0版本中引入。

目标ISA注意事项

支持所有目标架构。

add.u16x2add.s16x2 需要 sm_90 或更高版本。

示例

@p  add.u32     x,y,z;
    add.sat.s32 c,c,1;
    add.u16x2   u,v,w;

9.7.1.2. 整数算术指令:sub

子进程

从一个值中减去另一个值。

语法

sub.type       d, a, b;
sub{.sat}.s32  d, a, b;     // .sat applies only to .s32

.type = { .u16, .u32, .u64,
          .s16, .s32, .s64 };

描述

执行减法运算并将结果值写入目标寄存器。

语义

d = a - b;

注意事项

饱和度调节器:

.sat

将操作结果的大小限制在MININT..MAXINT范围内(无溢出)。仅适用于.s32类型。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

sub.s32 c,a,b;

9.7.1.3. 整数算术指令:mul

mul

将两个值相乘。

语法

mul.mode.type  d, a, b;

.mode = { .hi, .lo, .wide };
.type = { .u16, .u32, .u64,
          .s16, .s32, .s64 };

描述

计算两个值的乘积。

语义

t = a * b;
n = bitwidth of type;
d = t;            // for .wide
d = t<2n-1..n>;   // for .hi variant
d = t<n-1..0>;    // for .lo variant

注意事项

操作类型表示ab操作数的类型。如果指定了.hi.lo,则dab大小相同,并且结果的 上半部分或下半部分会被写入目标寄存器。如果指定了.wide,则d的 宽度是ab的两倍,以接收完整的乘法结果。

.wide 后缀仅支持16位和32位整数类型。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

mul.wide.s16 fa,fxs,fys;   // 16*16 bits yields 32 bits
mul.lo.s16 fa,fxs,fys;     // 16*16 bits, save only the low 16 bits
mul.wide.s32 z,x,y;        // 32*32 bits, creates 64 bit result

9.7.1.4. 整数算术指令:mad

mad

将两个值相乘,可选择提取中间结果的高半部分或低半部分,并加上第三个值。

语法

mad.mode.type  d, a, b, c;
mad.hi.sat.s32 d, a, b, c;

.mode = { .hi, .lo, .wide };
.type = { .u16, .u32, .u64,
          .s16, .s32, .s64 };

描述

将两个值相乘,可选择提取中间结果的高半部分或低半部分,并加上第三个值。将结果写入目标寄存器。

语义

t = a * b;
n = bitwidth of type;
d = t + c;           // for .wide
d = t<2n-1..n> + c;  // for .hi variant
d = t<n-1..0> + c;   // for .lo variant

注意事项

操作类型表示ab操作数的类型。如果指定了.hi或.lo,则dcab大小相同,并将结果的上半部分或下半部分写入目标寄存器。如果指定了.wide,则dc的宽度是ab的两倍,以接收乘法结果。

.wide 后缀仅支持16位和32位整数类型。

饱和度调节器:

.sat

将操作结果的大小限制在MININT..MAXINT范围内(无溢出)。

仅适用于.hi模式中的.s32类型。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

@p  mad.lo.s32 d,a,b,c;
    mad.lo.s32 r,p,q,r;

9.7.1.5. 整数算术指令:mul24

mul24

将两个24位整数值相乘。

语法

mul24.mode.type  d, a, b;

.mode = { .hi, .lo };
.type = { .u32, .s32 };

描述

计算两个存储在32位源寄存器中的24位整数值的乘积,并返回48位结果的高32位或低32位。

语义

t = a * b;
d = t<47..16>;    // for .hi variant
d = t<31..0>;     // for .lo variant

注意事项

整数乘法的结果大小是输入操作数的两倍,即48位。

mul24.hi 执行24x24位乘法运算,并返回48位结果的高32位。

mul24.lo 执行24x24位乘法运算,并返回48位结果的低32位。

所有操作数都具有相同的类型和大小。

mul24.hi 在没有24位乘法硬件支持的机器上效率可能较低。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

mul24.lo.s32 d,a,b;   // low 32-bits of 24x24-bit signed multiply.

9.7.1.6. 整数算术指令:mad24

mad24

将两个24位整数值相乘并加上第三个值。

语法

mad24.mode.type  d, a, b, c;
mad24.hi.sat.s32 d, a, b, c;

.mode = { .hi, .lo };
.type = { .u32, .s32 };

描述

计算两个存储在32位源寄存器中的24位整数值的乘积,并将第三个32位值加到48位结果的高32位或低32位上。返回48位结果的高32位或低32位。

语义

t = a * b;
d = t<47..16> + c;   // for .hi variant
d = t<31..0> + c;    // for .lo variant

注意事项

整数乘法产生的结果是输入操作数的两倍大小,即48位。

mad24.hi 执行24x24位乘法,并将48位结果的高32位与第三个值相加。

mad24.lo 执行24x24位乘法,并将48位结果的低32位与第三个值相加。

所有操作数都具有相同的类型和大小。

饱和度调节器:

.sat

将32位有符号加法的结果限制在MININT..MAXINT范围内(防止溢出)。仅适用于.hi模式下的.s32类型。

mad24.hi 在没有硬件支持24位乘法的机器上可能效率较低。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

mad24.lo.s32 d,a,b,c;   // low 32-bits of 24x24-bit signed multiply.

9.7.1.7. 整数算术指令:sad

悲伤

绝对差之和。

语法

sad.type  d, a, b, c;

.type = { .u16, .u32, .u64,
          .s16, .s32, .s64 };

描述

a-b的绝对值加到c上,并将结果值写入d

语义

d = c + ((a<b) ? b-a : a-b);

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

sad.s32  d,a,b,c;
sad.u32  d,a,b,d;  // running sum

9.7.1.8. 整数算术指令:div

div

将一个值除以另一个值。

语法

div.type  d, a, b;

.type = { .u16, .u32, .u64,
          .s16, .s32, .s64 };

描述

a 除以 b,结果存储在 d 中。

语义

d = a / b;

注意事项

除以零会产生一个未定义的、与机器相关的值。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

div.s32  b,n,i;

9.7.1.9. 整数算术指令:rem

rem

整数除法的余数。

语法

rem.type  d, a, b;

.type = { .u16, .u32, .u64,
          .s16, .s32, .s64 };

描述

a 除以 b,余数存储在 d 中。

语义

d = a % b;

注意事项

负数的行为取决于机器,并且取决于除法是向零舍入还是向负无穷舍入。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

rem.s32  x,x,8;    // x = x%8;

9.7.1.10. 整数算术指令:abs

abs

绝对值。

语法

abs.type  d, a;

.type = { .s16, .s32, .s64 };

描述

a 的绝对值并存储到 d 中。

语义

d = |a|;

注意事项

仅适用于有符号整数。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

abs.s32  r0,a;

9.7.1.11. 整数算术指令:neg

neg

算术取反。

语法

neg.type  d, a;

.type = { .s16, .s32, .s64 };

描述

a的符号取反并将结果存储在d中。

语义

d = -a;

注意事项

仅适用于有符号整数。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

neg.s32  r0,a;

9.7.1.12. 整数算术指令:min

最小值

找出两个值中的最小值。

语法

min.atype         d, a, b;
min{.relu}.btype  d, a, b;

.atype = { .u16, .u32, .u64,
           .u16x2, .s16, .s64 };
.btype = { .s16x2, .s32 };

描述

ab中的最小值存储在d中。

对于.u16x2.s16x2指令类型,通过从源操作数中提取半字值来形成输入向量。然后并行处理这些半字操作数,在目标位置生成.u16x2.s16x2结果。

操作数 dab 与指令类型具有相同类型。对于指令类型 .u16x2.s16x2,操作数 dab 的类型为 .b32

语义

if (type == u16x2 || type == s16x2) {
    iA[0] = a[0:15];
    iA[1] = a[16:31];
    iB[0] = b[0:15];
    iB[1] = b[16:31];
    for (i = 0; i < 2; i++) {
         d[i] = (iA[i] < iB[i]) ? iA[i] : iB[i];
    }
} else {
    d = (a < b) ? a : b; // Integer (signed and unsigned)
}

注意事项

有符号和无符号不同。

Saturation modifier:

min.relu.{s16x2, s32} 如果结果为负,则将其限制为0。

PTX ISA 说明

在PTX ISA版本1.0中引入。

min.u16x2, min{.relu}.s16x2min.relu.s32 在PTX ISA 8.0版本中引入。

目标ISA注意事项

支持所有目标架构。

min.u16x2, min{.relu}.s16x2min.relu.s32 需要 sm_90 或更高版本。

示例

    min.s32  r0,a,b;
@p  min.u16  h,i,j;
    min.s16x2.relu u,v,w;

9.7.1.13. 整数算术指令:max

最大值

找出两个值中的最大值。

语法

max.atype         d, a, b;
max{.relu}.btype  d, a, b;

.atype = { .u16, .u32, .u64,
           .u16x2, .s16, .s64 };
.btype = { .s16x2, .s32 };

描述

ab中的最大值存储到d中。

对于.u16x2.s16x2指令类型,通过从源操作数中提取半字值来形成输入向量。然后并行处理这些半字操作数,在目标位置生成.u16x2.s16x2结果。

操作数 dab 与指令类型具有相同类型。对于指令类型 .u16x2.s16x2,操作数 dab 的类型为 .b32

语义

if (type == u16x2 || type == s16x2) {
    iA[0] = a[0:15];
    iA[1] = a[16:31];
    iB[0] = b[0:15];
    iB[1] = b[16:31];
    for (i = 0; i < 2; i++) {
         d[i] = (iA[i] > iB[i]) ? iA[i] : iB[i];
    }
} else {
    d = (a > b) ? a : b; // Integer (signed and unsigned)
}

注意事项

有符号和无符号不同。

Saturation modifier:

max.relu.{s16x2, s32} 如果结果为负,则将其限制为0。

PTX ISA 说明

在PTX ISA版本1.0中引入。

max.u16x2, max{.relu}.s16x2max.relu.s32 在PTX ISA 8.0版本中引入。

目标ISA注意事项

支持所有目标架构。

max.u16x2max{.relu}.s16x2max.relu.s32 需要 sm_90 或更高版本。

示例

max.u32  d,a,b;
max.s32  q,q,0;
max.relu.s16x2 t,t,u;

9.7.1.14. 整数算术指令:popc

popc

人口计数。

语法

popc.type  d, a;

.type = { .b32, .b64 };

描述

计算a中1的位数,并将结果人口计数放入32位目标寄存器d中。操作数a具有指令类型,目标d的类型为.u32

语义

.u32  d = 0;
while (a != 0) {
   if (a & 0x1)  d++;
   a = a >> 1;
}

PTX ISA 说明

在PTX ISA版本2.0中引入。

目标ISA注意事项

popc 需要 sm_20 或更高版本。

示例

popc.b32  d, a;
popc.b64  cnt, X;  // cnt is .u32

9.7.1.15. 整数算术指令:clz

clz

计算前导零的数量。

语法

clz.type  d, a;

.type = { .b32, .b64 };

描述

计算从最高有效位开始的a中前导零的数量,并将结果存入32位目标寄存器d。操作数a具有指令类型,目标d的类型为.u32。对于.b32类型,前导零的数量在0到32之间(含)。对于.b64类型,前导零的数量在0到64之间(含)。

语义

.u32  d = 0;
if (.type == .b32)   { max = 32; mask = 0x80000000; }
else                 { max = 64; mask = 0x8000000000000000; }

while (d < max && (a&mask == 0) ) {
    d++;
    a = a << 1;
}

PTX ISA 说明

在PTX ISA 2.0版本中引入。

目标ISA注意事项

clz 需要 sm_20 或更高版本。

示例

clz.b32  d, a;
clz.b64  cnt, X;  // cnt is .u32

9.7.1.16. 整数算术指令:bfind

bfind

查找最高有效非符号位。

语法

bfind.type           d, a;
bfind.shiftamt.type  d, a;

.type = { .u32, .u64,
          .s32, .s64 };

描述

找到a中最高有效非符号位的位置,并将结果存入d。操作数a具有指令类型,目标d的类型为.u32。对于无符号整数,bfind返回最高有效1的位位置。对于有符号整数,bfind对负输入返回最高有效0的位位置,对非负输入返回最高有效1的位位置。

如果指定了.shiftamtbfind将返回将找到的位左移到最高有效位位置所需的位移量。

bfind 如果没有找到非符号位,则返回 0xffffffff

语义

msb = (.type==.u32 || .type==.s32) ? 31 : 63;
// negate negative signed inputs
if ( (.type==.s32 || .type==.s64) && (a & (1<<msb)) ) {
    a = ~a;
}
.u32  d = 0xffffffff;
for (.s32 i=msb; i>=0; i--) {
    if (a & (1<<i))  { d = i; break; }
}
if (.shiftamt && d != 0xffffffff)  { d = msb - d; }

PTX ISA 说明

在PTX ISA版本2.0中引入。

目标ISA注意事项

bfind 需要 sm_20 或更高版本。

示例

bfind.u32  d, a;
bfind.shiftamt.s64  cnt, X;  // cnt is .u32

9.7.1.17. 整数算术指令:fns

函数

查找第n个设置位

语法

fns.b32 d, mask, base, offset;

描述

给定一个32位值mask和一个整数值base(范围0到31),从base位开始查找mask中第n个(由offset指定)置位,并将该位位置存入d。如果未找到,则将0xffffffff存入d

操作数 mask 为32位类型。操作数 base 具有 .b32.u32.s32 类型。操作数 offset 为 .s32 类型。目标 d 的类型为 .b32.

操作数 base 必须小于等于31,否则行为未定义。

语义

d = 0xffffffff;
if (offset == 0) {
    if (mask[base] == 1) {
        d = base;
    }
} else {
    pos = base;
    count = |offset| - 1;
    inc = (offset > 0) ? 1 : -1;

    while ((pos >= 0) && (pos < 32)) {
        if (mask[pos] == 1) {
            if (count == 0) {
              d = pos;
              break;
           } else {
               count = count - 1;
           }
        }
        pos = pos + inc;
    }
}

PTX ISA 说明

在PTX ISA 6.0版本中引入。

目标ISA注意事项

fns 需要 sm_30 或更高版本。

示例

fns.b32 d, 0xaaaaaaaa, 3, 1;   // d = 3
fns.b32 d, 0xaaaaaaaa, 3, -1;  // d = 3
fns.b32 d, 0xaaaaaaaa, 2, 1;   // d = 3
fns.b32 d, 0xaaaaaaaa, 2, -1;  // d = 1

9.7.1.18. 整数算术指令:brev

brev

位反转。

语法

brev.type  d, a;

.type = { .b32, .b64 };

描述

对输入执行位反转操作。

语义

msb = (.type==.b32) ? 31 : 63;

for (i=0; i<=msb; i++) {
    d[i] = a[msb-i];
}

PTX ISA 说明

在PTX ISA版本2.0中引入。

目标ISA注意事项

brev 需要 sm_20 或更高版本。

示例

brev.b32  d, a;

9.7.1.19. 整数算术指令:bfe

bfe

位域提取。

语法

bfe.type  d, a, b, c;

.type = { .u32, .u64,
          .s32, .s64 };

描述

a中提取位字段,并将零扩展或符号扩展的结果放入d。源b给出位字段的起始位位置,源c给出位字段的长度(以位为单位)。

操作数 ad 与指令类型相同。操作数 bc.u32 类型,但被限制在8位值范围 0..255 内。

提取字段的符号位定义如下:

.u32, .u64:

.s32, .s64:

msb 输入a的最高有效位,如果提取的字段超出了a的msb范围,则取提取字段的msb,否则

如果位字段长度为零,则结果为零。

目标d会用提取字段的符号位进行填充。如果起始位置超出输入的msb,则目标d会用提取字段的复制符号位填充。

语义

msb = (.type==.u32 || .type==.s32) ? 31 : 63;
pos = b & 0xff;  // pos restricted to 0..255 range
len = c & 0xff;  // len restricted to 0..255 range

if (.type==.u32 || .type==.u64 || len==0)
    sbit = 0;
else
    sbit = a[min(pos+len-1,msb)];

d = 0;
for (i=0; i<=msb; i++) {
    d[i] = (i<len && pos+i<=msb) ? a[pos+i] : sbit;
}

PTX ISA 说明

在PTX ISA 2.0版本中引入。

目标ISA注意事项

bfe 需要 sm_20 或更高版本。

示例

bfe.b32  d,a,start,len;

9.7.1.20. 整数算术指令:bfi

bfi

位域插入。

语法

bfi.type  f, a, b, c, d;

.type = { .b32, .b64 };

描述

将位字段从a对齐并插入到b中,并将结果放入f。源c 提供插入的起始位位置,源d给出位字段的长度(以位为单位)。

操作数 abf 与指令类型相同。操作数 cd 的类型为 .u32,但被限制在8位值范围 0..255 内。

如果位字段长度为零,则结果为b

如果起始位置超出输入的msb,则结果为b

语义

msb = (.type==.b32) ? 31 : 63;
pos = c & 0xff;  // pos restricted to 0..255 range
len = d & 0xff;  // len restricted to 0..255 range

f = b;
for (i=0; i<len && pos+i<=msb; i++) {
    f[pos+i] = a[i];
}

PTX ISA 说明

在PTX ISA 2.0版本中引入。

目标ISA注意事项

bfi 需要 sm_20 或更高版本。

示例

bfi.b32  d,a,b,start,len;

9.7.1.21. 整数算术指令: szext

szext

符号扩展或零扩展。

语法

szext.mode.type  d, a, b;

.mode = { .clamp, .wrap };
.type = { .u32, .s32 };

描述

将操作数a中的N位值进行符号扩展或零扩展,其中N由操作数b指定。结果值存储在目标操作数d中。

对于.s32指令类型,a中的值被视为N位有符号值,并且这个N位值的最高有效位会复制到位31。对于.u32指令类型,a中的值被视为N位无符号数,并零扩展到32位。操作数b是一个无符号32位值。

如果N的值为0,则szext的结果为0。如果N的值为32或更高,则szext的结果取决于.mode限定符的值,具体如下:

  • 如果 .mode 设置为 .clamp,则结果与源操作数 a 相同。

  • 如果.mode.wrap,则结果会使用N的环绕值进行计算。

语义

b1        = b & 0x1f;
too_large = (b >= 32 && .mode == .clamp) ? true : false;
mask      = too_large ? 0 : (~0) << b1;
sign_pos  = (b1 - 1) & 0x1f;

if (b1 == 0 || too_large || .type != .s32) {
    sign_bit = false;
} else {
    sign_bit = (a >> sign_pos) & 1;
}
d = (a & ~mask) | (sign_bit ? mask | 0);

PTX ISA 说明

在PTX ISA版本7.6中引入。

目标ISA注意事项

szext 需要 sm_70 或更高版本。

示例

szext.clamp.s32 rd, ra, rb;
szext.wrap.u32  rd, 0xffffffff, 0; // Result is 0.

9.7.1.22. 整数算术指令:bmsk

bmsk

位域掩码。

语法

bmsk.mode.b32  d, a, b;

.mode = { .clamp, .wrap };

描述

从操作数a指定的比特位开始,生成一个32位的掩码,其宽度由操作数b指定。生成的位掩码存储在目标操作数d中。

在以下情况下,生成的位掩码为0:

  • a的值为32或更高且.mode.clamp时。

  • b的指定值或b的包装值(当.mode被指定为.wrap时)为0时。

语义

a1    = a & 0x1f;
mask0 = (~0) << a1;
b1    = b & 0x1f;
sum   = a1 + b1;
mask1 = (~0) << sum;

sum-overflow          = sum >= 32 ? true : false;
bit-position-overflow = false;
bit-width-overflow    = false;

if (.mode == .clamp) {
    if (a >= 32) {
        bit-position-overflow = true;
        mask0 = 0;
    }
    if (b >= 32) {
        bit-width-overflow = true;
    }
}

if (sum-overflow || bit-position-overflow || bit-width-overflow) {
    mask1 = 0;
} else if (b1 == 0) {
    mask1 = ~0;
}
d = mask0 & ~mask1;

注意事项

操作数b指定的位掩码宽度在.clamp模式下限制为0..32范围,在.wrap模式下限制为0..31范围。

PTX ISA 说明

在PTX ISA版本7.6中引入。

目标ISA注意事项

bmsk 需要 sm_70 或更高版本。

示例

bmsk.clamp.b32  rd, ra, rb;
bmsk.wrap.b32   rd, 1, 2; // Creates a bitmask of 0x00000006.

9.7.1.23. 整数算术指令:dp4a

dp4a

四路字节点积累加。

语法

dp4a.atype.btype  d, a, b, c;

.atype = .btype = { .u32, .s32 };

描述

四路字节点积,结果累加为32位。

操作数 ab 是32位输入,以打包形式保存4字节输入用于点积计算。

如果.atype.btype都是.u32类型,则操作数c的类型为.u32;否则操作数c的类型为.s32

语义

d = c;

// Extract 4 bytes from a 32bit input and sign or zero extend
// based on input type.
Va = extractAndSignOrZeroExt_4(a, .atype);
Vb = extractAndSignOrZeroExt_4(b, .btype);

for (i = 0; i < 4; ++i) {
    d += Va[i] * Vb[i];
}

PTX ISA 说明

在PTX ISA版本5.0中引入。

目标ISA注意事项

需要 sm_61 或更高版本。

示例

dp4a.u32.u32           d0, a0, b0, c0;
dp4a.u32.s32           d1, a1, b1, c1;

9.7.1.24. 整数算术指令:dp2a

dp2a

双向点积累加。

语法

dp2a.mode.atype.btype  d, a, b, c;

.atype = .btype = { .u32, .s32 };
.mode = { .lo, .hi };

描述

双向16位到8位的点积运算,结果以32位累加。

操作数 ab 是32位输入。操作数 a 以打包形式包含两个16位输入,而操作数 b 以打包形式包含4个字节输入用于点积计算。

根据指定的.mode,将使用操作数b的下半部分或上半部分进行点积运算。

如果.atype.btype都是.u32类型,则操作数c的类型为.u32,否则操作数c的类型为.s32

语义

d = c;
// Extract two 16-bit values from a 32-bit input and sign or zero extend
// based on input type.
Va = extractAndSignOrZeroExt_2(a, .atype);

// Extract four 8-bit values from a 32-bit input and sign or zer extend
// based on input type.
Vb = extractAndSignOrZeroExt_4(b, .btype);

b_select = (.mode == .lo) ? 0 : 2;

for (i = 0; i < 2; ++i) {
    d += Va[i] * Vb[b_select + i];
}

PTX ISA 说明

在PTX ISA版本5.0中引入。

目标ISA注意事项

需要 sm_61 或更高版本。

示例

dp2a.lo.u32.u32           d0, a0, b0, c0;
dp2a.hi.u32.s32           d1, a1, b1, c1;

9.7.2. 扩展精度整数算术指令

指令 add.ccaddcsub.ccsubcmad.ccmadc 引用了一个隐式指定的条件码寄存器(CC),该寄存器包含单个进位标志位(CC.CF),用于保存进位输入/输出或借位输入/输出。这些指令支持扩展精度整数的加法、减法和乘法运算。其他指令不会访问条件码,也不支持设置、清除或测试条件码。条件码寄存器在调用过程中不会被保留,主要用于在直线代码序列中计算扩展精度整数的加法、减法和乘法。

扩展精度算术指令包括:

  • add.cc, addc

  • sub.cc, subc

  • mad.cc, madc

9.7.2.1. 扩展精度算术指令:add.cc

add.cc

带进位相加两个值。

语法

add.cc.type  d, a, b;

.type = { .u32, .s32, .u64, .s64 };

描述

执行整数加法并将进位值写入条件码寄存器。

语义

d = a + b;

执行写入到 CC.CF

注意事项

无整数舍入修饰符。

无饱和。

无符号整数和有符号整数的行为相同。

PTX ISA 说明

32位add.cc在PTX ISA 1.2版本中引入。

64位add.cc在PTX ISA版本4.3中引入。

目标ISA注意事项

32位add.cc在所有目标架构上都受支持。

64位 add.cc 需要 sm_20 或更高版本。

示例

@p  add.cc.u32   x1,y1,z1;   // extended-precision addition of
@p  addc.cc.u32  x2,y2,z2;   // two 128-bit values
@p  addc.cc.u32  x3,y3,z3;
@p  addc.u32     x4,y4,z4;

9.7.2.2. 扩展精度算术指令:addc

addc

带进位输入和可选进位输出的两个值相加。

语法

addc{.cc}.type  d, a, b;

.type = { .u32, .s32, .u64, .s64 };

描述

执行带进位输入的整数加法运算,并可选择将进位输出值写入条件码寄存器。

语义

d = a + b + CC.CF;

如果指定了.cc,则将执行结果写入CC.CF

注意事项

无整数舍入修饰符。

无饱和。

无符号整数和有符号整数的行为相同。

PTX ISA 说明

PTX ISA 1.2版本中引入的32位addc

64位addc在PTX ISA 4.3版本中引入。

目标ISA注意事项

32位addc在所有目标架构上都受支持。

64位 addc 需要 sm_20 或更高版本。

示例

@p  add.cc.u32   x1,y1,z1;   // extended-precision addition of
@p  addc.cc.u32  x2,y2,z2;   // two 128-bit values
@p  addc.cc.u32  x3,y3,z3;
@p  addc.u32     x4,y4,z4;

9.7.2.3. 扩展精度算术指令:sub.cc

sub.cc

从一个值中减去另一个值,并带有借位输出。

语法

sub.cc.type  d, a, b;

.type = { .u32, .s32, .u64, .s64 };

描述

执行整数减法运算,并将借位值写入条件码寄存器。

语义

d = a - b;

借出记录写入 CC.CF

注意事项

无整数舍入修饰符。

无饱和。

无符号整数和有符号整数的行为相同。

PTX ISA 说明

32位sub.cc在PTX ISA版本1.2中引入。

64位sub.cc在PTX ISA版本4.3中引入。

目标ISA注意事项

32位sub.cc在所有目标架构上都受支持。

64位 sub.cc 需要 sm_20 或更高版本。

示例

@p  sub.cc.u32   x1,y1,z1;   // extended-precision subtraction
@p  subc.cc.u32  x2,y2,z2;   // of two 128-bit values
@p  subc.cc.u32  x3,y3,z3;
@p  subc.u32     x4,y4,z4;

9.7.2.4. 扩展精度算术指令:subc

子命令

从一个值中减去另一个值,支持借位输入和可选的借位输出。

语法

subc{.cc}.type  d, a, b;

.type = { .u32, .s32, .u64, .s64 };

描述

执行带借位的整数减法运算,并可选择将借出值写入条件码寄存器。

语义

d = a  - (b + CC.CF);

如果指定了.cc,则借出写入CC.CF

注意事项

无整数舍入修饰符。

无饱和。

无符号整数和有符号整数的行为相同。

PTX ISA 说明

32位subc在PTX ISA 1.2版本中引入。

64位subc在PTX ISA 4.3版本中引入。

目标ISA注意事项

32位subc在所有目标架构上都受支持。

64位 subc 需要 sm_20 或更高版本。

示例

@p  sub.cc.u32   x1,y1,z1;   // extended-precision subtraction
@p  subc.cc.u32  x2,y2,z2;   // of two 128-bit values
@p  subc.cc.u32  x3,y3,z3;
@p  subc.u32     x4,y4,z4;

9.7.2.5. 扩展精度算术指令:mad.cc

mad.cc

将两个值相乘,提取结果的高半部分或低半部分,并与第三个值相加(带进位输出)。

语法

mad{.hi,.lo}.cc.type  d, a, b, c;

.type = { .u32, .s32, .u64, .s64 };

描述

将两个值相乘,提取结果的高位或低位部分,并加上第三个值。将结果写入目标寄存器,并将加法产生的进位输出写入条件码寄存器。

语义

t = a * b;
d = t<63..32> + c;    // for .hi variant
d = t<31..0> + c;     // for .lo variant

加法产生的进位写入CC.CF

注意事项

通常与madcaddc结合使用,以实现扩展精度的多字乘法运算。示例请参见madc

PTX ISA 说明

32位mad.cc在PTX ISA 3.0版本中引入。

64位mad.cc在PTX ISA 4.3版本中引入。

目标ISA注意事项

需要目标 sm_20 或更高版本。

示例

@p  mad.lo.cc.u32 d,a,b,c;
    mad.lo.cc.u32 r,p,q,r;

9.7.2.6. 扩展精度算术指令:madc

madc

将两个值相乘,提取结果的高半部分或低半部分,并与第三个值相加,支持进位输入和可选的进位输出。

语法

madc{.hi,.lo}{.cc}.type  d, a, b, c;

.type = { .u32, .s32, .u64, .s64 };

描述

将两个值相乘,提取结果的高位或低位部分,并与第三个值及进位输入相加。将结果写入目标寄存器,并可选地将加法产生的进位输出写入条件码寄存器。

语义

t = a * b;
d = t<63..32> + c + CC.CF;     // for .hi variant
d = t<31..0> + c + CC.CF;      // for .lo variant

如果指定了.cc,则加法进位会写入CC.CF

注意事项

通常与mad.ccaddc结合使用,以实现扩展精度的多字乘法。参见下面的示例。

PTX ISA 说明

32位madc在PTX ISA 3.0版本中引入。

64位madc在PTX ISA 4.3版本中引入。

目标ISA注意事项

需要目标 sm_20 或更高版本。

示例

// extended-precision multiply:  [r3,r2,r1,r0] = [r5,r4] * [r7,r6]
mul.lo.u32     r0,r4,r6;      // r0=(r4*r6).[31:0], no carry-out
mul.hi.u32     r1,r4,r6;      // r1=(r4*r6).[63:32], no carry-out
mad.lo.cc.u32  r1,r5,r6,r1;   // r1+=(r5*r6).[31:0], may carry-out
madc.hi.u32    r2,r5,r6,0;    // r2 =(r5*r6).[63:32]+carry-in,
                              // no carry-out
mad.lo.cc.u32   r1,r4,r7,r1;  // r1+=(r4*r7).[31:0], may carry-out
madc.hi.cc.u32  r2,r4,r7,r2;  // r2+=(r4*r7).[63:32]+carry-in,
                              // may carry-out
addc.u32        r3,0,0;       // r3 = carry-in, no carry-out
mad.lo.cc.u32   r2,r5,r7,r2;  // r2+=(r5*r7).[31:0], may carry-out
madc.hi.u32     r3,r5,r7,r3;  // r3+=(r5*r7).[63:32]+carry-in

9.7.3. 浮点运算指令

浮点指令对.f32.f64寄存器操作数及常量立即值进行操作。浮点指令包括:

  • testp

  • copysign

  • add

  • sub

  • mul

  • fma

  • mad

  • div

  • abs

  • neg

  • min

  • max

  • rcp

  • sqrt

  • rsqrt

  • sin

  • cos

  • lg2

  • ex2

  • tanh

支持舍入修饰符的指令符合IEEE-754标准。双精度指令支持次正规输入和结果。单精度指令默认情况下对sm_20及后续目标支持次正规输入和结果,而对sm_1x目标则将次正规输入和结果刷新为保留符号的零值。单精度指令的可选.ftz修饰符通过将次正规输入和结果刷新为保留符号的零值(无论目标架构如何),提供了与sm_1x目标的向后兼容性。

单精度运算addsubmulmad支持将结果饱和至[0.0, 1.0]范围,并将NaN值清零为正零。双精度指令支持NaN有效载荷(除rcp.approx.ftz.f64rsqrt.approx.ftz.f64外,这两个指令会将输入NaN映射为标准NaN)。单精度指令返回未指定的NaN值。请注意未来实现可能会支持单精度指令的NaN有效载荷,因此PTX程序不应依赖当前生成的特定单精度NaN值。

表28总结了PTX中的浮点指令。

表 28 浮点指令摘要

指令

.rn

.rz

.rm

.rp

.ftz

.sat

备注

{add,sub,mul}.rnd.f32

x

x

x

x

x

x

如果未指定舍入修饰符, 默认值为.rn,指令可能会被合并为乘加运算。

{add,sub,mul}.rnd.f64

x

x

x

x

不适用

不适用

如果未指定舍入修饰符, 默认使用.rn,指令可能会 被合并为乘加运算。

mad.f32

不适用

不适用

不适用

不适用

x

x

.target sm_1x

无舍入修饰符。

{mad,fma}.rnd.f32

x

x

x

x

x

x

.target sm_20 或更高版本

mad.f32fma.f32 是相同的。

{mad,fma}.rnd.f64

x

x

x

x

不适用

不适用

mad.f64fma.f64 是相同的。

div.full.f32

不适用

不适用

不适用

不适用

x

不适用

无舍入修饰符。

{div,rcp,sqrt}.approx.f32

不适用

不适用

不适用

不适用

x

不适用

不适用

rcp.approx.ftz.f64

不适用

不适用

不适用

不适用

x

不适用

.target sm_20 或更高版本

{div,rcp,sqrt}.rnd.f32

x

x

x

x

x

不适用

.target sm_20 或更高版本

{div,rcp,sqrt}.rnd.f64

x

x

x

x

不适用

不适用

.target sm_20 或更高版本

{abs,neg,min,max}.f32

不适用

不适用

不适用

不适用

x

不适用

{abs,neg,min,max}.f64

不适用

不适用

不适用

不适用

不适用

不适用

rsqrt.approx.f32

不适用

不适用

不适用

不适用

x

不适用

rsqrt.approx.f64

不适用

不适用

不适用

不适用

不适用

不适用

rsqrt.approx.ftz.f64

不适用

不适用

不适用

不适用

x

不适用

.target sm_20 或更高版本

{sin,cos,lg2,ex2}.approx.f32

不适用

不适用

不适用

不适用

x

不适用

tanh.approx.f32

不适用

不适用

不适用

不适用

不适用

不适用

.target sm_75 或更高版本

9.7.3.1. 浮点指令:testp

测试p

测试浮点数属性。

语法

testp.op.type  p, a;  // result is .pred

.op   = { .finite, .infinite,
          .number, .notanumber,
          .normal, .subnormal };
.type = { .f32, .f64 };

描述

testp 测试浮点数的常见属性,如果为真则返回谓词值 1,如果为假则返回 0

testp.finite

True 如果输入不是无限或 NaN

testp.infinite

True 如果输入是正无穷或负无穷

testp.number

True 如果输入不是 NaN

testp.notanumber

True 如果输入是 NaN

testp.normal

True 如果输入是常规数字(不是NaN,不是无穷大)

testp.subnormal

True 如果输入是一个次正规数(不是 NaN,不是无穷大)

作为一种特殊情况,正零和负零被视为常规数字。

PTX ISA 说明

在PTX ISA 2.0版本中引入。

目标ISA注意事项

需要 sm_20 或更高版本。

示例

testp.notanumber.f32  isnan, f0;
testp.infinite.f64    p, X;

9.7.3.2. 浮点指令:copysign

copysign

将一个输入的符号复制到另一个输入。

语法

copysign.type  d, a, b;

.type = { .f32, .f64 };

描述

a的符号位复制到b的值中,并将结果作为d返回。

PTX ISA 说明

在PTX ISA 2.0版本中引入。

目标ISA注意事项

需要 sm_20 或更高版本。

示例

copysign.f32  x, y, z;
copysign.f64  A, B, C;

9.7.3.3. 浮点运算指令:加法

add

将两个值相加。

语法

add{.rnd}{.ftz}{.sat}.f32  d, a, b;
add{.rnd}{.ftz}.f32x2      d, a, b;
add{.rnd}.f64              d, a, b;

.rnd = { .rn, .rz, .rm, .rp };

描述

执行加法运算并将结果值写入目标寄存器。

对于.f32x2指令类型,从源操作数形成单精度(.f32)值的输入向量。然后并行相加单精度(.f32)操作数以在目标位置产生.f32x2结果。

对于.f32x2指令类型,操作数dab具有.b64类型。

语义

if (type == f32 || type == f64) {
    d = a + b;
} else if (type == f32x2) {
    fA[0] = a[0:31];
    fA[1] = a[32:63];
    fB[0] = b[0:31];
    fB[1] = b[32:63];
    for (i = 0; i < 2; i++) {
        d[i] = fA[i] + fB[i];
    }
}

注意事项

舍入修饰符:

.rn

尾数最低有效位向最近的偶数舍入

.rz

尾数最低有效位向零舍入

.rm

尾数最低有效位向负无穷方向舍入

.rp

尾数最低有效位向正无穷方向舍入

舍入修饰符的默认值为.rn。请注意,带有显式舍入修饰符的add指令会被代码优化器保守处理。而没有舍入修饰符的add指令默认采用向最近偶数舍入,并可能被代码优化器积极优化。特别是,没有舍入修饰符的mul/add序列可能会被优化为目标设备上的融合乘加指令。

次正规数:

sm_20+

默认情况下,支持次正规数。

add.ftz.f32, add.ftz.f32x2 会将次正规数输入和结果刷新为保留符号的零值。

sm_1x

add.f64 支持次正规数。

add.f32 将次正规输入和结果刷新为保留符号的零。

饱和度调节器:

add.sat.f32 将结果限制在[0.0, 1.0]范围内。NaN结果会被清零为+0.0f

PTX ISA 说明

在PTX ISA版本1.0中引入。

add.f32x2 在PTX ISA版本8.6中引入。

目标ISA注意事项

add.f32 在所有目标架构上都受支持。

add.f64 需要 sm_13 或更高版本。

舍入修饰符有以下目标要求:

.rn, .rz

适用于所有目标

.rm, .rp

对于 add.f64,需要 sm_13 或更高版本。

对于 add.f32,需要 sm_20 或更高版本。

add.f32x2 需要 sm_100 或更高版本。

示例

@p  add.rz.ftz.f32  f1,f2,f3;
add.rp.ftz.f32x2    d, a, b;

9.7.3.4. 浮点指令:sub

子进程

从一个值中减去另一个值。

语法

sub{.rnd}{.ftz}{.sat}.f32  d, a, b;
sub{.rnd}{.ftz}.f32x2      d, a, b;
sub{.rnd}.f64              d, a, b;

.rnd = { .rn, .rz, .rm, .rp };

描述

执行减法运算并将结果值写入目标寄存器。

对于.f32x2指令类型,从源操作数形成单精度(.f32)值的输入向量。然后并行减去单精度(.f32)操作数以在目标中产生.f32x2结果。

对于.f32x2指令类型,操作数dab具有.b64类型。

语义

if (type == f32 || type == f64) {
    d = a - b;
} else if (type == f32x2) {
    fA[0] = a[0:31];
    fA[1] = a[32:63];
    fB[0] = b[0:31];
    fB[1] = b[32:63];
    for (i = 0; i < 2; i++) {
        d[i] = fA[i] - fB[i];
    }
}

注意事项

舍入修饰符:

.rn

尾数最低有效位向最近的偶数舍入

.rz

尾数最低有效位向零舍入

.rm

尾数最低有效位向负无穷方向舍入

.rp

尾数最低有效位向正无穷方向舍入

舍入修饰符的默认值为.rn。请注意,带有显式舍入修饰符的sub指令会被代码优化器保守处理。而未指定舍入修饰符的sub指令默认采用向最近偶数舍入方式,并可能被代码优化器进行激进优化。特别地,未指定舍入修饰符的mul/sub指令序列可能会被优化为目标设备上的融合乘加指令。

次正规数:

sm_20+

默认情况下,支持次正规数。

sub.ftz.f32, sub.ftz.f32x2 会将次正规输入和结果刷新为保留符号的零值。

sm_1x

sub.f64 支持次正规数。

sub.f32 将次正规输入和结果刷新为保留符号的零。

饱和度调节器:

sub.sat.f32 将结果限制在[0.0, 1.0]范围内。NaN结果会被清零为+0.0f

PTX ISA 说明

在PTX ISA版本1.0中引入。

sub.f32x2 在PTX ISA版本8.6中引入。

目标ISA注意事项

sub.f32 在所有目标架构上都受支持。

sub.f64 需要 sm_13 或更高版本。

舍入修饰符有以下目标要求:

.rn, .rz

适用于所有目标

.rm, .rp

对于 sub.f64,需要 sm_13 或更高版本。

对于 sub.f32,需要 sm_20 或更高版本。

sub.f32x2 需要 sm_100 或更高版本。

示例

sub.f32 c,a,b;
sub.rn.ftz.f32  f1,f2,f3;

9.7.3.5. 浮点指令:mul

mul

将两个值相乘。

语法

mul{.rnd}{.ftz}{.sat}.f32  d, a, b;
mul{.rnd}{.ftz}.f32x2      d, a, b;
mul{.rnd}.f64              d, a, b;

.rnd = { .rn, .rz, .rm, .rp };

描述

计算两个值的乘积。

对于.f32x2指令类型,从源操作数形成单精度(.f32)值的输入向量。然后并行相乘单精度(.f32)操作数,在目标中产生.f32x2结果。

对于.f32x2指令类型,操作数dab具有.b64类型。

语义

if (type == f32 || type == f64) {
    d = a * b;
} else if (type == f32x2) {
    fA[0] = a[0:31];
    fA[1] = a[32:63];
    fB[0] = b[0:31];
    fB[1] = b[32:63];
    for (i = 0; i < 2; i++) {
        d[i] = fA[i] * fB[i];
    }
}

注意事项

对于浮点数乘法,所有操作数的大小必须相同。

舍入修饰符:

.rn

尾数最低有效位向最近的偶数舍入

.rz

尾数最低有效位向零舍入

.rm

尾数最低有效位向负无穷方向舍入

.rp

尾数最低有效位向正无穷方向舍入

舍入修饰符的默认值为.rn。请注意,带有显式舍入修饰符的mul指令会被代码优化器保守处理。而没有舍入修饰符的mul指令默认采用向最近偶数舍入方式,并可能被代码优化器积极优化。特别地,不带舍入修饰符的mul/addmul/sub序列可能会被优化为目标设备上的融合乘加指令。

次正规数:

sm_20+

默认情况下,支持次正规数。

mul.ftz.f32, mul.ftz.f32x2 会将次正规输入和结果刷新为保留符号的零值。

sm_1x

mul.f64 支持非规格化数字。

mul.f32 将次正规输入和结果刷新为保留符号的零。

饱和度调节器:

mul.sat.f32 将结果限制在[0.0, 1.0]范围内。NaN结果会被清零为+0.0f

PTX ISA 说明

在PTX ISA版本1.0中引入。

mul.f32x2 在PTX ISA版本8.6中引入。

目标ISA注意事项

mul.f32 在所有目标架构上都受支持。

mul.f64 需要 sm_13 或更高版本。

舍入修饰符有以下目标要求:

.rn, .rz

适用于所有目标

.rm, .rp

对于 mul.f64,需要 sm_13 或更高版本。

对于 mul.f32,需要 sm_20 或更高版本。

mul.f32x2 需要 sm_100 或更高版本。

示例

mul.ftz.f32 circumf,radius,pi  // a single-precision multiply

9.7.3.6. 浮点指令:fma

fma

融合乘加运算。

语法

fma.rnd{.ftz}{.sat}.f32  d, a, b, c;
fma.rnd{.ftz}.f32x2      d, a, b, c;
fma.rnd.f64              d, a, b, c;

.rnd = { .rn, .rz, .rm, .rp };

描述

执行融合乘加运算,中间乘积和加法运算不会损失精度。

对于.f32x2指令类型,从源操作数形成单精度(.f32)值的输入向量。然后并行操作单精度(.f32)操作数以在目标中产生.f32x2结果。

对于.f32x2指令类型,操作数dabc具有.b64类型。

语义

if (type == f32 || type == f64) {
    d = a * b + c;
} else if (type == f32x2) {
    fA[0] = a[0:31];
    fA[1] = a[32:63];
    fB[0] = b[0:31];
    fB[1] = b[32:63];
    fC[0] = c[0:31];
    fC[1] = c[32:63];
    for (i = 0; i < 2; i++) {
        d[i] = fA[i] * fB[i] + fC[i];
    }
}

注意事项

fma.f32 以无限精度计算 ab 的乘积,然后以无限精度将 c 加到该乘积上。最终结果根据 .rnd 指定的舍入模式舍入为单精度浮点数。

fma.f64 以无限精度计算 ab 的乘积,然后以无限精度将 c 加到该乘积上。最终结果根据 .rnd 指定的舍入模式舍入为双精度浮点数。

fma.f64mad.f64 相同。

舍入修饰符(无默认值):

.rn

尾数最低有效位向最近的偶数舍入

.rz

尾数最低有效位向零舍入

.rm

尾数最低有效位向负无穷方向舍入

.rp

尾数最低有效位向正无穷方向舍入

次正规数:

sm_20+

默认情况下,支持次正规数。

fma.ftz.f32, fma.ftz.f32x2 会将次正规数输入和结果刷新为保留符号的零。

sm_1x

fma.f64 支持次正规数。

fma.f32sm_1x 目标架构上未实现。

饱和度:

fma.sat.f32 将结果限制在[0.0, 1.0]范围内。NaN结果会被清零为+0.0f

PTX ISA 说明

fma.f64 在PTX ISA 1.4版本中引入。

fma.f32 在PTX ISA 2.0版本中引入。

fma.f32x2 在PTX ISA版本8.6中引入。

目标ISA注意事项

fma.f32 需要 sm_20 或更高版本。

fma.f64 需要 sm_13 或更高版本。

fma.f32x2 需要 sm_100 或更高版本。

示例

    fma.rn.ftz.f32  w,x,y,z;
@p  fma.rn.f64      d,a,b,c;
    fma.rp.ftz.f32x2 p,q,r,s;

9.7.3.7. 浮点指令:mad

mad

将两个值相乘并加上第三个值。

语法

mad{.ftz}{.sat}.f32      d, a, b, c;    // .target sm_1x
mad.rnd{.ftz}{.sat}.f32  d, a, b, c;    // .target sm_20
mad.rnd.f64              d, a, b, c;    // .target sm_13 and higher

.rnd = { .rn, .rz, .rm, .rp };

描述

将两个值相乘并加上第三个值,然后将结果值写入目标寄存器。

语义

d = a*b + c;

注意事项

对于 .target sm_20 及更高版本:

  • mad.f32 以无限精度计算 ab 的乘积,然后以无限精度将 c 加到此乘积上。最终结果使用 .rnd 指定的舍入模式舍入为单精度。

  • mad.f64 以无限精度计算 ab 的乘积,然后以无限精度将 c 加到该乘积上。最终结果会根据 .rnd 指定的舍入模式舍入为双精度值。

  • mad.{f32,f64}fma.{f32,f64} 相同。

对于 .target sm_1x:

  • mad.f32 以双精度计算 ab 的乘积,然后将尾数截断为23位,但保留指数。请注意,这与使用 mul 计算乘积不同,后者可能会对尾数进行舍入并限制指数范围。mad.f32 的例外情况是当 c = +/-0.0 时,此时 mad.f32 的结果与分别使用乘法和加法指令计算的结果相同。当为SM 2.0设备进行JIT编译时,mad.f32 会被实现为融合乘加运算(即 fma.rn.ftz.f32)。在这种情况下,mad.f32 可能会产生略微不同的数值结果,且不保证向后兼容性。

  • mad.f64 以无限精度计算 ab 的乘积,然后以无限精度将 c 加到该乘积上。随后根据 .rnd 指定的舍入模式将结果值舍入为双精度。与 mad.f32 不同,对次正规输入和输出的处理遵循 IEEE 754 标准。

  • mad.f64fma.f64 相同。

舍入修饰符(无默认值):

.rn

尾数最低有效位向最近的偶数舍入

.rz

尾数最低有效位向零舍入

.rm

尾数最低有效位向负无穷方向舍入

.rp

尾数最低有效位向正无穷方向舍入

次正规数:

sm_20+

默认情况下,支持次正规数。

mad.ftz.f32 将次正规数输入和结果刷新为保留符号的零值。

sm_1x

mad.f64 支持次正规数。

mad.f32 将次正规输入和结果刷新为保留符号的零。

饱和度调节器:

mad.sat.f32 将结果限制在[0.0, 1.0]范围内。NaN结果会被清零为+0.0f

PTX ISA 说明

在PTX ISA版本1.0中引入。

在PTX ISA 1.4及更高版本中,mad.f64指令需要使用舍入修饰符。

旧版mad.f64指令若未指定舍入修饰符,将映射为mad.rn.f64

在PTX ISA 2.0及更高版本中,针对sm_20及以上目标平台,mad.f32指令必须使用舍入修饰符。

勘误表

mad.f32 在面向 sm_20 及更高版本目标时需要指定舍入修饰符。但对于 PTX ISA 3.0 及更早版本,ptxas 不会强制此要求,mad.f32 会默认静默转为 mad.rn.f32。对于 PTX ISA 3.1 版本,ptxas 会生成警告并默认转为 mad.rn.f32,而在后续版本中,ptxas 将对 PTX ISA 3.2 及更高版本强制实施此要求。

目标ISA注意事项

mad.f32 在所有目标架构上都受支持。

mad.f64 需要 sm_13 或更高版本。

舍入修饰符有以下目标要求:

  • .rn,.rz,.rm,.rp 用于 mad.f64,需要 sm_13 或更高版本。

  • .rn,.rz,.rm,.rp 用于 mad.f32,需要 sm_20 或更高版本。

示例

@p  mad.f32  d,a,b,c;

9.7.3.8. 浮点运算指令:div

div

将一个值除以另一个值。

语法

div.approx{.ftz}.f32  d, a, b;  // fast, approximate divide
div.full{.ftz}.f32    d, a, b;  // full-range approximate divide
div.rnd{.ftz}.f32     d, a, b;  // IEEE 754 compliant rounding
div.rnd.f64           d, a, b;  // IEEE 754 compliant rounding

.rnd = { .rn, .rz, .rm, .rp };

描述

a 除以 b,结果存储在 d 中。

语义

d = a / b;

注意事项

快速、近似的单精度除法:

  • div.approx.f32 实现了一种快速的除法近似计算,公式为 d = a * (1/b)。当 |b| 处于[2-126, 2126]区间时,最大ulp误差为2。对于2126 < |b| < 2128的情况,如果a为无穷大,div.approx.f32会返回NaN,否则返回0。

  • div.full.f32 实现了一个相对快速的全范围近似计算,通过缩放操作数来提高精度,但并非完全符合IEEE 754标准且不支持舍入修饰符。在所有输入范围内,最大ulp误差为2。

  • 次正规输入和结果会被刷新为保留符号的零值。快速近似除以零会产生无穷大值(符号与a相同)。

使用符合IEEE 754标准的舍入方式进行除法运算:

舍入修饰符(无默认值):

.rn

尾数最低有效位向最近的偶数舍入

.rz

尾数最低有效位向零舍入

.rm

尾数最低有效位向负无穷方向舍入

.rp

尾数最低有效位向正无穷方向舍入

次正规数:

sm_20+

默认情况下,支持次正规数。

div.ftz.f32 将次正规输入和结果刷新为保留符号的零。

sm_1x

div.f64 支持次正规数。

div.f32 将次正规输入和结果刷新为保留符号的零。

PTX ISA 说明

div.f32div.f64 在PTX ISA 1.0版本中引入。

显式修饰符 .approx, .full, .ftz 以及PTX ISA 1.4版本引入的舍入模式。

对于PTX ISA版本1.4及更高版本,必须指定.approx.full.rnd中的一个。

对于PTX ISA版本1.0至1.3,div.f32默认为div.approx.ftz.f32,而 div.f64默认为div.rn.f64

目标ISA注意事项

div.approx.f32div.full.f32 在所有目标架构上都受支持。

div.rnd.f32 需要 sm_20 或更高版本。

div.rn.f64 需要 sm_13 或更高版本,或者 .target map_f64_to_f32

div.{rz,rm,rp}.f64 需要 sm_20 或更高版本。

示例

div.approx.ftz.f32  diam,circum,3.14159;
div.full.ftz.f32    x, y, z;
div.rn.f64          xd, yd, zd;

9.7.3.9. 浮点指令:abs

abs

绝对值。

语法

abs{.ftz}.f32  d, a;
abs.f64        d, a;

描述

a 的绝对值并将结果存储在 d 中。

语义

d = |a|;

注意事项

次正规数:

sm_20+

默认情况下,支持次正规数。

abs.ftz.f32 将次正规输入和结果刷新为保留符号的零。

sm_1x

abs.f64 支持次正规数。

abs.f32 将次正规输入和结果刷新为保留符号的零。

对于abs.f32,输入NaN会产生未指定的NaN结果。而对于abs.f64NaN输入会原样输出不作改变。未来的实现可能会遵循IEEE 754标准,保留有效载荷并仅修改符号位。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

abs.f32 支持所有目标架构。

abs.f64 需要 sm_13 或更高版本。

示例

abs.ftz.f32  x,f0;

9.7.3.10. 浮点指令:neg

neg

算术取反。

语法

neg{.ftz}.f32  d, a;
neg.f64        d, a;

描述

a的符号取反,并将结果存储在d中。

语义

d = -a;

注意事项

次正规数:

sm_20+

默认情况下,支持次正规数。

neg.ftz.f32 将次正规输入和结果刷新为保留符号的零。

sm_1x

neg.f64 支持次正规数。

neg.f32 将次正规输入和结果刷新为保留符号的零。

NaN 输入会产生未指定的 NaN。未来的实现可能会遵循 IEEE 754 标准,保留有效载荷并仅修改符号位。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

neg.f32 在所有目标架构上都受支持。

neg.f64 需要 sm_13 或更高版本。

示例

neg.ftz.f32  x,f0;

9.7.3.11. 浮点指令:min

最小值

找出两个值中的最小值。

语法

min{.ftz}{.NaN}{.xorsign.abs}.f32  d, a, b;
min.f64                            d, a, b;

描述

ab中的最小值存储在d中。

如果指定了.NaN修饰符,那么当任一输入为NaN时,结果将是规范的NaN

如果指定了.abs修饰符,目标操作数d的幅度将是两个输入参数绝对值中的较小值。

如果指定了.xorsign修饰符,目标d的符号位将等于两个输入符号位的异或结果。

修饰符 .abs.xorsign 必须同时指定,且 .xorsign 在应用 .abs 操作前会考虑两个输入值的符号位。

如果min的结果是NaN,那么.xorsign.abs修饰符将被忽略。

语义

if (.xorsign) {
    xorsign = getSignBit(a) ^ getSignBit(b);
    if (.abs) {
        a = |a|;
        b = |b|;
   }
}
if (isNaN(a) && isNaN(b))                 d = NaN;
else if (.NaN && (isNaN(a) || isNaN(b)))  d = NaN;
else if (isNaN(a))                        d = b;
else if (isNaN(b))                        d = a;
else                                      d = (a < b) ? a : b;
if (.xorsign && !isNaN(d)) {
    setSignBit(d, xorsign);
}

注意事项

次正规数:

sm_20+

默认情况下,支持次正规数。

min.ftz.f32 将次正规输入和结果刷新为保留符号的零值。

sm_1x

min.f64 支持次正规数。

min.f32 将次正规输入和结果刷新为保留符号的零。

如果两个输入值均为0.0,则+0.0 > -0.0。

PTX ISA 说明

在PTX ISA版本1.0中引入。

min.NaN在PTX ISA版本7.0中引入。

min.xorsign.abs 在PTX ISA 7.2版本中引入。

目标ISA注意事项

min.f32 在所有目标架构上都受支持。

min.f64 需要 sm_13 或更高版本。

min.NaN需要sm_80或更高版本。

min.xorsign.abs 需要 sm_86 或更高版本。

示例

@p  min.ftz.f32  z,z,x;
    min.f64      a,b,c;
    // fp32 min with .NaN
    min.NaN.f32  f0,f1,f2;
    // fp32 min with .xorsign.abs
    min.xorsign.abs.f32 Rd, Ra, Rb;

9.7.3.12. 浮点指令: max

最大值

找出两个值中的最大值。

语法

max{.ftz}{.NaN}{.xorsign.abs}.f32  d, a, b;
max.f64                            d, a, b;

描述

ab中的最大值存储到d中。

如果指定了.NaN修饰符,当任一输入为NaN时,结果将是规范的NaN

如果指定了.abs修饰符,目标操作数d的数值将是两个输入参数绝对值的最大值。

如果指定了.xorsign修饰符,则目标d的符号位等于两个输入符号位的异或结果。

修饰符 .abs.xorsign 必须同时指定,且 .xorsign 在应用 .abs 操作前会考虑两个输入值的符号位。

如果max的结果是NaN,那么.xorsign.abs修饰符将被忽略。

语义

if (.xorsign) {
    xorsign = getSignBit(a) ^ getSignBit(b);
    if (.abs) {
        a = |a|;
        b = |b|;
    }
}
if (isNaN(a) && isNaN(b))                 d = NaN;
else if (.NaN && (isNaN(a) || isNaN(b)))  d = NaN;
else if (isNaN(a))                        d = b;
else if (isNaN(b))                        d = a;
else                                      d = (a > b) ? a : b;
if (.xorsign && !isNaN(d)) {
    setSignBit(d, xorsign);
}

注意事项

次正规数:

sm_20+

默认情况下,支持次正规数。

max.ftz.f32 将次正规输入和结果刷新为保留符号的零。

sm_1x

max.f64 支持次正规数。

max.f32 将次正规输入和结果刷新为保留符号的零。

如果两个输入值均为0.0,则+0.0 > -0.0。

PTX ISA 说明

在PTX ISA版本1.0中引入。

max.NaN在PTX ISA版本7.0中引入。

max.xorsign.abs 在PTX ISA 7.2版本中引入。

目标ISA注意事项

max.f32 在所有目标架构上都受支持。

max.f64 需要 sm_13 或更高版本。

max.NaN需要sm_80或更高版本。

max.xorsign.abs 需要 sm_86 或更高版本。

示例

max.ftz.f32  f0,f1,f2;
max.f64      a,b,c;
// fp32 max with .NaN
max.NaN.f32  f0,f1,f2;
// fp32 max with .xorsign.abs
max.xorsign.abs.f32 Rd, Ra, Rb;

9.7.3.13. 浮点指令: rcp

rcp

取一个值的倒数。

语法

rcp.approx{.ftz}.f32  d, a;  // fast, approximate reciprocal
rcp.rnd{.ftz}.f32     d, a;  // IEEE 754 compliant rounding
rcp.rnd.f64           d, a;  // IEEE 754 compliant rounding

.rnd = { .rn, .rz, .rm, .rp };

描述

计算 1/a,将结果存储在 d 中。

语义

d = 1 / a;

注意事项

快速近似单精度倒数计算:

rcp.approx.f32 实现了对倒数运算的快速近似计算。在整个输入范围内,最大ulp误差为1。

输入

结果

-Inf

-0.0

-0.0

-Inf

+0.0

+无穷大

+Inf

+0.0

NaN

NaN

符合IEEE 754标准的倒数计算(带舍入):

舍入修饰符(无默认值):

.rn

尾数最低有效位向最近的偶数舍入

.rz

尾数最低有效位向零舍入

.rm

尾数最低有效位向负无穷方向舍入

.rp

尾数最低有效位向正无穷方向舍入

次正规数:

sm_20+

默认情况下,支持次正规数。

rcp.ftz.f32 将次正规输入和结果刷新为保留符号的零。

sm_1x

rcp.f64 支持次正规数。

rcp.f32 将次正规输入和结果刷新为保留符号的零值。

PTX ISA 说明

rcp.f32rcp.f64 在PTX ISA 1.0版本中引入。rcp.rn.f64 以及显式修饰符 .approx.ftz 是在PTX ISA 1.4版本中加入的。通用舍入修饰符是在 PTX ISA 2.0版本中新增的。

对于PTX ISA版本1.4及更高版本,必须使用.approx.rnd其中之一。

对于PTX ISA版本1.0至1.3,rcp.f32默认为rcp.approx.ftz.f32, 而rcp.f64默认为rcp.rn.f64

目标ISA注意事项

rcp.approx.f32 在所有目标架构上都受支持。

rcp.rnd.f32 需要 sm_20 或更高版本。

rcp.rn.f64 需要 sm_13 或更高版本,或者 .target map_f64_to_f32.

rcp.{rz,rm,rp}.f64 需要 sm_20 或更高版本。

示例

rcp.approx.ftz.f32  ri,r;
rcp.rn.ftz.f32      xi,x;
rcp.rn.f64          xi,x;

9.7.3.14. 浮点指令:rcp.approx.ftz.f64

rcp.approx.ftz.f64

计算一个快速粗略的近似倒数。

语法

rcp.approx.ftz.f64  d, a;

描述

按如下方式计算一个快速粗略的倒数近似值:

  1. 提取.f64操作数a中最高有效的32位,采用1.11.20 IEEE浮点格式(即忽略a的最低有效32位)

  2. 使用操作数a的尾数最高有效20位计算该值的近似.f64倒数

  3. 将生成的32位数据以1.11.20 IEEE浮点格式放置在目标d的最高有效32位中,并且

  4. .f64目标d的最低有效32位尾数置零。

语义

tmp = a[63:32]; // upper word of a, 1.11.20 format
d[63:32] = 1.0 / tmp;
d[31:0] = 0x00000000;

注意事项

rcp.approx.ftz.f64 实现了一种快速但粗略的倒数近似计算。

输入 a[63:32]

结果 d[63:32]

-Inf

-0.0

-次正规数

-无穷大

-0.0

-Inf

+0.0

+无穷大

+次正规数

+无穷大

+Inf

+0.0

NaN

NaN

输入的NaN会被映射为使用编码0x7fffffff00000000的标准NaN

次正规输入和结果会被刷新为保留符号的零。

PTX ISA 说明

rcp.approx.ftz.f64 在PTX ISA 2.1版本中引入。

目标ISA注意事项

rcp.approx.ftz.f64 需要 sm_20 或更高版本。

示例

rcp.approx.ftz.f64  xi,x;

9.7.3.15. 浮点指令:平方根

sqrt

计算一个值的平方根。

语法

sqrt.approx{.ftz}.f32  d, a; // fast, approximate square root
sqrt.rnd{.ftz}.f32     d, a; // IEEE 754 compliant rounding
sqrt.rnd.f64           d, a; // IEEE 754 compliant rounding

.rnd = { .rn, .rz, .rm, .rp };

描述

计算 sqrt(a) 并将结果存储到 d 中。

语义

d = sqrt(a);

注意事项

sqrt.approx.f32 实现了平方根的快速近似计算。在整个正有限浮点数范围内,最大相对误差为2-23

针对各种边界情况输入,sqrt指令的结果如下表所示:

输入

结果

-Inf

NaN

-normal

NaN

-0.0

-0.0

+0.0

+0.0

+Inf

+Inf

NaN

NaN

符合IEEE 754标准的平方根计算:

舍入修饰符(无默认值):

.rn

尾数最低有效位向最近的偶数舍入

.rz

尾数最低有效位向零舍入

.rm

尾数最低有效位向负无穷方向舍入

.rp

尾数最低有效位向正无穷方向舍入

次正规数:

sm_20+

默认情况下,支持次正规数。

sqrt.ftz.f32 将次正规输入和结果刷新为保留符号的零值。

sm_1x

sqrt.f64 支持次正规数。

sqrt.f32 将次正规输入和结果刷新为保留符号的零。

PTX ISA 说明

sqrt.f32sqrt.f64 在PTX ISA 1.0版本中引入。sqrt.rn.f64 以及显式修饰符 .approx.ftz 是在PTX ISA 1.4版本中加入的。通用舍入修饰符则在PTX ISA 2.0版本中新增。

对于PTX ISA版本1.4及更高版本,必须使用.approx.rnd其中之一。

对于PTX ISA版本1.0至1.3,sqrt.f32默认为sqrt.approx.ftz.f32,而 sqrt.f64默认为sqrt.rn.f64

目标ISA注意事项

sqrt.approx.f32 支持所有目标架构。

sqrt.rnd.f32 需要 sm_20 或更高版本。

sqrt.rn.f64 需要 sm_13 或更高版本,或者 .target map_f64_to_f32

sqrt.{rz,rm,rp}.f64 需要 sm_20 或更高版本。

示例

sqrt.approx.ftz.f32  r,x;
sqrt.rn.ftz.f32      r,x;
sqrt.rn.f64          r,x;

9.7.3.16. 浮点指令: rsqrt

rsqrt

取一个值的平方根的倒数。

语法

rsqrt.approx{.ftz}.f32  d, a;
rsqrt.approx.f64        d, a;

描述

计算 1/sqrt(a) 并将结果存储在 d 中。

语义

d = 1/sqrt(a);

注意事项

rsqrt.approx 实现了倒数平方根的近似计算。

输入

结果

-Inf

NaN

-normal

NaN

-0.0

-Inf

+0.0

+无穷大

+Inf

+0.0

NaN

NaN

在整个正有限浮点数范围内,rsqrt.f32的最大相对误差为2-22.9

次正规数:

sm_20+

默认情况下,支持次正规数。

rsqrt.ftz.f32 将次正规输入和结果刷新为保留符号的零。

sm_1x

rsqrt.f64 支持次正规数。

rsqrt.f32 将次正规输入和结果刷新为保留符号的零。

请注意,rsqrt.approx.f64是通过软件模拟实现的,速度相对较慢。

PTX ISA 说明

rsqrt.f32rsqrt.f64 在PTX ISA 1.0版本中引入。显式修饰符 .approx.ftz 在PTX ISA 1.4版本中引入。

对于PTX ISA版本1.4及更高版本,需要使用.approx修饰符。

对于PTX ISA版本1.0至1.3,rsqrt.f32默认为rsqrt.approx.ftz.f32,而 rsqrt.f64默认为rsqrt.approx.f64

目标ISA注意事项

rsqrt.f32 在所有目标架构上都受支持。

rsqrt.f64 需要 sm_13 或更高版本。

示例

rsqrt.approx.ftz.f32  isr, x;
rsqrt.approx.f64      ISR, X;

9.7.3.17. 浮点指令:rsqrt.approx.ftz.f64

rsqrt.approx.ftz.f64

计算一个值的平方根倒数的近似值。

语法

rsqrt.approx.ftz.f64 d, a;

描述

计算一个值的平方根倒数的双精度(.f64)近似值。该双精度(.f64)目标d的最低有效32位全为零。

语义

tmp = a[63:32]; // upper word of a, 1.11.20 format
d[63:32] = 1.0 / sqrt(tmp);
d[31:0] = 0x00000000;

注意事项

rsqrt.approx.ftz.f64 实现了对数值平方根倒数的快速近似计算。

输入

结果

-Inf

NaN

-次正规数

-无穷大

-0.0

-Inf

+0.0

+无穷大

+次正规数

+无穷大

+Inf

+0.0

NaN

NaN

输入的NaN会被映射为使用编码0x7fffffff00000000的标准NaN

次正规输入和结果会被刷新为保留符号的零。

PTX ISA 说明

rsqrt.approx.ftz.f64 在PTX ISA版本4.0中引入。

目标ISA注意事项

rsqrt.approx.ftz.f64 需要 sm_20 或更高版本。

示例

rsqrt.approx.ftz.f64 xi,x;

9.7.3.18. 浮点指令:sin

正弦函数

计算一个值的正弦值。

语法

sin.approx{.ftz}.f32  d, a;

描述

计算角度a的正弦值(以弧度为单位)。

语义

d = sin(a);

注意事项

sin.approx.f32 实现了正弦函数的快速近似计算。

输入

结果

-Inf

NaN

-0.0

-0.0

+0.0

+0.0

+Inf

NaN

NaN

NaN

输入范围内的最大绝对误差如下:

范围

[-2pi .. 2pi]

[-100pi .. +100pi]

错误

2-20.5

2-14.7

在范围 [-100pi .. +100pi] 之外,仅提供尽力而为的支持。没有定义错误保证。

次正规数:

sm_20+

默认情况下,支持次正规数。

sin.ftz.f32 将次正规输入和结果刷新为保留符号的零值。

sm_1x

次正规输入和结果转换为保留符号的零。

PTX ISA 说明

sin.f32 在PTX ISA 1.0版本中引入。显式修饰符 .approx.ftz 在PTX ISA 1.4版本中引入。

对于PTX ISA 1.4及更高版本,必须使用.approx修饰符。

对于PTX ISA版本1.0到1.3,sin.f32默认使用sin.approx.ftz.f32

目标ISA注意事项

支持所有目标架构。

示例

sin.approx.ftz.f32  sa, a;

9.7.3.19. 浮点指令: cos

cos

计算一个值的余弦值。

语法

cos.approx{.ftz}.f32  d, a;

描述

求角度a(以弧度表示)的余弦值。

语义

d = cos(a);

注意事项

cos.approx.f32 实现了余弦函数的快速近似计算。

输入

结果

-Inf

NaN

-0.0

+1.0

+0.0

+1.0

+Inf

NaN

NaN

NaN

输入范围内的最大绝对误差如下:

范围

[-2pi .. 2pi]

[-100pi .. +100pi]

错误

2-20.5

2-14.7

在范围 [-100pi .. +100pi] 之外,仅提供尽力而为的支持。没有定义错误保证。

次正规数:

sm_20+

默认情况下,支持次正规数。

cos.ftz.f32 将次正规数输入和结果刷新为保留符号的零值。

sm_1x

次正规输入和结果转换为保留符号的零。

PTX ISA 说明

cos.f32 在PTX ISA 1.0版本中引入。显式修饰符 .approx.ftz 在PTX ISA 1.4版本中引入。

对于PTX ISA版本1.4及更高版本,需要使用.approx修饰符。

对于PTX ISA版本1.0到1.3,cos.f32默认使用cos.approx.ftz.f32

目标ISA注意事项

支持所有目标架构。

示例

cos.approx.ftz.f32  ca, a;

9.7.3.20. 浮点指令:lg2

lg2

计算一个数值的以2为底的对数。

语法

lg2.approx{.ftz}.f32  d, a;

描述

计算a的以2为底的对数。

语义

d = log(a) / log(2);

注意事项

lg2.approx.f32 实现了对log2(a)的快速近似计算。

输入

结果

-Inf

NaN

-normal

NaN

-0.0

-Inf

+0.0

-Inf

+Inf

+Inf

NaN

NaN

当输入操作数在范围(0.5, 2)内时,最大绝对误差为2-22。对于此区间外的正有限输入,最大相对误差为2-22

次正规数:

sm_20+

默认情况下,支持次正规数。

lg2.ftz.f32 将次正规输入和结果刷新为保留符号的零。

sm_1x

次正规输入和结果转换为保留符号的零。

PTX ISA 说明

lg2.f32 在PTX ISA 1.0版本中引入。显式修饰符 .approx.ftz 在PTX ISA 1.4版本中引入。

对于PTX ISA版本1.4及更高版本,需要使用.approx修饰符。

对于PTX ISA版本1.0至1.3,lg2.f32默认使用lg2.approx.ftz.f32

目标ISA注意事项

支持所有目标架构。

示例

lg2.approx.ftz.f32  la, a;

9.7.3.21. 浮点指令: ex2

示例2

计算一个值的以2为底的指数。

语法

ex2.approx{.ftz}.f32  d, a;

描述

计算2的a次方。

语义

d = 2 ^ a;

注意事项

ex2.approx.f32 实现了对2a的快速近似计算。

输入

结果

-Inf

+0.0

-0.0

+1.0

+0.0

+1.0

+Inf

+Inf

NaN

NaN

最大ULP误差为2 ULP,相对于全输入范围内的正确舍入结果。

次正规数:

sm_20+

默认情况下,支持次正规数。

ex2.ftz.f32 将次正规输入和结果刷新为保留符号的零。

sm_1x

次正规输入和结果转换为保留符号的零。

PTX ISA 说明

ex2.f32 在PTX ISA 1.0版本中引入。显式修饰符 .approx.ftz 在PTX ISA 1.4版本中引入。

对于PTX ISA版本1.4及更高版本,需要使用.approx修饰符。

对于PTX ISA版本1.0到1.3,ex2.f32默认使用ex2.approx.ftz.f32

目标ISA注意事项

支持所有目标架构。

示例

ex2.approx.ftz.f32  xa, a;

9.7.3.22. 浮点指令: tanh

tanh

计算给定值(以弧度为单位)的双曲正切值

语法

tanh.approx.f32 d, a;

描述

计算a的双曲正切值。

操作数 da 的类型为 .f32

语义

d = tanh(a);

注意事项

tanh.approx.f32 实现了对FP32双曲正切函数的快速近似计算。

针对各种极端情况输入,tanh函数的结果如下:

输入

结果

-Inf

-1.0

-0.0

-0.0

+0.0

+0.0

+Inf

1.0

NaN

NaN

在整个浮点数范围内的最大相对误差为2-11。 支持次正规数。

PTX ISA 说明

在PTX ISA版本7.0中引入。

目标ISA注意事项

需要 sm_75 或更高版本。

示例

tanh.approx.f32 ta, a;

9.7.4. 半精度浮点指令

半精度浮点指令操作在.f16.f16x2寄存器操作数上。半精度浮点指令包括:

  • add

  • sub

  • mul

  • fma

  • neg

  • abs

  • min

  • max

  • tanh

  • ex2

半精度addsubmulfma支持将结果饱和到[0.0, 1.0]范围内,并将NaN值清零为正零。半精度指令会返回一个未指定的NaN

9.7.4.1. 半精度浮点指令:加法

add

将两个值相加。

语法

add{.rnd}{.ftz}{.sat}.f16   d, a, b;
add{.rnd}{.ftz}{.sat}.f16x2 d, a, b;

add{.rnd}.bf16   d, a, b;
add{.rnd}.bf16x2 d, a, b;

.rnd = { .rn };

描述

执行加法运算并将结果值写入目标寄存器。

对于.f16x2.bf16x2指令类型,通过从源操作数中提取半字值来形成输入向量。然后并行相加半字操作数以在目标中生成.f16x2.bf16x2结果。

对于.f16指令类型,操作数dab具有.f16.b16类型。对于 .f16x2指令类型,操作数dab具有.b32类型。对于.bf16 指令类型,操作数dab具有.b16类型。对于.bf16x2指令类型, 操作数dab具有.b32类型。

语义

if (type == f16 || type == bf16) {
    d = a + b;
} else if (type == f16x2 || type == bf16x2) {
    fA[0] = a[0:15];
    fA[1] = a[16:31];
    fB[0] = b[0:15];
    fB[1] = b[16:31];
    for (i = 0; i < 2; i++) {
         d[i] = fA[i] + fB[i];
    }
}

注意事项

舍入修饰符:

.rn

尾数最低有效位向最近的偶数舍入

舍入修饰符的默认值为.rn。请注意,带有显式舍入修饰符的add指令会被代码优化器保守处理。而没有舍入修饰符的add指令默认采用向最近偶数舍入,并可能被代码优化器积极优化。特别是,没有舍入修饰符的mul/add序列可能会被优化为目标设备上的融合乘加指令。

Subnormal numbers:

默认情况下支持次正规数。 add.ftz.{f16, f16x2} 会将次正规输入和结果刷新为保留符号的零。

Saturation modifier:

add.sat.{f16, f16x2} 将结果限制在[0.0, 1.0]范围内。NaN结果会被清零为+0.0f

PTX ISA 说明

在PTX ISA版本4.2中引入。

add{.rnd}.bf16add{.rnd}.bf16x2 在PTX ISA 7.8版本中引入。

目标ISA注意事项

需要 sm_53 或更高版本。

add{.rnd}.bf16add{.rnd}.bf16x2 需要 sm_90 或更高版本。

示例

// scalar f16 additions
add.f16        d0, a0, b0;
add.rn.f16     d1, a1, b1;
add.bf16       bd0, ba0, bb0;
add.rn.bf16    bd1, ba1, bb1;

// SIMD f16 addition
cvt.rn.f16.f32 h0, f0;
cvt.rn.f16.f32 h1, f1;
cvt.rn.f16.f32 h2, f2;
cvt.rn.f16.f32 h3, f3;
mov.b32  p1, {h0, h1};   // pack two f16 to 32bit f16x2
mov.b32  p2, {h2, h3};   // pack two f16 to 32bit f16x2
add.f16x2  p3, p1, p2;   // SIMD f16x2 addition

// SIMD bf16 addition
cvt.rn.bf16x2.f32 p4, f4, f5; // Convert two f32 into packed bf16x2
cvt.rn.bf16x2.f32 p5, f6, f7; // Convert two f32 into packed bf16x2
add.bf16x2  p6, p4, p5;       // SIMD bf16x2 addition

// SIMD fp16 addition
ld.global.b32   f0, [addr];     // load 32 bit which hold packed f16x2
ld.global.b32   f1, [addr + 4]; // load 32 bit which hold packed f16x2
add.f16x2       f2, f0, f1;     // SIMD f16x2 addition

ld.global.b32   f3, [addr + 8];  // load 32 bit which hold packed bf16x2
ld.global.b32   f4, [addr + 12]; // load 32 bit which hold packed bf16x2
add.bf16x2      f5, f3, f4;      // SIMD bf16x2 addition

9.7.4.2. 半精度浮点指令:sub

子进程

两个值相减。

语法

sub{.rnd}{.ftz}{.sat}.f16   d, a, b;
sub{.rnd}{.ftz}{.sat}.f16x2 d, a, b;

sub{.rnd}.bf16   d, a, b;
sub{.rnd}.bf16x2 d, a, b;

.rnd = { .rn };

描述

执行减法运算并将结果值写入目标寄存器。

对于.f16x2.bf16x2指令类型,通过从源操作数中提取半字值来形成输入向量。然后并行执行半字操作数的减法运算,最终在目标位置生成.f16x2.bf16x2结果。

对于.f16指令类型,操作数dab具有.f16.b16类型。对于 .f16x2指令类型,操作数dab具有.b32类型。对于.bf16 指令类型,操作数dab具有.b16类型。对于.bf16x2指令类型, 操作数dab具有.b32类型。

语义

if (type == f16 || type == bf16) {
    d = a - b;
} else if (type == f16x2 || type == bf16x2) {
    fA[0] = a[0:15];
    fA[1] = a[16:31];
    fB[0] = b[0:15];
    fB[1] = b[16:31];
    for (i = 0; i < 2; i++) {
         d[i] = fA[i] - fB[i];
    }
}

注意事项

舍入修饰符:

.rn

尾数最低有效位向最近的偶数舍入

舍入修饰符的默认值为.rn。请注意,带有显式舍入修饰符的sub指令会被代码优化器保守处理。而未指定舍入修饰符的sub指令默认采用向最近偶数舍入方式,并可能被代码优化器进行激进优化。特别地,未指定舍入修饰符的mul/sub指令序列可能会被优化为目标设备上的融合乘加指令。

Subnormal numbers:

默认情况下支持次正规数。 sub.ftz.{f16, f16x2} 会将次正规输入和结果刷新为保留符号的零。

Saturation modifier:

sub.sat.{f16, f16x2} 将结果限制在[0.0, 1.0]范围内。NaN结果会被刷新为+0.0f

PTX ISA 说明

在PTX ISA版本4.2中引入。

sub{.rnd}.bf16sub{.rnd}.bf16x2 在PTX ISA 7.8版本中引入。

目标ISA注意事项

需要 sm_53 或更高版本。

sub{.rnd}.bf16sub{.rnd}.bf16x2 需要 sm_90 或更高版本。

示例

// scalar f16 subtractions
sub.f16        d0, a0, b0;
sub.rn.f16     d1, a1, b1;
sub.bf16       bd0, ba0, bb0;
sub.rn.bf16    bd1, ba1, bb1;

// SIMD f16 subtraction
cvt.rn.f16.f32 h0, f0;
cvt.rn.f16.f32 h1, f1;
cvt.rn.f16.f32 h2, f2;
cvt.rn.f16.f32 h3, f3;
mov.b32  p1, {h0, h1};   // pack two f16 to 32bit f16x2
mov.b32  p2, {h2, h3};   // pack two f16 to 32bit f16x2
sub.f16x2  p3, p1, p2;   // SIMD f16x2 subtraction

// SIMD bf16 subtraction
cvt.rn.bf16x2.f32 p4, f4, f5; // Convert two f32 into packed bf16x2
cvt.rn.bf16x2.f32 p5, f6, f7; // Convert two f32 into packed bf16x2
sub.bf16x2  p6, p4, p5;       // SIMD bf16x2 subtraction

// SIMD fp16 subtraction
ld.global.b32   f0, [addr];     // load 32 bit which hold packed f16x2
ld.global.b32   f1, [addr + 4]; // load 32 bit which hold packed f16x2
sub.f16x2       f2, f0, f1;     // SIMD f16x2 subtraction

// SIMD bf16 subtraction
ld.global.b32   f3, [addr + 8];  // load 32 bit which hold packed bf16x2
ld.global.b32   f4, [addr + 12]; // load 32 bit which hold packed bf16x2
sub.bf16x2      f5, f3, f4;      // SIMD bf16x2 subtraction

9.7.4.3. 半精度浮点指令:乘法

mul

将两个值相乘。

语法

mul{.rnd}{.ftz}{.sat}.f16   d, a, b;
mul{.rnd}{.ftz}{.sat}.f16x2 d, a, b;

mul{.rnd}.bf16   d, a, b;
mul{.rnd}.bf16x2 d, a, b;

.rnd = { .rn };

描述

执行乘法运算并将结果值写入目标寄存器。

对于.f16x2.bf16x2指令类型,通过从源操作数中提取半字值来形成输入向量。然后并行执行半字操作数的乘法运算,最终在目标位置生成.f16x2.bf16x2结果。

对于.f16指令类型,操作数dab具有.f16.b16类型。对于 .f16x2指令类型,操作数dab具有.b32类型。对于.bf16 指令类型,操作数dab具有.b16类型。对于.bf16x2指令类型, 操作数dab具有.b32类型。

语义

if (type == f16 || type == bf16) {
    d = a * b;
} else if (type == f16x2 || type == bf16x2) {
    fA[0] = a[0:15];
    fA[1] = a[16:31];
    fB[0] = b[0:15];
    fB[1] = b[16:31];
    for (i = 0; i < 2; i++) {
         d[i] = fA[i] * fB[i];
    }
}

注意事项

舍入修饰符:

.rn

尾数最低有效位向最近的偶数舍入

舍入修饰符的默认值为.rn。请注意,带有显式舍入修饰符的mul指令会被代码优化器保守处理。而没有舍入修饰符的mul指令默认采用向最近偶数舍入方式,并可能被代码优化器积极优化。特别地,不带舍入修饰符的mul/addmul/sub指令序列可能会被优化为目标设备上的融合乘加指令。

Subnormal numbers:

默认情况下支持次正规数。 mul.ftz.{f16, f16x2} 会将次正规输入和结果刷新为保留符号的零。

Saturation modifier:

mul.sat.{f16, f16x2} 将结果限制在[0.0, 1.0]范围内。NaN结果会被清零为+0.0f

PTX ISA 说明

在PTX ISA版本4.2中引入。

mul{.rnd}.bf16mul{.rnd}.bf16x2 在PTX ISA 7.8版本中引入。

目标ISA注意事项

需要 sm_53 或更高版本。

mul{.rnd}.bf16mul{.rnd}.bf16x2 需要 sm_90 或更高版本。

示例

// scalar f16 multiplications
mul.f16        d0, a0, b0;
mul.rn.f16     d1, a1, b1;
mul.bf16       bd0, ba0, bb0;
mul.rn.bf16    bd1, ba1, bb1;

// SIMD f16 multiplication
cvt.rn.f16.f32 h0, f0;
cvt.rn.f16.f32 h1, f1;
cvt.rn.f16.f32 h2, f2;
cvt.rn.f16.f32 h3, f3;
mov.b32  p1, {h0, h1};   // pack two f16 to 32bit f16x2
mov.b32  p2, {h2, h3};   // pack two f16 to 32bit f16x2
mul.f16x2  p3, p1, p2;   // SIMD f16x2 multiplication

// SIMD bf16 multiplication
cvt.rn.bf16x2.f32 p4, f4, f5; // Convert two f32 into packed bf16x2
cvt.rn.bf16x2.f32 p5, f6, f7; // Convert two f32 into packed bf16x2
mul.bf16x2  p6, p4, p5;       // SIMD bf16x2 multiplication

// SIMD fp16 multiplication
ld.global.b32   f0, [addr];     // load 32 bit which hold packed f16x2
ld.global.b32   f1, [addr + 4]; // load 32 bit which hold packed f16x2
mul.f16x2       f2, f0, f1;     // SIMD f16x2 multiplication

// SIMD bf16 multiplication
ld.global.b32   f3, [addr + 8];  // load 32 bit which hold packed bf16x2
ld.global.b32   f4, [addr + 12]; // load 32 bit which hold packed bf16x2
mul.bf16x2      f5, f3, f4;      // SIMD bf16x2 multiplication

9.7.4.4. 半精度浮点指令:fma

fma

融合乘加运算

语法

fma.rnd{.ftz}{.sat}.f16     d, a, b, c;
fma.rnd{.ftz}{.sat}.f16x2   d, a, b, c;
fma.rnd{.ftz}.relu.f16      d, a, b, c;
fma.rnd{.ftz}.relu.f16x2    d, a, b, c;
fma.rnd{.relu}.bf16         d, a, b, c;
fma.rnd{.relu}.bf16x2       d, a, b, c;
fma.rnd.oob.{relu}.type     d, a, b, c;

.rnd = { .rn };

描述

执行融合乘加运算,中间乘积和加法运算不会损失精度。

对于.f16x2.bf16x2指令类型,通过从源操作数中提取半字值来形成输入向量。然后并行操作这些半字操作数,以在目标中生成.f16x2.bf16x2结果。

对于.f16指令类型,操作数dabc具有.f16.b16类型。对于.f16x2指令类型,操作数dabc具有.b32类型。对于.bf16指令类型,操作数dabc具有.b16类型。对于.bf16x2指令类型,操作数dabc具有.b32类型。

语义

if (type == f16 || type == bf16) {
    d = a * b + c;
} else if (type == f16x2 || type == bf16x2) {
    fA[0] = a[0:15];
    fA[1] = a[16:31];
    fB[0] = b[0:15];
    fB[1] = b[16:31];
    fC[0] = c[0:15];
    fC[1] = c[16:31];
    for (i = 0; i < 2; i++) {
         d[i] = fA[i] * fB[i] + fC[i];
    }
}

注意事项

舍入修饰符(默认为 .rn):

.rn

尾数最低有效位向最近的偶数舍入

Subnormal numbers:

默认情况下支持次正规数。 fma.ftz.{f16, f16x2} 会将次正规输入和结果刷新为保留符号的零。

Saturation modifier:

fma.sat.{f16, f16x2} 将结果限制在[0.0, 1.0]范围内。NaN结果会被清零为+0.0ffma.relu.{f16, f16x2, bf16, bf16x2} 如果结果为负则限制为0。NaN结果会被转换为标准NaN

Out Of Bounds modifier:

fma.oob.{f16, f16x2, bf16, bf16x2} 如果任一操作数是OOB NaN(定义见Tensors)值,则将结果钳制为0。对于每个SIMD操作,都会独立执行对特殊NaN值的检测,并将结果强制设为+0.0。

PTX ISA 说明

在PTX ISA版本4.2中引入。

fma.relu.{f16, f16x2}fma{.relu}.{bf16, bf16x2} 在PTX ISA 7.0版本中引入。

支持PTX ISA版本8.1中引入的修饰符.oob

目标ISA注意事项

需要 sm_53 或更高版本。

fma.relu.{f16, f16x2}fma{.relu}.{bf16, bf16x2} 需要 sm_80 或更高版本。

fma{.oob}.{f16, f16x2, bf16, bf16x2} 需要 sm_90 或更高版本。

示例

// scalar f16 fused multiply-add
fma.rn.f16         d0, a0, b0, c0;
fma.rn.f16         d1, a1, b1, c1;
fma.rn.relu.f16    d1, a1, b1, c1;
fma.rn.oob.f16      d1, a1, b1, c1;
fma.rn.oob.relu.f16 d1, a1, b1, c1;

// scalar bf16 fused multiply-add
fma.rn.bf16        d1, a1, b1, c1;
fma.rn.relu.bf16   d1, a1, b1, c1;
fma.rn.oob.bf16       d1, a1, b1, c1;
fma.rn.oob.relu.bf16  d1, a1, b1, c1;

// SIMD f16 fused multiply-add
cvt.rn.f16.f32 h0, f0;
cvt.rn.f16.f32 h1, f1;
cvt.rn.f16.f32 h2, f2;
cvt.rn.f16.f32 h3, f3;
mov.b32  p1, {h0, h1}; // pack two f16 to 32bit f16x2
mov.b32  p2, {h2, h3}; // pack two f16 to 32bit f16x2
fma.rn.f16x2  p3, p1, p2, p2;   // SIMD f16x2 fused multiply-add
fma.rn.relu.f16x2  p3, p1, p2, p2; // SIMD f16x2 fused multiply-add with relu saturation mode
fma.rn.oob.f16x2  p3, p1, p2, p2; // SIMD f16x2 fused multiply-add with oob modifier
fma.rn.oob.relu.f16x2 p3, p1, p2, p2; // SIMD f16x2 fused multiply-add with oob modifier and relu saturation mode

// SIMD fp16 fused multiply-add
ld.global.b32   f0, [addr];     // load 32 bit which hold packed f16x2
ld.global.b32   f1, [addr + 4]; // load 32 bit which hold packed f16x2
fma.rn.f16x2    f2, f0, f1, f1; // SIMD f16x2 fused multiply-add

// SIMD bf16 fused multiply-add
fma.rn.bf16x2       f2, f0, f1, f1; // SIMD bf16x2 fused multiply-add
fma.rn.relu.bf16x2  f2, f0, f1, f1; // SIMD bf16x2 fused multiply-add with relu saturation mode
fma.rn.oob.bf16x2  f2, f0, f1, f1; // SIMD bf16x2 fused multiply-add with oob modifier
fma.rn.oob.relu.bf16x2  f2, f0, f1, f1; // SIMD bf16x2 fused multiply-add with oob modifier and relu saturation mode

9.7.4.5. 半精度浮点指令:neg

neg

算术取反。

语法

neg{.ftz}.f16    d, a;
neg{.ftz}.f16x2  d, a;
neg.bf16         d, a;
neg.bf16x2       d, a;

描述

a的符号取反,并将结果存储在d中。

对于.f16x2.bf16x2指令类型,通过从源操作数提取半字值来形成输入向量。然后并行对半字操作数进行取反操作,最终在目标位置生成.f16x2.bf16x2结果。

对于.f16指令类型,操作数da具有.f16.b16类型。对于 .f16x2指令类型,操作数da具有.b32类型。对于.bf16指令 类型,操作数da具有.b16类型。对于.bf16x2指令类型,操作数da具有.b32类型。

语义

if (type == f16 || type == bf16) {
    d = -a;
} else if (type == f16x2 || type == bf16x2) {
    fA[0] = a[0:15];
    fA[1] = a[16:31];
    for (i = 0; i < 2; i++) {
         d[i] = -fA[i];
    }
}

注意事项

Subnormal numbers:

默认情况下支持次正规数。 neg.ftz.{f16, f16x2} 会将次正规输入和结果刷新为保留符号的零。

NaN 输入会产生未指定的 NaN。未来的实现可能会遵循 IEEE 754 标准,保留有效载荷并仅修改符号位。

PTX ISA 说明

在PTX ISA 6.0版本中引入。

neg.bf16neg.bf16x2 在PTX ISA 7.0中引入。

目标ISA注意事项

需要 sm_53 或更高版本。

neg.bf16neg.bf16x2 需要架构 sm_80 或更高版本。

示例

neg.ftz.f16  x,f0;
neg.bf16     x,b0;
neg.bf16x2   x1,b1;

9.7.4.6. 半精度浮点指令:abs

abs

绝对值

语法

abs{.ftz}.f16    d, a;
abs{.ftz}.f16x2  d, a;
abs.bf16         d, a;
abs.bf16x2       d, a;

描述

a 的绝对值并将结果存储在 d 中。

对于.f16x2.bf16x2指令类型,通过从源操作数提取半字值来形成输入向量。然后并行计算半字操作数的绝对值,以在目标中生成.f16x2.bf16x2结果。

对于.f16指令类型,操作数da具有.f16.b16类型。对于 .f16x2指令类型,操作数da具有.f16x2.b32类型。对于 .bf16指令类型,操作数da具有.b16类型。对于.bf16x2指令 类型,操作数da具有.b32类型。

语义

if (type == f16 || type == bf16) {
    d = |a|;
} else if (type == f16x2 || type == bf16x2) {
    fA[0] = a[0:15];
    fA[1] = a[16:31];
    for (i = 0; i < 2; i++) {
         d[i] = |fA[i]|;
    }
}

注意事项

Subnormal numbers:

默认情况下支持次正规数。 abs.ftz.{f16, f16x2} 会将次正规输入和结果刷新为保留符号的零。

NaN 输入会产生未指定的 NaN。未来的实现可能会遵循 IEEE 754 标准,保留有效载荷并仅修改符号位。

PTX ISA 说明

在PTX ISA 6.5版本中引入。

abs.bf16abs.bf16x2 在PTX ISA 7.0中引入。

目标ISA注意事项

需要 sm_53 或更高版本。

abs.bf16abs.bf16x2 需要架构 sm_80 或更高版本。

示例

abs.ftz.f16  x,f0;
abs.bf16     x,b0;
abs.bf16x2   x1,b1;

9.7.4.7. 半精度浮点指令:min

最小值

找出两个值中的最小值。

语法

min{.ftz}{.NaN}{.xorsign.abs}.f16      d, a, b;
min{.ftz}{.NaN}{.xorsign.abs}.f16x2    d, a, b;
min{.NaN}{.xorsign.abs}.bf16           d, a, b;
min{.NaN}{.xorsign.abs}.bf16x2         d, a, b;

描述

ab中的最小值存储在d中。

对于.f16x2.bf16x2指令类型,输入向量由源操作数的半字值组成。然后并行处理这些半字操作数,将.f16x2.bf16x2结果存储到目标位置。

对于.f16指令类型,操作数da具有.f16.b16类型。对于 .f16x2指令类型,操作数da具有.f16x2.b32类型。对于 .bf16指令类型,操作数da具有.b16类型。对于.bf16x2指令 类型,操作数da具有.b32类型。

如果指定了.NaN修饰符,那么当任一输入为NaN时,结果将是规范的NaN

如果指定了.abs修饰符,目标操作数d的幅度将是两个输入参数绝对值中的较小值。

如果指定了.xorsign修饰符,目标d的符号位将等于两个输入符号位的异或结果。

修饰符 .abs.xorsign 必须同时指定,且 .xorsign 在应用 .abs 操作前会考虑两个输入值的符号位。

如果min的结果是NaN,那么.xorsign.abs修饰符将被忽略。

语义

if (type == f16 || type == bf16) {
    if (.xorsign) {
        xorsign = getSignBit(a) ^ getSignBit(b);
        if (.abs) {
            a = |a|;
            b = |b|;
        }
    }
    if (isNaN(a) && isNaN(b))              d = NaN;
    if (.NaN && (isNaN(a) || isNaN(b)))    d = NaN;
    else if (isNaN(a))                     d = b;
    else if (isNaN(b))                     d = a;
    else                                   d = (a < b) ? a : b;
    if (.xorsign && !isNaN(d)) {
         setSignBit(d, xorsign);
    }
} else if (type == f16x2 || type == bf16x2) {
    fA[0] = a[0:15];
    fA[1] = a[16:31];
    fB[0] = b[0:15];
    fB[1] = b[16:31];
    for (i = 0; i < 2; i++) {
        if (.xorsign) {
            xorsign = getSignBit(fA[i]) ^ getSignBit(fB[i]);
            if (.abs) {
               fA[i] = |fA[i]|;
               fB[i] = |fB[i]|;
           }
        }
        if (isNaN(fA[i]) && isNaN(fB[i]))              d[i] = NaN;
        if (.NaN && (isNaN(fA[i]) || isNaN(fB[i])))    d[i] = NaN;
        else if (isNaN(fA[i]))                         d[i] = fB[i];
        else if (isNaN(fB[i]))                         d[i] = fA[i];
        else                                           d[i] = (fA[i] < fB[i]) ? fA[i] : fB[i];
        if (.xorsign && !isNaN(d[i])) {
            setSignBit(d[i], xorsign);
        }
    }
}

注意事项

Subnormal numbers:

默认情况下支持次正规数。 min.ftz.{f16, f16x2} 会将次正规输入和结果刷新为保留符号的零。

如果两个输入值均为0.0,则+0.0 > -0.0。

PTX ISA 说明

在PTX ISA版本7.0中引入。

min.xorsign 在PTX ISA 7.2版本中引入。

目标ISA注意事项

需要 sm_80 或更高版本。

min.xorsign.abs 功能需要 sm_86 或更高版本支持。

示例

min.ftz.f16       h0,h1,h2;
min.f16x2         b0,b1,b2;
// SIMD fp16 min with .NaN
min.NaN.f16x2     b0,b1,b2;
min.bf16          h0, h1, h2;
// SIMD bf16 min with NaN
min.NaN.bf16x2    b0, b1, b2;
// scalar bf16 min with xorsign.abs
min.xorsign.abs.bf16 Rd, Ra, Rb

9.7.4.8. 半精度浮点指令:max

最大值

找出两个值中的最大值。

语法

max{.ftz}{.NaN}{.xorsign.abs}.f16      d, a, b;
max{.ftz}{.NaN}{.xorsign.abs}.f16x2    d, a, b;
max{.NaN}{.xorsign.abs}.bf16           d, a, b;
max{.NaN}{.xorsign.abs}.bf16x2         d, a, b;

描述

ab中的最大值存储到d中。

对于.f16x2.bf16x2指令类型,输入向量由源操作数中的半字值组成。然后并行处理这些半字操作数,将.f16x2.bf16x2结果存储到目标位置。

对于.f16指令类型,操作数da具有.f16.b16类型。对于 .f16x2指令类型,操作数da具有.f16x2.b32类型。对于 .bf16指令类型,操作数da具有.b16类型。对于.bf16x2指令 类型,操作数da具有.b32类型。

如果指定了.NaN修饰符,当任一输入为NaN时,结果将是规范的NaN

如果指定了.abs修饰符,目标操作数d的数值将是两个输入参数绝对值的最大值。

如果指定了.xorsign修饰符,目标d的符号位将等于两个输入符号位的异或结果。

修饰符 .abs.xorsign 必须同时指定,且 .xorsign 在应用 .abs 操作前会考虑两个输入值的符号位。

如果max的结果是NaN,那么.xorsign.abs修饰符将被忽略。

语义

if (type == f16 || type == bf16) {
    if (.xorsign) {
        xorsign = getSignBit(a) ^ getSignBit(b);
        if (.abs) {
            a = |a|;
            b = |b|;
        }
    }
    if (isNaN(a) && isNaN(b))              d = NaN;
    if (.NaN && (isNaN(a) || isNaN(b)))    d = NaN;
    else if (isNaN(a))                     d = b;
    else if (isNaN(b))                     d = a;
    else                                   d = (a > b) ? a : b;
    if (.xorsign && !isNaN(d)) {
         setSignBit(d, xorsign);
    }
} else if (type == f16x2 || type == bf16x2) {
    fA[0] = a[0:15];
    fA[1] = a[16:31];
    fB[0] = b[0:15];
    fB[1] = b[16:31];
    for (i = 0; i < 2; i++) {
        if (.xorsign) {
            xorsign = getSignBit(fA[i]) ^ getSignBit(fB[i]);
            if (.abs) {
                fA[i] = |fA[i]|;
                fB[i] = |fB[i]|;
            }
        }
        if (isNaN(fA[i]) && isNaN(fB[i]))              d[i] = NaN;
        if (.NaN && (isNaN(fA[i]) || isNaN(fB[i])))    d[i] = NaN;
        else if (isNaN(fA[i]))                         d[i] = fB[i];
        else if (isNaN(fB[i]))                         d[i] = fA[i];
        else                                           d[i] = (fA[i] > fB[i]) ? fA[i] : fB[i];
        if (.xorsign && !isNaN(fA[i])) {
            setSignBit(d[i], xorsign);
        }
    }
}

注意事项

Subnormal numbers:

默认情况下支持次正规数。 max.ftz.{f16, f16x2} 会将次正规输入和结果刷新为保留符号的零。

如果两个输入值均为0.0,则+0.0 > -0.0。

PTX ISA 说明

在PTX ISA版本7.0中引入。

max.xorsign.abs 在PTX ISA 7.2版本中引入。

目标ISA注意事项

需要 sm_80 或更高版本。

max.xorsign.abs 功能需要 sm_86 或更高版本支持。

示例

max.ftz.f16       h0,h1,h2;
max.f16x2         b0,b1,b2;
// SIMD fp16 max with NaN
max.NaN.f16x2     b0,b1,b2;
// scalar f16 max with xorsign.abs
max.xorsign.abs.f16 Rd, Ra, Rb;
max.bf16          h0, h1, h2;
// scalar bf16 max and NaN
max.NaN.bf16x2    b0, b1, b2;
// SIMD bf16 max with xorsign.abs
max.xorsign.abs.bf16x2 Rd, Ra, Rb;

9.7.4.9. 半精度浮点指令:tanh

tanh

计算给定值(以弧度为单位)的双曲正切值

语法

tanh.approx.type d, a;

.type = {.f16, .f16x2, .bf16, .bf16x2}

描述

计算a的双曲正切值。

操作数 da 的类型由 .type 指定。

对于.f16x2.bf16x2指令类型,每个半字操作数都是并行运算的,结果会被适当地打包成.f16x2.bf16x2格式。

语义

if (.type == .f16 || .type == .bf16) {
  d = tanh(a)
} else if (.type == .f16x2 || .type == .bf16x2) {
  fA[0] = a[0:15];
  fA[1] = a[16:31];
  d[0] = tanh(fA[0])
  d[1] = tanh(fA[1])
}

注意事项

tanh.approx.{f16, f16x2, bf16, bf16x2} 实现了目标格式中的近似双曲正切函数。

针对各种极端情况输入,tanh函数的结果如下:

输入

结果

-Inf

-1.0

-0.0

-0.0

+0.0

+0.0

+Inf

1.0

NaN

NaN

.f16 类型的最大绝对误差为 2-10.987。.bf16 类型的最大绝对误差为 2-8。

支持次正规数。

PTX ISA 说明

在PTX ISA版本7.0中引入。

tanh.approx.{bf16/bf16x2} 在PTX ISA 7.8版本中引入。

目标ISA注意事项

需要 sm_75 或更高版本。

tanh.approx.{bf16/bf16x2} 需要 sm_90 或更高版本。

示例

tanh.approx.f16    h1, h0;
tanh.approx.f16x2  hd1, hd0;
tanh.approx.bf16   b1, b0;
tanh.approx.bf16x2 hb1, hb0;

9.7.4.10. 半精度浮点指令:ex2

示例2

计算输入的以2为底的指数。

语法

ex2.approx.atype     d, a;
ex2.approx.ftz.btype d, a;

.atype = { .f16,  .f16x2}
.btype = { .bf16, .bf16x2}

描述

计算2的a次方。

操作数 da 的类型由 .type 指定。

对于.f16x2.bf16x2指令类型,每个半字操作数都是并行运算的,结果会被适当地打包成.f16x2.bf16x2格式。

语义

if (.type == .f16 || .type == .bf16) {
  d = 2 ^ a
} else if (.type == .f16x2 || .type == .bf16x2) {
  fA[0] = a[0:15];
  fA[1] = a[16:31];
  d[0] = 2 ^ fA[0]
  d[1] = 2 ^ fA[1]
}

注意事项

ex2.approx.{f16, f16x2, bf16, bf16x2} 实现了对2a的快速近似计算。

对于.f16类型,支持次正规输入。 ex2.approx.ftz.bf16会将次正规输入和结果刷新为保留符号的零值。

针对各种边界情况输入,ex2.approx.ftz.bf16 的结果如下:

输入

结果

-Inf

+0.0

-次正规数

+1.0

-0.0

+1.0

+0.0

+1.0

+次正规数

+1.0

+Inf

+Inf

NaN

NaN

针对各种极端情况输入,ex2.approx.f16的结果如下:

输入

结果

-无穷大

+0.0

-0.0

+1.0

+0.0

+1.0

+Inf

+Inf

NaN

NaN

.f16 类型的最大相对误差为 2-9.9。.bf16 类型的最大相对误差为 2-7。

PTX ISA 说明

在PTX ISA版本7.0中引入。

ex2.approx.ftz.{bf16/bf16x2} 在PTX ISA 7.8版本中引入。

目标ISA注意事项

需要 sm_75 或更高版本。

ex2.approx.ftz.{bf16/bf16x2} 需要 sm_90 或更高版本。

示例

ex2.approx.f16         h1, h0;
ex2.approx.f16x2       hd1, hd0;
ex2.approx.ftz.bf16    b1, b2;
ex2.approx.ftz.bf16x2  hb1, hb2;

9.7.5. 混合精度浮点指令

混合精度浮点指令可对不同精度的浮点数据进行操作。在执行指定运算前,需要将不同精度的操作数进行转换,使所有指令操作数都能以统一的浮点精度表示。用于存储特定操作数的寄存器变量取决于指令类型的组合。有关具体数据类型应使用的寄存器操作数详情,请参阅基础类型替代浮点数据格式

混合精度浮点指令包括:

  • add

  • sub

  • fma

混合精度的addsubfma操作支持将结果饱和到[0.0, 1.0]范围内,并将NaN值清零为正零。

9.7.5.1. 混合精度浮点指令:加法

add

将两个值相加。

语法

add{.rnd}{.sat}.f32.atype  d, a, c;

.atype = { .f16, .bf16};
.rnd   = { .rn, .rz, .rm, .rp };

描述

将输入操作数 a.atype 类型转换为 .f32 类型。转换后的值随后用于加法运算。结果值存储在目标操作数 d 中。

语义

d = convert(a) + c;

注意事项

舍入修饰符:

.rn

尾数最低有效位向最近的偶数舍入

.rz

尾数最低有效位向零舍入

.rm

尾数最低有效位向负无穷方向舍入

.rp

尾数最低有效位向正无穷方向舍入

舍入修饰符的默认值为.rn。请注意,带有显式舍入修饰符的add指令会被代码优化器保守处理。而没有舍入修饰符的add指令默认采用向最近偶数舍入,并可能被代码优化器积极优化。特别是,没有舍入修饰符的mul/add序列可能会被优化为目标设备上的融合乘加指令。

Subnormal numbers:

默认情况下,支持次正规数。

Saturation modifier:

add.sat 将结果限制在[0.0, 1.0]范围内。NaN结果会被清零为+0.0f

PTX ISA 说明

add.f32.{f16/bf16} 在PTX ISA版本8.6中引入。

目标ISA注意事项

add.f32.{f16/bf16} 需要 sm_100 或更高版本。

示例

.reg .f32 fc, fd;
.reg .b16 ba;
add.rz.f32.bf16.sat   fd, fa, fc;

9.7.5.2. 混合精度浮点指令: sub

子进程

从一个值中减去另一个值。

语法

sub{.rnd}{.sat}.f32.atype  d, a, c;

.atype = { .f16, .bf16};
.rnd   = { .rn, .rz, .rm, .rp };

描述

将输入操作数 a.atype 类型转换为 .f32 类型。转换后的值随后用于减法运算。结果值存储在目标操作数 d 中。

语义

d = convert(a) - c;

注意事项

舍入修饰符:

.rn

尾数最低有效位向最近的偶数舍入

.rz

尾数最低有效位向零舍入

.rm

尾数最低有效位向负无穷方向舍入

.rp

尾数最低有效位向正无穷方向舍入

舍入修饰符的默认值为.rn。请注意,带有显式舍入修饰符的sub指令会被代码优化器保守处理。而没有舍入修饰符的sub指令默认采用向最近偶数舍入方式,并可能被代码优化器积极优化。特别是,没有舍入修饰符的mul/sub序列可能会被优化为目标设备上的融合乘加指令。

Subnormal numbers:

默认情况下,支持次正规数。

Saturation modifier:

sub.sat 将结果限制在[0.0, 1.0]范围内。NaN结果会被清零为+0.0f

PTX ISA 说明

sub.f32.{f16/bf16} 在PTX ISA 8.6版本中引入。

目标ISA注意事项

sub.f32.{f16/bf16} 需要 sm_100 或更高版本。

示例

.reg .f32 fc, fd;
.reg .f16 ha;
sub.rz.f32.f16.sat   fd, ha, fc;

9.7.5.3. 混合精度浮点指令: fma

fma

融合乘加运算。

语法

fma.rnd{.ftz}{.sat}.f32.abtype  d, a, b, c;

.abtype = { .f16, .bf16};
.rnd    = { .rn, .rz, .rm, .rp };

描述

将输入操作数 ab.atype 类型转换为 .f32 类型。转换后的值随后用于执行融合乘加运算,中间结果和加法运算不会损失精度。最终结果存储在目标操作数 d 中。

语义

d = convert(a) * convert(b) + c;

注意事项

fma.f32.{f16/bf16} 以无限精度计算 ab 的乘积,然后以无限精度将 c 加到该乘积上。最终结果会根据 .rnd 指定的舍入模式舍入为单精度浮点数。

舍入修饰符(无默认值):

.rn

尾数最低有效位向最近的偶数舍入

.rz

尾数最低有效位向零舍入

.rm

尾数最低有效位向负无穷方向舍入

.rp

尾数最低有效位向正无穷方向舍入

Subnormal numbers:

默认情况下,支持次正规数。

Saturation modifier:

fma.sat 将结果限制在[0.0, 1.0]范围内。NaN结果会被清零为+0.0f

PTX ISA 说明

fma.f32.{f16/bf16} 在PTX ISA版本8.6中引入。

目标ISA注意事项

fma.f32.{f16/bf16} 需要 sm_100 或更高版本。

示例

.reg .f32 fc, fd;
.reg .f16 ha, hb;
fma.rz.sat.f32.f16.sat   fd, ha, hb, fc;

9.7.6. 比较与选择指令

比较选择指令如下:

  • set

  • setp

  • selp

  • slct

与单精度浮点指令类似,setsetpslct指令在sm_20及更高版本目标架构中支持次正规数,而对于sm_1x目标架构会将单精度次正规输入刷新为保留符号的零值。可选的.ftz修饰符通过将次正规输入和结果刷新为保留符号的零值(无论目标架构如何),提供了与sm_1x目标架构的向后兼容性。

9.7.6.1. 比较与选择指令:set

设置

使用关系运算符比较两个数值,并可选地通过应用布尔运算符将此结果与谓词值组合。

语法

set.CmpOp{.ftz}.dtype.stype         d, a, b;
set.CmpOp.BoolOp{.ftz}.dtype.stype  d, a, b, {!}c;

.CmpOp  = { eq, ne, lt, le, gt, ge, lo, ls, hi, hs,
            equ, neu, ltu, leu, gtu, geu, num, nan };
.BoolOp = { and, or, xor };
.dtype  = { .u32, .s32, .f32 };
.stype  = { .b16, .b32, .b64,
            .u16, .u32, .u64,
            .s16, .s32, .s64,
                  .f32, .f64 };

描述

比较两个数值,并可选地通过应用布尔运算符将结果与另一个谓词值组合。如果结果为True,则对于浮点目标类型写入1.0f,对于整数目标类型写入0xffffffff。否则写入0x00000000

操作数 d 的类型为 .dtype;操作数 ab 的类型为 .stype;操作数 c 的类型为 .pred

语义

t = (a CmpOp b) ? 1 : 0;
if (isFloat(dtype))
    d = BoolOp(t, c) ? 1.0f : 0x00000000;
else
    d = BoolOp(t, c) ? 0xffffffff : 0x00000000;

整数说明

有符号和无符号比较运算符包括eqneltlegtge

对于无符号值,可以使用比较运算符lolshihs分别表示低于、低于或等于、高于和高于或等于,而不是使用ltlegtge

无类型的位大小比较操作符是 eqne

浮点数注意事项

有序比较运算符包括eqneltlegtge。如果任一操作数是NaN,则结果为False

为了在存在NaN值时辅助比较操作,包含了无序版本: equ, neu, ltu, leu, gtu, geu。如果两个操作数都是数值(非 NaN),那么这些比较结果与其有序对应版本相同。如果任一 操作数是NaN,则这些比较的结果为True

num 当两个操作数都是数值(非NaN)时返回True,而nan当任一操作数为NaN时返回True

次正规数:

sm_20+

默认情况下,支持次正规数。

set.ftz.dtype.f32 将次正规输入刷新为保留符号的零。

sm_1x

set.dtype.f64 支持次正规数。

set.dtype.f32 将次正规输入刷新为保留符号的零值。

修饰符 .ftz 仅适用于 .f32 比较。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

set 使用 .f64 源类型需要 sm_13 或更高版本。

示例

@p  set.lt.and.f32.s32  d,a,b,r;
    set.eq.u32.u32      d,i,n;

9.7.6.2. 比较与选择指令:setp

步骤

使用关系运算符比较两个数值,并(可选地)通过应用布尔运算符将此结果与谓词值组合。

语法

setp.CmpOp{.ftz}.type         p[|q], a, b;
setp.CmpOp.BoolOp{.ftz}.type  p[|q], a, b, {!}c;

.CmpOp  = { eq, ne, lt, le, gt, ge, lo, ls, hi, hs,
            equ, neu, ltu, leu, gtu, geu, num, nan };
.BoolOp = { and, or, xor };
.type   = { .b16, .b32, .b64,
            .u16, .u32, .u64,
            .s16, .s32, .s64,
                  .f32, .f64 };

描述

比较两个值,并通过应用布尔运算符将结果与另一个谓词值组合。该结果被写入第一个目标操作数。使用比较结果的补集计算的另一个相关值被写入第二个目标操作数。

适用于所有数值类型。操作数ab的类型为.type;操作数pqc的类型为.pred。接收符号"_"可用于替代任意一个目标操作数。

语义

t = (a CmpOp b) ? 1 : 0;
p = BoolOp(t, c);
q = BoolOp(!t, c);

整数说明

有符号和无符号比较运算符包括 eq, ne, lt, le, gt, ge

对于无符号值,可以使用比较运算符lolshihs分别表示低于、低于或等于、高于和高于或等于,而不是使用ltlegtge

无类型的位大小比较操作符是 eqne

浮点数注意事项

有序比较运算符包括eqneltlegtge。如果任一操作数为NaN,则结果为False

为了在存在NaN值的情况下辅助比较操作,包含了无序版本: equ, neu, ltu, leu, gtu, geu。如果两个操作数都是数值(非 NaN),那么这些比较结果与其有序对应版本相同。如果任一 操作数是NaN,则这些比较的结果为True

num 当两个操作数都是数值(非NaN)时返回True,而nan当任一操作数为NaN时返回True

次正规数:

sm_20+

默认情况下,支持次正规数。

setp.ftz.dtype.f32 将次正规输入刷新为保留符号的零。

sm_1x

setp.dtype.f64 支持次正规数。

setp.dtype.f32 将次正规输入刷新为保留符号的零。

修饰符 .ftz 仅适用于 .f32 比较。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

setp 使用 .f64 源类型需要 sm_13 或更高版本。

示例

    setp.lt.and.s32  p|q,a,b,r;
@q  setp.eq.u32      p,i,n;

9.7.6.3. 比较与选择指令:selp

自我

根据谓词源操作数的值,在源操作数之间进行选择。

语法

selp.type d, a, b, c;

.type = { .b16, .b32, .b64,
          .u16, .u32, .u64,
          .s16, .s32, .s64,
                .f32, .f64 };

描述

条件选择。如果cTrue,则将a存入d,否则存入b。操作数 dab必须为相同类型。操作数c为谓词条件。

语义

d = (c == 1) ? a : b;

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

selp.f64 需要 sm_13 或更高版本。

示例

    selp.s32  r0,r,g,p;
@q  selp.f32  f0,t,x,xp;

9.7.6.4. 比较与选择指令:slct

slct

根据第三个操作数的符号选择一个源操作数。

语法

slct.dtype.s32        d, a, b, c;
slct{.ftz}.dtype.f32  d, a, b, c;

.dtype = { .b16, .b32, .b64,
           .u16, .u32, .u64,
           .s16, .s32, .s64,
                 .f32, .f64 };

描述

条件选择。如果 c >= 0,则将 a 存入 d,否则将 b 存入 d。操作数 dab 被视为与第一条指令类型相同位宽的数据类型;操作数 c 必须匹配第二条指令类型(.s32.f32)。所选输入会原样复制到输出中。

语义

d = (c >= 0) ? a : b;

浮点数注意事项

对于.f32比较,负零等于零。

次正规数:

sm_20+

默认情况下,支持次正规数。

slct.ftz.dtype.f32 将操作数 c 的次正规值刷新为保留符号的零值,并选择操作数 a

sm_1x

slct.dtype.f32 将操作数 c 的次正规值刷新为保留符号的零,并选择操作数 a

修饰符 .ftz 仅适用于 .f32 比较。

如果操作数 cNaN,则比较是无序的,将选择操作数 b

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

slct.f64 需要 sm_13 或更高版本。

示例

slct.u32.s32  x, y, z, val;
slct.ftz.u64.f32  A, B, C, fval;

9.7.7. 半精度比较指令

比较指令如下:

  • set

  • setp

9.7.7.1. 半精度比较指令:set

设置

使用关系运算符比较两个数值,并可选地通过应用布尔运算符将此结果与谓词值组合。

语法

set.CmpOp{.ftz}.f16.stype            d, a, b;
set.CmpOp.BoolOp{.ftz}.f16.stype     d, a, b, {!}c;

set.CmpOp.bf16.stype                 d, a, b;
set.CmpOp.BoolOp.bf16.stype          d, a, b, {!}c;

set.CmpOp{.ftz}.dtype.f16            d, a, b;
set.CmpOp.BoolOp{.ftz}.dtype.f16     d, a, b, {!}c;
.dtype  = { .u16, .s16, .u32, .s32}

set.CmpOp.dtype.bf16                 d, a, b;
set.CmpOp.BoolOp.dtype.bf16          d, a, b, {!}c;
.dtype  = { .u16, .s16, .u32, .s32}

set.CmpOp{.ftz}.dtype.f16x2          d, a, b;
set.CmpOp.BoolOp{.ftz}.dtype.f16x2   d, a, b, {!}c;
.dtype  = { .f16x2, .u32, .s32}

set.CmpOp.dtype.bf16x2               d, a, b;
set.CmpOp.BoolOp.dtype.bf16x2        d, a, b, {!}c;
.dtype  = { .bf16x2, .u32, .s32}

.CmpOp  = { eq, ne, lt, le, gt, ge,
            equ, neu, ltu, leu, gtu, geu, num, nan };
.BoolOp = { and, or, xor };
.stype  = { .b16, .b32, .b64,
            .u16, .u32, .u64,
            .s16, .s32, .s64,
            .f16, .f32, .f64};

描述

比较两个数值,并可选地通过应用布尔运算符将结果与另一个谓词值组合。

该计算的结果按以下方式写入目标寄存器:

  • 如果结果为True,

    • 0xffffffff 是为目标类型 .u32/.s32 编写的。

    • 0xffff 用于目标类型 .u16/.s16

    • 目标精度浮点格式中的1.0是针对目标类型.f16.bf16编写的。

  • 如果结果为False,

    • 0x0 会被写入所有整数目标类型。

    • 目标精度浮点格式中的0.0被写入目标类型.f16.bf16

如果源类型是.f16x2.bf16x2,那么各个操作的结果会被打包到32位目标操作数中。

操作数 c 的类型为 .pred

语义

if (stype == .f16x2 || stype == .bf16x2) {
    fA[0] = a[0:15];
    fA[1] = a[16:31];
    fB[0] = b[0:15];
    fB[1] = b[16:31];
    t[0]   = (fA[0] CmpOp fB[0]) ? 1 : 0;
    t[1]   = (fA[1] CmpOp fB[1]) ? 1 : 0;
    if (dtype == .f16x2 || stype == .bf16x2) {
        for (i = 0; i < 2; i++) {
            d[i] = BoolOp(t[i], c) ? 1.0 : 0.0;
        }
    } else {
        for (i = 0; i < 2; i++) {
            d[i] = BoolOp(t[i], c) ? 0xffff : 0;
        }
    }
} else if (dtype == .f16 || stype == .bf16) {
    t = (a CmpOp b) ? 1 : 0;
    d = BoolOp(t, c) ? 1.0 : 0.0;
} else  { // Integer destination type
    trueVal = (isU16(dtype) || isS16(dtype)) ?  0xffff : 0xffffffff;
    t = (a CmpOp b) ? 1 : 0;
    d = BoolOp(t, c) ? trueVal : 0;
}

浮点数注意事项

有序比较运算符包括eqneltlegtge。如果任一操作数是NaN,则结果为False

为了在存在NaN值的情况下辅助比较操作,包含了无序版本: equ, neu, ltu, leu, gtu, geu。如果两个操作数都是数值(非 NaN),那么这些比较结果与其有序对应版本相同。如果任一 操作数是NaN,则这些比较的结果为True

num 当两个操作数都是数值(非NaN)时返回True,而nan当任一操作数为NaN时返回True

Subnormal numbers:

默认情况下,支持次正规数。

当指定了.ftz修饰符时,次正规输入和结果会被刷新为保留符号的零值。

PTX ISA 说明

在PTX ISA版本4.2中引入。

set.{u16, u32, s16, s32}.f16set.{u32, s32}.f16x2 是在PTX ISA 6.5版本中引入的。

set.{u16, u32, s16, s32}.bf16, set.{u32, s32, bf16x2}.bf16x2, set.bf16.{s16,u16,f16,b16,s32,u32,f32,b32,s64,u64,f64,b64} 在PTX ISA 7.8版本中引入。

目标ISA注意事项

需要 sm_53 或更高版本。

set.{u16, u32, s16, s32}.bf16, set.{u32, s32, bf16x2}.bf16x2, set.bf16.{s16,u16,f16,b16,s32,u32,f32,b32,s64,u64,f64,b64} 需要 sm_90 或更高版本。

示例

set.lt.and.f16.f16  d,a,b,r;
set.eq.f16x2.f16x2  d,i,n;
set.eq.u32.f16x2    d,i,n;
set.lt.and.u16.f16  d,a,b,r;
set.ltu.or.bf16.f16    d,u,v,s;
set.equ.bf16x2.bf16x2  d,j,m;
set.geu.s32.bf16x2     d,j,m;
set.num.xor.s32.bf16   d,u,v,s;

9.7.7.2. 半精度比较指令:setp

步骤

使用关系运算符比较两个数值,并可选地通过应用布尔运算符将此结果与谓词值组合。

语法

setp.CmpOp{.ftz}.f16           p, a, b;
setp.CmpOp.BoolOp{.ftz}.f16    p, a, b, {!}c;

setp.CmpOp{.ftz}.f16x2         p|q, a, b;
setp.CmpOp.BoolOp{.ftz}.f16x2  p|q, a, b, {!}c;

setp.CmpOp.bf16                p, a, b;
setp.CmpOp.BoolOp.bf16         p, a, b, {!}c;

setp.CmpOp.bf16x2              p|q, a, b;
setp.CmpOp.BoolOp.bf16x2       p|q, a, b, {!}c;

.CmpOp  = { eq, ne, lt, le, gt, ge,
            equ, neu, ltu, leu, gtu, geu, num, nan };
.BoolOp = { and, or, xor };

描述

比较两个值,并通过应用布尔运算符将结果与另一个谓词值组合。该结果将被写入目标操作数。

操作数 c, pq 的类型为 .pred

对于指令类型 .f16,操作数 ab 的类型为 .b16.f16

对于指令类型 .f16x2,操作数 ab 的类型为 .b32

对于指令类型 .bf16,操作数 ab 的类型为 .b16

对于指令类型 .bf16x2,操作数 ab 的类型为 .b32

语义

if (type == .f16 || type == .bf16) {
     t = (a CmpOp b) ? 1 : 0;
     p = BoolOp(t, c);
} else if (type == .f16x2 || type == .bf16x2) {
    fA[0] = a[0:15];
    fA[1] = a[16:31];
    fB[0] = b[0:15];
    fB[1] = b[16:31];
    t[0] = (fA[0] CmpOp fB[0]) ? 1 : 0;
    t[1] = (fA[1] CmpOp fB[1]) ? 1 : 0;
    p = BoolOp(t[0], c);
    q = BoolOp(t[1], c);
}

浮点数注意事项

有序比较运算符包括eqneltlegtge。如果任一操作数是NaN,则结果为False

为了在存在NaN值的情况下辅助比较操作,包含了无序版本: equ, neu, ltu, leu, gtu, geu。如果两个操作数都是数值(非 NaN),那么这些比较结果与其有序对应版本相同。如果任一 操作数是NaN,则这些比较的结果为True

num 当两个操作数都是数值(非NaN)时返回True,而nan当任一操作数为NaN时返回True

Subnormal numbers:

默认情况下,支持次正规数。

setp.ftz.{f16,f16x2} 将次正规输入刷新为保留符号的零。

PTX ISA 说明

在PTX ISA版本4.2中引入。

setp.{bf16/bf16x2} 在PTX ISA版本7.8中引入。

目标ISA注意事项

需要 sm_53 或更高版本。

setp.{bf16/bf16x2} 需要 sm_90 或更高版本。

示例

setp.lt.and.f16x2  p|q,a,b,r;
@q  setp.eq.f16    p,i,n;

setp.gt.or.bf16x2  u|v,c,d,s;
@q  setp.eq.bf16   u,j,m;

9.7.8. 逻辑与移位指令

逻辑和移位指令本质上是无类型的,只要操作数大小相同,就可以对任何类型的操作数执行按位运算。这允许直接对浮点值进行按位操作,而无需通过定义联合类型来访问其二进制位。指令andorxornot同样适用于谓词运算。

逻辑移位指令包括:

  • and

  • or

  • xor

  • not

  • cnot

  • lop3

  • shf

  • shl

  • shr

9.7.8.1. 逻辑与移位指令:与运算

按位与。

语法

and.type d, a, b;

.type = { .pred, .b16, .b32, .b64 };

描述

ab中的位执行按位与运算。

语义

d = a & b;

注意事项

操作数的大小必须匹配,但类型不一定需要相同。

允许的类型包括谓词寄存器。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

and.b32  x,q,r;
and.b32  sign,fpvalue,0x80000000;

9.7.8.2. 逻辑与移位指令:或运算

按位或运算。

语法

or.type d, a, b;

.type = { .pred, .b16, .b32, .b64 };

描述

计算ab中各位的按位或操作。

语义

d = a | b;

注意事项

操作数的大小必须匹配,但类型不一定需要相同。

允许的类型包括谓词寄存器。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

or.b32  mask mask,0x00010001
or.pred  p,q,r;

9.7.8.3. 逻辑与移位指令:异或

异或

按位异或(不相等)。

语法

xor.type d, a, b;

.type = { .pred, .b16, .b32, .b64 };

描述

计算ab中各位的按位异或操作。

语义

d = a ^ b;

注意事项

操作数的大小必须匹配,但类型不一定需要相同。

允许的类型包括谓词寄存器。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

xor.b32  d,q,r;
xor.b16  d,x,0x0001;

9.7.8.4. 逻辑与移位指令:非运算

按位取反;二进制补码。

语法

not.type d, a;

.type = { .pred, .b16, .b32, .b64 };

描述

反转a中的位。

语义

d = ~a;

注意事项

操作数的大小必须匹配,但类型不一定需要相同。

允许的类型包括谓词。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

not.b32  mask,mask;
not.pred  p,q;

9.7.8.5. 逻辑与移位指令:cnot

cnot

C/C++ 风格的逻辑非运算。

语法

cnot.type d, a;

.type = { .b16, .b32, .b64 };

描述

使用C/C++语义计算逻辑非。

语义

d = (a==0) ? 1 : 0;

注意事项

操作数的大小必须匹配,但类型不一定需要相同。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

cnot.b32 d,a;

9.7.8.6. 逻辑与移位指令:lop3

lop3

对3个输入进行任意逻辑运算。

语法

lop3.b32 d, a, b, c, immLut;
lop3.BoolOp.b32 d|p, a, b, c, immLut, q;

.BoolOp   = { .or , .and };

描述

对输入abc执行按位逻辑运算,并将结果存储到目标d中。

可选地,可以指定.BoolOp来通过以下方式对目标操作数d与谓词q执行布尔运算,从而计算谓词结果p

p = (d != 0) BoolOp q;

当指定了.BoolOp限定符时,可以使用汇点符号'_'代替目标操作数d

逻辑运算通过查找表定义,对于3个输入的情况,该查找表可以表示为8位数值,由操作数immLut指定(具体说明如下)。immLut是一个取值范围为0到255的整数常量,因此允许对输入abc执行最多256种不同的逻辑运算。

对于一个逻辑运算 F(a, b, c)immLut 的值可以通过对三个预定义常量值应用相同的运算来计算,如下所示:

ta = 0xF0;
tb = 0xCC;
tc = 0xAA;

immLut = F(ta, tb, tc);

示例:

If F = (a & b & c);
immLut = 0xF0 & 0xCC & 0xAA = 0x80

If F = (a | b | c);
immLut = 0xF0 | 0xCC | 0xAA = 0xFE

If F = (a & b & ~c);
immLut = 0xF0 & 0xCC & (~0xAA) = 0x40

If F = ((a & b | c) ^ a);
immLut = (0xF0 & 0xCC | 0xAA) ^ 0xF0 = 0x1A

下表展示了不同逻辑操作下immLut的计算方式:

ta

tb

tc

操作0 (假)

操作1 (ta & tb & tc)

操作2 (ta & tb & ~tc)

操作254 (ta | tb | tc)

操作255 (真)

0

0

0

0

0

0

0

1

0

0

1

0

0

0

1

1

0

1

0

0

0

0

1

1

0

1

1

0

0

0

1

1

1

0

0

0

0

0

1

1

1

0

1

0

0

0

1

1

1

1

0

0

0

1

1

1

1

1

1

0

1

0

1

1

immLut

0x0

0x80

0x40

0xFE

0xFF

语义

F = GetFunctionFromTable(immLut); // returns the function corresponding to immLut value
d = F(a, b, c);
if (BoolOp specified) {
    p = (d != 0) BoolOp q;
}

PTX ISA 说明

在PTX ISA版本4.3中引入。

支持PTX ISA 8.2版本中引入的.BoolOp限定符。

目标ISA注意事项

需要 sm_50 或更高版本。

限定符 .BoolOp 需要 sm_70 或更高版本。

示例

lop3.b32       d, a, b, c, 0x40;
lop3.or.b32  d|p, a, b, c, 0x3f, q;
lop3.and.b32 _|p, a, b, c, 0x3f, q;

9.7.8.7. 逻辑与移位指令:shf

shf

漏斗偏移。

语法

shf.l.mode.b32  d, a, b, c;  // left shift
shf.r.mode.b32  d, a, b, c;  // right shift

.mode = { .clamp, .wrap };

描述

将由操作数ab拼接形成的64位值,按照c中指定的无符号32位数值进行左移或右移。操作数b保存64位源值的高32位(63:32),操作数a保存低32位(31:0)。源值会根据c中的截断或循环值进行左移或右移。对于shf.l指令,结果的高32位将写入d;对于shf.r指令,结果的低32位将写入d

语义

u32  n = (.mode == .clamp) ? min(c, 32) : c & 0x1f;
switch (shf.dir) {  // shift concatenation of [b, a]
    case shf.l:     // extract 32 msbs
           u32  d = (b << n)      | (a >> (32-n));
    case shf.r:     // extract 32 lsbs
           u32  d = (b << (32-n)) | (a >> n);
}

注意事项

对于多字移位操作和旋转操作,请使用漏斗移位。在钳位模式下,移位量限制在0..32范围内;在环绕模式下限制在0..31范围内。因此,要移动超过32位的多字值,需要先移动32位字,然后使用shf来移位剩余的0..31距离。

要将大于64位的数据向右移位,可对相邻字重复应用shf.r指令,操作顺序从最低有效字向最高有效字推进。每一步计算移位结果的一个单字。结果的最高有效字通过shr.{u32,s32}指令计算,该指令会根据类型进行零填充或符号填充。

要左移大于64位的数据量,可对相邻字重复应用shf.l指令,操作顺序从最高有效字向最低有效字进行。每一步会计算出一个移位结果的单字。结果的最后有效字则通过shl指令计算得出。

通过为源参数ab提供相同的值,使用漏斗移位执行32位左旋或右旋操作。

PTX ISA 说明

在PTX ISA版本3.1中引入。

目标ISA注意事项

需要 sm_32 或更高版本。

示例

shf.l.clamp.b32  r3,r1,r0,16;

// 128-bit left shift; n < 32
// [r7,r6,r5,r4] = [r3,r2,r1,r0] << n
shf.l.clamp.b32  r7,r2,r3,n;
shf.l.clamp.b32  r6,r1,r2,n;
shf.l.clamp.b32  r5,r0,r1,n;
shl.b32          r4,r0,n;

// 128-bit right shift, arithmetic; n < 32
// [r7,r6,r5,r4] = [r3,r2,r1,r0] >> n
shf.r.clamp.b32  r4,r0,r1,n;
shf.r.clamp.b32  r5,r1,r2,n;
shf.r.clamp.b32  r6,r2,r3,n;
shr.s32          r7,r3,n;     // result is sign-extended

shf.r.clamp.b32  r1,r0,r0,n;  // rotate right by n; n < 32
shf.l.clamp.b32  r1,r0,r0,n;  // rotate left by n; n < 32

// extract 32-bits from [r1,r0] starting at position n < 32
shf.r.clamp.b32  r0,r0,r1,n;

9.7.8.8. 逻辑与移位指令: shl

shl

向左移位,右侧用零填充。

语法

shl.type d, a, b;

.type = { .b16, .b32, .b64 };

描述

a向左移动,移动量由b中的无符号32位值指定。

语义

d = a << b;

注意事项

移位量大于寄存器宽度N时将被截断至N

目标操作数和第一个源操作数的大小必须匹配,但类型不一定需要相同。无论指令类型如何,b操作数必须是32位值。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

shl.b32  q,a,2;

9.7.8.9. 逻辑与移位指令:shr

shr

向右移位,左侧进行符号填充或零填充。

语法

shr.type d, a, b;

.type = { .b16, .b32, .b64,
          .u16, .u32, .u64,
          .s16, .s32, .s64 };

描述

a向右移动由b中无符号32位值指定的位数。有符号移位使用符号位填充,无符号和未类型化移位使用0填充。

语义

d = a >> b;

注意事项

移位量大于寄存器宽度N时将被截断为N

目标操作数和第一个源操作数的大小必须匹配,但类型不一定需要相同。无论指令类型如何,b操作数必须是32位值。

包含位大小类型是为了与shl保持对称性。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

shr.u16  c,a,2;
shr.s32  i,i,1;
shr.b16  k,i,j;

9.7.9. 数据移动与转换指令

这些指令用于在不同位置之间以及不同状态空间之间复制数据,可能还会将数据从一种格式转换为另一种格式。movldldust可操作标量和向量类型。isspacep指令用于查询通用地址是否落在特定状态空间窗口内。cvta指令可在genericconstgloballocalshared状态空间之间转换地址。

指令 ld, st, suldsust 支持可选的缓存操作。

数据移动和转换指令包括:

  • mov

  • shfl.sync

  • prmt

  • ld

  • ldu

  • st

  • st.async

  • st.bulk

  • multimem.ld_reduce, multimem.st, multimem.red

  • prefetch, prefetchu

  • isspacep

  • cvta

  • cvt

  • cvt.pack

  • cp.async

  • cp.async.commit_group

  • cp.async.wait_group, cp.async.wait_all

  • cp.async.bulk

  • cp.reduce.async.bulk

  • cp.async.bulk.prefetch

  • cp.async.bulk.tensor

  • cp.reduce.async.bulk.tensor

  • cp.async.bulk.prefetch.tensor

  • cp.async.bulk.commit_group

  • cp.async.bulk.wait_group

  • tensormap.replace

9.7.9.1. Cache Operators

PTX ISA 2.0版本引入了针对加载和存储指令的可选缓存操作符。这些缓存操作符要求目标架构为sm_20或更高版本。

加载或存储指令上的缓存操作符仅被视为性能提示。在ldst指令上使用缓存操作符不会改变程序的内存一致性行为。

对于sm_20及更高版本,缓存操作符具有以下定义和行为。

表 29 内存加载指令的缓存操作符

运算符

含义

.ca

在所有层级进行缓存,很可能再次被访问。

默认的加载指令缓存操作是ld.ca,它会在所有层级(L1和L2)分配缓存行,采用常规的替换策略。全局数据在L2层级是保持一致的,但多个L1缓存对全局数据不具备一致性。如果一个线程通过某个L1缓存向全局内存存储数据,而第二个线程通过另一个L1缓存使用ld.ca加载该地址时,第二个线程可能会获取到过时的L1缓存数据,而非第一个线程存储的最新数据。驱动程序必须在并行线程的依赖网格之间使全局L1缓存行失效。这样,第一个网格程序的存储操作才能被第二个网格程序通过默认的ld.ca加载指令正确获取,这些加载操作会缓存在L1中。

.cg

在全局级别缓存(缓存在L2及以下层级,不包括L1)。

使用ld.cg仅全局缓存加载,绕过L1缓存,并仅在L2缓存中缓存。

.cs

缓存流式处理,可能仅访问一次。

ld.cs加载缓存流操作采用L1和L2中的驱逐优先策略分配全局线路,以限制可能仅访问一两次的临时流数据造成的缓存污染。当ld.cs应用于本地窗口地址时,它会执行ld.lu操作。

.lu

上次使用。

编译器/程序员在恢复溢出寄存器和弹出函数堆栈帧时可以使用ld.lu指令,以避免对那些不会再使用的缓存行进行不必要的写回操作。ld.lu指令对全局地址执行缓存流式加载操作(ld.cs)。

.cv

不缓存并重新获取(将缓存的系统内存行视为过时,重新获取)。

应用于全局系统内存地址的ld.cv加载操作会使匹配的L2缓存行失效(丢弃),并在每次新加载时重新获取该行。

表 30 内存存储指令的缓存操作符

运算符

含义

.wb

将所有一致性级别缓存写回。

默认的存储指令缓存操作是st.wb,它会按照正常的淘汰策略回写一致性缓存级别的缓存行。

如果一个线程绕过其L1缓存向全局内存存储数据,而另一个位于不同SM中的线程稍后通过ld.ca指令从该地址加载数据(使用不同的L1缓存),第二个线程可能会命中过时的L1缓存数据,而非获取第一个线程存储到L2或内存中的数据。

驱动程序必须在依赖的线程数组网格之间使全局L1缓存行失效。这样第一个网格程序的存储操作就能在L1中正确缺失,并由第二个网格程序通过默认的ld.ca加载指令获取。

.cg

在全局级别缓存(缓存在L2及以下层级,不包括L1)。

使用st.cg仅全局缓存全局存储数据,绕过L1缓存,仅在L2缓存中进行缓存。

.cs

缓存流式数据,可能仅访问一次。

st.cs存储缓存流操作采用逐出优先策略分配缓存行,以限制流输出数据对缓存的污染。

.wt

缓存直写(到系统内存)。

st.wt存储直写操作应用于全局系统内存地址时,会穿透L2缓存进行写入。

9.7.9.2. 缓存驱逐优先级提示

PTX ISA 7.4版本新增了针对加载和存储指令的可选缓存驱逐优先级提示功能。缓存驱逐优先级要求目标架构为sm_70或更高版本。

加载或存储指令中的缓存驱逐优先级被视为性能提示。它支持.global状态空间以及指向.global状态空间的通用地址。

表31 内存加载和存储指令的缓存逐出优先级提示

缓存驱逐优先级

含义

evict_normal

以常规淘汰优先级缓存数据。这是默认的淘汰优先级。

evict_first

以此优先级缓存的数据将位于驱逐优先级队列的首位,当需要执行缓存驱逐时,这类数据很可能被优先移除。该优先级适用于流式数据场景。

evict_last

使用此优先级缓存的数据将在驱逐优先级顺序中排在最后,并且很可能只有在其他具有evict_normalevict_first驱逐优先级的数据已被驱逐后才会被驱逐。此优先级适用于应持久保留在缓存中的数据。

evict_unchanged

不要在此操作中更改驱逐优先级顺序。

no_allocate

不要将数据分配到缓存。此优先级适用于流式数据。

9.7.9.3. 数据移动与转换指令:mov

mov

设置一个寄存器变量的值为另一个寄存器变量或立即数。获取全局、局部或共享状态空间中变量的非泛型地址。

语法

mov.type  d, a;
mov.type  d, sreg;
mov.type  d, avar;       // get address of variable
mov.type  d, avar+imm;   // get address of variable with offset
mov.u32   d, fname;      // get address of device function
mov.u64   d, fname;      // get address of device function
mov.u32   d, kernel;     // get address of entry function
mov.u64   d, kernel;     // get address of entry function

.type = { .pred,
          .b16, .b32, .b64,
          .u16, .u32, .u64,
          .s16, .s32, .s64,
                .f32, .f64 };

描述

将寄存器 d 写入值为 a

操作数 a 可以是寄存器、特殊寄存器、带有可寻址内存空间中可选偏移量的变量,或函数名称。

对于在.const.global.local.shared状态空间中声明的变量,mov指令将该变量的非通用地址(即变量在其状态空间中的地址)存入目标寄存器。要生成constgloballocalshared状态空间中变量的通用地址,可先通过mov获取该状态空间内的地址,再使用cvta指令将其转换为通用地址;或者,也可以直接使用cvta指令获取声明在constgloballocalshared状态空间中的变量的通用地址。

请注意,如果设备函数参数的地址被移动到寄存器中,该参数将被复制到堆栈上,且地址将位于本地状态空间。

语义

d = a;
d = sreg;
d = &avar;        // address is non-generic; i.e., within the variable's declared state space
d = &avar+imm;

注意事项

  • 虽然仅需要谓词和位大小类型,但为了方便程序员使用,我们包含了算术类型:使用它们可以增强程序可读性并支持额外的类型检查。

  • 当移动内核或设备函数的地址时,仅允许使用.u32.u64指令类型。但如果使用了有符号类型,不会被视为编译错误。在这种情况下,编译器会发出警告。

PTX ISA 说明

在PTX ISA版本1.0中引入。

获取内核入口函数的地址需要PTX ISA 3.1或更高版本。内核函数地址应仅在CUDA动态并行系统调用的上下文中使用。详情请参阅CUDA动态并行编程指南

目标ISA注意事项

mov.f64 需要 sm_13 或更高版本。

获取内核入口函数的地址需要sm_35或更高版本。

示例

mov.f32  d,a;
mov.u16  u,v;
mov.f32  k,0.1;
mov.u32  ptr, A;        // move address of A into ptr
mov.u32  ptr, A[5];     // move address of A[5] into ptr
mov.u32  ptr, A+20;     // move address with offset into ptr
mov.u32  addr, myFunc;  // get address of device function 'myFunc'
mov.u64  kptr, main;    // get address of entry function 'main'

9.7.9.4. 数据移动与转换指令:mov

mov

将向量转换为标量(打包)或标量转换为向量(解包)。

语法

mov.type  d, a;

.type = { .b16, .b32, .b64, .b128 };

描述

将向量寄存器a的打包值写入标量寄存器d,或将标量寄存器a的解包值写入向量寄存器d

当目标操作数 d 是向量寄存器时,如果至少有一个元素是标量寄存器,则可以使用下划线符号 '_' 来表示一个或多个元素。

对于位大小的类型,mov可用于将向量元素打包到标量寄存器中,或将标量寄存器的子字段解包到向量中。向量的总大小和标量的大小必须与指令类型的大小匹配。

语义

// pack two 8-bit elements into .b16
d = a.x | (a.y << 8)
// pack four 8-bit elements into .b32
d = a.x | (a.y << 8)  | (a.z << 16) | (a.w << 24)
// pack two 16-bit elements into .b32
d = a.x | (a.y << 16)
// pack four 16-bit elements into .b64
d = a.x | (a.y << 16)  | (a.z << 32) | (a.w << 48)
// pack two 32-bit elements into .b64
d = a.x | (a.y << 32)
// pack four 32-bit elements into .b128
d = a.x | (a.y << 32)  | (a.z << 64) | (a.w << 96)
// pack two 64-bit elements into .b128
d = a.x | (a.y << 64)

// unpack 8-bit elements from .b16
{ d.x, d.y } = { a[0..7], a[8..15] }
// unpack 8-bit elements from .b32
{ d.x, d.y, d.z, d.w }
        { a[0..7], a[8..15], a[16..23], a[24..31] }

// unpack 16-bit elements from .b32
{ d.x, d.y }  = { a[0..15], a[16..31] }
// unpack 16-bit elements from .b64
{ d.x, d.y, d.z, d.w } =
        { a[0..15], a[16..31], a[32..47], a[48..63] }

// unpack 32-bit elements from .b64
{ d.x, d.y } = { a[0..31], a[32..63] }

// unpack 32-bit elements from .b128
{ d.x, d.y, d.z, d.w } =
        { a[0..31], a[32..63], a[64..95], a[96..127] }
// unpack 64-bit elements from .b128
{ d.x, d.y } = { a[0..63], a[64..127] }

PTX ISA 说明

在PTX ISA版本1.0中引入。

支持PTX ISA版本8.3中引入的.b128类型。

目标ISA注意事项

支持所有目标架构。

支持 .b128 类型需要 sm_70 或更高版本。

示例

mov.b32 %r1,{a,b};      // a,b have type .u16
mov.b64 {lo,hi}, %x;    // %x is a double; lo,hi are .u32
mov.b32 %r1,{x,y,z,w};  // x,y,z,w have type .b8
mov.b32 {r,g,b,a},%r1;  // r,g,b,a have type .u8
mov.b64 {%r1, _}, %x;   // %x is.b64, %r1 is .b32
mov.b128 {%b1, %b2}, %y;   // %y is.b128, %b1 and % b2 are .b64
mov.b128 %y, {%b1, %b2};   // %y is.b128, %b1 and % b2 are .b64

9.7.9.5. 数据移动与转换指令:shfl (已弃用)

shfl (已弃用)

在warp线程内注册数据洗牌。

语法

shfl.mode.b32  d[|p], a, b, c;

.mode = { .up, .down, .bfly, .idx };

弃用说明

在PTX ISA版本6.0中,不带.sync限定符的shfl指令已被弃用。

  • 对于.target低于sm_70的版本,未来PTX ISA版本可能会移除对该指令的支持。

移除说明

在PTX ISA 6.4版本中,针对.targetsm_70或更高架构,移除了对不带.sync限定符的shfl指令的支持。

描述

在warp的线程之间交换寄存器数据。

当前执行warp中的每个线程将根据输入操作数bc以及mode计算源通道索引j。如果计算出的源通道索引j在有效范围内,该线程会将输入操作数a从通道j复制到其自身的目标寄存器d;否则,线程将直接将其自身的输入a复制到目标d。可选的谓词目标p会在计算出的源通道有效时设为True,否则设为False

请注意,b的超出范围值仍可能产生有效的计算源通道索引j。在这种情况下,数据传输会发生且目标谓词p为True。

请注意,如果在warp内的发散控制流中,一个活跃线程从一个非活跃线程获取寄存器,结果将是未定义的。

操作数 b 根据模式指定源通道或源通道偏移量。

操作数 c 包含两个打包值,分别用于指定将线程束逻辑分割为子段的掩码,以及用于限制源通道索引的上界值。

语义

lane[4:0]  = [Thread].laneid;  // position of thread in warp
bval[4:0] = b[4:0];            // source lane or lane offset (0..31)
cval[4:0] = c[4:0];            // clamp value
mask[4:0] = c[12:8];

// get value of source register a if thread is active and
// guard predicate true, else unpredictable
if (isActive(Thread) && isGuardPredicateTrue(Thread)) {
    SourceA[lane] = a;
} else {
    // Value of SourceA[lane] is unpredictable for
    // inactive/predicated-off threads in warp
}
maxLane = (lane[4:0] & mask[4:0]) | (cval[4:0] & ~mask[4:0]);
minLane = (lane[4:0] & mask[4:0]);

switch (.mode) {
    case .up:    j = lane - bval; pval = (j >= maxLane); break;
    case .down:  j = lane + bval; pval = (j <= maxLane); break;
    case .bfly:  j = lane ^ bval; pval = (j <= maxLane); break;
    case .idx:   j = minLane  | (bval[4:0] & ~mask[4:0]);
                                 pval = (j <= maxLane); break;
}
if (!pval) j = lane;  // copy from own lane
d = SourceA[j];       // copy input a from lane j
if (dest predicate selected)
    p = pval;

PTX ISA 说明

在PTX ISA 3.0版本中引入。

在PTX ISA 6.0版本中已弃用,推荐使用shfl.sync

对于.target sm_70或更高版本,PTX ISA 6.4版本不支持。

目标ISA注意事项

shfl 需要 sm_30 或更高版本。

shfl 从 PTX ISA 版本 6.4 开始不再支持 sm_70 或更高版本。

示例

    // Warp-level INCLUSIVE PLUS SCAN:
    //
    // Assumes input in following registers:
    //     - Rx  = sequence value for this thread
    //
    shfl.up.b32  Ry|p, Rx, 0x1,  0x0;
@p  add.f32      Rx, Ry, Rx;
    shfl.up.b32  Ry|p, Rx, 0x2,  0x0;
@p  add.f32      Rx, Ry, Rx;
    shfl.up.b32  Ry|p, Rx, 0x4,  0x0;
@p  add.f32      Rx, Ry, Rx;
    shfl.up.b32  Ry|p, Rx, 0x8,  0x0;
@p  add.f32      Rx, Ry, Rx;
    shfl.up.b32  Ry|p, Rx, 0x10, 0x0;
@p  add.f32      Rx, Ry, Rx;


    // Warp-level INCLUSIVE PLUS REVERSE-SCAN:
    //
    // Assumes input in following registers:
    //     - Rx  = sequence value for this thread
    //
    shfl.down.b32  Ry|p, Rx, 0x1,  0x1f;
@p  add.f32        Rx, Ry, Rx;
    shfl.down.b32  Ry|p, Rx, 0x2,  0x1f;
@p  add.f32        Rx, Ry, Rx;
    shfl.down.b32  Ry|p, Rx, 0x4,  0x1f;
@p  add.f32        Rx, Ry, Rx;
    shfl.down.b32  Ry|p, Rx, 0x8,  0x1f;
@p  add.f32        Rx, Ry, Rx;
    shfl.down.b32  Ry|p, Rx, 0x10, 0x1f;
@p  add.f32        Rx, Ry, Rx;


    // BUTTERFLY REDUCTION:
    //
    // Assumes input in following registers:
    //     - Rx  = sequence value for this thread
    //
    shfl.bfly.b32  Ry, Rx, 0x10, 0x1f;   // no predicate dest
    add.f32        Rx, Ry, Rx;
    shfl.bfly.b32  Ry, Rx, 0x8,  0x1f;
    add.f32        Rx, Ry, Rx;
    shfl.bfly.b32  Ry, Rx, 0x4,  0x1f;
    add.f32        Rx, Ry, Rx;
    shfl.bfly.b32  Ry, Rx, 0x2,  0x1f;
    add.f32        Rx, Ry, Rx;
    shfl.bfly.b32  Ry, Rx, 0x1,  0x1f;
    add.f32        Rx, Ry, Rx;
    //
    // All threads now hold sum in Rx

9.7.9.6. 数据移动与转换指令:shfl.sync

shfl.sync

在warp线程内注册数据洗牌。

语法

shfl.sync.mode.b32  d[|p], a, b, c, membermask;

.mode = { .up, .down, .bfly, .idx };

描述

在warp的线程之间交换寄存器数据。

shfl.sync 会使执行线程等待,直到与membermask对应的所有未退出线程都使用相同的限定符和相同的membermask值执行了shfl.sync后,才会继续执行。

操作数 membermask 指定一个32位整数,该整数是一个掩码,指示参与屏障的线程,其中位位置对应于线程的 laneid

shfl.syncmembermask 中的线程之间交换寄存器数据。

当前执行warp中的每个线程将根据输入操作数bc以及mode计算源通道索引j。如果计算出的源通道索引j在有效范围内,该线程会将输入操作数a从通道j复制到自己的目标寄存器d;否则,线程会直接将自身的输入a复制到目标d。可选的目标谓词p在计算出的源通道有效时设为True,否则设为False

请注意,b的越界值仍可能产生有效的计算源通道索引j。在这种情况下,数据传输会发生且目标谓词p为True。

请注意,如果线程从未激活的线程或不在membermask中的线程获取寄存器,结果将是未定义的。

操作数 b 根据模式指定源通道或源通道偏移量。

操作数 c 包含两个打包值,分别用于逻辑上将线程束分割为子段的掩码,以及用于钳位源通道索引的上限值。

如果执行线程不在membermask中,shfl.sync的行为将是未定义的。

注意

对于目标架构sm_6x或更低版本,membermask中的所有线程必须在收敛时执行相同的shfl.sync指令,并且只有当线程属于某个membermask时才能在执行shfl.sync指令期间保持活跃状态。否则,行为将是未定义的。

语义

// wait for all threads in membermask to arrive
wait_for_specified_threads(membermask);

lane[4:0]  = [Thread].laneid;  // position of thread in warp
bval[4:0] = b[4:0];            // source lane or lane offset (0..31)
cval[4:0] = c[4:0];            // clamp value
segmask[4:0] = c[12:8];

// get value of source register a if thread is active and
// guard predicate true, else unpredictable
if (isActive(Thread) && isGuardPredicateTrue(Thread)) {
    SourceA[lane] = a;
} else {
    // Value of SourceA[lane] is unpredictable for
    // inactive/predicated-off threads in warp
}
maxLane = (lane[4:0] & segmask[4:0]) | (cval[4:0] & ~segmask[4:0]);
minLane = (lane[4:0] & segmask[4:0]);

switch (.mode) {
    case .up:    j = lane - bval; pval = (j >= maxLane); break;
    case .down:  j = lane + bval; pval = (j <= maxLane); break;
    case .bfly:  j = lane ^ bval; pval = (j <= maxLane); break;
    case .idx:   j = minLane  | (bval[4:0] & ~segmask[4:0]);
                                 pval = (j <= maxLane); break;
}
if (!pval) j = lane;  // copy from own lane
d = SourceA[j];       // copy input a from lane j
if (dest predicate selected)
    p = pval;

PTX ISA 说明

在PTX ISA 6.0版本中引入。

目标ISA注意事项

需要 sm_30 或更高版本。

示例

shfl.sync.up.b32  Ry|p, Rx, 0x1,  0x0, 0xffffffff;

9.7.9.7. 数据移动与转换指令:prmt

prmt

对寄存器对中的字节进行置换。

语法

prmt.b32{.mode}  d, a, b, c;

.mode = { .f4e, .b4e, .rc8, .ecl, .ecr, .rc16 };

描述

从两个32位寄存器中任意选取四个字节,并将它们重新组合成一个32位目标寄存器。

在通用形式(未指定模式)中,置换控制由四个4位选择值组成。两个源寄存器中的字节编号从0到7:{b, a} = {{b7, b6, b5, b4}, {b3, b2, b1, b0}}。对于目标寄存器中的每个字节,都定义了一个4位选择值。

选择值的低3位指定应将8个源字节中的哪一个移动到目标位置。最高位定义是应复制字节值,还是应将字节的符号位(字节的最高位)复制到目标位置的所有8位上(对字节值进行符号扩展);msb=0表示复制字面值;msb=1表示复制符号位。请注意,符号扩展仅作为通用形式的一部分执行。

因此,这四个4位值完全指定了一个任意字节排列,作为16b排列码。

默认模式

d.b3

源选择

d.b2

源选择

d.b1

源选择

d.b0

源选择

索引

c[15:12]

c[11:8]

c[7:4]

c[3:0]

这种更专业化的置换控制形式使用操作数c(通常是一个地址指针)的两个最低有效位来控制字节提取。

模式

选择器

c[1:0]

d.b3

来源

d.b2

来源

d.b1

来源

d.b0

来源

f4e (前向4提取)

0

3

2

1

0

1

4

3

2

1

2

5

4

3

2

3

6

5

4

3

b4e (反向4提取)

0

5

6

7

0

1

6

7

0

1

2

7

0

1

2

3

0

1

2

3

rc8 (复制8份)

0

0

0

0

0

1

1

1

1

1

2

2

2

2

2

3

3

3

3

3

ecl (左侧边缘固定)

0

3

2

1

0

1

3

2

1

1

2

3

2

2

2

3

3

3

3

3

ecr (右侧边缘夹紧)

0

0

0

0

0

1

1

1

1

0

2

2

2

1

0

3

3

2

1

0

rc16 (复制16份)

0

1

0

1

0

1

3

2

3

2

2

1

0

1

0

3

3

2

3

2

语义

tmp64 = (b<<32) | a;  // create 8 byte source

if ( ! mode ) {
   ctl[0] = (c >>  0) & 0xf;
   ctl[1] = (c >>  4) & 0xf;
   ctl[2] = (c >>  8) & 0xf;
   ctl[3] = (c >> 12) & 0xf;
} else {
   ctl[0] = ctl[1] = ctl[2] = ctl[3] = (c >>  0) & 0x3;
}

tmp[07:00] = ReadByte( mode, ctl[0], tmp64 );
tmp[15:08] = ReadByte( mode, ctl[1], tmp64 );
tmp[23:16] = ReadByte( mode, ctl[2], tmp64 );
tmp[31:24] = ReadByte( mode, ctl[3], tmp64 );

PTX ISA 说明

在PTX ISA 2.0版本中引入。

目标ISA注意事项

prmt 需要 sm_20 或更高版本。

示例

prmt.b32      r1, r2, r3, r4;
prmt.b32.f4e  r1, r2, r3, r4;

9.7.9.8. 数据移动与转换指令:ld

ld

从可寻址状态空间变量加载寄存器变量。

语法

ld{.weak}{.ss}{.cop}{.level::cache_hint}{.level::prefetch_size}{.vec}.type  d, [a]{.unified}{, cache-policy};

ld{.weak}{.ss}{.level::eviction_priority}{.level::cache_hint}{.level::prefetch_size}{.vec}.type  d, [a]{.unified}{, cache-policy};

ld.volatile{.ss}{.level::prefetch_size}{.vec}.type  d, [a];

ld.relaxed.scope{.ss}{.level::eviction_priority}{.level::cache_hint}{.level::prefetch_size}{.vec}.type  d, [a]{, cache-policy};

ld.acquire.scope{.ss}{.level::eviction_priority}{.level::cache_hint}{.level::prefetch_size}{.vec}.type  d, [a]{, cache-policy};

ld.mmio.relaxed.sys{.global}.type  d, [a];

.ss =                       { .const, .global, .local, .param{::entry, ::func}, .shared{::cta, ::cluster} };
.cop =                      { .ca, .cg, .cs, .lu, .cv };
.level::eviction_priority = { .L1::evict_normal, .L1::evict_unchanged,
                              .L1::evict_first, .L1::evict_last, .L1::no_allocate };
.level::cache_hint =        { .L2::cache_hint };
.level::prefetch_size =     { .L2::64B, .L2::128B, .L2::256B }
.scope =                    { .cta, .cluster, .gpu, .sys };
.vec =                      { .v2, .v4 };
.type =                     { .b8, .b16, .b32, .b64, .b128,
                              .u8, .u16, .u32, .u64,
                              .s8, .s16, .s32, .s64,
                              .f32, .f64 };

描述

从源地址操作数a指定的位置加载寄存器变量d,该操作在指定的状态空间中进行。如果未指定状态空间,则使用通用寻址执行加载操作。

如果未为.shared状态空间指定子限定符,则默认假定为::cta

操作数 a 支持的寻址模式和对齐要求详见 Addresses as Operands

如果未使用.param状态空间指定子限定符,则:

  • ::func 当访问发生在设备函数内部时被假定。

  • 从入口函数访问内核函数参数时,默认使用::entry。否则,当从入口函数访问设备函数参数或其他.param变量时,默认使用::func

对于ld.param::entry指令,操作数a必须是内核参数地址,否则行为未定义。对于ld.param::func指令,操作数a必须是设备函数参数地址,否则行为未定义。

用于读取设备函数调用返回值的指令ld.param{::func}不可被谓词化。关于ld.param的正确使用方法,请参阅参数状态空间函数声明与定义中的说明。

.relaxed.acquire 限定符表示内存同步,如内存一致性模型中所述。.scope 限定符表示与ld.relaxedld.acquire指令可以直接同步的线程集合1.weak限定符表示没有同步的内存指令。该指令的效果只有在通过其他方式建立同步后才会对其他线程可见。

.mmio限定符的语义细节在内存一致性模型中有详细描述。对于ld.mmio操作,仅.sys线程范围有效。.mmio.relaxed限定符必须同时指定。

.volatile限定符的语义细节在内存一致性模型中有详细描述。

.weak.volatile.relaxed.acquire限定符是互斥的。当未指定这些限定符时,默认会采用.weak限定符。

限定符 .volatile.relaxed.acquire 只能用于 .global.shared 空间以及通用寻址模式,其中地址指向 .global.shared 空间。这些限定符不允许用于缓存操作。限定符 .mmio 只能用于 .global 空间和通用寻址模式,其中地址指向 .global 空间。

如果操作数a是使用.unified属性声明的变量地址(如变量和函数属性指令:.attribute中所述),则必须在操作数a上指定可选限定符.unified

限定符 .level::eviction_priority 指定了在内存访问期间将使用的逐出策略。

.level::prefetch_size 限定符是一个提示,用于将指定大小的额外数据预取到相应的缓存层级。子限定符 prefetch_size 可以设置为 64B128B256B,从而允许预取大小分别为64字节、128字节或256字节。

限定符 .level::prefetch_size 只能与 .global 状态空间以及指向 .global 状态空间的通用寻址一起使用。如果通用地址不在全局内存的地址窗口范围内,则预取行为是未定义的。

.level::prefetch_size 限定符仅被视为性能提示。

当指定可选参数cache-policy时,限定符.level::cache_hint是必需的。64位操作数cache-policy指定了在内存访问期间可能使用的缓存逐出策略。

限定符 .unified.level::cache_hint 仅支持 .global 状态空间以及指向 .global 状态空间的通用寻址。

cache-policy 是对缓存子系统的提示,可能不会始终被遵循。它仅被视为性能提示,不会改变程序的内存一致性行为。

1 这种同步通过因果顺序的传递性进一步扩展到其他线程,如内存一致性模型中所述。

语义

d = a;             // named variable a
d = *(&a+immOff)   // variable-plus-offset
d = *a;            // register
d = *(a+immOff);   // register-plus-offset
d = *(immAddr);    // immediate address

注意事项

目标 d 必须位于 .reg 状态空间中。

可以使用比指定类型更宽的目标寄存器。对于有符号整数,加载的值会进行符号扩展以填满目标寄存器的宽度;对于无符号和位宽类型,则进行零扩展。有关这些宽松类型检查规则的描述,请参阅表27

.f16 数据可以使用 ld.b16 加载,然后通过 cvt 转换为 .f32.f64,也可以用于半精度浮点指令。

.f16x2 数据可以通过 ld.b32 指令加载,然后用于半精度浮点运算指令中。

PTX ISA 说明

ld在PTX ISA 1.0版本中引入。ld.volatile在PTX ISA 1.1版本中引入。

PTX ISA 2.0版本中引入的通用寻址和缓存操作。

支持PTX ISA 6.0版本中引入的作用域限定符:.relaxed.acquire.weak

在PTX ISA版本3.1中增加了对.const空间的通用寻址支持。

支持PTX ISA版本7.4中引入的.level::eviction_priority.level::prefetch_size.level::cache_hint限定符。

支持在PTX ISA版本7.8中引入的.cluster作用域限定符。

支持PTX ISA版本7.8中引入的::cta::cluster子限定符。

支持PTX ISA 8.0版本中引入的.unified限定符。

支持PTX ISA版本8.2中引入的.mmio限定符。

在PTX ISA 8.3版本中引入了对::entry::func子限定符在.param空间的支持。

支持PTX ISA版本8.3中引入的.b128类型。

支持在PTX ISA 8.4版本中引入的.sys作用域与.b128类型。

目标ISA注意事项

ld.f64 需要 sm_13 或更高版本。

支持作用域限定符.relaxed.acquire.weak,这些限定符需要sm_70或更高版本。

通用寻址需要sm_20或更高版本。

缓存操作需要sm_20或更高版本。

支持.level::eviction_priority限定符需要sm_70或更高版本。

支持 .level::prefetch_size 限定符需要 sm_75 或更高版本。

支持 .L2::256B.L2::cache_hint 限定符需要 sm_80 或更高版本。

支持.cluster作用域限定符需要sm_90或更高版本。

子限定符 ::cta 需要 sm_30 或更高版本。

子限定符 ::cluster 需要 sm_90 或更高版本。

支持.unified限定符需要sm_90或更高版本。

支持 .mmio 限定符需要 sm_70 或更高版本。

支持 .b128 类型需要 sm_70 或更高版本。

示例

ld.global.f32    d,[a];
ld.shared.v4.b32 Q,[p];
ld.const.s32     d,[p+4];
ld.local.b32     x,[p+-8]; // negative offset
ld.local.b64     x,[240];  // immediate address

ld.global.b16    %r,[fs];  // load .f16 data into 32-bit reg
cvt.f32.f16      %r,%r;    // up-convert f16 data to f32

ld.global.b32    %r0, [fs];     // load .f16x2 data in 32-bit reg
ld.global.b32    %r1, [fs + 4]; // load .f16x2 data in 32-bit reg
add.rn.f16x2     %d0, %r0, %r1; // addition of f16x2 data
ld.global.relaxed.gpu.u32 %r0, [gbl];
ld.shared.acquire.gpu.u32 %r1, [sh];
ld.global.relaxed.cluster.u32 %r2, [gbl];
ld.shared::cta.acquire.gpu.u32 %r2, [sh + 4];
ld.shared::cluster.u32 %r3, [sh + 8];
ld.global.mmio.relaxed.sys.u32 %r3, [gbl];

ld.global.f32    d,[ugbl].unified;
ld.b32           %r0, [%r1].unified;

ld.global.L1::evict_last.u32  d, [p];

ld.global.L2::64B.b32   %r0, [gbl]; // Prefetch 64B to L2
ld.L2::128B.f64         %r1, [gbl]; // Prefetch 128B to L2
ld.global.L2::256B.f64  %r2, [gbl]; // Prefetch 256B to L2

createpolicy.fractional.L2::evict_last.L2::evict_unchanged.b64 cache-policy, 1;
ld.global.L2::cache_hint.b64  x, [p], cache-policy;
ld.param::entry.b32 %rp1, [kparam1];

ld.global.b128   %r0, [gbl];   // 128-bit load

9.7.9.9. 数据移动与转换指令:ld.global.nc

ld.global.nc

通过非一致性缓存从全局状态空间加载寄存器变量。

语法

ld.global{.cop}.nc{.level::cache_hint}{.level::prefetch_size}.type                 d, [a]{, cache-policy};
ld.global{.cop}.nc{.level::cache_hint}{.level::prefetch_size}.vec.type             d, [a]{, cache-policy};

ld.global.nc{.level::eviction_priority}{.level::cache_hint}{.level::prefetch_size}.type      d, [a]{, cache-policy};
ld.global.nc{.level::eviction_priority}{.level::cache_hint}{.level::prefetch_size}.vec.type  d, [a]{, cache-policy};

.cop  =                     { .ca, .cg, .cs };     // cache operation
.level::eviction_priority = { .L1::evict_normal, .L1::evict_unchanged,
                              .L1::evict_first, .L1::evict_last, .L1::no_allocate};
.level::cache_hint =        { .L2::cache_hint };
.level::prefetch_size =     { .L2::64B, .L2::128B, .L2::256B }
.vec  =                     { .v2, .v4 };
.type =                     { .b8, .b16, .b32, .b64, .b128,
                              .u8, .u16, .u32, .u64,
                              .s8, .s16, .s32, .s64,
                              .f32, .f64 };

描述

从全局状态空间中由源地址操作数a指定的位置加载寄存器变量d,并可选择性地缓存到非一致性只读缓存中。

注意

在某些架构中,纹理缓存比全局内存缓存更大、带宽更高但延迟也更长。对于具备足够并行性来掩盖较长延迟的应用程序,ld.global.nc在这些架构上的性能表现应优于ld.global

地址操作数 a 可以包含指向 .global 状态空间的通用地址。操作数 a 支持的寻址模式和对齐要求在地址作为操作数中有详细说明。

限定符 .level::eviction_priority 指定了在内存访问期间将使用的逐出策略。

.level::prefetch_size 限定符是一个提示,用于将指定大小的额外数据预取到相应的缓存层级。子限定符 prefetch_size 可以设置为 64B128B256B,从而允许预取大小分别为64字节、128字节或256字节。

.level::prefetch_size 限定符仅被视为性能提示。

当指定可选参数cache-policy时,限定符.level::cache_hint是必需的。64位操作数cache-policy指定了在内存访问期间可能使用的缓存逐出策略。

cache-policy 是对缓存子系统的提示,可能不会始终被遵循。它仅被视为性能提示,不会改变程序的内存一致性行为。

语义

d = a;             // named variable a
d = *(&a+immOff)   // variable-plus-offset
d = *a;            // register
d = *(a+immOff);   // register-plus-offset
d = *(immAddr);    // immediate address

注意事项

目标 d 必须位于 .reg 状态空间中。

可以使用比指定类型更宽的目标寄存器。对于有符号整数,加载的值会进行符号扩展以填满目标寄存器的宽度;对于无符号和位宽类型,则会进行零扩展以填满目标寄存器的宽度。

.f16 数据可以使用 ld.b16 加载,然后通过 cvt 转换为 .f32.f64

PTX ISA 说明

在PTX ISA版本3.1中引入。

支持PTX ISA版本7.4中引入的.level::eviction_priority.level::prefetch_size.level::cache_hint限定符。

支持PTX ISA版本8.3中引入的.b128类型。

目标ISA注意事项

需要 sm_32 或更高版本。

支持.level::eviction_priority限定符需要sm_70或更高版本。

支持 .level::prefetch_size 限定符需要 sm_75 或更高版本。

支持 .level::cache_hint 限定符需要 sm_80 或更高版本。

支持 .b128 类型需要 sm_70 或更高版本。

示例

ld.global.nc.f32           d, [a];
ld.gloal.nc.L1::evict_last.u32 d, [a];

createpolicy.fractional.L2::evict_last.b64 cache-policy, 0.5;
ld.global.nc.L2::cache_hint.f32  d, [a], cache-policy;

ld.global.nc.L2::64B.b32      d,  [a];     // Prefetch 64B to L2
ld.global.nc.L2::256B.f64     d,  [a];     // Prefetch 256B to L2

ld.global.nc.b128             d,  [a];

9.7.9.10. 数据移动与转换指令:ldu

ldu

从线程束(warp)内线程共享的地址加载只读数据。

语法

ldu{.ss}.type      d, [a];       // load from address
ldu{.ss}.vec.type  d, [a];       // vec load from address

.ss   = { .global };             // state space
.vec  = { .v2, .v4 };
.type = { .b8, .b16, .b32, .b64, .b128,
          .u8, .u16, .u32, .u64,
          .s8, .s16, .s32, .s64,
                     .f32, .f64 };

描述

只读数据从全局状态空间中由源地址操作数a指定的位置加载到寄存器变量d中,该地址保证在warp内的所有线程中保持一致。如果未指定状态空间,则使用通用寻址执行加载操作。

操作数 a 支持的寻址模式和对齐要求详见 Addresses as Operands

语义

d = a;             // named variable a
d = *(&a+immOff)   // variable-plus-offset
d = *a;            // register
d = *(a+immOff);   // register-plus-offset
d = *(immAddr);    // immediate address

注意事项

目标 d 必须位于 .reg 状态空间中。

可以使用比指定类型更宽的目标寄存器。对于有符号整数,加载的值会进行符号扩展以填满目标寄存器的宽度;对于无符号和位宽类型,则进行零扩展。有关这些宽松类型检查规则的描述,请参阅表27

.f16 数据可以使用 ldu.b16 加载,然后通过 cvt 转换为 .f32.f64,也可以用于半精度浮点指令。

.f16x2 数据可以使用 ldu.b32 加载,然后用于半精度浮点指令。

PTX ISA 说明

在PTX ISA 2.0版本中引入。

支持PTX ISA版本8.3中引入的.b128类型。

目标ISA注意事项

ldu.f64 需要 sm_13 或更高版本。

支持 .b128 类型需要 sm_70 或更高版本。

示例

ldu.global.f32    d,[a];
ldu.global.b32    d,[p+4];
ldu.global.v4.f32 Q,[p];
ldu.global.b128   d,[a];

9.7.9.11. 数据移动与转换指令:st

st

将数据存储到可寻址的状态空间变量中。

语法

st{.weak}{.ss}{.cop}{.level::cache_hint}{.vec}.type   [a], b{, cache-policy};
st{.weak}{.ss}{.level::eviction_priority}{.level::cache_hint}{.vec}.type
                                                      [a], b{, cache-policy};
st.volatile{.ss}{.vec}.type                           [a], b;
st.relaxed.scope{.ss}{.level::eviction_priority}{.level::cache_hint}{.vec}.type
                                                      [a], b{, cache-policy};
st.release.scope{.ss}{.level::eviction_priority}{.level::cache_hint}{.vec}.type
                                                      [a], b{, cache-policy};
st.mmio.relaxed.sys{.global}.type         [a], b;

.ss =                       { .global, .local, .param{::func}, .shared{::cta, ::cluster} };
.level::eviction_priority = { .L1::evict_normal, .L1::evict_unchanged,
                              .L1::evict_first, .L1::evict_last, .L1::no_allocate };
.level::cache_hint =        { .L2::cache_hint };
.cop =                      { .wb, .cg, .cs, .wt };
.sem =                      { .relaxed, .release };
.scope =                    { .cta, .cluster, .gpu, .sys };
.vec =                      { .v2, .v4 };
.type =                     { .b8, .b16, .b32, .b64, .b128,
                              .u8, .u16, .u32, .u64,
                              .s8, .s16, .s32, .s64,
                              .f32, .f64 };

描述

将操作数b的值存储到由目标地址操作数a在指定状态空间中所指定的位置。如果未给出状态空间,则使用通用寻址执行存储操作。向常量内存执行存储操作是非法的。

如果未为.shared状态空间指定子限定符,则默认假定为::cta

操作数 a 支持的寻址模式和对齐要求详见 Addresses as Operands

如果指定了.param但没有任何子限定符,则默认使用.param::func

用于向设备函数传递参数的指令st.param{::func}不能被预测。关于st.param的正确使用方法,请参阅参数状态空间函数声明与定义

限定符 .relaxed.release 表示内存同步,如内存一致性模型中所述。.scope 限定符表示与 st.relaxedst.release 指令可以直接同步的线程集合1.weak 限定符表示没有同步的内存指令。该指令的效果只有在通过其他方式建立同步时才会对其他线程可见。

.mmio限定符的语义细节在内存一致性模型中有详细描述。只有.sys线程范围对st.mmio操作有效。.mmio.relaxed限定符必须同时指定。

.volatile限定符的语义细节在内存一致性模型中有详细描述。

.weak.volatile.relaxed.release限定符是互斥的。当未指定这些限定符时,默认会采用.weak限定符。

限定符 .volatile.relaxed.release 只能用于 .global.shared 空间以及通用寻址模式,其中地址指向 .global.shared 空间。这些限定符不允许用于缓存操作。限定符 .mmio 只能用于 .global 空间和通用寻址模式,其中地址指向 .global 空间。

限定符 .level::eviction_priority 指定了在内存访问期间将使用的逐出策略。

当指定可选参数cache-policy时,限定符.level::cache_hint是必需的。64位操作数cache-policy指定了在内存访问期间可能使用的缓存逐出策略。

限定符 .level::cache_hint 仅支持 .global 状态空间以及指向 .global 状态空间的通用寻址。

cache-policy 是对缓存子系统的提示,可能不会始终被遵循。它仅被视为性能提示,不会改变程序的内存一致性行为。

1 这种同步通过因果顺序的传递性进一步扩展到其他线程,如内存一致性模型中所述。

语义

d = a;                // named variable d
*(&a+immOffset) = b;            // variable-plus-offset
*a = b;               // register
*(a+immOffset) = b;   // register-plus-offset
*(immAddr) = b;       // immediate address

注意事项

操作数 b 必须位于 .reg 状态空间中。

可以使用比指定类型更宽的源寄存器。对应于指令类型宽度的低n位将被存储到内存中。有关这些宽松类型检查规则的描述,请参阅表26

.f16cvt 指令产生的数据可以使用 st.b16 存储。

.f16x2 数据可以使用 st.b32 存储。

PTX ISA 说明

最初在PTX ISA版本1.0中引入。st.volatile在PTX ISA版本1.1中引入。

PTX ISA 2.0版本中引入的通用寻址和缓存操作。

支持PTX ISA 6.0版本中引入的作用域限定符:.relaxed.release.weak限定符。

支持PTX ISA版本7.4中引入的.level::eviction_priority.level::cache_hint限定符。

支持在PTX ISA版本7.8中引入的.cluster作用域限定符。

支持PTX ISA版本7.8中引入的::cta::cluster子限定符。

支持PTX ISA版本8.2中引入的.mmio限定符。

在PTX ISA 8.3版本中引入了对.param空间上::func子限定符的支持。

支持PTX ISA版本8.3中引入的.b128类型。

支持在PTX ISA 8.4版本中引入的.sys作用域与.b128类型。

目标ISA注意事项

st.f64 需要 sm_13 或更高版本。

支持作用域限定符.relaxed.release.weak需要sm_70或更高版本。

通用寻址需要sm_20或更高版本。

缓存操作需要sm_20或更高版本。

支持.level::eviction_priority限定符需要sm_70或更高版本。

支持 .level::cache_hint 限定符需要 sm_80 或更高版本。

支持.cluster作用域限定符需要sm_90或更高版本。

子限定符 ::cta 需要 sm_30 或更高版本。

子限定符 ::cluster 需要 sm_90 或更高版本。

支持 .mmio 限定符需要 sm_70 或更高版本。

支持 .b128 类型需要 sm_70 或更高版本。

示例

st.global.f32    [a],b;
st.local.b32     [q+4],a;
st.global.v4.s32 [p],Q;
st.local.b32     [q+-8],a; // negative offset
st.local.s32     [100],r7; // immediate address

cvt.f16.f32      %r,%r;    // %r is 32-bit register
st.b16           [fs],%r;  // store lower
st.global.relaxed.sys.u32 [gbl], %r0;
st.shared.release.cta.u32 [sh], %r1;
st.global.relaxed.cluster.u32 [gbl], %r2;
st.shared::cta.release.cta.u32 [sh + 4], %r1;
st.shared::cluster.u32 [sh + 8], %r1;
st.global.mmio.relaxed.sys.u32 [gbl], %r1;

st.global.L1::no_allocate.f32 [p], a;

createpolicy.fractional.L2::evict_last.b64 cache-policy, 0.25;
st.global.L2::cache_hint.b32  [a], b, cache-policy;

st.param::func.b64 [param1], %rp1;

st.global.b128  [a], b;  // 128-bit store

9.7.9.12. 数据移动与转换指令:st.async

st.async

异步存储操作。

语法

st.async{.sem}{.scope}{.ss}{.completion_mechanism}{.vec}.type [a], b, [mbar];

.sem  =                 { .weak };
.scope =                { .cluster };
.ss   =                 { .shared::cluster };
.type =                 { .b32, .b64,
                          .u32, .u64,
                          .s32, .s64,
                          .f32, .f64 };
.vec  =                 { .v2, .v4 };
.completion_mechanism = { .mbarrier::complete_tx::bytes };

st.async{.mmio}.sem{.scope}{.ss}{.completion_mechanism}.type [a], b;

.sem =                  { .release };
.scope =                { .gpu, .sys };
.ss =                   { .global };
.completion_mechanism = { };
.type =                 { .b8, .b16, .b32, .b64,
                          .u8, .u16, .u32, .u64,
                          .s8, .s16, .s32, .s64,
                                     .f32, .f64 };

描述

st.async 是一个非阻塞指令,用于发起异步存储操作,将源操作数 b 指定的值存储到目标操作数 a 指定的内存位置。

操作数

  • a 是一个目标地址,必须是一个寄存器,或者是 register + immOff 的形式,如 Addresses as Operands 中所述。

  • b 是一个源值,其类型由限定符 .type 指定。

  • mbar 是一个mbarrier对象地址。

限定符

  • .mmio 表示这是否是一个 mmio操作

  • .sem 指定了内存排序语义,具体描述请参考内存一致性模型

    • 如果未指定.sem,则默认为.weak

  • .scope 指定了该指令可以直接同步的线程集合。

  • .ss 指定目标操作数 a 和 mbarrier 操作数 mbar 的状态空间。

  • .completion_mechanism 指定了观察异步操作完成的机制。

    • .completion_mechanism设置为.mbarrier::complete_tx::bytes时:在异步操作完成后,系统将在操作数mbar指定的mbarrier对象上执行complete-tx操作,其中completeCount参数等于以字节为单位存储的数据量。

    • 当未指定.completion_mechanism时:存储的完成与CTA的结束同步。

  • .type 指定源操作数 b 的类型。

条件

.sem.weak 时:

  • 这是一个弱存储操作到共享内存,通过mbarrier对象发出完成信号。

  • 存储操作被视为弱内存操作。

  • mbarrier上的complete-tx操作在.cluster范围内具有.release语义。

  • 要求:

    • 目标操作数 ambarrier对象 mbar 的共享内存地址与执行线程属于同一集群内的同一个CTA。

    • 集群中的CTA数量严格大于1;%cluster_nctarank > 1为真。

    否则,行为是未定义的。

  • .mmio 不可被指定。

  • 如果指定了.scope,则必须为.cluster

  • 如果未指定.scope,则默认为.cluster

  • 如果指定了.ss,则必须为.shared::cluster

  • 如果未指定.ss,则对操作数ambar使用通用寻址。 如果指定的通用地址不在.shared::cluster状态空间的地址窗口范围内,则行为是未定义的。

  • 如果指定了.completion_mechanism,则必须为.mbarrier::complete_tx::bytes

  • 如果未指定.completion_mechanism,则默认为.mbarrier::complete_tx::bytes

.sem.release 时:

  • 这是一个向全局内存的释放存储操作。

  • 存储操作是一种强内存操作,在.scope指定的作用域内具有.release语义。

  • 如果指定了.mmio,则.scope必须为.sys

  • 如果指定了.scope,它可以是.gpu.sys

  • 如果未指定.scope,则默认为.sys

  • 如果指定了.ss,则必须为.global

  • 如果未指定.ss,则对操作数a使用通用寻址方式。 如果指定的通用地址不在.global状态空间的地址窗口范围内,则行为未定义。

  • .completion_mechanism 不能指定。

PTX ISA 说明

在PTX ISA版本8.1中引入。

支持PTX ISA 8.7版本中引入的.mmio限定符、.release语义、.global状态空间以及.scope限定符。

目标ISA注意事项

需要 sm_90 或更高版本。

.mmio 限定符、.release 语义、.global 状态空间以及 .scope 限定符需要 sm_100 或更高版本支持。

示例

st.async.shared::cluster.mbarrier::complete_tx::bytes.u32 [addr], b, [mbar_addr];

st.async.release.global.u32 [addr], b;

9.7.9.13. 数据移动与转换指令:st.bulk

st.bulk

根据状态空间初始化一块内存区域。

语法

st.bulk{.weak}{.shared::cta}  [a], size, initval; // initval must be zero

描述

st.bulk 指令初始化从目标地址操作数 a 指定的位置开始的一块共享内存区域。

64位整数操作数size指定了以字节数为单位需要初始化的内存大小。size必须是8的倍数。如果该值不是8的倍数,则行为是未定义的。size操作数的最大值可以是34359738360。

整数立即操作数 initval 指定内存位置的初始值。操作数 initval 唯一允许的数值是0。

如果未指定状态空间,则使用通用寻址。如果a指定的地址不在.shared状态空间的地址窗口内,则行为是未定义的。

可选限定符 .weak 用于指定 st.bulk 指令的内存同步效果,具体描述见 Memory Consistency Model

PTX ISA 说明

在PTX ISA版本8.6中引入。

目标ISA注意事项

需要 sm_100 或更高版本。

示例

st.bulk.weak.shared::cta  [dst], n, 0;

st.bulk                   [gdst], 4096, 0;

9.7.9.14. 数据移动与转换指令:multimem.ld_reduce, multimem.st, multimem.red

multimem.* 操作作用于 multimem 地址,并访问该地址指向的所有多个内存位置。

多内存地址只能通过multimem.*操作访问。使用ldst或其他任何内存操作访问多内存地址将导致未定义行为。

请参阅CUDA编程指南了解多内存地址的创建与管理。

multimem.ld_reduce, multimem.st, multimem.red

对multimem地址执行内存操作。

语法

// Integer type:

multimem.ld_reduce{.ldsem}{.scope}{.ss}.op.type      d, [a];
multimem.st{.stsem}{.scope}{.ss}.type                [a], b;
multimem.red{.redsem}{.scope}{.ss}.op.type           [a], b;

.ss =       { .global }
.ldsem =    { .weak, .relaxed, .acquire }
.stsem =    { .weak, .relaxed, .release }
.redsem =   { .relaxed, .release }
.scope =    { .cta, .cluster, .gpu, .sys }
.op  =      { .min, .max, .add, .and, .or, .xor }
.type =     { .b32, .b64,  .u32, .u64, .s32, .s64 }

// Floating point type:

multimem.ld_reduce{.ldsem}{.scope}{.ss}.op{.acc_prec}{.vec}.type    d, [a];
multimem.st{.stsem}{.scope}{.ss}{.vec}.type                         [a], b;
multimem.red{.redsem}{.scope}{.ss}.redop{.vec}.redtype              [a], b;

.ss =       { .global }
.ldsem =    { .weak, .relaxed, .acquire }
.stsem =    { .weak, .relaxed, .release }
.redsem =   { .relaxed, .release }
.scope =    { .cta, .cluster, .gpu, .sys }
.op  =      { .min, .max, .add }
.redop  =   { .add }
.acc_prec = { .acc::f32, .acc::f16 }
.vec =      { .v2, .v4, .v8 }
.type=      { .f16, .f16x2, .bf16, .bf16x2, .f32, .f64, .e5m2, .e5m2x2, .e5m2x4, .e4m3, .e4m3x2, .e4m3x4 }
.redtype =  { .f16, .f16x2, .bf16, .bf16x2, .f32, .f64 }

描述

指令 multimem.ld_reduce 执行以下操作:

  • 对多内存地址a的加载操作,涉及从该多内存地址a指向的所有多个内存位置加载数据

  • 对从多存储器地址a加载的多个数据执行由.op指定的归约操作。

归约操作的结果返回到寄存器 d 中。

指令 multimem.st 执行将输入操作数 b 存储到由多存储器地址 a 指向的所有内存位置的操作。

指令 multimem.red 对由多内存地址 a 指向的所有内存位置执行归约操作,操作数为 b

指令 multimem.ld_reduce 对从multimem地址指向的所有内存位置加载的值执行归约操作。相比之下,multimem.red 则对multimem地址指向的所有内存位置执行归约操作。

地址操作数 a 必须是多重内存地址,否则行为未定义。操作数a支持的寻址模式和对齐要求在Addresses as Operands中有详细说明。

如果未指定状态空间,则使用通用寻址。如果a指定的地址不在.global状态空间的地址窗口内,则行为是未定义的。

对于浮点类型的多重操作,指定类型的大小与.vec必须等于32位、64位或128位。不允许.vec与类型的其他组合。类型.f64不能与.vec限定符一起使用。

下表描述了.op与基础类型的有效组合:

操作符

基础类型

.add

.u32, .u64, .s32
.f16, .f16x2, .bf16, .bf16x2
.f32, .f64, .e5m2, .e5m2x2,
.e5m2x4, .e4m3, .e4m3x2,
.e4m3x4

.and, .or, .xor

.b32, .b64

.min, .max

.u32, .s32, .u64, .s64
.f16, .f16x2, .bf16, .bf16x2
.e5m2, .e5m2x2, .e5m2x4,
.e4m3, .e4m3x2, .e4m3x4

对于multimem.ld_reduce,中间累加的默认精度与指定类型相同。

可选地,可以指定.acc_prec限定符来更改中间计算的精度,如下所示:

.type

.acc::prec

将精度更改为

.f16, .f16x2, .bf16, .bf16x2

.acc::f32

.f32

.e5m2, .e4m3, .e5m2x2, .e4m3x2, .e4m3x4, .e5m2x4

.acc::f16

.f16

可选限定符 .ldsem.stsem.redsem 分别指定了 multimem.ld_reducemultimem.stmultimem.red 的内存同步效果,如内存一致性模型中所述。如果未明确指定语义限定符,则 multimem.ld_reducemultimem.st 默认为 .weak,而 multimem.red 默认为 .relaxed

可选的.scope限定符指定了能够直接观察到该操作内存同步效果的线程集合,如内存一致性模型中所述。如果未为multimem.red指定.scope限定符,则默认采用.sys作用域。

PTX ISA 说明

在PTX ISA版本8.1中引入。

支持在PTX ISA版本8.2中引入的.acc::f32限定符。

支持在PTX ISA 8.6版本中引入的类型 .e5m2, .e5m2x2, .e5m2x4, .e4m3, .e4m3x2, .e4m3x4

支持PTX ISA 8.6版本中引入的.acc::f16限定符。

目标ISA注意事项

需要 sm_90 或更高版本。

类型 .e5m2, .e5m2x2, .e5m2x4, .e4m3, .e4m3x2, .e4m3x4 在以下架构上受支持:

  • sm_100a

  • sm_101a

  • sm_120a

限定符 .acc::f16 在以下架构上受支持:

  • sm_100a

  • sm_101a

  • sm_120a

示例

multimem.ld_reduce.and.b32                    val1_b32, [addr1];
multimem.ld_reduce.acquire.gpu.global.add.u32 val2_u32, [addr2];

multimem.st.relaxed.gpu.b32                [addr3], val3_b32;
multimem.st.release.cta.global.u32         [addr4], val4_u32;

multimem.red.relaxed.gpu.max.f64           [addr5], val5_f64;
multimem.red.release.cta.global.add.v4.f32 [addr6], {val6, val7, val8, val9};
multimem.ld_reduce.add.acc::f32.v2.f16x2   {val_10, val_11}, [addr7];

multimem.ld_reduce.relaxed.cta.min.v2.e4m3x2 {val_12, val_13}, [addr8];
multimem.ld_reduce.relaxed.cta.add.v4.e4m3   {val_14, val_15, val_16, val_17}, [addr9];
multimem.ld_reduce.add.acc::f16.v4.e5m2      {val_18, val_19, val_20, val_21}, [addr10];

9.7.9.15. 数据移动与转换指令:prefetch, prefetchu

prefetch, prefetchu

在内存层次结构的指定级别预取包含通用地址的行,位于指定的状态空间中。

语法

prefetch{.space}.level                    [a];   // prefetch to data cache
prefetch.global.level::eviction_priority  [a];   // prefetch to data cache

prefetchu.L1  [a];             // prefetch to uniform cache

prefetch{.tensormap_space}.tensormap [a];  // prefetch the tensormap

.space =                    { .global, .local };
.level =                    { .L1, .L2 };
.level::eviction_priority = { .L2::evict_last, .L2::evict_normal };
.tensormap_space =          { .const, .param };

描述

prefetch指令将包含全局或本地内存状态空间中指定地址的缓存行预取到指定的缓存层级。

如果指定了.tensormap限定符,那么prefetch指令会将包含.const.param内存状态空间中指定地址的缓存行预取,以供后续cp.async.bulk.tensor指令使用。

如果没有给定状态空间,prefetch将使用Generic Addressing

可选地,可以通过修饰符 .level::eviction_priority 指定要应用于预取缓存行的逐出优先级。

操作数 a 支持的寻址模式和对齐要求详见 Addresses as Operands

prefetchu指令将包含指定通用地址的缓存行预取到指定的统一缓存级别中。

对共享内存位置的prefetch操作不会执行任何实际动作。

prefetch预取到统一缓存需要一个通用地址,如果该地址映射到constlocalshared内存位置,则不会执行任何操作。

PTX ISA 说明

在PTX ISA 2.0版本中引入。

支持PTX ISA版本7.4中引入的.level::eviction_priority限定符。

.tensormap限定符的支持在PTX ISA 8.0版本中引入。

目标ISA注意事项

prefetchprefetchu 需要 sm_20 或更高版本。

支持.level::eviction_priority限定符需要sm_80或更高版本。

支持 .tensormap 修饰符需要 sm_90 或更高版本。

示例

prefetch.global.L1             [ptr];
prefetch.global.L2::evict_last [ptr];
prefetchu.L1  [addr];
prefetch.const.tensormap       [ptr];

9.7.9.16. 数据移动与转换指令:applypriority

applypriority

将缓存淘汰优先级应用于指定缓存级别中的指定地址。

语法

applypriority{.global}.level::eviction_priority  [a], size;

.level::eviction_priority = { .L2::evict_normal };

描述

applypriority指令将.level::eviction_priority限定符指定的缓存驱逐优先级应用于指定缓存层级中的地址范围[a..a+size)

如果未指定状态空间,则使用通用寻址。如果指定地址不在.global状态空间的地址窗口范围内,则行为是未定义的。

操作数size是一个整数常量,用于指定要应用优先级的缓存级别中的数据量(以字节为单位)。size操作数唯一支持的值为128。

操作数a支持的寻址模式在Addresses as Operands中有描述。a必须对齐到128字节。

如果地址a指向的数据尚未存在于指定的缓存级别中,则将在应用指定优先级之前预取该数据。

PTX ISA 说明

在PTX ISA版本7.4中引入。

目标ISA注意事项

需要 sm_80 或更高版本。

示例

applypriority.global.L2::evict_normal [ptr], 128;

9.7.9.17. 数据移动与转换指令:discard

丢弃

使指定地址和缓存级别的缓存数据失效。

语法

discard{.global}.level  [a], size;

.level = { .L2 };

描述

discard指令会使地址范围[a .. a + (size - 1)]内的数据在由.level限定符指定的缓存层级中失效,且不会将缓存中的数据写回内存。因此在丢弃操作后,地址范围[a .. a+ (size - 1)]内的数据将具有不确定的值。

操作数 size 是一个整数常量,用于指定要丢弃的缓存数据量(以字节为单位),该缓存级别由 .level 限定符指定。对于 size 操作数,唯一支持的值是128。

如果未指定状态空间,则使用通用寻址。如果指定地址不在.global状态空间的地址窗口范围内,则行为是未定义的。

地址操作数a支持的寻址模式在地址作为操作数中有描述。a必须128字节对齐。

PTX ISA 说明

在PTX ISA版本7.4中引入。

目标ISA注意事项

需要 sm_80 或更高版本。

示例

discard.global.L2 [ptr], 128;

9.7.9.18. 数据移动与转换指令:createpolicy

创建策略

为指定的缓存级别创建缓存淘汰策略。

语法

// Range-based policy
createpolicy.range{.global}.level::primary_priority{.level::secondary_priority}.b64
                                   cache-policy, [a], primary-size, total-size;

// Fraction-based policy
createpolicy.fractional.level::primary_priority{.level::secondary_priority}.b64
                                   cache-policy{, fraction};

// Converting the access property from CUDA APIs
createpolicy.cvt.L2.b64            cache-policy, access-property;

.level::primary_priority =   { .L2::evict_last, .L2::evict_normal,
                               .L2::evict_first, .L2::evict_unchanged };
.level::secondary_priority = { .L2::evict_first, .L2::evict_unchanged };

描述

createpolicy指令为指定缓存级别创建一个缓存淘汰策略,该策略存储在目标操作数cache-policy指定的不透明64位寄存器中。该缓存淘汰策略规定了如何将缓存淘汰优先级应用于带有.level::cache_hint限定符的内存操作所使用的全局内存地址。

缓存淘汰策略有两种类型:

  • 基于范围的政策

    使用createpolicy.range创建的缓存淘汰策略为以下三个地址范围指定了缓存淘汰行为:

    • [a .. a + (primary-size - 1)] 称为主范围。

    • [a + primary-size .. a + (total-size - 1)] 被称为尾部次级范围。

    • [a - (total-size - primary-size) .. (a - 1)] 称为前置次要范围。

    当在带有.level::cache_hint修饰符的内存操作中使用基于范围的缓存淘汰策略时,淘汰优先级按以下方式应用:

    • 如果内存地址落在主范围内,将应用由.L2::primary_priority指定的驱逐优先级。

    • 如果内存地址落在任何次要范围内,将应用由.L2::secondary_priority指定的驱逐优先级。

    • 如果内存地址不在上述任一范围内,则应用的驱逐优先级未指定。

    32位操作数primary-size指定主范围的大小(以字节为单位)。32位操作数total-size指定地址范围的总大小(包括主范围和次范围,以字节为单位)。primary-size的值必须小于或等于total-size的值。total-size的最大允许值为4GB。

    如果未指定.L2::secondary_priority,则默认使用.L2::evict_unchanged

    如果未指定状态空间,则使用通用寻址。如果指定地址不在.global状态空间的地址窗口范围内,则行为是未定义的。

  • 基于比例的缓存策略

    带有.level::cache_hint修饰符的内存操作可以使用基于比例的缓存驱逐策略,通过32位浮点操作数fraction指定比例,请求将.L2:primary_priority指定的缓存驱逐优先级应用于该比例的缓存访问。剩余的缓存访问将获得.L2::secondary_priority指定的驱逐优先级。这意味着在使用基于比例缓存策略的内存操作中,内存访问有fraction操作数指定的概率获得.L2::primary_priority指定的缓存驱逐优先级。

    操作数fraction的有效值范围是(0.0,.., 1.0]。如果未指定操作数fraction,则默认为1.0。

    如果未指定.L2::secondary_priority,则默认为.L2::evict_unchanged

使用CUDA API创建的访问属性可以通过指令createpolicy.cvt转换为缓存驱逐策略。源操作数access-property是一个64位不透明寄存器。更多详情请参阅CUDA编程指南

PTX ISA 说明

在PTX ISA版本7.4中引入。

目标ISA注意事项

需要 sm_80 或更高版本。

示例

createpolicy.fractional.L2::evict_last.b64                      policy, 1.0;
createpolicy.fractional.L2::evict_last.L2::evict_unchanged.b64  policy, 0.5;

createpolicy.range.L2::evict_last.L2::evict_first.b64
                                            policy, [ptr], 0x100000, 0x200000;

// access-prop is created by CUDA APIs.
createpolicy.cvt.L2.b64 policy, access-prop;

9.7.9.19. 数据移动与转换指令:isspacep

isspacep

查询一个通用地址是否落在指定的状态空间窗口内。

语法

isspacep.space  p, a;    // result is .pred

.space = { const, .global, .local, .shared{::cta, ::cluster}, .param{::entry} };

描述

如果通用地址a落在指定的状态空间窗口内,则将谓词寄存器p写入1,否则写入0。目标p的类型为.pred;源地址操作数必须是.u32.u64类型。

isspacep.param{::entry} 如果通用地址落在Kernel Function Parameters窗口范围内则返回1,否则返回0。如果仅指定.param而不带任何子限定符,则默认为.param::entry

isspacep.global 对于Kernel Function Parameters返回1,因为.param窗口包含在.global窗口内。

如果未为.shared状态空间指定子限定符,则默认假定为::cta

注意

ispacep.shared::cluster 会对集群内线程可访问的每个共享内存地址返回1,而 ispacep.shared::cta 仅当该地址属于执行CTA中声明的变量时才会返回1。

PTX ISA 说明

在PTX ISA 2.0版本中引入。

isspacep.const 在PTX ISA 3.1版本中引入。

isspacep.param 在PTX ISA版本7.7中引入。

支持PTX ISA版本7.8中引入的::cta::cluster子限定符。

在PTX ISA 8.3版本中引入了对.param空间上子限定符::entry的支持。

目标ISA注意事项

isspacep 需要 sm_20 或更高版本。

isspacep.param{::entry} 需要 sm_70 或更高版本。

子限定符 ::cta 需要 sm_30 或更高版本。

子限定符 ::cluster 需要 sm_90 或更高版本。

示例

isspacep.const           iscnst, cptr;
isspacep.global          isglbl, gptr;
isspacep.local           islcl,  lptr;
isspacep.shared          isshrd, sptr;
isspacep.param::entry    isparam, pptr;
isspacep.shared::cta     isshrdcta, sptr;
isspacep.shared::cluster ishrdany sptr;

9.7.9.20. 数据移动与转换指令:cvta

cvta

将地址从.constKernel Function Parameters (.param)、.global.local.shared状态空间转换为通用地址,反之亦然。获取声明在.constKernel Function Parameters (.param)、.global.local.shared状态空间中变量的通用地址。

语法

// convert const, global, local, or shared address to generic address
cvta.space.size  p, a;        // source address in register a
cvta.space.size  p, var;      // get generic address of var
cvta.space.size  p, var+imm;  // generic address of var+offset

// convert generic address to const, global, local, or shared address
cvta.to.space.size  p, a;

.space = { .const, .global, .local, .shared{::cta, ::cluster}, .param{::entry} };
.size  = { .u32, .u64 };

描述

const常量、Kernel Function Parameters内核函数参数(.param)、global全局地址、local局部地址或shared共享地址转换为通用地址,反之亦然。源地址和目标地址必须大小相同。使用cvt.u32.u64cvt.u64.u32指令来截断或零扩展地址。

对于在.constKernel Function Parameters(.param)、.global.local.shared状态空间中声明的变量,可以使用cvta获取该变量的通用地址。源可以是寄存器,也可以是在constKernel Function Parameters(.param)、globallocalshared内存中定义的变量,并可选择带偏移量。

当将通用地址转换为constKernel Function Parameters (.param)、globallocalshared地址时,如果通用地址不在指定状态空间的地址窗口范围内,则生成的地址是未定义的。程序可以使用isspacep来防止这种错误行为。

对于具有.shared状态空间的cvta,地址必须属于由::cta::cluster子限定符指定的空间,否则行为是未定义的。如果未为.shared状态空间指定子限定符,则默认假定为::cta

如果指定了.param而没有任何子限定符,则默认为.param::entry。对于 .param{::entry}状态空间,操作数a必须是内核参数地址,否则 行为是未定义的。

PTX ISA 说明

在PTX ISA 2.0版本中引入。

cvta.constcvta.to.const 在PTX ISA 3.1版本中引入。

cvta.paramcvta.to.param 在PTX ISA 7.7版本中引入。

注意:当前实现不允许在包含指向常量缓冲区指针(作为内核参数传递)的程序中,对const空间变量使用泛型指针。

支持PTX ISA版本7.8中引入的::cta::cluster子限定符。

在PTX ISA 8.3版本中引入了对.param空间上子限定符::entry的支持。

目标ISA注意事项

cvta 需要 sm_20 或更高版本。

cvta.param{::entry}cvta.to.param{::entry} 需要 sm_70 或更高版本。

子限定符 ::cta 需要 sm_30 或更高版本。

子限定符 ::cluster 需要 sm_90 或更高版本。

示例

cvta.const.u32   ptr,cvar;
cvta.local.u32   ptr,lptr;
cvta.shared::cta.u32  p,As+4;
cvta.shared::cluster.u32 ptr, As;
cvta.to.global.u32  p,gptr;
cvta.param.u64   ptr,pvar;
cvta.to.param::entry.u64  epptr, ptr;

9.7.9.21. 数据移动与转换指令:cvt

cvt

将值从一种类型转换为另一种类型。

语法

cvt{.irnd}{.ftz}{.sat}.dtype.atype         d, a;  // integer rounding
cvt{.frnd}{.ftz}{.sat}.dtype.atype         d, a;  // fp rounding

cvt.frnd2{.relu}{.satfinite}.f16.f32       d, a;
cvt.frnd2{.relu}{.satfinite}.f16x2.f32     d, a, b;
cvt.rs{.relu}{.satfinite}.f16x2.f32        d, a, b, rbits;

cvt.frnd2{.relu}{.satfinite}.bf16.f32      d, a;
cvt.frnd2{.relu}{.satfinite}.bf16x2.f32    d, a, b;
cvt.rs{.relu}{.satfinite}.bf16x2.f32       d, a, b, rbits;

cvt.rna{.satfinite}.tf32.f32               d, a;
cvt.frnd2{.satfinite}{.relu}.tf32.f32      d, a;

cvt.rn.satfinite{.relu}.f8x2type.f32       d, a, b;
cvt.rn.satfinite{.relu}.f8x2type.f16x2     d, a;
cvt.rn.{.relu}.f16x2.f8x2type              d, a;
cvt.rs{.relu}.satfinite.f8x4type.f32       d, {a, b, e, f}, rbits;

cvt.rn.satfinite{.relu}.f4x2type.f32       d, a, b;
cvt.rn{.relu}.f16x2.f4x2type               d, a;
cvt.rs{.relu}.satfinite.f4x4type.f32       d, {a, b, e, f}, rbits;

cvt.rn.satfinite{.relu}.f6x2type.f32       d, a, b;
cvt.rn{.relu}.f16x2.f6x2type               d, a;
cvt.rs{.relu}.satfinite.f6x4type.f32       d, {a, b, e, f}, rbits;

cvt.frnd3{.satfinite}.ue8m0x2.f32          d, a, b;
cvt.frnd3{.satfinite}.ue8m0x2.bf16x2       d, a;
cvt.rn.bf16x2.ue8m0x2                      d, a;

.irnd   = { .rni, .rzi, .rmi, .rpi };
.frnd   = { .rn,  .rz,  .rm,  .rp  };
.frnd2  = { .rn,  .rz };
.frnd2  = { .rn,  .rz };
.frnd3  = { .rz,  .rp };
.dtype = .atype = { .u8,   .u16, .u32, .u64,
                    .s8,   .s16, .s32, .s64,
                    .bf16, .f16, .f32, .f64 };
.f8x2type = { .e4m3x2, .e5m2x2 };
.f4x2type = { .e2m1x2 };
.f6x2type = { .e2m3x2, .e3m2x2 };
.f4x4type = { .e2m1x4 };
.f8x4type = { .e4m3x4, .e5m2x4 };
.f6x4type = { .e2m3x4, .e3m2x4 };

描述

在不同类型和大小之间进行转换。

对于.f16x2.bf16x2指令类型,两个.f32类型的输入ab会被转换为.f16.bf16类型,转换后的值被打包到目标寄存器d中,其中从输入a转换的值存储在d的上半部分,从输入b转换的值存储在d的下半部分

对于.f16x2指令类型,目标操作数d具有.f16x2.b32类型。对于 .bf16指令类型,操作数d具有.b16类型。对于.bf16x2指令类型, 操作数d具有.b32类型。对于.tf32指令类型,操作数d具有.b32类型。

当转换为.e4m3x2/.e5m2x2数据格式时,目标操作数d具有.b16类型。当将两个.f32输入转换为.e4m3x2/.e5m2x2时,每个输入都会被转换为指定格式,转换后的值被打包到目标操作数d中,其中从输入a转换的值存储在d的高8位,从输入b转换的值存储在d的低8位。当将.f16x2输入转换为.e4m3x2/.e5m2x2时,操作数a中的每个.f16输入都会被转换为指定格式。转换后的值被打包到目标操作数d中,其中从输入a的高16位转换的值存储在d的高8位,从输入a的低16位转换的值存储在d的低8位。

当从.e4m3x2/.e5m2x2转换为.f16x2时,源操作数a具有.b16类型。操作数a中的每个8位输入值将被转换为.f16类型。转换后的值被打包到目标操作数d中,使得从a的高8位转换的值存储在d的高16位,而从a的低8位转换的值存储在d的低16位。

当转换为.e2m1x2数据格式时,目标操作数d具有.b8类型。 当将两个.f32输入转换为.e2m1x2时,每个输入都会被转换为指定格式, 转换后的值被打包到目标操作数d中,使得从输入a转换的值存储在d的高4位, 而从输入b转换的值存储在d的低4位。

当从.e2m1x2转换为.f16x2时,源操作数a具有.b8类型。操作数a中的每个4位输入值将被转换为.f16类型。转换后的值会打包到目标操作数d中,其中从a的高4位转换的值存储在d的高16位,而从a的低4位转换的值存储在d的低16位。

当转换为.e2m1x4数据格式时,目标操作数d具有.b16类型。当将四个.f32输入转换为.e2m1x4时,每个输入都会被转换为指定格式,转换后的值被打包到目标操作数d中,使得从输入abef转换的值分别存储在d的高位开始的每4位中。

当转换为.e2m3x2/.e3m2x2数据格式时,目标操作数d具有.b16类型。当将两个.f32输入转换为.e2m3x2/.e3m2x2时,每个输入会被转换为指定格式,转换后的值被打包到目标操作数d中,其中从输入a转换的值存储在d的高8位(最高2位补零),从输入b转换的值存储在d的低8位(最高2位补零)。

当从.e2m3x2/.e3m2x2转换为.f16x2时,源操作数a具有.b16类型。 操作数a中每个最高有效位(MSB)为0的8位输入值将被转换为.f16类型。转换后的 值被打包到目标操作数d中,使得从a的高8位转换的值 存储在d的高16位,而从a的低8位转换的值 存储在d的低16位。

当转换为.e5m2x4/.e4m3x4/.e3m2x4/.e2m3x4数据格式时,目标操作数d具有.b32类型。当将四个.f32输入转换为.e5m2x4/.e4m3x4/.e3m2x4/.e2m3x4时,每个输入都会被转换为指定格式,转换后的值被打包到目标操作数d中,使得从输入abef转换的值分别存储在d的高位开始的每个8位中。对于.e3m2x4/.e2m3x4,每个8位输出的最高2位会用零填充。

当转换为.ue8m0x2数据格式时,目标操作数d具有.b16类型。当将两个.f32或两个打包的.bf16输入转换为.ue8m0x2时,每个输入都会被转换为指定格式,转换后的值被打包到目标操作数d中,其中从输入a转换的值存储在d的高8位,从输入b转换的值存储在d的低8位。

当从.ue8m0x2转换为.bf16x2时,源操作数a具有.b16类型。操作数a中的每个8位输入值将被转换为.bf16类型。转换后的值被打包到目标操作数d中,使得从a的高8位转换的值存储在d的高16位,而从a的低8位转换的值存储在d的低16位。

rbits 是一个 .b32 类型的寄存器操作数,用于为 .rs 舍入模式提供随机位。

当转换为.f16x2时,会从rbits中提供两个16位值,其中高16位的13个最低有效位用作操作数a的随机位(最高3位为0),而低16位的13个最低有效位用作操作数b的随机位(最高3位为0)。

当转换为.bf16x2时,会从rbits提供两个16位值,其中高16位用作操作数a的随机位,低16位用作操作数b的随机位。

当转换为.e4m3x4/.e5m2x4/.e2m3x4/.e3m2x4时,会从rbits提供两个16位值,其中低16位用于操作数ef,高16位用于操作数ab

当转换为.e2m1x4时,会从rbits提供两个16位值,其中使用rbits两个16位半部分的低8位作为操作数ef,而使用两个16位半部分的高8位作为操作数ab

在以下所有情况下,舍入修饰符是必需的:

  • 浮点到浮点的转换,当目标类型小于源类型时

  • 所有浮点数到整数的转换

  • 所有整数到浮点数的转换

  • 所有涉及.f16x2.e4m3x2, .e5m2x2,.bf16x2.tf32.e2m1x2.e2m3x2.e3m2x2.e4m3x4.e5m2x4.e2m1x4.e2m3x4.e3m2x4以及 .ue8m0x2指令类型的转换。

.satfinite 修饰符仅支持涉及以下类型的转换:

  • .e4m3x2, .e5m2x2, .e2m1x2, .e2m3x2, .e3m2x2, .e4m3x4, .e5m2x4, .e2m1x4, .e2m3x4, .e3m2x4 目标类型。 此类转换必须使用 .satfinite 修饰符。

  • .f16, .bf16, .f16x2, .bf16x2, .tf32, .ue8m0x2 作为目标类型。

语义

if (/* inst type is .f16x2 or .bf16x2 */) {
    d[31:16] = convert(a);
    d[15:0]  = convert(b);
} else if (/* inst destination type is .e5m2x2 or .e4m3x2 or .ue8m0x2 */) {
    d[15:8] = convert(a);
    d[7:0]  = convert(b);
} else if (/* inst destination type is .e2m1x2 */) {
    d[7:4] = convert(a);
    d[3:0] = convert(b);
} else if (/* inst destination type is .e2m3x2 or .e3m2x2 */) {
    d[15:14] = 0;
    d[13:8] = convert(a);
    d[7:6] = 0;
    d[5:0] = convert(b);
} else if (/* inst destination type is .e2m1x4 */) {
    d[15:12] = convert(a);
    d[11:8] = convert(b);
    d[7:4] = convert(e);
    d[3:0] = convert(f);
} else if (/* inst destination type is .e4m3x4 or .e5m2x4 */) {
    d[31:24] = convert(a);
    d[23:16] = convert(b);
    d[15:8] = convert(e);
    d[7:0] = convert(f);
} else if (/* inst destination type is .e2m3x4 or .e3m2x4 */) {
    d[31:30] = 0;
    d[29:24] = convert(a);
    d[23:22] = 0;
    d[21:16] = convert(b);
    d[15:14] = 0;
    d[13:8] = convert(e);
    d[7:6] = 0;
    d[5:0] = convert(f);
} else {
    d = convert(a);
}

// 随机位 rbits 对于 .rs 舍入的语义:

  1. 目标类型 .f16:随机位布局详情请参考图37

    _images/cvt-rs-rbits-layout-f16.png

    图37 使用.rs舍入和.f16目标类型的随机位布局

  2. 目标类型 .bf16: 随机位布局详情请参考图38

    _images/cvt-rs-rbits-layout-bf16.png

    图38 使用.rs舍入和.bf16目标类型的随机位布局

  3. 目标类型 .e2m1x4: 随机位布局详情请参考图39

    _images/cvt-rs-rbits-layout-f4.png

    图39 使用.rs舍入和.e2m1x4目标类型的随机位布局

  4. 目标类型 .e5m2x4, .e4m3x4, .e3m2x4, .e2m3x4: 随机位布局详情请参阅图40

    _images/cvt-rs-rbits-layout-f8-f6.png

    图40 使用.rs舍入时.e5m2x4/.e4m3x4/.e3m2x4/.e2m3x4目标类型的随机位布局

整数说明

浮点数到整数的转换以及相同大小的浮点数到浮点数的转换(当值被舍入为整数时)需要进行整数舍入。在所有其他情况下,整数舍入都是非法的。

整数取整修饰符:

.rni

四舍五入到最接近的整数,当源数值与两个整数距离相等时选择偶数

.rzi

向零方向四舍五入到最接近的整数

.rmi

向负无穷方向四舍五入到最接近的整数

.rpi

向正无穷方向四舍五入到最接近的整数

在浮点数到整数的转换中,NaN输入会被转换为0。

次正规数:

sm_20+

默认情况下,支持次正规数。

对于cvt.ftz.dtype.f32浮点到整数转换以及cvt.ftz.f32.f32采用整数舍入的浮点到浮点转换,次正规输入会被刷新为保留符号的零。修饰符.ftz仅当.dtype.atype.f32时才能指定,并且仅适用于单精度(.f32)输入和结果。

sm_1x

对于cvt.ftz.dtype.f32浮点到整数转换以及cvt.ftz.f32.f32采用整数舍入的浮点到浮点转换,次正规输入会被刷新为保留符号的零。在这些情况下,可以明确指定可选的.ftz修饰符。

注意: 在PTX ISA 1.4及更早版本中,当目标类型大小为64位时,cvt指令不会将单精度次正规数输入或结果刷新为零。编译器将为旧版PTX代码保留此行为。

饱和度调节器:

.sat

对于整数目标类型,.sat会将结果限制在MININT..MAXINT范围内,具体取决于操作的大小。请注意,饱和处理同时适用于有符号和无符号整数类型。

饱和度修饰符仅允许在目标类型的值范围不是源类型值范围的超集时使用;即,在基于源类型和目标类型不可能进行饱和度处理的情况下,.sat修饰符是非法的。

对于浮点数到整数的转换,默认情况下结果会被截断到目标范围内;也就是说,.sat是多余的。

浮点数注意事项

浮点舍入在以下情况下是必需的:浮点数到浮点数的转换导致精度损失时,以及整数到浮点数的转换时。在所有其他情况下,浮点舍入都是非法的。

浮点数舍入修饰符:

.rn

四舍五入到最接近的值,平局时取偶数

.rna

四舍五入到最接近的值,平局时远离零

.rz

向零取整

.rm

向负无穷方向舍入

.rp

向正无穷方向舍入

.rs

随机舍入是通过使用提供的随机位实现的。运算结果根据提供的随机位(rbits)与输入尾数被截断(丢弃)部分的整数相加产生的进位,向零方向或远离零方向进行舍入。

浮点数值可以通过整数舍入修饰符(参见整数说明)舍入为整数值。操作数必须具有相同的大小。结果是一个整数值,以浮点格式存储。

次正规数:

sm_20+

默认情况下支持次正规数。可以指定修饰符.ftz将单精度次正规输入和结果刷新为保留符号的零。修饰符.ftz只能在.dtype.atype.f32时指定,并且仅适用于单精度(.f32)输入和结果。

sm_1x

单精度次正规输入和结果会被刷新为保留符号的零。在这些情况下,为了清晰起见,可以指定可选的.ftz修饰符。

注意: 在PTX ISA 1.4及更早版本中,cvt指令不会将单精度次正规数输入或结果刷新为零,如果源类型或目标类型是.f64。编译器将为遗留PTX代码保留此行为。具体来说,如果PTX ISA版本为1.4或更早,单精度次正规数输入和结果仅在cvt.f32.f16cvt.f16.f32cvt.f32.f32指令中被刷新为保留符号的零。

饱和度调节器:

.sat:

对于浮点目标类型,.sat会将结果限制在[0.0, 1.0]范围内。NaN结果会被清零为正零。适用于.f16.f32.f64类型。

.relu:

对于.f16.f16x2.bf16.bf16x2.e4m3x2.e5m2x2.e2m1x2.e2m3x2.e3m2x2.e4m3x4.e5m2x4.e2m1x4.e2m3x4.e3m2x4.tf32 目标类型,.relu会将负数结果截断为0。NaN结果会被转换为标准NaN

.satfinite:

对于.f16.f16x2.bf16.bf16x2.e4m3x2.e5m2x2.ue8m0x2.e4m3x4.e5m2x4.tf32目标格式,如果输入值为NaN,则结果将转换为指定目标格式的NaN。对于.e2m1x2.e2m3x2.e3m2x2.e2m1x4.e2m3x4.e3m2x4目标格式,NaN结果会被转换为正的MAX_NORM。 如果输入值的绝对值(忽略符号)大于指定目标格式的MAX_NORM,则结果将保留符号的目标格式MAX_NORM, 对于不支持目标符号的.ue8m0x2格式则输出正的MAX_NORM

注意事项

可以使用比指定类型更宽的源寄存器,除非源操作数具有.bf16.bf16x2格式。转换时使用对应于指令类型宽度的低n位。有关这些宽松类型检查规则的描述,请参阅操作数大小超过指令类型大小

可以使用比指定类型更宽的目标寄存器,除非目标操作数具有.bf16.bf16x2.tf32格式。对于有符号整数,转换结果会进行符号扩展至目标寄存器宽度;对于无符号整数、位宽类型和浮点类型,则进行零扩展至目标寄存器宽度。有关这些宽松类型检查规则的详细说明,请参阅操作数大小超出指令类型大小

对于cvt.f32.bf16NaN输入会产生未指定的NaN

PTX ISA 说明

在PTX ISA版本1.0中引入。

.relu 修饰符以及 {.f16x2, .bf16, .bf16x2, .tf32} 目标格式 在PTX ISA 7.0版本中引入。

cvt.bf16.{u8/s8/u16/s16/u32/s32/u64/s64/f16/f64/bf16}, cvt.{u8/s8/u16/s16/u32/s32/u64/s64/f16/f64}.bf16, 以及 cvt.tf32.f32.{relu}.{rn/rz} 这些指令 在PTX ISA 7.8版本中引入。

cvt 支持 .e4m3x2/.e5m2x2 格式,适用于 PTX ISA 7.8 版本及以上引入的 sm_90 或更高架构。

cvt.satfinite.{e4m3x2, e5m2x2}.{f32, f16x2} 适用于 sm_90 或更高版本,在PTX ISA 7.8版本中引入。

cvt 针对 .e4m3x2/.e5m2x2 格式,在 PTX ISA 8.1 版本中为 sm_89 架构引入。

cvt.satfinite.{e4m3x2, e5m2x2}.{f32, f16x2} 针对 sm_89 在 PTX ISA 8.1 版本中引入。

cvt.satfinite.{f16, bf16, f16x2, bf16x2, tf32}.f32 在PTX ISA 8.1版本中引入。

cvt.{rn/rz}.satfinite.tf32.f32 在PTX ISA版本8.6中引入。

cvt.rn.satfinite{.relu}.{e2m1x2/e2m3x2/e3m2x2/ue8m0x2}.f32 在PTX ISA版本8.6中引入。

cvt.rn{.relu}.f16x2.{e2m1x2/e2m3x2/e3m2x2} 在PTX ISA 8.6版本中引入。

cvt.{rp/rz}{.satfinite}{.relu}.ue8m0x2.bf16x2 在PTX ISA版本8.6中引入。

cvt.{rz/rp}.satfinite.ue8m0x2.f32 在PTX ISA版本8.6中引入。

cvt.rn.bf16x2.ue8m0x2 在PTX ISA版本8.6中引入。

.rs 舍入模式在PTX ISA 8.7版本中引入。

cvt.rs{.e2m1x4/.e4m3x4/.e5m2x4/.e3m2x4/.e2m3x4}.f32 在PTX ISA 8.7版本中引入。

目标ISA注意事项

cvt 转换至或从 .f64 需要 sm_13 或更高版本。

.relu 修饰符以及 {.f16x2, .bf16, .bf16x2, .tf32} 目标格式需要 sm_80 或更高版本。

cvt.bf16.{u8/s8/u16/s16/u32/s32/u64/s64/f16/f64/bf16}, cvt.{u8/s8/u16/s16/u32/s32/u64/s64/f16/f64}.bf16, 以及 cvt.tf32.f32.{relu}.{rn/rz} 需要 sm_90 或更高版本。

cvt 使用 .e4m3x2/.e5m2x2 需要 sm89 或更高版本。

cvt.satfinite.{e4m3x2, e5m2x2}.{f32, f16x2} 需要 sm_89 或更高版本。

cvt.{rn/rz}.satfinite.tf32.f32 需要 sm_100 或更高版本。

cvt.rn.satfinite{.relu}.{e2m1x2/e2m3x2/e3m2x2/ue8m0x2}.f32 在以下架构上受支持:

  • sm_100a

  • sm_101a

  • sm_120a

cvt.rn{.relu}.f16x2.{e2m1x2/e2m3x2/e3m2x2} 在以下架构上受支持:

  • sm_100a

  • sm_101a

  • sm_120a

cvt.{rz/rp}{.satfinite}{.relu}.ue8m0x2.bf16x2 在以下架构上受支持:

  • sm_100a

  • sm_101a

  • sm_120a

cvt.{rz/rp}.satfinite.ue8m0x2.f32 在以下架构上受支持:

  • sm_100a

  • sm_101a

  • sm_120a

cvt.rn.bf16x2.ue8m0x2 在以下架构上受支持:

  • sm_100a

  • sm_101a

  • sm_120a

.rs 舍入模式需要 sm_100a

cvt.rs{.e2m1x4/.e4m3x4/.e5m2x4/.e3m2x4/.e2m3x4}.f32 需要 sm_100a

示例

cvt.f32.s32 f,i;
cvt.s32.f64 j,r;     // float-to-int saturates by default
cvt.rni.f32.f32 x,y; // round to nearest int, result is fp
cvt.f32.f32 x,y;     // note .ftz behavior for sm_1x targets
cvt.rn.relu.f16.f32      b, f;        // result is saturated with .relu saturation mode
cvt.rz.f16x2.f32         b1, f, f1;   // convert two fp32 values to packed fp16 outputs
cvt.rn.relu.satfinite.f16x2.f32    b1, f, f1;   // convert two fp32 values to packed fp16 outputs with .relu saturation on each output
cvt.rn.bf16.f32          b, f;        // convert fp32 to bf16
cvt.rz.relu.satfinite.bf16.f3 2    b, f;        // convert fp32 to bf16 with .relu and .satfinite saturation
cvt.rz.satfinite.bf16x2.f32        b1, f, f1;   // convert two fp32 values to packed bf16 outputs
cvt.rn.relu.bf16x2.f32   b1, f, f1;   // convert two fp32 values to packed bf16 outputs with .relu saturation on each output
cvt.rna.satfinite.tf32.f32         b1, f;       // convert fp32 to tf32 format
cvt.rn.relu.tf32.f32     d, a;        // convert fp32 to tf32 format
cvt.f64.bf16.rp          f, b;        // convert bf16 to f64 format
cvt.bf16.f16.rz          b, f         // convert f16 to bf16 format
cvt.bf16.u64.rz          b, u         // convert u64 to bf16 format
cvt.s8.bf16.rpi          s, b         // convert bf16 to s8 format
cvt.bf16.bf16.rpi        b1, b2       // convert bf16 to corresponding int represented in bf16 format
cvt.rn.satfinite.e4m3x2.f32 d, a, b;  // convert a, b to .e4m3 and pack as .e4m3x2 output
cvt.rn.relu.satfinite.e5m2x2.f16x2 d, a; // unpack a and convert the values to .e5m2 outputs with .relu
                                         // saturation on each output and pack as .e5m2x2
cvt.rn.f16x2.e4m3x2 d, a;             // unpack a, convert two .e4m3 values to packed f16x2 output
cvt.rn.satfinite.tf32.f32 d, a;       // convert fp32 to tf32 format
cvt.rn.relu.f16x2.e2m1x2 d, a;        // unpack a, convert two .e2m1 values to packed f16x2 output
cvt.rn.satfinite.e2m3x2.f32 d, a, b;  // convert a, b to .e2m3 and pack as .e2m3x2 output
cvt.rn.relu.f16x2.e3m2x2 d, a;        // unpack a, convert two .e3m2 values to packed f16x2 output

cvt.rs.f16x2.f32    d, a, b, rbits;  // convert 2 fp32 values to packed fp16 with applying .rs rounding
cvt.rs.satfinite.e2m1x4.f32  d, {a, b, e, f}, rbits; // convert 4 fp32 values to packed 4 e2m1 values with applying .rs rounding

9.7.9.22. 数据移动与转换指令: cvt.pack

cvt.pack

将两个整数值从一种整数类型转换为另一种类型并打包结果。

语法

cvt.pack.sat.convertType.abType  d, a, b;
    .convertType  = { .u16, .s16 }
    .abType       = { .s32 }

cvt.pack.sat.convertType.abType.cType  d, a, b, c;
    .convertType  = { .u2, .s2, .u4, .s4, .u8, .s8 }
    .abType       = { .s32 }
    .cType        = { .b32 }

描述

将两个32位整数ab转换为指定类型并将结果打包到d中。

目标 d 是一个无符号32位整数。源操作数 ab 是类型为 .abType 的整数,源操作数 c 是类型为 .cType 的整数。

输入 ab 会被转换为由 .convertType 指定的类型值(带饱和处理),转换后的结果会被打包到 d 的低位中。

如果指定了操作数 c,则 d 的剩余位将从 c 的低位复制。

语义

ta = a < MIN(convertType) ? MIN(convertType) : a;
ta = a > MAX(convertType) ? MAX(convertType) : a;
tb = b < MIN(convertType) ? MIN(convertType) : b;
tb = b > MAX(convertType) ? MAX(convertType) : b;

size = sizeInBits(convertType);
td = tb ;
for (i = size; i <= 2 * size - 1; i++) {
    td[i] = ta[i - size];
}

if (isU16(convertType) || isS16(convertType)) {
    d = td;
} else {
    for (i = 0; i < 2 * size; i++) {
        d[i] = td[i];
    }
    for (i = 2 * size; i <= 31; i++) {
        d[i] = c[i - 2 * size];
    }
}

.sat修饰符将转换后的值限制在MIN(convertType)MAX(convertedType)范围内(无溢出),如果对应的输入值不在.convertType指定的数据类型范围内。

PTX ISA 说明

在PTX ISA 6.5版本中引入。

目标ISA注意事项

需要 sm_72 或更高版本。

子字节类型 (.u4/.s4.u2/.s2) 需要 sm_75 或更高版本。

示例

cvt.pack.sat.s16.s32      %r1, %r2, %r3;           // 32-bit to 16-bit conversion
cvt.pack.sat.u8.s32.b32   %r4, %r5, %r6, 0;        // 32-bit to 8-bit conversion
cvt.pack.sat.u8.s32.b32   %r7, %r8, %r9, %r4;      // %r7 = { %r5, %r6, %r8, %r9 }
cvt.pack.sat.u4.s32.b32   %r10, %r12, %r13, %r14;  // 32-bit to 4-bit conversion
cvt.pack.sat.s2.s32.b32   %r15, %r16, %r17, %r18;  // 32-bits to 2-bit conversion

9.7.9.23. 数据移动与转换指令:mapa

地图

映射目标CTA中共享变量的地址。

语法

mapa{.space}.type          d, a, b;

// Maps shared memory address in register a into CTA b.
mapa.shared::cluster.type  d, a, b;

// Maps shared memory variable into CTA b.
mapa.shared::cluster.type  d, sh, b;

// Maps shared memory variable into CTA b.
mapa.shared::cluster.type  d, sh + imm, b;

// Maps generic address in register a into CTA b.
mapa.type                  d, a, b;

.space = { .shared::cluster }
.type  = { .u32, .u64 }

描述

获取由操作数b指定的CTA中的地址,该地址对应于由操作数a指定的地址。

指令类型 .type 表示目标操作数 d 和源操作数 a 的类型。

当空间为.shared::cluster时,源a可以是共享内存变量或包含有效共享内存地址的寄存器,而寄存器d包含共享内存地址。当未指定可选限定符.space时,ad都是包含指向共享内存的通用地址的寄存器。

b 是一个32位整数操作数,表示目标CTA的等级。

目标寄存器 d 将保存与操作数 a 对应的CTA b 中的地址。

PTX ISA 说明

在PTX ISA版本7.8中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

mapa.shared::cluster.u64 d1, %reg1, cta;
mapa.shared::cluster.u32 d2, sh, 3;
mapa.u64                 d3, %reg2, cta;

9.7.9.24. 数据移动与转换指令:getctarank

getctarank

生成该地址的CTA排名。

语法

getctarank{.space}.type d, a;

// Get cta rank from source shared memory address in register a.
getctarank.shared::cluster.type d, a;

// Get cta rank from shared memory variable.
getctarank.shared::cluster.type d, var;

// Get cta rank from shared memory variable+offset.
getctarank.shared::cluster.type d, var + imm;

// Get cta rank from generic address of shared memory variable in register a.
getctarank.type d, a;

.space = { .shared::cluster }
.type  = { .u32, .u64 }

描述

将包含操作数a中指定地址的CTA的排名写入目标寄存器d

指令类型 .type 表示源操作数 a 的类型。

当空间为.shared::cluster时,源a可以是共享内存变量,也可以是包含有效共享内存地址的寄存器。当未指定可选限定符.space时,a是一个包含指向共享内存的通用地址的寄存器。目标d始终是一个32位寄存器,用于保存CTA的排名。

PTX ISA 说明

在PTX ISA版本7.8中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

getctarank.shared::cluster.u32 d1, addr;
getctarank.shared::cluster.u64 d2, sh + 4;
getctarank.u64                 d3, src;

9.7.9.25. 数据移动与转换指令:异步拷贝

异步复制操作在后台以异步方式执行底层操作,从而使发出线程能够执行后续任务。

异步复制操作可以是处理大量数据的批量操作,也可以是处理较小数据量的非批量操作。批量异步操作处理的数据量必须是16字节的倍数。

异步复制操作通常包括以下步骤序列:

  • 可选地,从张量映射中读取。

  • 从源位置读取数据。

  • 将数据写入目标位置。

  • 写入操作对执行线程或其他线程可见。

9.7.9.25.1. 异步拷贝操作的完成机制

线程必须显式等待异步复制操作完成,才能访问该操作的结果。一旦启动异步复制操作,在异步操作完成之前修改源内存位置或张量描述符,或从目标内存位置读取,将导致未定义行为。

本节介绍PTX支持的两种异步拷贝操作完成机制:异步组机制和基于mbarrier的机制。

异步操作可以通过任一完成机制或两种机制进行跟踪。跟踪机制是特定于指令/指令变体的。

9.7.9.25.1.1. 异步组机制

在使用异步组完成机制时,发起线程通过提交操作指定一组称为异步组的异步操作,并通过等待操作跟踪该组的完成情况。发起异步操作的线程必须为批量和非批量异步操作创建独立的异步组

提交操作会创建一个每线程的异步组,其中包含由执行线程发起并通过异步组完成跟踪的所有先前异步操作,但不包含提交操作之后的任何异步操作。已提交的异步操作属于单个异步组

当一个异步组完成时,属于该组的所有异步操作均已完成,发起这些异步操作的执行线程可以读取异步操作的结果。由执行线程提交的所有异步组总是按照提交顺序完成。在同一个异步组内,各异步操作之间没有执行顺序要求。

使用async-group作为完成机制的典型模式如下:

  • 启动异步操作。

  • 使用提交操作将异步操作分组到异步组中。

  • 使用等待操作等待异步组的完成。

  • async-group完成后,访问该async-group中所有异步操作的结果。

9.7.9.25.1.2. 基于Mbarrier的机制

线程可以使用mbarrier对象的当前阶段来跟踪一个或多个异步操作的完成情况。当mbarrier对象的当前阶段完成时,意味着该阶段跟踪的所有异步操作都已完成,参与该mbarrier对象的所有线程都可以访问异步操作的结果。

用于跟踪异步操作完成的mbarrier对象可以随异步操作在其语法中一并指定,也可以作为单独操作指定。对于批量异步操作,mbarrier对象必须在异步操作中指定;而对于非批量操作,则可以在异步操作之后指定。

使用基于mbarrier的完成机制的典型模式如下:

  • 启动异步操作。

  • 设置一个mbarrier对象来跟踪其当前阶段的异步操作,既可以作为异步操作的一部分,也可以作为单独的操作。

  • 使用mbarrier.test_waitmbarrier.try_wait等待mbarrier对象完成当前阶段。

  • 一旦mbarrier.test_waitmbarrier.try_wait操作返回True,即可访问由mbarrier对象追踪的异步操作结果。

9.7.9.25.2. Async Proxy

cp{.reduce}.async.bulk操作在异步代理(或称async proxy)中执行。

跨多个代理访问相同内存位置需要使用跨代理栅栏。对于异步代理,应使用fence.proxy.async来同步通用代理异步代理之间的内存。

cp{.reduce}.async.bulk操作完成后会隐式执行一个通用异步代理栅栏。因此异步操作的结果在其完成时即可被通用代理观察到。必须使用异步组基于mbarrier的完成机制来等待cp{.reduce}.async.bulk指令的完成。

9.7.9.25.3. 数据移动与转换指令:非批量拷贝
9.7.9.25.3.1. 数据移动与转换指令:cp.async

cp.async

启动从一个状态空间到另一个状态空间的异步复制操作。

语法

cp.async.ca.shared{::cta}.global{.level::cache_hint}{.level::prefetch_size}
                         [dst], [src], cp-size{, src-size}{, cache-policy} ;
cp.async.cg.shared{::cta}.global{.level::cache_hint}{.level::prefetch_size}
                         [dst], [src], 16{, src-size}{, cache-policy} ;
cp.async.ca.shared{::cta}.global{.level::cache_hint}{.level::prefetch_size}
                         [dst], [src], cp-size{, ignore-src}{, cache-policy} ;
cp.async.cg.shared{::cta}.global{.level::cache_hint}{.level::prefetch_size}
                         [dst], [src], 16{, ignore-src}{, cache-policy} ;

.level::cache_hint =     { .L2::cache_hint }
.level::prefetch_size =  { .L2::64B, .L2::128B, .L2::256B }
cp-size =                { 4, 8, 16 }

描述

cp.async 是一个非阻塞指令,用于启动从源地址操作数 src 指定的位置到目标地址操作数 dst 指定位置的异步数据复制操作。操作数 src 指定全局状态空间中的一个位置,而 dst 指定共享状态空间中的一个位置。

操作数 cp-size 是一个整数常量,用于指定要复制到目标 dst 的数据大小(以字节为单位)。cp-size 只能是 4、8 或 16。

指令 cp.async 允许可选地指定一个32位整数操作数 src-size。操作数 src-size 表示要从 src 复制到 dst 的数据大小(以字节为单位),且必须 小于 cp-size。在这种情况下,目标地址 dst 中剩余的字节会被填充为 零。如果指定的 src-size 大于 cp-size,将导致未定义行为。

可选且非立即执行的谓词参数ignore-src用于指定是否应完全忽略源位置src的数据。如果忽略源数据,则会将零值复制到目标位置dst。若未指定参数ignore-src,则默认值为False

操作数 srcdst 所支持的对齐要求和寻址模式在地址作为操作数中有详细说明。

强制性的.async限定符表示cp指令将异步启动内存复制操作,并且在复制操作完成前控制权会返回到执行线程。执行线程随后可以使用基于异步组的完成机制基于mbarrier的完成机制来等待异步复制操作的完成。其他同步机制均无法保证异步复制操作的完成。

如果两个cp.async操作没有显式地使用cp.async.wait_allcp.async.wait_groupmbarrier指令进行同步,则它们之间没有顺序保证。

Cache Operators中所述,.cg限定符表示数据仅缓存在全局级别的L2缓存中,而不缓存在L1中,而.ca限定符表示数据在所有级别(包括L1缓存)都会被缓存。缓存操作符仅被视为性能提示。

cp.async内存一致性模型中被视为弱内存操作。

.level::prefetch_size 限定符是一个提示,用于将指定大小的额外数据预取到相应的缓存层级。子限定符 prefetch_size 可以设置为 64B128B256B,从而允许预取大小分别为64字节、128字节或256字节。

限定符 .level::prefetch_size 只能与 .global 状态空间以及指向 .global 状态空间的通用寻址一起使用。如果通用地址不在全局内存的地址窗口范围内,则预取行为是未定义的。

.level::prefetch_size 限定符仅被视为性能提示。

当指定可选参数cache-policy时,限定符.level::cache_hint是必需的。64位操作数cache-policy指定了在内存访问期间可能使用的缓存逐出策略。

限定符 .level::cache_hint 仅支持 .global 状态空间以及指向 .global 状态空间的通用寻址。

cache-policy 是对缓存子系统的提示,可能不会始终被遵循。它仅被视为性能提示,不会改变程序的内存一致性行为。

PTX ISA 说明

在PTX ISA版本7.0中引入。

支持在PTX ISA版本7.4中引入的.level::cache_hint.level::prefetch_size限定符。

支持在PTX ISA 7.5版本中引入的ignore-src操作数。

支持PTX ISA版本7.8中引入的子限定符::cta

目标ISA注意事项

需要 sm_80 或更高版本。

子限定符 ::cta 需要 sm_30 或更高版本。

示例

cp.async.ca.shared.global  [shrd],    [gbl + 4], 4;
cp.async.ca.shared::cta.global  [%r0 + 8], [%r1],     8;
cp.async.cg.shared.global  [%r2],     [%r3],     16;

cp.async.cg.shared.global.L2::64B   [%r2],      [%r3],     16;
cp.async.cg.shared.global.L2::128B  [%r0 + 16], [%r1],     16;
cp.async.cg.shared.global.L2::256B  [%r2 + 32], [%r3],     16;

createpolicy.fractional.L2::evict_last.L2::evict_unchanged.b64 cache-policy, 0.25;
cp.async.ca.shared.global.L2::cache_hint [%r2], [%r1], 4, cache-policy;

cp.async.ca.shared.global                   [shrd], [gbl], 4, p;
cp.async.cg.shared.global.L2::cache_hint   [%r0], [%r2], 16, q, cache-policy;
9.7.9.25.3.2. 数据移动与转换指令:cp.async.commit_group

cp.async.commit_group

将所有先前已发起但未提交的cp.async指令提交到一个cp.async-group中。

语法

cp.async.commit_group ;

描述

cp.async.commit_group 指令会为每个线程创建一个新的cp.async-group,并将执行线程发起但尚未提交到任何cp.async-group的所有先前cp.async指令批量归入这个新组。如果没有未提交的cp.async指令,则cp.async.commit_group会生成一个空的cp.async-group

执行线程可以使用cp.async.wait_group来等待cp.async-group中所有cp.async操作完成。

在同一cp.async-group内的任意两个cp.async操作之间不提供内存顺序保证。因此,在cp.async-group内有两个或多个cp.async操作向同一位置复制数据会导致未定义行为。

PTX ISA 说明

在PTX ISA版本7.0中引入。

目标ISA注意事项

需要 sm_80 或更高版本。

示例

// Example 1:
cp.async.ca.shared.global [shrd], [gbl], 4;
cp.async.commit_group ; // Marks the end of a cp.async group

// Example 2:
cp.async.ca.shared.global [shrd1],   [gbl1],   8;
cp.async.ca.shared.global [shrd1+8], [gbl1+8], 8;
cp.async.commit_group ; // Marks the end of cp.async group 1

cp.async.ca.shared.global [shrd2],    [gbl2],    16;
cp.async.cg.shared.global [shrd2+16], [gbl2+16], 16;
cp.async.commit_group ; // Marks the end of cp.async group 2
9.7.9.25.3.3. 数据移动与转换指令:cp.async.wait_group / cp.async.wait_all

cp.async.wait_group/cp.async.wait_all

等待之前异步复制操作完成。

语法

cp.async.wait_group N;
cp.async.wait_all ;

描述

cp.async.wait_group指令会使执行线程等待,直到最近的cp.async-group中只有N个或更少处于挂起状态,并且执行线程之前提交的所有cp.async-group都已完成。例如,当N为0时,执行线程会等待之前所有的cp.async-group完成。操作数N是一个整数常量。

cp.async.wait_all 等同于:

cp.async.commit_group;
cp.async.wait_group 0;

一个空的cp.async-group被视为自动完成。

通过cp.async操作执行的写入只有在以下情况后才会对执行线程可见:

  1. 完成 cp.async.wait_all

  2. 完成cp.async.wait_group操作,该操作属于cp.async-group组,其中包含cp.async操作,或者

  3. mbarrier.test_wait 在跟踪cp.async操作完成的mbarrier对象上返回True

两个未通过cp.async.wait_allcp.async.wait_groupmbarrier对象同步的cp.async操作之间不存在执行顺序保证。

cp.async.wait_groupcp.async.wait_all 除了对 cp.async 操作外,不提供任何其他内存操作的顺序性和可见性保证。

PTX ISA 说明

在PTX ISA版本7.0中引入。

目标ISA注意事项

需要 sm_80 或更高版本。

示例

// Example of .wait_all:
cp.async.ca.shared.global [shrd1], [gbl1], 4;
cp.async.cg.shared.global [shrd2], [gbl2], 16;
cp.async.wait_all;  // waits for all prior cp.async to complete

// Example of .wait_group :
cp.async.ca.shared.global [shrd3], [gbl3], 8;
cp.async.commit_group;  // End of group 1

cp.async.cg.shared.global [shrd4], [gbl4], 16;
cp.async.commit_group;  // End of group 2

cp.async.cg.shared.global [shrd5], [gbl5], 16;
cp.async.commit_group;  // End of group 3

cp.async.wait_group 1;  // waits for group 1 and group 2 to complete
9.7.9.25.4. 数据移动与转换指令:批量复制
9.7.9.25.4.1. 数据移动与转换指令:cp.async.bulk

cp.async.bulk

启动从一个状态空间到另一个状态空间的异步复制操作。

语法

// global -> shared::cta
cp.async.bulk.dst.src.completion_mechanism{.level::cache_hint}
                      [dstMem], [srcMem], size, [mbar] {, cache-policy}

.dst =                  { .shared::cta }
.src =                  { .global }
.completion_mechanism = { .mbarrier::complete_tx::bytes }
.level::cache_hint =    { .L2::cache_hint }


// global -> shared::cluster
cp.async.bulk.dst.src.completion_mechanism{.multicast}{.level::cache_hint}
                      [dstMem], [srcMem], size, [mbar] {, ctaMask} {, cache-policy}

.dst =                  { .shared::cluster }
.src =                  { .global }
.completion_mechanism = { .mbarrier::complete_tx::bytes }
.level::cache_hint =    { .L2::cache_hint }
.multicast =            { .multicast::cluster  }


// shared::cta -> shared::cluster
cp.async.bulk.dst.src.completion_mechanism [dstMem], [srcMem], size, [mbar]

.dst =                  { .shared::cluster }
.src =                  { .shared::cta }
.completion_mechanism = { .mbarrier::complete_tx::bytes }


// shared::cta -> global
cp.async.bulk.dst.src.completion_mechanism{.level::cache_hint}{.cp_mask}
                      [dstMem], [srcMem], size {, cache-policy} {, byteMask}

.dst =                  { .global }
.src =                  { .shared::cta }
.completion_mechanism = { .bulk_group }
.level::cache_hint =    { .L2::cache_hint }

描述

cp.async.bulk 是一条非阻塞指令,用于启动从源地址操作数 srcMem 指定位置到目标地址操作数 dstMem 指定位置的异步批量复制操作。

批量复制的方向是从.src修饰符指定的状态空间到.dst修饰符指定的状态空间。

32位操作数size指定要复制的内存量,以字节数为单位。size必须是16的倍数。如果该值不是16的倍数,则行为未定义。内存范围[dstMem, dstMem + size - 1]不得超出目标内存空间,内存范围[srcMem, srcMem + size - 1]不得超出源内存空间。否则,行为未定义。地址dstMemsrcMem必须按16字节对齐。

当复制目标地址为.shared::cta时,目标地址必须位于集群内执行CTA的共享内存中,否则行为将是未定义的。

当复制操作的来源是.shared::cta而目标是.shared::cluster时,目标必须位于集群内另一个CTA的共享内存中。

修饰符 .completion_mechanism 指定了该指令变体支持的完成机制。不同变体支持的完成机制总结如下表:

.completion-mechanism

.dst

.src

完成机制

完成整个异步操作所需

可选用于完成以下操作: - 从数据源读取数据 - 从tensormap读取(如适用)

.mbarrier::...

.shared::cta

.global

基于mbarrier

批量异步组 基础

.shared::cluster

.global

.shared::cluster

.shared::cta

.bulk_group

.global

.shared::cta

批量异步分组 为基础

修饰符 .mbarrier::complete_tx::bytes 指定了 cp.async.bulk 变体使用基于mbarrier的完成机制。complete-tx操作将以等于复制数据字节数的completeCount参数值,在操作数mbar指定的mbarrier对象上执行。

修饰符 .bulk_group 指定 cp.async.bulk 变体使用基于批量异步组的完成机制。

可选修饰符 .multicast::cluster 允许将数据从全局内存复制到集群中多个CTA的共享内存。操作数 ctaMask 指定集群中的目标CTA,其中16位 ctaMask 操作数的每个比特位对应目标CTA的 %ctaid。源数据会被多播到每个目标CTA共享内存中与 dstMem 相同的CTA相对偏移位置。mbarrier信号也会被多播到目标CTA共享内存中与 mbar 相同的CTA相对偏移位置。

当指定可选参数cache-policy时,限定符.level::cache_hint是必需的。64位操作数cache-policy指定了在内存访问期间可能使用的缓存逐出策略。

cache-policy 是对缓存子系统的提示,可能不会总是被遵守。它仅被视为性能提示,不会改变程序的内存一致性行为。修饰符 .level::cache_hint 仅在至少一个 .src.dst 状态空间是 .global 状态空间时才受支持。

当指定可选限定符.cp_mask时,必须提供参数byteMask。 16位宽的byteMask操作数中的第i位决定了源数据每个16字节宽块中的第i个字节是否被复制到目标位置。如果该位被设置,则复制该字节。

cp.async.bulk中的复制操作被视为弱内存操作,而mbarrier上的complete-tx操作在.cluster范围内具有.release语义,如Memory Consistency Model中所述。

注意事项

.multicast::cluster 限定符针对目标架构 sm_90a/sm_100a/sm_101a 进行了优化, 在其他目标架构上性能可能会大幅降低,因此建议将 .multicast::cluster.target sm_90a/sm_100a/sm_101a 配合使用。

PTX ISA 说明

在PTX ISA版本8.0中引入。

在PTX ISA版本8.6中引入了对.shared::cta作为目标状态空间的支持。

支持PTX ISA版本8.6中引入的.cp_mask限定符。

目标ISA注意事项

需要 sm_90 或更高版本。

.multicast::cluster 修饰符建议与 .target sm_90asm_100asm_101a 一起使用。

支持 .cp_mask 限定符需要 sm_100 或更高版本。

示例

// .global -> .shared::cta (strictly non-remote):
cp.async.bulk.shared::cta.global.mbarrier::complete_tx::bytes [dstMem], [srcMem], size, [mbar];

cp.async.bulk.shared::cta.global.mbarrier::complete_tx::bytes.L2::cache_hint
                                             [dstMem], [srcMem], size, [mbar], cache-policy;

// .global -> .shared::cluster:
cp.async.bulk.shared::cluster.global.mbarrier::complete_tx::bytes [dstMem], [srcMem], size, [mbar];

cp.async.bulk.shared::cluster.global.mbarrier::complete_tx::bytes.multicast::cluster
                                             [dstMem], [srcMem], size, [mbar], ctaMask;

cp.async.bulk.shared::cluster.global.mbarrier::complete_tx::bytes.L2::cache_hint
                                             [dstMem], [srcMem], size, [mbar], cache-policy;


// .shared::cta -> .shared::cluster (strictly remote):
cp.async.bulk.shared::cluster.shared::cta.mbarrier::complete_tx::bytes [dstMem], [srcMem], size, [mbar];

// .shared::cta -> .global:
cp.async.bulk.global.shared::cta.bulk_group [dstMem], [srcMem], size;

cp.async.bulk.global.shared::cta.bulk_group.L2::cache_hint} [dstMem], [srcMem], size, cache-policy;

// .shared::cta -> .global with .cp_mask:
cp.async.bulk.global.shared::cta.bulk_group.L2::cache_hint.cp_mask [dstMem], [srcMem], size, cache-policy, byteMask;
9.7.9.25.4.2. 数据移动与转换指令: cp.reduce.async.bulk

cp.reduce.async.bulk

启动一个异步归约操作。

语法

cp.reduce.async.bulk.dst.src.completion_mechanism.redOp.type
              [dstMem], [srcMem], size, [mbar]

.dst =                  { .shared::cluster }
.src =                  { .shared::cta }
.completion_mechanism = { .mbarrier::complete_tx::bytes }
.redOp=                 { .and, .or, .xor,
                          .add, .inc, .dec,
                          .min, .max }
.type =                 { .b32, .u32, .s32, .b64, .u64 }


cp.reduce.async.bulk.dst.src.completion_mechanism{.level::cache_hint}.redOp.type
               [dstMem], [srcMem], size{, cache-policy}

.dst =                  { .global      }
.src =                  { .shared::cta }
.completion_mechanism = { .bulk_group }
.level::cache_hint    = { .L2::cache_hint }
.redOp=                 { .and, .or, .xor,
                          .add, .inc, .dec,
                          .min, .max }
.type =                 { .f16, .bf16, .b32, .u32, .s32, .b64, .u64, .s64, .f32, .f64 }


cp.reduce.async.bulk.dst.src.completion_mechanism{.level::cache_hint}.add.noftz.type
               [dstMem], [srcMem], size{, cache-policy}
.dst  =                 { .global }
.src  =                 { .shared::cta }
.completion_mechanism = { .bulk_group }
.type =                 { .f16, .bf16 }

描述

cp.reduce.async.bulk 是一个非阻塞指令,用于在由目标地址操作数 dstMem 指定的内存位置数组上启动异步归约操作,源数组的位置由源地址操作数 srcMem 指定。源数组和目标数组的大小必须相同,并由操作数 size 指定。

目标数组中的每个数据元素会与源数组中对应的数据元素进行内联缩减操作,缩减操作由修饰符.redOp指定。源数组和目标数组中每个数据元素的类型由修饰符.type指定。

源地址操作数 srcMem 位于由 .src 指定的状态空间中,而目标地址操作数 dstMem 则位于由 .dst 指定的状态中。

32位操作数size指定了从源位置复制并用于归约操作的内存大小,以字节数为单位。size必须是16的倍数。如果该值不是16的倍数,则行为未定义。内存范围[dstMem, dstMem + size - 1]不得超出目标内存空间,且内存范围[srcMem, srcMem + size - 1]不得超出源内存空间。否则,行为未定义。地址dstMemsrcMem必须按16字节对齐。

.redOp 支持的操作分类如下:

  • 位运算操作包括 .and.or.xor

  • 整数运算包括.add.inc.dec.min.max。其中.inc.dec运算返回的结果范围在[0..x]之间,这里的x表示源状态空间中的值。

  • 浮点运算.add会四舍五入到最近的偶数。当前实现的cp.reduce.async.bulk.add.f32会将次正规输入和结果刷新为保留符号的零值。而cp.reduce.async.bulk.add.f16cp.reduce.async.bulk.add.bf16运算需要.noftz限定符,它能保留输入和结果的次正规数,不会将其刷新为零。

下表描述了.redOp与元素类型的有效组合:

.dst

.redOp

元素类型

.shared::cluster

.add

.u32, .s32, .u64

.min, .max

.u32, .s32

.inc, .dec

.u32

.and, .or, .xor

.b32

.global

.add

.u32, .s32, .u64, .f32, .f64, .f16, .bf16

.min, .max

.u32, .s32, .u64, .s64, .f16, .bf16

.inc, .dec

.u32

.and, .or, .xor

.b32, .b64

修饰符 .completion_mechanism 指定了该指令变体支持的完成机制。不同变体支持的完成机制总结如下表:

.completion-mechanism

.dst

.src

完成机制

完成整个异步操作所需

可选用于完成以下操作: - 从数据源读取数据 - 从tensormap读取(如适用)

.mbarrier::...

.shared::cluster

.global

基于mbarrier

批量异步组 基础

.shared::cluster

.shared::cta

.bulk_group

.global

.shared::cta

批量异步分组 为基础

修饰符 .mbarrier::complete_tx::bytes 指定 cp.reduce.async.bulk 变体 使用基于mbarrier的完成机制。complete-tx 操作将以completeCount参数等于复制数据字节数的形式, 在操作数mbar指定的mbarrier对象上执行。

修饰符 .bulk_group 指定 cp.reduce.async.bulk 变体使用基于批量异步组的完成机制。

当指定可选参数cache-policy时,限定符.level::cache_hint是必需的。64位操作数cache-policy指定了在内存访问期间可能使用的缓存逐出策略。

cache-policy 是对缓存子系统的提示,可能不会总是被遵守。它仅被视为性能提示,不会改变程序的内存一致性行为。修饰符 .level::cache_hint 仅在至少一个 .src.dst 状态空间是 .global 状态空间时才受支持。

cp.reduce.async.bulk执行的每个归约操作都具有独立的.relaxed.gpu内存排序语义。cp.reduce.async.bulk中的加载操作被视为弱内存操作,而mbarrier上的complete-tx操作在.cluster范围内具有.release语义,如Memory Consistency Model中所述。

PTX ISA 说明

在PTX ISA版本8.0中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

cp.reduce.async.bulk.shared::cluster.shared::cta.mbarrier::complete_tx::bytes.add.u64
                                                                  [dstMem], [srcMem], size, [mbar];

cp.reduce.async.bulk.shared::cluster.shared::cta.mbarrier::complete_tx::bytes.min.s32
                                                                  [dstMem], [srcMem], size, [mbar];

cp.reduce.async.bulk.global.shared::cta.bulk_group.min.f16 [dstMem], [srcMem], size;

cp.reduce.async.bulk.global.shared::cta.bulk_group.L2::cache_hint.xor.s32 [dstMem], [srcMem], size, policy;

cp.reduce.async.bulk.global.shared::cta.bulk_group.add.noftz.f16 [dstMem], [srcMem], size;
9.7.9.25.4.3. 数据移动与转换指令:cp.async.bulk.prefetch

cp.async.bulk.prefetch

向系统提供提示,以异步预取数据到缓存中。

语法

cp.async.bulk.prefetch.L2.src{.level::cache_hint}   [srcMem], size {, cache-policy}

.src =                { .global }
.level::cache_hint =  { .L2::cache_hint }

描述

cp.async.bulk.prefetch 是一个非阻塞指令,可以从源地址操作数 srcMem 指定的位置(位于 .src 状态空间中)异步预取数据到 L2 缓存。

32位操作数size指定了预取内存的字节数。size必须是16的倍数。如果该值不是16的倍数,则行为是未定义的。地址srcMem必须按16字节对齐。

当指定可选参数cache-policy时,限定符.level::cache_hint是必需的。64位操作数cache-policy指定了在内存访问期间可能使用的缓存逐出策略。

cache-policy 是对缓存子系统的提示,可能不会始终被遵循。它仅被视为性能提示,不会改变程序的内存一致性行为。

PTX ISA 说明

在PTX ISA版本8.0中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

cp.async.bulk.prefetch.L2.global                 [srcMem], size;

cp.async.bulk.prefetch.L2.global.L2::cache_hint  [srcMem], size, policy;
9.7.9.25.5. 数据移动与转换指令:张量复制
9.7.9.25.5.1. 张量复制指令的限制

以下是针对类型 .b4x16, .b4x16_p64, .b6x16_p32.b6p2x16 的限制:

  1. cp.reduce.async.bulk 不支持类型 .b4x16, .b4x16_p64, .b6x16_p32.b6p2x16

  2. cp.async.bulk.tensor 使用方向 .global.shared::cta 时不支持类型 .b4x16_p64

  3. cp.async.bulk.tensor 使用方向 .shared::cluster.global 时,不支持在 sm_120a 架构上使用子字节类型。

  4. OOB-NaN填充模式不支持以下类型:.b4x16.b4x16_p64.b6x16_p32.b6p2x16

  5. Box-Size[0] 必须精确为:

    1. b6x16_p32.b6p2x16 需要 96B。

    2. 64B 用于 b4x16_p64

  6. Tensor-Size[0] 必须是以下数值的倍数:

    1. b6x16_p32.b6p2x16 各占 96B。

    2. 64B 用于 b4x16_p64

  7. 对于 .b4x16_p64.b6x16_p32.b6p2x16,tensorCoords参数向量中的第一个坐标必须是128的倍数。

  8. 对于 .b4x16_p64.b6x16_p32.b6p2x16,全局内存地址必须32字节对齐。

  9. .b4x16_p64, .b6x16_p32.b6p2x16 支持以下交换模式:

    1. 无。

    2. 128字节

以下是关于.global.shared::cta方向的限制:

  1. 沿D、W和H维度的边界框必须保持在张量边界内。这意味着:

    1. 边界框左下角坐标必须为非负数。

    2. 边界框右上角必须为非正值。

以下是针对sm_120a的限制条件:

  1. cp.async.bulk.tensor 在方向 .shared::cluster.global 下不支持:

    1. 子字节类型

    2. 限定符 .swizzle_atomicity

9.7.9.25.5.2. 数据移动与转换指令:cp.async.bulk.tensor

cp.async.bulk.tensor

在张量数据上启动一个从一种状态空间到另一种状态空间的异步复制操作。

语法

// global -> shared::cta
cp.async.bulk.tensor.dim.dst.src{.load_mode}.completion_mechanism{.cta_group}{.level::cache_hint}
                                   [dstMem], [tensorMap, tensorCoords], [mbar]{, im2colInfo} {, cache-policy}

.dst =                  { .shared::cta }
.src =                  { .global }
.dim =                  { .1d, .2d, .3d, .4d, .5d }
.completion_mechanism = { .mbarrier::complete_tx::bytes }
.cta_group =            { .cta_group::1, .cta_group::2 }
.load_mode =            { .tile, .tile::gather4, .im2col, .im2col::w, .im2col::w::128 }
.level::cache_hint =    { .L2::cache_hint }


// global -> shared::cluster
cp.async.bulk.tensor.dim.dst.src{.load_mode}.completion_mechanism{.multicast}{.cta_group}{.level::cache_hint}
                                   [dstMem], [tensorMap, tensorCoords], [mbar]{, im2colInfo}
                                   {, ctaMask} {, cache-policy}

.dst =                  { .shared::cluster }
.src =                  { .global }
.dim =                  { .1d, .2d, .3d, .4d, .5d }
.completion_mechanism = { .mbarrier::complete_tx::bytes }
.cta_group =            { .cta_group::1, .cta_group::2 }
.load_mode =            { .tile, .tile::gather4, .im2col, .im2col::w, .im2col::w::128 }
.level::cache_hint =    { .L2::cache_hint }
.multicast =            { .multicast::cluster  }


// shared::cta -> global
cp.async.bulk.tensor.dim.dst.src{.load_mode}.completion_mechanism{.level::cache_hint}
                                   [tensorMap, tensorCoords], [srcMem] {, cache-policy}

.dst =                  { .global }
.src =                  { .shared::cta }
.dim =                  { .1d, .2d, .3d, .4d, .5d }
.completion_mechanism = { .bulk_group }
.load_mode =            { .tile, .tile::scatter4, .im2col_no_offs }
.level::cache_hint =    { .L2::cache_hint }

描述

cp.async.bulk.tensor 是一个非阻塞指令,用于启动从 .src 状态空间到 .dst 状态空间的张量数据异步复制操作。

操作数 dstMem 指定了张量数据需要复制到的 .dst 状态空间中的位置,而 srcMem 则指定了张量数据需要从 .src 状态空间中的哪个位置复制。

.dst指定为.shared::cta时,地址dstMem必须位于集群内执行CTA的共享内存中,否则行为是未定义的。

.dst指定为.shared::cluster时,地址dstMem可以位于当前集群内任意CTA的共享内存中。

操作数 tensorMap 是位于 .param 空间、.const 空间或 .global 空间中的不透明张量映射对象的通用地址。操作数 tensorMap 指定了张量复制操作的属性,如 Tensor-map 中所述。tensorMap 通过张量映射代理进行访问。有关在主机端创建张量映射对象的信息,请参阅CUDA编程指南

张量数据的维度由.dim修饰符指定。

向量操作数 tensorCoords 指定了全局内存中张量数据的起始坐标,复制操作将从该坐标读取或写入数据。tensorCoords 中的各个张量坐标类型为 .s32。向量参数 tensorCoords 的格式取决于指定的 .load_mode,具体如下:

.load_mode

tensorCoords

语义

.tile::scatter4

{col_idx, row_idx0, row_idx1, row_idx2, row_idx3}

固定长度为5的向量。 这五个元素共同指定了四行的起始坐标。

.tile::gather4

重置所有

{d0, .., dn} for n = .dim

包含n个元素的向量,其中n = .dim。 这些元素表示每个维度上的偏移量。

修饰符 .completion_mechanism 指定了该指令变体支持的完成机制。不同变体支持的完成机制总结如下表:

.completion-mechanism

.dst

.src

完成机制

完成整个异步操作所需

可选用于完成以下操作: - 从数据源读取数据 - 从tensormap读取(如适用)

.mbarrier::...

.shared::cta

.global

基于mbarrier

批量异步组 基础

.shared::cluster

.global

.bulk_group

.global

.shared::cta

批量异步分组 为基础

修饰符 .mbarrier::complete_tx::bytes 指定 cp.async.bulk.tensor 变体 使用基于mbarrier的完成机制。当异步复制操作完成后, complete-tx 操作将以completeCount参数等于复制数据字节数的形式, 在操作数mbar指定的mbarrier对象上执行。

修饰符 .cta_group 只能与基于mbarrier的完成机制一起指定。该修饰符 .cta_group 用于在CTA-Pair中标记奇数编号的CTA或偶数编号的CTA。当指定 .cta_group::1 时,所指定的mbarrier对象 mbar 必须位于与共享内存目标 dstMem 相同的CTA的共享内存中。当指定 .cta_group::2 时,mbarrier对象 mbar 可以位于与共享内存目标 dstMem 相同的CTA的共享内存中,也可以位于其peer-CTA中。如果未指定 .cta_group,则默认为 .cta_group::1

修饰符 .bulk_group 指定 cp.async.bulk.tensor 变体使用基于批量异步组的完成机制。

限定符 .load_mode 指定了如何将源位置的数据复制到目标位置。如果未指定 .load_mode,则默认为 .tile

.tile模式下,源张量的多维布局会在目标张量中保留。 在.tile::gather4模式下,二维源张量中的四行会被合并成一个二维目标张量。 在.tile::scatter4模式下,单个二维源张量会被分割成二维目标张量中的四行。 关于.tile::scatter4/.tile::gather4模式的详细信息请参阅Tiled mode

.im2col.im2col::*模式下,源张量的某些维度会在目标位置展开为单维列。im2col.im2col::*模式的详细信息分别描述于Im2col模式im2col::w与im2col::w::128模式。在.im2col.im2col::*模式下,张量必须至少是三维的。向量操作数im2colInfo仅当.load_mode.im2col.im2col::w.im2col::w::128时可指定。向量参数im2colInfo的格式取决于具体的im2col模式,具体如下:

精确im2col模式

im2colInfo参数

语义

.im2col

{ i2cOffW , i2cOffH , i2cOffD } 当 .dim = .5d

一个im2col偏移向量,其向量大小比维度数.dim少两个。

.im2col::w

{ wHalo, wOffset }

一个包含2个参数的向量,参数为 wHalowOffsets

.im2col::w::128

.im2col_no_offs

im2colInfo 不适用。

im2colInfo 不适用。

参数 wHalo 是一个16位无符号整数,其有效值范围根据加载模式不同而有所差异,具体如下: - Im2col::w 模式:有效范围为 [0, 512)。 - Im2col::w::128 模式:有效范围为 [0, 32)。

参数 wOffset 是一个16位无符号整数,其有效取值范围为 [0, 32)。

可选修饰符 .multicast::cluster 允许将数据从全局内存复制到集群中多个CTA的共享内存。操作数 ctaMask 指定集群中的目标CTA,其中16位 ctaMask 操作数的每个位位置对应目标CTA的 %ctaid。源数据会被多播到每个目标CTA共享内存中与 dstMem 相同的偏移位置。当指定 .cta_group 时:

  • .cta_group::1 : mbarrier信号也会多播到与mbar相同的偏移量,位于目标CTA的共享内存中。

  • .cta_group::2 : mbarrier信号会被多播到对应的CTA-Pair中所有奇数编号CTA或偶数编号CTA。对于ctaMask中指定的每个目标CTA,mbarrier信号会根据存储mbarrier对象mbar的共享内存中CTAs %cluster_ctarank的奇偶性,发送到目标CTA或其peer-CTA

当指定可选参数cache-policy时,限定符.level::cache_hint是必需的。64位操作数cache-policy指定了在内存访问期间可能使用的缓存逐出策略。

cache-policy 是对缓存子系统的提示,可能不会始终被遵循。它仅被视为性能提示,不会改变程序的内存一致性行为。

cp.async.bulk.tensor中的复制操作被视为弱内存操作,而mbarrier上的complete-tx操作在.cluster范围内具有.release语义,如Memory Consistency Model中所述。

注意事项

.multicast::cluster 限定符针对目标架构 sm_90a/sm_100a/sm_101a 进行了优化, 在其他目标架构上性能可能会大幅降低,因此建议将 .multicast::cluster.target sm_90a/sm_100a/sm_101a 配合使用。

PTX ISA 说明

在PTX ISA版本8.0中引入。

在PTX ISA版本8.6中引入了对.shared::cta作为目标状态空间的支持。

支持在PTX ISA版本8.6中引入的限定符.tile::gather4.tile::scatter4

支持在PTX ISA版本8.6中引入的限定符 .im2col::w.im2col::w::128

在PTX ISA版本8.6中引入了对限定符.cta_group的支持。

目标ISA注意事项

需要 sm_90 或更高版本。

.multicast::cluster 修饰符建议与 .target sm_90asm_100asm_101a 一起使用。

限定符 .tile::gather4.im2col::w 需要满足以下条件:

  • 当目标状态空间为.shared::cluster时使用sm_100a

  • 当目标状态空间为.shared::cta时,需要sm_100或更高版本。

限定符 .tile::scatter4 在以下架构上受支持:

  • sm_100a

  • sm_101a

限定符 .im2col::w::128 在以下架构上受支持:

  • sm_100a

  • sm_101a

限定符 .cta_group 在以下架构上受支持:

  • sm_100a

  • sm_101a

示例

.reg .b16 ctaMask;
.reg .u16 i2cOffW, i2cOffH, i2cOffD;
.reg .b64 l2CachePolicy;

cp.async.bulk.tensor.1d.shared::cta.global.mbarrier::complete_tx::bytes.tile  [sMem0], [tensorMap0, {tc0}], [mbar0];

@p cp.async.bulk.tensor.5d.shared::cta.global.im2col.mbarrier::complete_tx::bytes
                     [sMem2], [tensorMap2, {tc0, tc1, tc2, tc3, tc4}], [mbar2], {i2cOffW, i2cOffH, i2cOffD};

cp.async.bulk.tensor.1d.shared::cluster.global.mbarrier::complete_tx::bytes.tile  [sMem0], [tensorMap0, {tc0}], [mbar0];

@p cp.async.bulk.tensor.2d.shared::cluster.global.mbarrier::complete_tx::bytes.multicast::cluster
                     [sMem1], [tensorMap1, {tc0, tc1}], [mbar2], ctaMask;

@p cp.async.bulk.tensor.5d.shared::cluster.global.im2col.mbarrier::complete_tx::bytes
                     [sMem2], [tensorMap2, {tc0, tc1, tc2, tc3, tc4}], [mbar2], {i2cOffW, i2cOffH, i2cOffD};

@p cp.async.bulk.tensor.3d.im2col.shared::cluster.global.mbarrier::complete_tx::bytes.L2::cache_hint
                     [sMem3], [tensorMap3, {tc0, tc1, tc2}], [mbar3], {i2cOffW}, policy;

@p cp.async.bulk.tensor.1d.global.shared::cta.bulk_group  [tensorMap3, {tc0}], [sMem3];

cp.async.bulk.tensor.2d.tile::gather4.shared::cluster.global.mbarrier::complete_tx::bytes
                     [sMem5], [tensorMap6, {x0, y0, y1, y2, y3}], [mbar5];

cp.async.bulk.tensor.3d.im2col::w.shared::cluster.global.mbarrier::complete_tx::bytes
                     [sMem4], [tensorMap5, {t0, t1, t2}], [mbar4], {im2colwHalo, im2colOff};

cp.async.bulk.tensor.1d.shared::cluster.global.tile.cta_group::2
                     [sMem6], [tensorMap7, {tc0}], [peerMbar];
9.7.9.25.5.3. 数据移动与转换指令:cp.reduce.async.bulk.tensor

cp.reduce.async.bulk.tensor

对张量数据启动异步归约操作。

语法

// shared::cta -> global:
cp.reduce.async.bulk.tensor.dim.dst.src.redOp{.load_mode}.completion_mechanism{.level::cache_hint}
                                          [tensorMap, tensorCoords], [srcMem] {,cache-policy}

.dst =                  { .global }
.src =                  { .shared::cta }
.dim =                  { .1d, .2d, .3d, .4d, .5d }
.completion_mechanism = { .bulk_group }
.load_mode =            { .tile, .im2col_no_offs }
.redOp =                { .add, .min, .max, .inc, .dec, .and, .or, .xor}

描述

cp.reduce.async.bulk.tensor 是一个非阻塞指令,用于启动 .dst 状态空间中的张量数据与 .src 状态空间中的张量数据的异步归约操作。

操作数 srcMem 使用 .src 状态空间指定了张量数据的位置,基于此位置将执行归约操作。

操作数 tensorMap 是位于 .param 空间、.const 空间或 .global 空间中的不透明张量映射对象的通用地址。操作数 tensorMap 指定了张量复制操作的属性,如 Tensor-map 中所述。tensorMap 通过张量映射代理进行访问。有关在主机端创建张量映射对象的信息,请参阅CUDA编程指南

张量数据在.dst状态空间中的每个元素会与.src状态空间中对应的张量元素进行内联归约操作。修饰符.redOp指定了用于内联归约的归约运算类型。源张量和目标张量中每个数据元素的类型在Tensor-map中指定。

张量的维度由.dim修饰符指定。

向量操作数 tensorCoords 指定了要在全局内存中执行归约操作的张量数据的起始坐标。向量参数 tensorCoords 中的张量坐标数量应与修饰符 .dim 指定的维度相等。各个张量坐标的类型为 .s32

下表描述了.redOp与元素类型的有效组合:

.redOp

元素类型

.add

.u32, .s32, .u64, .f32, .f16, .bf16

.min, .max

.u32, .s32, .u64, .s64, .f16, .bf16

.inc, .dec

.u32

.and, .or, .xor

.b32, .b64

修饰符.completion_mechanism指定指令变体支持的完成机制。修饰符.completion_mechanism的值.bulk_group表示cp.reduce.async.bulk.tensor指令使用基于批量异步组的完成机制。

限定符.load_mode指定了源位置中的数据如何被复制到目标位置。如果未指定.load_mode,则默认为.tile模式。在.tile模式下,源张量的多维布局会在目标位置保持不变。而在.im2col_no_offs模式下,源张量的某些维度会在目标位置展开为单维列。im2col模式的详细说明请参阅Im2col mode。在.im2col模式下,张量必须至少是3维的。

当指定可选参数cache-policy时,限定符.level::cache_hint是必需的。64位操作数cache-policy指定了在内存访问期间可能使用的缓存逐出策略。

cache-policy 是对缓存子系统的提示,可能不会总是被遵守。它仅被视为性能提示,不会改变程序的内存一致性行为。修饰符 .level::cache_hint 仅在至少一个 .src.dst 状态空间是 .global 状态空间时才受支持。

cp.reduce.async.bulk.tensor执行的每个归约操作都具有独立的.relaxed.gpu内存排序语义。cp.reduce.async.bulk.tensor中的加载操作被视为弱内存操作,而mbarrier上的complete-tx操作在.cluster范围内具有.release语义,如Memory Consistency Model中所述。

PTX ISA 说明

在PTX ISA版本8.0中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

cp.reduce.async.bulk.tensor.1d.global.shared::cta.add.tile.bulk_group
                                             [tensorMap0, {tc0}], [sMem0];

cp.reduce.async.bulk.tensor.2d.global.shared::cta.and.bulk_group.L2::cache_hint
                                             [tensorMap1, {tc0, tc1}], [sMem1] , policy;

cp.reduce.async.bulk.tensor.3d.global.shared::cta.xor.im2col.bulk_group
                                             [tensorMap2, {tc0, tc1, tc2}], [sMem2]
9.7.9.25.5.4. 数据移动与转换指令:cp.async.bulk.prefetch.tensor

cp.async.bulk.prefetch.tensor

向系统提供提示,以异步预取张量数据到缓存中。

语法

// global -> shared::cluster:
cp.async.bulk.prefetch.tensor.dim.L2.src{.load_mode}{.level::cache_hint} [tensorMap, tensorCoords]
                                                             {, im2colInfo } {, cache-policy}

.src =                { .global }
.dim =                { .1d, .2d, .3d, .4d, .5d }
.load_mode =          { .tile, .tile::gather4, .im2col, .im2col::w, .im2col::w::128 }
.level::cache_hint =  { .L2::cache_hint }

描述

cp.async.bulk.prefetch.tensor 是一个非阻塞指令,可以从 .src 状态空间异步预取张量数据到 L2 缓存。

操作数 tensorMap 是位于 .param 空间、.const 空间或 .global 空间中的不透明张量映射对象的通用地址。操作数 tensorMap 指定了张量复制操作的属性,如 Tensor-map 中所述。tensorMap 通过张量映射代理进行访问。有关在主机端创建张量映射对象的信息,请参阅CUDA编程指南

张量数据的维度由.dim修饰符指定。

向量操作数 tensorCoords 指定了全局内存中张量数据的起始坐标,复制操作将从该坐标读取或写入数据。tensorCoords 中的各个张量坐标类型为 .s32。向量参数 tensorCoords 的格式取决于指定的 .load_mode,具体如下:

.load_mode

tensorCoords

语义

.tile::gather4

{col_idx, row_idx0, row_idx1, row_idx2, row_idx3}

固定长度为5的向量。 这五个元素共同指定了四行的起始坐标。

重置所有

{d0, .., dn} for n = .dim

n维向量,其中n = .dim。 元素表示每个维度上的偏移量。

限定符 .load_mode 指定了如何将源位置的数据复制到目标位置。如果未指定 .load_mode,则默认为 .tile

.tile模式下,源张量的多维布局会在目标位置保持不变。 在.tile::gather4模式下,二维源张量中的四行数据会被提取到L2缓存中。 关于.tile::gather4模式的详细说明, 请参阅Tiled mode

.im2col.im2col::*模式下,源张量的某些维度会在目标位置展开为单维列向量。im2col.im2col::*模式的具体细节分别描述于Im2col模式im2col::w与im2col::w::128模式。在.im2col.im2col::*模式下,张量必须至少是三维的。向量操作数im2colInfo仅当.load_mode.im2col.im2col::w.im2col::w::128`时才能指定。向量参数im2colInfo的格式取决于具体的im2col模式,具体如下:

精确im2col模式

im2colInfo参数

语义

.im2col

{ i2cOffW , i2cOffH , i2cOffD } 当 .dim = .5d

一个im2col偏移向量,其向量大小比维度数.dim少两个。

.im2col::w

{ wHalo, wOffset }

一个包含2个参数的向量,参数为 wHalowOffsets

.im2col::w::128

当指定可选参数cache-policy时,限定符.level::cache_hint是必需的。64位操作数cache-policy指定了在内存访问期间可能使用的缓存逐出策略。

cache-policy 是对缓存子系统的提示,可能不会始终被遵循。它仅被视为性能提示,不会改变程序的内存一致性行为。

cp.async.bulk.prefetch.tensor内存一致性模型中被视为弱内存操作。

PTX ISA 说明

在PTX ISA版本8.0中引入。

支持PTX ISA版本8.6中引入的限定符.tile::gather4

支持在PTX ISA版本8.6中引入的限定符 .im2col::w.im2col::w::128

目标ISA注意事项

需要 sm_90 或更高版本。

限定符 .tile::gather4 在以下架构上受支持:

  • sm_100a

  • sm_101a

限定符 .im2col::w.im2col::w::128 在以下架构上受支持:

  • sm_100a

  • sm_101a

示例

.reg .b16 ctaMask, im2colwHalo, im2colOff;
.reg .u16 i2cOffW, i2cOffH, i2cOffD;
.reg .b64 l2CachePolicy;

cp.async.bulk.prefetch.tensor.1d.L2.global.tile  [tensorMap0, {tc0}];

@p cp.async.bulk.prefetch.tensor.2d.L2.global    [tensorMap1, {tc0, tc1}];

@p cp.async.bulk.prefetch.tensor.5d.L2.global.im2col
                      [tensorMap2, {tc0, tc1, tc2, tc3, tc4}], {i2cOffW, i2cOffH, i2cOffD};

@p cp.async.bulk.prefetch.tensor.3d.L2.global.im2col.L2::cache_hint
                      [tensorMap3, {tc0, tc1, tc2}], {i2cOffW}, policy;

cp.async.bulk.prefetch.tensor.2d.L2.global.tile::gather4 [tensorMap5, {col_idx, row_idx0, row_idx1, row_idx2, row_idx3}];

cp.async.bulk.prefetch.tensor.4d.L2.global.im2col::w::128
                      [tensorMap4, {t0, t1, t2, t3}], {im2colwHalo, im2colOff};
9.7.9.25.6. 数据移动与转换指令:批量及张量拷贝完成指令
9.7.9.25.6.1. 数据移动与转换指令: cp.async.bulk.commit_group

cp.async.bulk.commit_group

将所有先前已发起但未提交的cp.async.bulk指令提交到一个cp.async.bulk-group中。

语法

cp.async.bulk.commit_group;

描述

cp.async.bulk.commit_group 指令会创建一个新的每线程批量异步组,并将所有满足以下条件的先前cp{.reduce}.async.bulk.{.prefetch}{.tensor}指令批量归入这个新的批量异步组中:

  • 之前的cp{.reduce}.async.bulk.{.prefetch}{.tensor}指令使用基于bulk_group的完成机制,并且

  • 它们由执行线程发起,但未提交到任何批量异步组

如果没有未提交的cp{.reduce}.async.bulk.{.prefetch}{.tensor}指令,那么cp.async.bulk.commit_group将产生一个空的批量异步组

执行线程可以使用cp.async.bulk.wait_group等待批量异步组中所有cp{.reduce}.async.bulk.{.prefetch}{.tensor}操作完成。

在同一批量异步组内的任意两个cp{.reduce}.async.bulk.{.prefetch}{.tensor}操作之间不提供内存排序保证。

PTX ISA 说明

在PTX ISA版本8.0中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

cp.async.bulk.commit_group;
9.7.9.25.6.2. 数据移动与转换指令:cp.async.bulk.wait_group

cp.async.bulk.wait_group

等待批量异步组完成。

语法

cp.async.bulk.wait_group{.read} N;

描述

cp.async.bulk.wait_group 指令会使执行线程等待,直到最近批量异步组中只有N个或更少处于挂起状态,并且执行线程提交的所有先前批量异步组都已完成。例如,当N为0时,执行线程会等待所有先前的批量异步组完成。操作数N是一个整数常量。

默认情况下,cp.async.bulk.wait_group指令会使执行线程等待,直到指定的批量异步组中的所有批量异步操作完成。批量异步操作包括以下内容:

  • 可选地,从张量映射中读取。

  • 从源位置读取数据。

  • 写入各自的目标位置。

  • 使写入操作对执行线程可见。

可选的 .read 修饰符表示必须等待,直到指定的批量异步组中的所有批量异步操作都完成:

  1. 从张量映射读取

  2. 从它们的源位置读取数据。

PTX ISA 说明

在PTX ISA版本8.0中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

cp.async.bulk.wait_group.read   0;
cp.async.bulk.wait_group        2;

9.7.9.26. 数据移动与转换指令:tensormap.replace

tensormap.replace

修改张量映射对象的字段。

语法

tensormap.replace.mode.field1{.ss}.b1024.type  [addr], new_val;
tensormap.replace.mode.field2{.ss}.b1024.type  [addr], ord, new_val;
tensormap.replace.mode.field3{.ss}.b1024.type  [addr], new_val;

.mode    = { .tile }
.field1  = { .global_address, .rank }
.field2  = { .box_dim, .global_dim, .global_stride, .element_stride  }
.field3  = { .elemtype,  .interleave_layout, .swizzle_mode, .swizzle_atomicity, .fill_mode }
.ss      = { .global, .shared::cta }
.type    = { .b32, .b64 }

描述

tensormap.replace指令将根据地址操作数addr指定的位置,用参数new_val提供的新值替换由.field限定符指定的张量映射对象字段。

限定符 .mode 指定位于地址操作数 addrtensor-map 对象的模式。

指令类型 .b1024 表示 tensor-map 对象的大小为1024位。

操作数 new_val 的类型为 .type。当 .field 指定为 .global_address.global_stride 时,.type 必须为 .b64。否则,.type 必须为 .b32

立即整数操作数ord指定了需要在tensor-map对象中替换的张量秩范围内的字段序号。

对于字段 .rank,操作数 new_val 必须比期望的张量秩小1,因为该字段使用从零开始的编号。

当指定.field3时,操作数new_val必须是一个立即数, Table 32展示了操作数new_val在不同字段间的映射关系。

表 32 张量映射新值有效性

new_val

.field3

.elemtype

.interleave_layout

.swizzle_mode

.swizzle_atomicity

.fill_mode

0

.u8

无交错

无混洗

16B

零填充

1

.u16

16B 交错

32B 数据混洗

32B

OOB-NaN 填充

2

.u32

32B 交错

64B 数据交换

32B + 8B 翻转

x

3

.s32

x

128B 交换

64B

x

4

.u64

x

x

x

x

5

.s64

x

x

x

x

6

.f16

x

x

x

x

7

.f32

x

x

x

x

8

.f32.ftz

x

x

x

x

9

.f64

x

x

x

x

10

.bf16

x

x

x

x

11

.tf32

x

x

x

x

12

.tf32.ftz

x

x

x

x

13

.b4x16

x

x

x

x

14

.b4x16_p64

x

x

x

x

15

.b6x16_p32.b6p2x16

x

x

x

x

如果未指定状态空间,则使用通用寻址。 如果addr指定的地址不在.global.shared::cta状态空间的地址窗口内,则行为未定义。

tensormap.replace 在整个1024位不透明的tensor-map对象上被视为弱内存操作,遵循Memory Consistency Model

PTX ISA 说明

在PTX ISA版本8.3中引入。

限定符 .swizzle_atomicity 在PTX ISA版本8.6中引入。

限定符 .elemtype 的取值范围从 1315(包含边界值),该特性从 PTX ISA 8.7 版本开始支持。

目标ISA注意事项

支持以下架构:

  • sm_90a

  • sm_100a

  • sm_101a

  • sm_120a

限定符 .swizzle_atomicity 在以下架构上受支持:

  • sm_100a

  • sm_101a

  • sm_120a (有关sm_120a的限制请参阅section)

.field3 变体 .elemtype 对应 new_val13, 1415 在以下架构上受支持:

  • sm_100a

  • sm_101a

  • sm_120a (有关sm_120a的限制请参阅章节)

示例

tensormap.replace.tile.global_address.shared::cta.b1024.b64   [sMem], new_val;

9.7.10. 纹理指令

本节介绍用于访问纹理(textures)和采样器(samplers)的PTX指令。PTX支持对纹理和采样器描述符进行以下操作:

  • 纹理和采样器描述符的静态初始化。

  • 模块作用域和每个入口作用域中纹理和采样器描述符的定义。

  • 能够查询纹理和采样器描述符中的字段。

9.7.10.1. 纹理模式

在处理纹理和采样器时,PTX有两种操作模式。在统一模式下,纹理和采样器信息通过单一的.texref句柄访问。而在独立模式下,纹理和采样器信息各自拥有独立的句柄,这使得它们可以被单独定义,并在程序使用处进行组合。

统一模式的优势在于每个内核允许256个采样器(对于sm_3x之前的架构为128个),但限制条件是它们必须与每个内核可能存在的256个纹理(对于sm_3x之前的架构为128个)一一对应。独立模式的优势在于纹理和采样器可以自由组合搭配,但采样器数量受到极大限制,每个内核仅允许32个(对于sm_3x之前的架构为16个)。

表33总结了不同纹理模式下可用的纹理、采样器和表面数量。

表 33 纹理、采样器和表面限制

纹理模式

资源

sm_1x, sm_2x

sm_3x+

统一模式

纹理

128

256

采样器

128

256

表面

8

16

独立模式

纹理

128

256

采样器

16

32

表面

8

16

纹理模式通过.target选项texmode_unifiedtexmode_independent进行选择。一个PTX模块只能声明一种纹理模式。如果未声明纹理模式,则默认该模块使用统一模式。

示例: 计算元素的功率贡献为元素功率/元素总数。

.target texmode_independent
.global .samplerref tsamp1 = { addr_mode_0 = clamp_to_border,
                               filter_mode = nearest
                             };
...
.entry compute_power
  ( .param .texref tex1 )
{
  txq.width.b32  r6, [tex1]; // get tex1's width
  txq.height.b32 r5, [tex1]; // get tex1's height
  tex.2d.v4.f32.f32  {r1,r2,r3,r4}, [tex1, tsamp1, {f1,f2}];
  mul.u32 r5, r5, r6;
  add.f32 r1, r1, r2;
  add.f32 r3, r3, r4;
  add.f32 r1, r1, r3;
  cvt.f32.u32 r5, r5;
  div.f32 r1, r1, r5;
}

9.7.10.2. Mipmaps

Mipmap是一系列纹理的序列,其中每个纹理都是同一图像逐渐降低分辨率的表现形式。在mipmap中,每个图像(或称细节层级LOD)的高度和宽度都是前一级别的二分之一。Mipmap在图形应用中被用于提升渲染速度并减少锯齿伪影。例如,高分辨率的mipmap图像用于靠近用户的物体;当物体显得较远时则使用低分辨率图像。在切换两个细节层级(LOD)时提供了mipmap过滤模式,以避免视觉保真度出现突兀变化。

示例: 如果纹理的基础尺寸为256×256像素,那么关联的mipmap集合可能包含八张图像序列,每张图像的总面积是前一张的四分之一:128×128像素、64×64、32×32、16×16、8×8、4×4、2×2、1×1(单像素)。例如,当场景在40×40像素的空间渲染该纹理时,将使用32×32图像的放大版本(无三线性插值)或64×64与32×32 mipmap之间的插值结果(启用三线性插值时)。

完整mipmap金字塔中的LOD总数通过以下公式计算:

numLODs = 1 + floor(log2(max(w, h, d)))

最精细的LOD称为基础层级,即第0级。下一级(更粗糙的)是第1级,依此类推。最粗糙的层级是尺寸为(1 x 1 x 1)的级别。每个连续的较小mipmap级别具有前一级别{宽度,高度,深度}的一半,但如果这个半值是分数,则向下取整到下一个最大的整数。本质上,mipmap级别的大小可以指定为:

max(1, floor(w_b / 2^i)) x
max(1, floor(h_b / 2^i)) x
max(1, floor(d_b / 2^i))

其中i表示第i层(从第0层即基础层开始计算)。w_bh_bd_b分别表示基础层的宽度、高度和深度。

PTX对mipmap的支持

PTX tex 指令支持三种指定LOD的模式:基础模式、层级模式和梯度模式。在基础模式下,该指令始终选择0级。在层级模式下,会提供一个额外参数来指定要获取的LOD级别。在梯度模式下,两个浮点向量参数提供偏导数(例如对于2D纹理的{ds/dx, dt/dx}{ds/dy, dt/dy}),tex指令会利用这些值来计算LOD。

这些指令提供了对纹理内存的访问。

  • tex

  • tld4

  • txq

9.7.10.3. 纹理指令: tex

tex

执行纹理内存查找。

语法

tex.geom.v4.dtype.ctype  d, [a, c] {, e} {, f};
tex.geom.v4.dtype.ctype  d[|p], [a, b, c] {, e} {, f};  // explicit sampler

tex.geom.v2.f16x2.ctype  d[|p], [a, c] {, e} {, f};
tex.geom.v2.f16x2.ctype  d[|p], [a, b, c] {, e} {, f};  // explicit sampler

// mipmaps
tex.base.geom.v4.dtype.ctype   d[|p], [a, {b,} c] {, e} {, f};
tex.level.geom.v4.dtype.ctype  d[|p], [a, {b,} c], lod {, e} {, f};
tex.grad.geom.v4.dtype.ctype   d[|p], [a, {b,} c], dPdx, dPdy {, e} {, f};

tex.base.geom.v2.f16x2.ctype   d[|p], [a, {b,} c] {, e} {, f};
tex.level.geom.v2.f16x2.ctype  d[|p], [a, {b,} c], lod {, e} {, f};
tex.grad.geom.v2.f16x2.ctype   d[|p], [a, {b,} c], dPdx, dPdy {, e} {, f};

.geom  = { .1d, .2d, .3d, .a1d, .a2d, .cube, .acube, .2dms, .a2dms };
.dtype = { .u32, .s32, .f16,  .f32 };
.ctype = {       .s32, .f32 };          // .cube, .acube require .f32
                                        // .2dms, .a2dms require .s32

描述

tex.{1d,2d,3d}

使用纹理坐标向量进行纹理查找。该指令从由操作数a命名的纹理中,根据操作数c提供的坐标,将数据加载到目标d中。操作数c对于一维纹理是标量或单元素元组;对于二维纹理是二元向量;对于三维纹理是四元向量(其中第四个元素被忽略)。可以指定一个可选的纹理采样器b。如果未指定采样器,则采样行为是命名纹理的属性。可选的谓词目标p如果指定坐标处的纹理数据驻留在内存中则设为True,否则设为False。当可选谓词目标p设为False时,加载的数据将全为零。纹理数据在指定坐标处的内存驻留性取决于内核启动前通过Driver API调用设置的执行环境。有关详细信息(包括任何系统/实现特定的行为),请参阅Driver API文档。

可以指定一个可选操作数e。操作数e是一个由.s32值组成的向量,用于指定坐标偏移量。偏移量会在执行纹理查找前应用于坐标。偏移值的范围在-8到+7之间。对于一维纹理,操作数e是单元素元组;对于二维纹理是双元素向量;对于三维纹理则是四元素向量(其中第四个元素会被忽略)。

对于depth textures可以指定一个可选操作数f。深度纹理是一种特殊类型的纹理,用于存储来自深度缓冲区的数据。深度缓冲区包含每个像素的深度信息。操作数f是一个.f32标量值,用于指定深度纹理的深度比较值。从纹理获取的每个元素都会与f操作数中给定的值进行比较。如果比较通过,结果为1.0;否则结果为0.0。这些逐元素的比较结果将用于过滤。当使用深度比较操作数时,纹理坐标向量c中的元素具有.f32类型。

深度比较操作不支持3d纹理。

该指令针对目标类型.f16x2返回一个二元向量。对于其他所有目标类型,该指令返回一个四元向量。坐标可以用32位有符号整数或32位浮点数形式给出。

纹理基地址假定对齐到16字节边界,坐标向量给出的地址必须自然对齐到访问大小的倍数。如果地址未正确对齐,则行为未定义;即,访问可能会通过静默屏蔽低位地址位来实现正确舍入,或者指令可能会出错。

tex.{a1d,a2d}

纹理数组选择,随后进行纹理查找。该指令首先使用数组坐标向量c的第一个元素给出的索引,从名为操作数a的纹理数组中选择一个纹理。然后,该指令根据操作数c的剩余元素给出的坐标,从选定的纹理中将数据加载到目标d中。操作数c是一个位大小类型的向量或元组,包含纹理数组的索引,后跟选定纹理内的坐标,如下所示:

  • 对于一维纹理数组,操作数 c 的类型为 .v2.b32。第一个元素被解释为指向纹理数组的无符号整数索引 (.u32),第二个元素被解释为类型 .ctype 的一维纹理坐标。

  • 对于2D纹理数组,操作数c的类型为.v4.b32。第一个元素被解释为指向纹理数组的无符号整数索引(.u32),接下来的两个元素被解释为类型.ctype的2D纹理坐标。第四个元素被忽略。

可以指定一个可选的纹理采样器b。如果未指定采样器,则采样行为是该命名纹理的一个属性。

可以指定一个可选操作数e。操作数e是一个由.s32值组成的向量,用于指定坐标偏移量。偏移量会在执行纹理查找前应用于坐标。偏移值范围在-8到+7之间。对于一维纹理数组,操作数e是单元素元组;对于二维纹理数组,则是双元素向量。

对于深度纹理数组,可以指定一个可选操作数f。操作数f是一个.f32标量值,用于指定深度纹理的深度比较值。当使用深度比较操作数时,纹理坐标向量c中的坐标具有.f32类型。

该指令针对目标类型.f16x2返回一个二元向量。对于其他所有目标类型,该指令返回一个四元向量。纹理数组索引是32位无符号整数,纹理坐标元素是32位有符号整数或浮点数值。

可选的目标谓词p会被设置为True(如果指定坐标处的纹理数据驻留在内存中),否则设为False。当可选目标谓词p设为False时,加载的数据将全为零。指定坐标处纹理数据的内存驻留性取决于内核启动前通过Driver API调用设置的执行环境。有关详细信息(包括任何系统/实现特定的行为),请参阅Driver API文档。

tex.cube

Cubemap 纹理查找。该指令从由操作数 a 命名的立方体贴图纹理中,根据操作数 c 提供的坐标,将数据加载到目标 d。立方体贴图纹理是一种特殊的二维分层纹理,由代表立方体六个面的六层组成。立方体贴图中的所有层都具有相同的大小且为正方形(即宽度等于高度)。

当访问立方体贴图时,纹理坐标向量c的类型为.v4.f32,包含三个浮点坐标(s, t, r)和一个被忽略的第四个填充参数。坐标(s, t, r)会被投影到六个立方体面之一上。(s, t, r)坐标可以视为从立方体中心发出的方向向量。在这三个坐标(s, t, r)中,绝对值最大的坐标(主轴)决定了选择哪个立方体面。然后,其他两个坐标(次轴)除以主轴的绝对值,生成一个新的(s, t)坐标对,用于在选定的立方体面上进行查找。

可以指定一个可选的纹理采样器b。如果未指定采样器,则采样行为是命名纹理的一个属性。

立方体贴图纹理不支持偏移向量操作数 e

对于立方体贴图深度纹理,可以指定一个可选操作数f。操作数f是一个.f32标量值,用于指定立方体贴图深度纹理的深度比较值。

可选的目标谓词p会被设置为True(如果指定坐标处的纹理数据驻留在内存中),否则设为False。当可选目标谓词p设为False时,加载的数据将全为零。指定坐标处纹理数据的内存驻留性取决于内核启动前通过Driver API调用设置的执行环境。有关详细信息(包括任何系统/实现特定的行为),请参阅Driver API文档。

tex.acube

立方体贴图数组选择,随后进行立方体贴图查找。该指令首先从由操作数a命名的立方体贴图数组中,使用数组坐标向量c的第一个元素给出的索引选择立方体贴图纹理。然后,该指令从选定的立方体贴图纹理中,根据操作数c剩余元素给出的坐标,将数据加载到目标d中。

立方体贴图数组纹理由一系列立方体贴图组成,即总层数是六的倍数。当访问立方体贴图数组纹理时,坐标向量c的类型为.v4.b32。第一个元素被解释为指向立方体贴图数组的无符号整数索引(.u32),其余三个元素被解释为浮点立方体贴图坐标(s, t, r),用于按照上述方式在选定的立方体贴图中进行查找。

可以指定一个可选的纹理采样器b。如果未指定采样器,则采样行为是该命名纹理的属性。

立方体贴图阵列不支持偏移向量操作数 e

对于立方体贴图深度纹理数组,可以指定一个可选操作数f。操作数f是一个.f32标量值,用于指定立方体贴图深度纹理的深度比较值。

可选的目标谓词p会被设置为True(如果指定坐标的纹理数据驻留在内存中),否则设为False。当可选目标谓词p设为False时,加载的数据将全为零。纹理数据在指定坐标的内存驻留性取决于内核启动前通过Driver API调用设置执行环境。有关详细信息(包括任何系统/实现特定的行为),请参阅Driver API文档。

tex.2dms

使用纹理坐标向量进行多采样纹理查找。多采样纹理每个数据元素包含多个样本。该指令从操作数a命名的纹理中加载数据,样本编号由操作数c的第一个元素给出,坐标由操作数c的剩余元素给出,加载到目标d中。当访问多采样纹理时,纹理坐标向量c的类型为.v4.b32。操作数c的第一个元素被解释为无符号整数样本编号(.u32),接下来的两个元素被解释为有符号整数(.s32)二维纹理坐标。第四个元素被忽略。可以指定一个可选的纹理采样器b。如果未指定采样器,则采样行为是命名纹理的属性。

可以指定一个可选操作数e。操作数e是一个类型为.v2.s32的向量,用于指定坐标偏移量。该偏移量会在执行纹理查找前应用于坐标。偏移值的范围在-8到+7之间。

多采样纹理不支持深度比较操作数 f

可选的目标谓词p会被设置为True(如果指定坐标处的纹理数据驻留在内存中),否则设为False。当可选目标谓词p设为False时,加载的数据将全为零。指定坐标处纹理数据的内存驻留性取决于内核启动前通过Driver API调用设置的执行环境。有关详细信息(包括任何系统/实现特定的行为),请参阅Driver API文档。

tex.a2dms

多采样纹理数组选择,随后进行多采样纹理查找。该指令首先使用数组坐标向量c的第一个元素给出的索引,从操作数a命名的多采样纹理数组中选择一个多采样纹理。然后,该指令从操作数c的第二个元素给出的样本编号处加载数据,在操作数c的剩余元素给出的坐标处,将数据加载到目标d中。当访问多采样纹理数组时,纹理坐标向量c的类型为.v4.b32。操作数c中的第一个元素被解释为无符号整数采样器编号,第二个元素被解释为多采样纹理数组中的无符号整数索引(.u32),接下来的两个元素被解释为有符号整数(.s32)二维纹理坐标。可以指定一个可选的纹理采样器b。如果未指定采样器,则采样行为是命名纹理的属性。

可以指定一个可选操作数e。操作数e是一个类型为.v2.s32的向量值,用于指定坐标偏移量。该偏移量会在进行纹理查找前应用于坐标。偏移值的范围在-8到+7之间。

多采样纹理数组不支持深度比较操作数 f

可选的目标谓词p会被设置为True(如果指定坐标处的纹理数据驻留在内存中),否则设为False。当可选目标谓词p设为False时,加载的数据将全为零。指定坐标处纹理数据的内存驻留性取决于内核启动前通过Driver API调用设置的执行环境。有关详细信息(包括任何系统/实现特定的行为),请参阅Driver API文档。

Mipmaps

.base (lod zero)

选择级别0(基础级别)。如果未指定mipmap模式,则此为默认值。无需额外参数。

.level (lod explicit)

需要一个额外的32位标量参数lod,该参数指定要获取的LOD层级。lod的类型遵循.ctype(可能是.s32.f32)。在此模式下不支持.2dms.a2dms几何类型。

.grad (lod gradient)

需要两个.f32向量dPdxdPdy来指定偏导数。对于1d和a1d纹理,这些向量是单元素;对于2d和a2d纹理是双元素向量;对于3d、立方体和acube纹理则是四元素向量(其中3d和立方体几何会忽略第四个元素)。此模式不支持.2dms.a2dms几何类型。

对于mipmap纹理查找,可以指定一个可选操作数e。操作数e是一个.s32向量,用于指定坐标偏移量。偏移量会在执行纹理查找前应用于坐标。偏移值范围在-8到+7之间。立方体和立方体贴图几何体不支持偏移向量操作数。

对于mipmap纹理,可以指定一个可选操作数f。操作数f是一个.f32标量值,用于指定深度纹理的深度比较值。当使用深度比较操作数时,纹理坐标向量c中的坐标具有.f32类型。

可选的目标谓词p会被设置为True(如果指定坐标处的纹理数据驻留在内存中),否则设为False。当可选目标谓词p设为False时,加载的数据将全为零。指定坐标处纹理数据的内存驻留性取决于内核启动前通过Driver API调用设置的执行环境。有关详细信息(包括任何系统/实现特定的行为),请参阅Driver API文档。

深度比较操作不支持3d纹理。

间接纹理访问

从PTX ISA 3.1版本开始,针对目标架构sm_20或更高版本,统一模式下支持间接纹理访问。在间接访问中,操作数a是一个.u64寄存器,其中保存着.texref变量的地址。

注意事项

为了与早期版本的PTX兼容,方括号不是必需的,并且.v4坐标向量可用于任何几何形状,额外的元素将被忽略。

PTX ISA 说明

统一模式纹理在PTX ISA 1.0版本中引入。使用不透明.texref.samplerref类型的扩展以及独立模式纹理在PTX ISA 1.5版本中引入。

纹理数组 tex.{a1d,a2d} 在PTX ISA 2.3版本中引入。

PTX ISA 3.0版本中引入了立方体贴图和立方体贴图数组。

PTX ISA 3.1版本中引入了对mipmaps的支持。

间接纹理访问在PTX ISA 3.1版本中引入。

PTX ISA 3.2版本中引入了多重采样纹理和多重采样纹理数组。

支持在PTX ISA 4.2版本中引入的返回.f16.f16x2数据的纹理功能。

支持在PTX ISA版本4.3中引入的tex.grad.{cube, acube}

PTX ISA 4.3版本中引入的偏移向量操作数。

深度比较操作数在PTX ISA版本4.3中引入。

支持PTX ISA版本7.1中引入的可选目标谓词功能。

目标ISA注意事项

支持所有目标架构。

立方体贴图阵列几何体(.acube)需要sm_20或更高版本。

Mipmaps需要sm_20或更高版本。

间接纹理访问需要sm_20或更高版本。

多采样纹理和多采样纹理数组需要sm_30或更高版本。

纹理提取返回.f16.f16x2数据需要sm_53或更高版本。

tex.grad.{cube, acube} 需要 sm_20 或更高版本。

偏移向量操作数需要sm_30或更高版本。

深度比较操作数需要 sm_30 或更高版本。

支持可选目标谓词需要sm_60或更高版本。

示例

 // Example of unified mode texturing
 // - f4 is required to pad four-element tuple and is ignored
 tex.3d.v4.s32.s32  {r1,r2,r3,r4}, [tex_a,{f1,f2,f3,f4}];

 // Example of independent mode texturing
 tex.1d.v4.s32.f32  {r1,r2,r3,r4}, [tex_a,smpl_x,{f1}];

 // Example of 1D texture array, independent texturing mode
 tex.a1d.v4.s32.s32 {r1,r2,r3,r4}, [tex_a,smpl_x,{idx,s1}];

 // Example of 2D texture array, unified texturing mode
 // - f3 is required to pad four-element tuple and is ignored
 tex.a2d.v4.s32.f32 {r1,r2,r3,r4}, [tex_a,{idx,f1,f2,f3}];

 // Example of cubemap array, unified textureing mode
 tex.acube.v4.f32.f32 {r0,r1,r2,r3}, [tex_cuarray,{idx,f1,f2,f3}];

 // Example of multi-sample texture, unified texturing mode
 tex.2dms.v4.s32.s32 {r0,r1,r2,r3}, [tex_ms,{sample,r6,r7,r8}];

 // Example of multi-sample texture, independent texturing mode
 tex.2dms.v4.s32.s32 {r0,r1,r2,r3}, [tex_ms, smpl_x,{sample,r6,r7,r8}];

 // Example of multi-sample texture array, unified texturing mode
 tex.a2dms.v4.s32.s32 {r0,r1,r2,r3}, [tex_ams,{idx,sample,r6,r7}];

 // Example of texture returning .f16 data
 tex.1d.v4.f16.f32  {h1,h2,h3,h4}, [tex_a,smpl_x,{f1}];

 // Example of texture returning .f16x2 data
 tex.1d.v2.f16x2.f32  {h1,h2}, [tex_a,smpl_x,{f1}];

 // Example of 3d texture array access with tex.grad,unified texturing mode
 tex.grad.3d.v4.f32.f32 {%f4,%f5,%f6,%f7},[tex_3d,{%f0,%f0,%f0,%f0}],
                 {fl0,fl1,fl2,fl3},{fl0,fl1,fl2,fl3};

// Example of cube texture array access with tex.grad,unified texturing mode
 tex.grad.cube.v4.f32.f32{%f4,%f5,%f6,%f7},[tex_cube,{%f0,%f0,%f0,%f0}],
                 {fl0,fl1,fl2,fl3},{fl0,fl1,fl2,fl3};

 // Example of 1d texture lookup with offset, unified texturing mode
 tex.1d.v4.s32.f32  {r1,r2,r3,r4}, [tex_a, {f1}], {r5};

 // Example of 2d texture array lookup with offset, unified texturing mode
 tex.a2d.v4.s32.f32  {r1,r2,r3,r4}, [tex_a,{idx,f1,f2}], {f5,f6};

 // Example of 2d mipmap texture lookup with offset, unified texturing mode
 tex.level.2d.v4.s32.f32  {r1,r2,r3,r4}, [tex_a,{f1,f2}],
                          flvl, {r7, r8};

 // Example of 2d depth texture lookup with compare, unified texturing mode
 tex.1d.v4.f32.f32  {f1,f2,f3,f4}, [tex_a, {f1}], f0;

 // Example of depth 2d texture array lookup with offset, compare
 tex.a2d.v4.s32.f32  {f0,f1,f2,f3}, [tex_a,{idx,f4,f5}], {r5,r6}, f6;

 // Example of destination predicate use
 tex.3d.v4.s32.s32 {r1,r2,r3,r4}|p, [tex_a,{f1,f2,f3,f4}];

9.7.10.4. 纹理指令: tld4

tld4

执行4纹理双线性插值区域的纹理提取。

语法

tld4.comp.2d.v4.dtype.f32    d[|p], [a, c] {, e} {, f};
tld4.comp.geom.v4.dtype.f32  d[|p], [a, b, c] {, e} {, f};  // explicit sampler

.comp  = { .r, .g, .b, .a };
.geom  = { .2d, .a2d, .cube, .acube };
.dtype = { .u32, .s32, .f32 };

描述

使用纹理坐标向量获取4个纹素的双线性插值区域。该指令从操作数a命名的纹理中,根据操作数c提供的坐标,将双线性插值区域加载到向量目标d中。每个纹素样本获取的纹理分量由.comp指定。四个纹素样本按从左下角开始的逆时针顺序放入目标向量d

可以指定一个可选的纹理采样器b。如果未指定采样器,则采样行为是该命名纹理的属性。

可选的目标谓词p会被设置为True(如果指定坐标的纹理数据驻留在内存中),否则设为False。当可选目标谓词p设为False时,加载的数据将全为零。纹理数据在指定坐标的内存驻留性取决于内核启动前通过Driver API调用设置执行环境。有关详细信息(包括任何系统/实现特定的行为),请参阅Driver API文档。

对于深度纹理,可以指定一个可选操作数f。深度纹理是一种特殊类型的纹理,用于存储来自深度缓冲区的数据。深度缓冲区包含每个像素的深度信息。操作数f是一个.f32标量值,用于指定深度纹理的深度比较值。从纹理获取的每个元素都会与f操作数中给定的值进行比较。如果比较通过,结果为1.0;否则结果为0.0。这些逐元素的比较结果将用于过滤。

纹理基地址假定对齐到16字节边界,坐标向量给出的地址必须自然对齐到访问大小的倍数。如果地址未正确对齐,则行为未定义;即,访问可能会通过静默屏蔽低位地址位来实现正确舍入,或者指令可能会出错。

tld4.2d

对于2D纹理,操作数c指定坐标为一个包含两个元素的32位浮点向量。

可以指定一个可选操作数e。操作数e是一个类型为.v2.s32的向量,用于指定坐标偏移量。该偏移量会在执行纹理获取前应用于坐标。偏移值的范围在-8到+7之间。

tld4.a2d

纹理数组选择,随后进行2D纹理的tld4纹理获取。对于2D纹理数组,操作数c是一个四元素的32位向量。操作数c中的第一个元素被解释为纹理数组的无符号整数索引(.u32),接下来的两个元素被解释为2D纹理的32位浮点坐标。第四个元素被忽略。

可以指定一个可选操作数e。操作数e是一个类型为.v2.s32的向量,用于指定坐标偏移量。该偏移量会在执行纹理获取前应用于坐标。偏移值的范围在-8到+7之间。

tld4.cube

对于立方体贴图纹理,操作数c指定了一个四元素向量,其中包含三个浮点坐标(s, t, r)和一个被忽略的第四个填充参数。

立方体贴图是一种特殊的二维分层纹理,由六个面组成,代表立方体的各个面。立方体贴图中的所有层都具有相同的大小且为正方形(即宽度等于高度)。

坐标(s, t, r)会被投影到立方体的六个面之一上。(s, t, r)坐标可以被视为从立方体中心发出的方向向量。在这三个坐标(s, t, r)中,绝对值最大的坐标(主轴)决定了选择哪个立方体面。然后,其他两个坐标(次轴)会被主轴的绝对值相除,生成一个新的(s, t)坐标对,用于在所选的立方体面上进行查找。

立方体贴图纹理不支持偏移向量操作数 e

tld4.acube

立方体贴图数组选择,随后执行tld4纹理获取立方体贴图。操作数c的第一个元素被解释为无符号整数索引(.u32),指向立方体贴图数组,其余三个元素被解释为浮点立方体贴图坐标(s, t, r),用于在选定的立方体贴图中进行查找。

立方体贴图阵列不支持偏移向量操作数 e

间接纹理访问

从PTX ISA版本3.1开始,针对目标架构sm_20或更高版本,在统一模式下支持间接纹理访问。在间接访问中,操作数a是一个.u64寄存器,它保存着.texref变量的地址。

PTX ISA 说明

在PTX ISA版本2.2中引入。

间接纹理访问在PTX ISA 3.1版本中引入。

tld4.{a2d,cube,acube} 在PTX ISA版本4.3中引入。

PTX ISA 4.3版本中引入的偏移向量操作数。

深度比较操作数在PTX ISA版本4.3中引入。

支持PTX ISA版本7.1中引入的可选目标谓词功能。

目标ISA注意事项

tld4 需要 sm_20 或更高版本。

间接纹理访问需要sm_20或更高版本。

tld4.{a2d,cube,acube} 需要 sm_30 或更高版本。

偏移向量操作数需要sm_30或更高版本。

深度比较操作数需要 sm_30 或更高版本。

支持可选目标谓词需要sm_60或更高版本。

示例

//Example of unified mode texturing
tld4.r.2d.v4.s32.f32  {r1,r2,r3,r4}, [tex_a,{f1,f2}];

// Example of independent mode texturing
tld4.r.2d.v4.u32.f32  {u1,u2,u3,u4}, [tex_a,smpl_x,{f1,f2}];

// Example of unified mode texturing using offset
tld4.r.2d.v4.s32.f32  {r1,r2,r3,r4}, [tex_a,{f1,f2}], {r5, r6};

// Example of unified mode texturing using compare
tld4.r.2d.v4.f32.f32  {f1,f2,f3,f4}, [tex_a,{f5,f6}], f7;

// Example of optional destination predicate
tld4.r.2d.v4.f32.f32 {f1,f2,f3,f4}|p, [tex_a,{f5,f6}], f7;

9.7.10.5. 纹理指令: txq

txq

查询纹理和采样器属性。

语法

txq.tquery.b32         d, [a];       // texture attributes
txq.level.tlquery.b32  d, [a], lod;  // texture attributes
txq.squery.b32         d, [a];       // sampler attributes

.tquery  = { .width, .height, .depth,
             .channel_data_type, .channel_order,
             .normalized_coords, .array_size,
             .num_mipmap_levels, .num_samples};

.tlquery = { .width, .height, .depth };

.squery  = { .force_unnormalized_coords, .filter_mode,
             .addr_mode_0, addr_mode_1, addr_mode_2 };

描述

查询纹理或采样器的一个属性。操作数 a 可以是 .texref.samplerref 变量,也可以是一个 .u64 寄存器。

查询

返回结果

.width

.height

.depth

元素中的值

.channel_data_type

无符号整数,对应源语言通道数据类型的枚举值。如果源语言将通道数据类型和通道顺序合并为单一枚举类型,则该值将同时作为channel_data_type和通道顺序查询的返回值。

.channel_order

与源语言通道顺序枚举对应的无符号整数。如果源语言将通道数据类型和通道顺序合并为单一枚举类型,则该值将同时返回给channel_data_typechannel_order查询。

.normalized_coords

1 (True) 或 0 (False)。

.force_unnormalized_coords

1 (True)0 (False). 仅针对独立纹理模式下的.samplerref变量定义。会覆盖tex指令中与.samplerref一起使用的.texref变量的normalized_coords字段。

.filter_mode

整数来自 enum { nearest, linear }

.addr_mode_0

.addr_mode_1

.addr_mode_2

整数来自 enum { wrap, mirror, clamp_ogl, clamp_to_edge, clamp_to_border }

.array_size

对于纹理数组,表示数组中的纹理数量,否则为0。

.num_mipmap_levels

对于具有mipmap的纹理,表示细节级别(LOD)的数量;否则为0。

.num_samples

对于多采样纹理,表示采样数量,否则为0。

纹理属性通过向.texref参数提供txq来查询。在统一模式下,采样器属性也通过.texref参数访问,而在独立模式下,采样器属性则通过单独的.samplerref参数访问。

txq.level

txq.level 需要一个额外的32位整数参数 lod,该参数指定LOD并查询请求属性的指定LOD级别。

间接纹理访问

从PTX ISA版本3.1开始,针对目标架构sm_20或更高版本,在统一模式下支持间接纹理访问。在间接访问中,操作数a是一个.u64寄存器,它保存着.texref变量的地址。

PTX ISA 说明

在PTX ISA版本1.5中引入。

通道数据类型和通道顺序查询功能在PTX ISA 2.1版本中新增。

.force_unnormalized_coords 查询在PTX ISA 2.2版本中被添加。

间接纹理访问在PTX ISA 3.1版本中引入。

.array_size, .num_mipmap_levels, .num_samples 采样查询已在PTX ISA版本4.1中添加。

txq.level 在PTX ISA版本4.3中引入。

目标ISA注意事项

支持所有目标架构。

间接纹理访问需要sm_20或更高版本。

查询mipmap级别数量需要sm_20或更高版本。

查询样本数量需要sm_30或更高版本。

txq.level 需要 sm_30 或更高版本。

示例

txq.width.b32       %r1, [tex_A];
txq.filter_mode.b32 %r1, [tex_A];   // unified mode
txq.addr_mode_0.b32 %r1, [smpl_B];  // independent mode
txq.level.width.b32 %r1, [tex_A], %r_lod;

9.7.10.6. 纹理指令:istypep

istypep

查询某个寄存器是否指向指定类型的不透明变量。

语法

istypep.type   p, a;  // result is .pred

.type = { .texref, .samplerref, .surfref };

描述

如果寄存器a指向指定类型的不透明变量,则将谓词寄存器p写入1,否则写入0。目标寄存器p的类型为.pred;源地址操作数必须是.u64类型。

PTX ISA 说明

在PTX ISA版本4.0中引入。

目标ISA注意事项

istypep 需要 sm_30 或更高版本。

示例

istypep.texref istex, tptr;
istypep.samplerref issampler, sptr;
istypep.surfref issurface, surfptr;

9.7.11. Surface Instructions

本节介绍用于访问表面的PTX指令。PTX支持对表面描述符进行以下操作:

  • 表面描述符的静态初始化。

  • 模块范围和每个入口范围的表面描述符定义。

  • 能够查询表面描述符中的字段。

这些指令提供了对表面内存的访问。

  • suld

  • sust

  • sured

  • suq

9.7.11.1. 表面指令:suld

suld

从表面内存加载。

语法

suld.b.geom{.cop}.vec.dtype.clamp  d, [a, b];  // unformatted

.geom  = { .1d, .2d, .3d, .a1d, .a2d };
.cop   = { .ca, .cg, .cs, .cv };               // cache operation
.vec   = { none, .v2, .v4 };
.dtype = { .b8 , .b16, .b32, .b64 };
.clamp = { .trap, .clamp, .zero };

描述

suld.b.{1d,2d,3d}

使用表面坐标向量从表面内存加载数据。该指令从由操作数a命名的表面中,根据操作数b提供的坐标将数据加载到目标d中。操作数 a是一个.surfref变量或.u64寄存器。操作数b对于一维表面是标量或单元素元组; 对于二维表面是双元素向量;对于三维表面是四元素向量(其中第四个元素被忽略)。坐标元素的类型为.s32

suld.b执行二进制数据的非格式化加载。最低维度坐标表示表面中的字节偏移量且不进行缩放,数据传输的大小与目标操作数d的大小相匹配。

suld.b.{a1d,a2d}

表面层选择,随后从选定的表面加载数据。该指令首先使用数组坐标向量b的第一个元素作为索引,从名为操作数a的表面数组中选择一个表面层。然后,该指令根据操作数b剩余元素提供的坐标,从选定的表面加载数据到目标d。操作数a是一个.surfref变量或.u64寄存器。操作数b是一个位大小类型的向量或元组,包含表面数组的索引,后跟选定表面内的坐标,如下所示:

对于一维表面数组,操作数b的类型为.v2.b32。第一个元素被解释为指向表面数组的无符号整数索引(.u32),第二个元素被解释为类型为.s32的一维表面坐标。

对于二维表面数组,操作数b的类型为.v4.b32。第一个元素被解释为指向表面数组的无符号整数索引(.u32),接下来的两个元素被解释为.s32类型的二维表面坐标。第四个元素将被忽略。

假定表面基地址对齐到16字节边界,且坐标向量给出的地址必须自然对齐到访问大小的整数倍。如果地址未正确对齐,则行为未定义;即,访问可能会通过静默屏蔽低位地址位来实现正确舍入,或者指令可能会出错。

.clamp字段指定如何处理越界地址:

.trap

在越界地址上触发执行陷阱

.clamp

在最近的地表位置加载数据(尺寸已适当调整)

.zero

对于越界地址加载零值

间接表面访问

从PTX ISA版本3.1开始,针对目标架构sm_20或更高版本支持间接表面访问。在间接访问中,操作数a是一个.u64寄存器,它保存着.surfref变量的地址。

PTX ISA 说明

suld.b.trap 在PTX ISA版本1.5中引入。

PTX ISA 2.0版本中引入了额外的钳位修饰符和缓存操作。

suld.b.3dsuld.b.{a1d,a2d} 在PTX ISA 3.0版本中引入。

间接表面访问在PTX ISA 3.1版本中引入。

目标ISA注意事项

suld.b 支持所有目标架构。

sm_1x 目标仅支持 .trap 钳位修饰符。

suld.3dsuld.{a1d,a2d} 需要 sm_20 或更高版本。

间接表面访问需要sm_20或更高版本。

缓存操作需要sm_20或更高版本。

示例

suld.b.1d.v4.b32.trap  {s1,s2,s3,s4}, [surf_B, {x}];
suld.b.3d.v2.b64.trap  {r1,r2}, [surf_A, {x,y,z,w}];
suld.b.a1d.v2.b32      {r0,r1}, [surf_C, {idx,x}];
suld.b.a2d.b32         r0, [surf_D, {idx,x,y,z}];  // z ignored

9.7.11.2. 表面指令:sust

sust

存储到表面内存。

语法

sust.b.{1d,2d,3d}{.cop}.vec.ctype.clamp  [a, b], c;  // unformatted
sust.p.{1d,2d,3d}.vec.b32.clamp          [a, b], c;  // formatted

sust.b.{a1d,a2d}{.cop}.vec.ctype.clamp   [a, b], c;  // unformatted

.cop   = { .wb, .cg, .cs, .wt };                     // cache operation
.vec   = { none, .v2, .v4 };
.ctype = { .b8 , .b16, .b32, .b64 };
.clamp = { .trap, .clamp, .zero };

描述

sust.{1d,2d,3d}

使用表面坐标向量将数据存储到表面内存。该指令将数据从操作数c存储到由操作数a命名的表面,坐标由操作数b给出。操作数a.surfref变量或.u64寄存器。操作数b对于一维表面是标量或单元素元组;对于二维表面是双元素向量;对于三维表面是四元素向量(其中第四个元素被忽略)。坐标元素的类型为.s32

sust.b 执行二进制数据的非格式化存储。最低维度坐标表示表面中的字节偏移量,且不进行缩放。数据传输的大小与源操作数 c 的大小相匹配。

sust.p 执行将32位数据值向量格式化存储到表面样本的操作。源向量元素从左到右被解释为表面组件的 RGBA。这些元素会被写入对应的表面样本组件中。源向量中存在但表面样本中不存在的元素将被忽略。源向量中不存在但表面样本中存在的组件将被写入不可预测的值。最低维度的坐标代表样本偏移量而非字节偏移量。

源数据解释基于表面采样格式如下:如果表面格式包含UNORMSNORMFLOAT数据,则假定为.f32;如果表面格式包含UINT数据,则假定为.u32;如果表面格式包含SINT数据,则假定为.s32。源数据随后会从该类型转换为表面采样格式。

sust.b.{a1d,a2d}

表面层选择,随后将数据以未格式化形式存储到选定的表面。该指令首先从名为操作数a的表面数组中,使用数组坐标向量b的第一个元素作为索引来选择表面层。然后,该指令将操作数c中的数据存储到选定表面,存储位置由操作数b的剩余元素给出的坐标确定。操作数a是.surfref变量或.u64寄存器。操作数b是一个位大小类型的向量或元组,包含表面数组的索引,后跟选定表面内的坐标,具体如下:

  • 对于一维表面数组,操作数b的类型为.v2.b32。第一个元素被解释为指向表面数组的无符号整数索引(.u32),第二个元素被解释为类型为.s32的一维表面坐标。

  • 对于二维表面数组,操作数b的类型为.v4.b32。第一个元素被解释为指向表面数组的无符号整数索引(.u32),接下来的两个元素被解释为类型为.s32的二维表面坐标。第四个元素被忽略。

假定表面基地址对齐到16字节边界,坐标向量给出的地址必须自然对齐到访问大小的整数倍。如果地址未正确对齐,则行为未定义;即,访问可能会通过静默屏蔽低位地址位来实现正确舍入,或者指令可能会出错。

.clamp 字段指定如何处理越界地址:

.trap

在越界地址上触发执行陷阱

.clamp

将数据存储在最近的表层位置(大小适当)

.zero

丢弃存储到越界地址

间接表面访问

从PTX ISA版本3.1开始,针对目标架构sm_20或更高版本支持间接表面访问。在间接访问中,操作数a是一个.u64寄存器,它保存着.surfref变量的地址。

PTX ISA 说明

sust.b.trap 在PTX ISA 1.5版本中引入。sust.p、额外的钳位修饰符以及缓存操作在PTX ISA 2.0版本中引入。

sust.b.3dsust.b.{a1d,a2d} 在PTX ISA 3.0版本中引入。

间接表面访问在PTX ISA 3.1版本中引入。

目标ISA注意事项

sust.b 支持所有目标架构。

sm_1x 仅支持 .trap 钳位修饰符。

sust.3dsust.{a1d,a2d} 需要 sm_20 或更高版本。

sust.p 需要 sm_20 或更高版本。

间接表面访问需要sm_20或更高版本。

缓存操作需要sm_20或更高版本。

示例

sust.p.1d.v4.b32.trap  [surf_B, {x}], {f1,f2,f3,f4};
sust.b.3d.v2.b64.trap  [surf_A, {x,y,z,w}], {r1,r2};
sust.b.a1d.v2.b64      [surf_C, {idx,x}], {r1,r2};
sust.b.a2d.b32         [surf_D, {idx,x,y,z}], r0;  // z ignored

9.7.11.3. 表面指令:sured

确信

减少表面内存。

语法

sured.b.op.geom.ctype.clamp  [a,b],c; // byte addressing
sured.p.op.geom.ctype.clamp  [a,b],c; // sample addressing

.op    = { .add, .min, .max, .and, .or };
.geom  = { .1d, .2d, .3d };
.ctype = { .u32, .u64, .s32, .b32, .s64 };  // for sured.b
.ctype = { .b32, .b64 };                    // for sured.p
.clamp = { .trap, .clamp, .zero };

描述

使用表面坐标向量将数据归约到表面内存。该指令执行一个归约操作,将来自操作数c的数据写入由操作数a命名的表面,写入位置由操作数b给出的坐标确定。操作数a.surfref变量或.u64寄存器。操作数b对于一维表面是标量或单元素元组;对于二维表面是双元素向量;对于三维表面是四元素向量(其中第四个元素被忽略)。坐标元素的类型为.s32

sured.b.u32.s32.b32.u64.s64 数据类型执行非格式化归约操作。最低维坐标表示表面中的字节偏移量且不进行缩放。add 操作适用于 .u32.u64.s32 类型;minmax 操作适用于 .u32.s32.u64.s64 类型;andor 操作适用于 .b32 类型。

sured.p 对样本寻址数据执行归约操作。最低维度坐标代表样本偏移而非字节偏移。指令类型 .b64 仅限于 minmax 操作。对于类型 .b32,数据根据表面样本格式被解释为 .u32.s32,具体规则如下:若表面格式包含 UINT 数据,则假定为 .u32;若表面格式包含 SINT 数据,则假定为 .s32。对于类型 .b64,若表面格式包含 UINT 数据,则假定为 .u64;若表面格式包含 SINT 数据,则假定为 .s64

假定表面基地址对齐到16字节边界,坐标向量给出的地址必须自然对齐到访问大小的整数倍。如果地址未正确对齐,则行为未定义;即,访问可能会通过静默屏蔽低位地址位来实现正确舍入,或者指令可能会出错。

.clamp 字段指定如何处理越界地址:

.trap

在越界地址上触发执行陷阱

.clamp

将数据存储在最近的表层位置(大小适当)

.zero

丢弃存储到越界地址的数据

间接表面访问

从PTX ISA版本3.1开始,针对目标架构sm_20或更高版本支持间接表面访问。在间接访问中,操作数a是一个.u64寄存器,它保存着.surfref变量的地址。

PTX ISA 说明

在PTX ISA 2.0版本中引入。

间接表面访问在PTX ISA 3.1版本中引入。

.u64/.s64/.b64 类型以及 .min/.max 操作在 PTX ISA 8.1 版本中引入。

目标ISA注意事项

要求支持 sm_20 或更高版本。

间接表面访问需要sm_20或更高版本。

.u64/.s64/.b64 类型配合 .min/.max 操作需要 sm_50 或更高版本。

示例

sured.b.add.2d.u32.trap  [surf_A, {x,y}], r1;
sured.p.min.1d.u32.trap  [surf_B, {x}], r1;
sured.b.max.1d.u64.trap  [surf_C, {x}], r1;
sured.p.min.1d.b64.trap  [surf_D, {x}], r1;

9.7.11.4. 曲面指令: suq

suq

查询表面属性。

语法

suq.query.b32   d, [a];

.query = { .width, .height, .depth,
           .channel_data_type, .channel_order,
           .array_size, .memory_layout };

描述

查询表面的一个属性。操作数 a.surfref 变量或 .u64 寄存器。

查询

返回结果

.width

.height

.depth

元素中的值

.channel_data_type

无符号整数,对应源语言通道数据类型的枚举值。如果源语言将通道数据类型和通道顺序合并为单一枚举类型,则该值将同时作为channel_data_typechannel_order查询的返回值。

.channel_order

与源语言通道顺序枚举对应的无符号整数。如果源语言将通道数据类型和通道顺序合并为单一枚举类型,则该值将同时作为channel_data_typechannel_order查询的返回值。

.array_size

对于表面数组,表示数组中的表面数量,否则为0。

.memory_layout

1 表示具有线性内存布局的表面;0 表示其他情况

间接表面访问

从PTX ISA版本3.1开始,针对目标架构sm_20或更高版本支持间接表面访问。在间接访问中,操作数a是一个.u64寄存器,它保存着.surfref变量的地址。

PTX ISA 说明

在PTX ISA版本1.5中引入。

通道数据类型和通道顺序查询在PTX ISA版本2.1中新增。

间接表面访问在PTX ISA 3.1版本中引入。

.array_size查询在PTX ISA 4.1版本中被添加。

.memory_layout查询在PTX ISA 4.2版本中被添加。

目标ISA注意事项

支持所有目标架构。

间接表面访问需要sm_20或更高版本。

示例

suq.width.b32       %r1, [surf_A];

9.7.12. 控制流指令

以下PTX指令和语法用于控制PTX程序中的执行流程:

  • {}

  • @

  • bra

  • call

  • ret

  • exit

9.7.12.1. 控制流指令: {}

{}

指令分组。

语法

{ instructionList }

描述

大括号用于创建一组指令,主要用于定义函数体。大括号还提供了一种确定变量作用域的机制:在某个作用域内声明的任何变量在该作用域外不可用。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

{ add.s32  a,b,c; mov.s32  d,a; }

9.7.12.2. 控制流指令: @

@

谓词执行。

语法

@{!}p    instruction;

描述

为保护谓词为True的线程执行指令或指令块。保护谓词为False的线程不执行任何操作。

语义

如果 {!}p 则执行指令

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

    setp.eq.f32  p,y,0;     // is y zero?
@!p div.f32      ratio,x,y  // avoid division by zero

@q  bra L23;                // conditional branch

9.7.12.3. 控制流指令: bra

bra

跳转到目标位置并继续执行。

语法

@p   bra{.uni}  tgt;           // tgt is a label
     bra{.uni}  tgt;           // unconditional branch

描述

在目标处继续执行。条件分支通过使用守卫谓词来指定。分支目标必须是一个标签。

bra.uni 保证不会出现分支发散,即当前执行该指令的warp中所有活跃线程的保护谓词和分支目标值都相同。

语义

if (p) {
    pc = tgt;
}

PTX ISA 说明

在PTX ISA版本1.0中引入。

PTX ISA 2.1版本中引入的未实现的间接分支已从规范中移除。

目标ISA注意事项

支持所有目标架构。

示例

bra.uni  L_exit;    // uniform unconditional jump
@q  bra      L23;   // conditional branch

9.7.12.4. 控制流指令:brx.idx

brx.idx

根据潜在分支目标列表中的索引跳转到指定标签。

语法

@p    brx.idx{.uni} index, tlist;
      brx.idx{.uni} index, tlist;

描述

根据可能的目标标签列表进行索引,并从选定的标签继续执行。条件分支通过使用守卫谓词来指定。

brx.idx.uni 确保分支不会发散,即当前执行该指令的warp中所有活跃线程的保护谓词和index参数具有相同的值。

index操作数是一个.u32寄存器。tlist操作数必须是.branchtargets指令的标签。使用index作为从零开始的序列进行访问。如果index的值大于或等于tlist的长度,则行为未定义。

在使用.branchtargets指令前,必须在局部函数作用域内先定义该指令。它必须引用当前函数内的标签。

语义

if (p) {
    if (index < length(tlist)) {
      pc = tlist[index];
    } else {
      pc = undefined;
    }
}

PTX ISA 说明

在PTX ISA 6.0版本中引入。

目标ISA注意事项

需要 sm_30 或更高版本。

示例

.function foo () {
    .reg .u32 %r0;
    ...
    L1:
    ...
    L2:
    ...
    L3:
    ...
    ts: .branchtargets L1, L2, L3;
    @p brx.idx %r0, ts;
    ...
}

9.7.12.5. 控制流指令:调用

调用

调用一个函数,记录返回位置。

语法

// direct call to named function, func is a symbol
call{.uni} (ret-param), func, (param-list);
call{.uni} func, (param-list);
call{.uni} func;

// indirect call via pointer, with full list of call targets
call{.uni} (ret-param), fptr, (param-list), flist;
call{.uni} fptr, (param-list), flist;
call{.uni} fptr, flist;

// indirect call via pointer, with no knowledge of call targets
call{.uni} (ret-param), fptr, (param-list), fproto;
call{.uni} fptr, (param-list), fproto;
call{.uni} fptr, fproto;

描述

call指令会存储下一条指令的地址,这样在执行完ret指令后可以从该点继续执行。除非带有.uni后缀,否则call指令默认是发散式的。.uni后缀表示该call指令保证是非发散的,即当前执行该指令的warp中所有活跃线程的保护谓词和call目标地址都具有相同的值。

对于直接调用,被调用位置func必须是一个符号函数名称;对于间接调用, 被调用位置fptr必须是寄存器中保存的函数地址。输入参数 和返回值是可选的。参数可以是寄存器、立即数常量或位于 .param空间中的变量。参数采用传值方式传递。

间接调用需要一个额外的操作数,flistfproto,分别用于传递潜在的call目标列表或所有call目标的共同函数原型。在第一种情况下,flist提供了完整的潜在call目标列表,优化后端可以自由优化调用约定。在第二种情况下,当潜在call目标的完整列表可能未知时,会给出共同的函数原型,并且call必须遵守ABI的调用约定。

flist操作数可以是一个已初始化为函数名列表的数组(调用表)名称;也可以是与.calltargets指令关联的标签,该指令声明了潜在的call目标列表。在这两种情况下,fptr寄存器都保存着调用表或.calltargets列表中某个函数的地址,并且call操作数会根据flist所指示函数的类型签名进行类型检查。

fproto操作数是与.callprototype指令关联的标签名称。当无法确定完整的目标函数列表时,会使用此操作数。call操作数的类型会根据原型进行检查,代码生成将遵循ABI调用约定。如果调用的函数与原型不匹配,则行为是未定义的。

调用表可以在模块作用域或局部作用域中声明,位于常量或全局状态空间内。.calltargets.callprototype指令必须在函数体内声明。所有函数在call表初始化器或.calltargets指令中被引用前必须先声明。

PTX ISA 说明

直接call在PTX ISA 1.0版本中引入。间接call在PTX ISA 2.1版本中引入。

目标ISA注意事项

在所有目标架构上都支持直接call。间接call需要sm_20或更高版本。

示例

// examples of direct call
    call     init;    // call function 'init'
    call.uni g, (a);  // call function 'g' with parameter 'a'
@p  call     (d), h, (a, b);  // return value into register d

// call-via-pointer using jump table
.func (.reg .u32 rv) foo (.reg .u32 a, .reg .u32 b) ...
.func (.reg .u32 rv) bar (.reg .u32 a, .reg .u32 b) ...
.func (.reg .u32 rv) baz (.reg .u32 a, .reg .u32 b) ...

.global .u32 jmptbl[5] = { foo, bar, baz };
      ...
@p    ld.global.u32  %r0, [jmptbl+4];
@p    ld.global.u32  %r0, [jmptbl+8];
      call  (retval), %r0, (x, y), jmptbl;

// call-via-pointer using .calltargets directive
.func (.reg .u32 rv) foo (.reg .u32 a, .reg .u32 b) ...
.func (.reg .u32 rv) bar (.reg .u32 a, .reg .u32 b) ...
.func (.reg .u32 rv) baz (.reg .u32 a, .reg .u32 b) ...
      ...
@p    mov.u32  %r0, foo;
@q    mov.u32  %r0, baz;
Ftgt: .calltargets foo, bar, baz;
      call  (retval), %r0, (x, y), Ftgt;

// call-via-pointer using .callprototype directive
.func dispatch (.reg .u32 fptr, .reg .u32 idx)
{
...
Fproto: .callprototype _ (.param .u32 _, .param .u32 _);
      call  %fptr, (x, y), Fproto;
...

9.7.12.6. 控制流指令:ret

返回值

从函数返回到调用后的指令。

语法

ret{.uni};

描述

返回到调用者的执行环境。一个发散式返回会暂停线程,直到所有线程都准备好返回到调用者。这允许多个发散的ret指令。

除非存在.uni后缀表明返回值保证为非发散性,否则假定ret是发散的。

函数返回的任何值在执行ret指令前,都应移入返回参数变量中。

在顶层入口例程中执行的返回指令将终止线程执行。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

    ret;
@p  ret;

9.7.12.7. 控制流指令:exit

退出

终止一个线程。

语法

exit;

描述

终止线程的执行。

当线程退出时,会检查等待所有线程的屏障,以确定退出的线程是否是CTA中所有线程尚未到达屏障{.cta}或集群中所有线程尚未到达barrier.cluster的唯一线程。如果退出的线程阻碍了屏障,则该屏障将被释放。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

    exit;
@p  exit;

9.7.13. 并行同步与通信指令

这些说明是:

  • bar{.cta}, barrier{.cta}

  • bar.warp.sync

  • barrier.cluster

  • membar

  • atom

  • red

  • red.async

  • vote

  • match.sync

  • activemask

  • redux.sync

  • griddepcontrol

  • elect.sync

  • mbarrier.init

  • mbarrier.inval

  • mbarrier.arrive

  • mbarrier.arrive_drop

  • mbarrier.test_wait

  • mbarrier.try_wait

  • mbarrier.pending_count

  • cp.async.mbarrier.arrive

  • tensormap.cp_fenceproxy

  • clusterlaunchcontrol.try_cancel

  • clusterlaunchcontrol.query_cancel

9.7.13.1. 并行同步与通信指令:bar, barrier

bar{.cta}, barrier{.cta}

屏障同步。

语法

barrier{.cta}.sync{.aligned}      a{, b};
barrier{.cta}.arrive{.aligned}    a, b;

barrier{.cta}.red.popc{.aligned}.u32  d, a{, b}, {!}c;
barrier{.cta}.red.op{.aligned}.pred   p, a{, b}, {!}c;

bar{.cta}.sync      a{, b};
bar{.cta}.arrive    a, b;

bar{.cta}.red.popc.u32  d, a{, b}, {!}c;
bar{.cta}.red.op.pred   p, a{, b}, {!}c;

.op = { .and, .or };

描述

在CTA内部执行屏障同步和通信。每个CTA实例有16个编号为0..15的屏障。

barrier{.cta} 指令可被CTA内的线程用于同步和通信。

操作数 abd 的类型为 .u32;操作数 pc 是谓词。源操作数 a 将逻辑屏障资源指定为立即常量或寄存器,其值为 015。操作数 b 指定参与屏障的线程数量。如果未指定线程数,则CTA中的所有线程都将参与屏障。指定线程数时,该值必须是线程束大小的倍数。请注意,barrier{.cta}.arrive 需要非零线程数。

根据操作数b,指定数量的线程(以warp大小的倍数)或CTA中的所有线程将参与barrier{.cta}指令。该barrier{.cta}指令表示执行线程已到达指定的屏障。

barrier{.cta} 指令会使执行线程等待其所属warp中所有未退出的线程,并标记warp已到达屏障。除了通知到达屏障外,barrier{.cta}.redbarrier{.cta}.sync 指令还会使执行线程等待参与该屏障的所有其他warp的未退出线程到达。barrier{.cta}.arrive 则不会使执行线程等待其他参与warp的线程。

当屏障完成时,等待的线程会立即重新启动,屏障也会被重新初始化以便可以立即重复使用。

barrier{.cta}.syncbarrier{.cta}.redbarrier{.cta}.arrive指令确保当屏障操作完成时,该线程之前请求的内存访问相对于参与屏障的所有线程都已执行完毕。barrier{.cta}.syncbarrier{.cta}.red指令还进一步保证在屏障完成前,该线程不会发起新的内存访问请求。

当读取的值已从内存传输且不能被参与屏障的其他线程修改时,即表示已完成内存读取操作(例如通过ldatom指令)。当写入的值对参与屏障的其他线程可见时(即无法再读取先前值时),即表示已完成内存写入操作(例如通过stredatom指令)。

barrier{.cta}.red 执行跨线程的归约操作。来自CTA中所有线程的c谓词(或其补集)会通过指定的归约运算符进行组合。一旦达到屏障计数,最终值将被写入所有在屏障处等待线程的目标寄存器中。

barrier{.cta}.red的归约操作包括:人口计数(.popc)、全线程为真(.and)和任意线程为真(.or)。.popc的结果是谓词为True的线程数量,而.and.or则分别表示所有线程的谓词都为True,或者任意线程的谓词为True

指令 barrier{.cta} 具有可选的 .aligned 修饰符。当指定该修饰符时,表示CTA中的所有线程将执行相同的 barrier{.cta} 指令。在条件执行的代码中,对齐的 barrier{.cta} 指令应仅在已知CTA中所有线程对条件的评估结果相同时使用,否则行为是未定义的。

不同的线程束(warp)可以使用相同的屏障名称和线程数执行不同形式的barrier{.cta}指令。一个典型的例子是混合使用barrier{.cta}.syncbarrier{.cta}.arrive来实现生产者/消费者模型。生产者线程执行barrier{.cta}.arrive来宣告它们已到达屏障并立即继续执行以生产下一个值,而消费者线程则执行barrier{.cta}.sync来等待资源被生产出来。然后角色会反转,使用另一个不同的屏障,生产者线程执行barrier{.cta}.sync等待资源被消费,而消费者线程通过barrier{.cta}.arrive宣告资源已被消费。必须注意避免在屏障重置前让线程束执行超出预期的barrier{.cta}指令(例如先执行barrier{.cta}.arrive再执行其他任何针对同一屏障的barrier{.cta}指令)。barrier{.cta}.red不应与使用相同活动屏障的barrier{.cta}.syncbarrier{.cta}.arrive混合使用,否则执行结果将不可预测。

可选的.cta限定符仅表示该屏障在CTA级别的适用性,它不会改变指令的语义。

bar{.cta}.sync 等同于 barrier{.cta}.sync.alignedbar{.cta}.arrive 等同于 barrier{.cta}.arrive.alignedbar{.cta}.red 等同于 barrier{.cta}.red.aligned

注意

对于 .target sm_6x 或更低版本,

  1. barrier{.cta} 指令如果不带 .aligned 修饰符,则等同于 .aligned 变体,并且具有与 .aligned 变体相同的限制。

  2. warp中的所有线程(除已退出的线程外)必须在收敛时执行barrier{.cta}指令

PTX ISA 说明

bar.sync 在PTX ISA 1.0版本中引入时无需指定线程数。

注册操作数、线程数以及PTX ISA 2.0版本中引入的bar.{arrive,red}

barrier 指令在PTX ISA 6.0版本中引入。

.cta 限定符在PTX ISA 7.8版本中引入。

目标ISA注意事项

注册操作数、线程数以及bar{.cta}.{arrive,red}需要sm_20或更高版本。

对于sm_1x目标,仅支持带有立即屏障编号的bar{.cta}.sync

barrier{.cta} 指令需要 sm_30 或更高版本。

示例

// Use bar.sync to arrive at a pre-computed barrier number and
// wait for all threads in CTA to also arrive:
    st.shared [r0],r1;  // write my result to shared memory
    bar.cta.sync  1;    // arrive, wait for others to arrive
    ld.shared r2,[r3];  // use shared results from other threads

// Use bar.sync to arrive at a pre-computed barrier number and
// wait for fixed number of cooperating threads to arrive:
    #define CNT1 (8*12) // Number of cooperating threads

    st.shared [r0],r1;     // write my result to shared memory
    bar.cta.sync  1, CNT1; // arrive, wait for others to arrive
    ld.shared r2,[r3];     // use shared results from other threads

// Use bar.red.and to compare results across the entire CTA:
    setp.eq.u32 p,r1,r2;         // p is True if r1==r2
    bar.cta.red.and.pred r3,1,p; // r3=AND(p) forall threads in CTA

// Use bar.red.popc to compute the size of a group of threads
// that have a specific condition True:
    setp.eq.u32 p,r1,r2;         // p is True if r1==r2
    bar.cta.red.popc.u32 r3,1,p; // r3=SUM(p) forall threads in CTA

// Examples of barrier.cta.sync
    st.shared         [r0],r1;
    barrier.cta.sync  0;
    ld.shared         r1, [r0];

/* Producer/consumer model. The producer deposits a value in
 * shared memory, signals that it is complete but does not wait
 * using bar.arrive, and begins fetching more data from memory.
 * Once the data returns from memory, the producer must wait
 * until the consumer signals that it has read the value from
 * the shared memory location. In the meantime, a consumer
 * thread waits until the data is stored by the producer, reads
 * it, and then signals that it is done (without waiting).
 */
    // Producer code places produced value in shared memory.
    st.shared   [r0],r1;
    bar.arrive  0,64;
    ld.global   r1,[r2];
    bar.sync    1,64;
    ...

    // Consumer code, reads value from shared memory
    bar.sync   0,64;
    ld.shared  r1,[r0];
    bar.arrive 1,64;
    ...

9.7.13.2. 并行同步与通信指令:bar.warp.sync

bar.warp.sync

对warp中的线程进行屏障同步。

语法

bar.warp.sync      membermask;

描述

bar.warp.sync 会使执行线程等待,直到与membermask对应的所有线程都执行了具有相同membermask值的bar.warp.sync操作后,才会继续执行。

操作数 membermask 指定一个32位整数,该整数是一个掩码,指示参与屏障的线程,其中位位置对应于线程的 laneid

如果执行线程不在membermask中,bar.warp.sync的行为将是未定义的。

bar.warp.sync 还能确保参与屏障的线程之间的内存顺序。因此, 希望通过内存进行通信的warp内线程可以先存储到内存,执行 bar.warp.sync,然后安全地读取warp中其他线程存储的值。

注意

对于目标架构sm_6x或更低版本,membermask中的所有线程必须在收敛时执行相同的bar.warp.sync指令,且只有当线程属于某个membermask时,在执行bar.warp.sync指令期间才能处于活跃状态。否则,其行为将是未定义的。

PTX ISA 说明

在PTX ISA 6.0版本中引入。

目标ISA注意事项

需要 sm_30 或更高版本。

示例

st.shared.u32 [r0],r1;         // write my result to shared memory
bar.warp.sync  0xffffffff;     // arrive, wait for others to arrive
ld.shared.u32 r2,[r3];         // read results written by other threads

9.7.13.3. 并行同步与通信指令:barrier.cluster

barrier.cluster

集群内的屏障同步。

语法

barrier.cluster.arrive{.sem}{.aligned};
barrier.cluster.wait{.acquire}{.aligned};

.sem = {.release, .relaxed}

描述

在集群内执行屏障同步和通信。

barrier.cluster 指令可被集群内的线程用于同步和通信。

barrier.cluster.arrive 指令标记线程束到达屏障,但不会导致执行线程等待其他参与线程束的线程。

barrier.cluster.wait指令会使执行线程等待集群中所有未退出的线程完成barrier.cluster.arrive操作。

此外,barrier.cluster指令会使执行线程等待其warp中所有未退出的线程。

当所有执行了barrier.cluster.arrive的非退出线程都执行了barrier.cluster.wait后,屏障完成并重新初始化,因此可以立即重用。在屏障完成之前,每个线程只能到达屏障一次。

barrier.cluster.wait指令确保当其执行完成时,集群中所有线程按程序顺序在先前barrier.cluster.arrive之前发起的内存访问(异步操作除外)均已完成,并且对当前执行线程可见。

在执行线程按程序顺序发出barrier.cluster.arrive之后、barrier.cluster.wait之前的内存访问,不保证内存顺序和可见性。

barrier.cluster.arrive上的可选限定符.relaxed表示,对于在barrier.cluster.arrive之前执行的内存访问,不提供内存排序和可见性保证。

指令barrier.cluster.arrivebarrier.cluster.wait中的可选限定符.sem.acquire用于指定内存同步行为,具体描述见内存一致性模型。如果barrier.cluster.arrive指令中省略可选的.sem限定符,则默认采用.release语义。如果barrier.cluster.wait指令中省略可选的.acquire限定符,则默认采用.acquire语义。

可选的.aligned限定符表示warp中的所有线程必须执行相同的barrier.cluster指令。在条件执行的代码中,只有当确定warp中的所有线程对条件的评估结果相同时,才应使用对齐的barrier.cluster指令,否则行为将是未定义的。

PTX ISA 说明

在PTX ISA版本7.8中引入。

支持PTX ISA 8.0版本中引入的.acquire.relaxed.release限定符。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

// use of arrive followed by wait
ld.shared::cluster.u32 r0, [addr];
barrier.cluster.arrive.aligned;
...
barrier.cluster.wait.aligned;
st.shared::cluster.u32 [addr], r1;

// use memory fence prior to arrive for relaxed barrier
@cta0 ld.shared::cluster.u32 r0, [addr];
fence.cluster.acq_rel;
barrier.cluster.arrive.relaxed.aligned;
...
barrier.cluster.wait.aligned;
@cta1 st.shared::cluster.u32 [addr], r1;

9.7.13.4. 并行同步与通信指令:membar/fence

内存屏障/栅栏

强制执行内存操作的顺序。

语法

// Thread fence:
fence{.sem}.scope;

// Thread fence (uni-directional):
fence.acquire.sync_restrict::shared::cluster.cluster;
fence.release.sync_restrict::shared::cta.cluster;

// Operation fence (uni-directional):
fence.op_restrict.release.cluster;

// Proxy fence (bi-directional):
fence.proxy.proxykind;

// Proxy fence (uni-directional):
fence.proxy.to_proxykind::from_proxykind.release.scope;
fence.proxy.to_proxykind::from_proxykind.acquire.scope  [addr], size;
fence.proxy.async::generic.acquire.sync_restrict::shared::cluster.cluster;
fence.proxy.async::generic.release.sync_restrict::shared::cta.cluster;

// Old style membar:
membar.level;
membar.proxy.proxykind;

.sem       = { .sc, .acq_rel, .acquire, .release };
.scope     = { .cta, .cluster, .gpu, .sys };
.level     = { .cta, .gl, .sys };
.proxykind = { .alias, .async, .async.global, .async.shared::{cta, cluster} };
.op_restrict = { .mbarrier_init };
.to_proxykind::from_proxykind = {.tensormap::generic};

描述

membar指令确保由该线程请求的先前内存访问(ldstatomred指令)在指定的level级别完成,然后才会执行该线程在membar指令之后请求的后续内存操作。level限定符指定了可能观察到该操作排序效果的一组线程。

当读取的值已从内存传输且无法被其他线程在指定级别修改时,即完成了一次内存读取(例如通过ldatom)。当写入的值对指定级别的其他线程可见时,即当无法再读取先前值时,即完成了一次内存写入(例如通过stredatom)。

fence指令用于在当前线程发起的内存访问请求(ldstatomred指令)之间建立顺序关系,具体描述见内存一致性模型。作用域限定符指定了能够观察到该操作排序效果的一组线程。

fence.acq_rel 是一个轻量级的栅栏,在大多数程序中足以实现内存同步。当与内存一致性模型中描述的acquirerelease模式结合使用时,fence.acq_rel实例会进行同步。如果可选的.sem限定符不存在,默认情况下会假定为.acq_rel

fence.sc 是一种较慢的栅栏指令,在适当位置使用时可以恢复顺序一致性,但会牺牲性能。具有足够作用域的fence.sc实例总是通过在运行时确定每个作用域内的全序关系来实现同步。这个全序关系还可以被程序中的其他同步机制进一步约束。

限定符 .op_restrict.sync_restrict 限制了 fence 指令提供内存顺序保证的内存操作类别。当 .op_restrict.mbarrier_init 时,栅栏的同步效果仅适用于同一线程在 .shared::cta 状态空间中对mbarrier对象执行的先前 mbarrier.init 操作。当 .sync_restrict.sync_restrict::shared::cta 时,.sem 必须为 .release,且栅栏的效果仅适用于对 .shared::cta 状态空间中的对象执行的操作。同样,当 .sync_restrict.sync_restrict::shared::cluster 时,.sem 必须为 .acquire,且栅栏的效果仅适用于对 .shared::cluster 状态空间中的对象执行的操作。当存在 .sync_restrict::shared::cta.sync_restrict::shared::cluster 时,必须将 .scope 指定为 .cluster

地址操作数 addr 和操作数 size 共同指定了内存范围 [addr, addr+size-1],该范围用于确保跨代理的内存访问顺序。操作数 size 唯一支持的值为128,且必须是一个常量整数字面量。通用寻址会被无条件使用,且由操作数 addr 指定的地址必须位于 .global 状态空间内。否则,行为将是未定义的。

sm_70及更高版本中,membarfence.sc1的同义词,且membar级别ctaglsys分别对应fence作用域中的ctagpusys

membar.proxyfence.proxy 指令建立了可能通过不同代理发生的内存访问之间的顺序关系。

from-proxykindto-proxykind单向代理排序建立了通过from-proxykind执行的先前内存访问与通过to-proxykind执行的后续内存访问之间的顺序关系。

两个代理类型之间的双向代理排序会建立两个单向代理排序:一个是从第一个代理类型到第二个代理类型,另一个是从第二个代理类型到第一个代理类型。

.proxykind限定符表示在通用代理与.proxykind指定的代理之间进行的内存访问所建立的双向代理顺序。

.alias限定符的值.proxykind表示使用虚拟别名地址对同一内存位置进行的访问。.proxykind限定符的值.async指定异步代理与通用代理之间建立的内存排序。内存排序仅适用于在指定状态空间中对对象执行的操作。如果未指定状态空间,则内存排序适用于所有状态空间。

一个.release代理栅栏可以形成一个释放序列,该序列与包含.acquire代理栅栏的获取序列同步。.to_proxykind.from_proxykind限定符表示所建立的单向代理排序。

sm_70及更高版本上,membar.proxyfence.proxy的同义词。

1sm_70中引入的fence.sc语义是membar语义的超集,两者兼容;当在sm_70或更高架构上执行时,membar将获得fence.sc的全部语义。

PTX ISA 说明

membar.{cta,gl} 在PTX ISA版本1.4中引入。

membar.sys 在PTX ISA 2.0版本中引入。

fence 在PTX ISA 6.0版本中引入。

membar.proxyfence.proxy 在PTX ISA 7.5版本中引入。

.cluster作用域限定符在PTX ISA 7.8版本中引入。

.op_restrict 限定符在PTX ISA 8.0版本中引入。

fence.proxy.async 在PTX ISA 8.0版本中引入。

.to_proxykind::from_proxykind 限定符在PTX ISA 8.3版本中引入。

.acquire.release 限定符用于 PTX ISA 8.6 版本中引入的 fence 指令。

.sync_restrict 限定符在PTX ISA版本8.6中引入。

目标ISA注意事项

membar.{cta,gl} 在所有目标架构上都受支持。

membar.sys 需要 sm_20 或更高版本。

fence 需要 sm_70 或更高版本。

membar.proxy 需要 sm_60 或更高版本。

fence.proxy 需要 sm_70 或更高版本。

.cluster 作用域限定符需要 sm_90 或更高版本。

.op_restrict 限定符需要 sm_90 或更高版本。

fence.proxy.async 需要 sm_90 或更高版本。

.to_proxykind::from_proxykind 限定符需要 sm_90 或更高版本。

.acquire.release 限定符用于 fence 指令需要 sm_90 或更高版本。

.sync_restrict 限定符需要 sm_90 或更高版本。

示例

membar.gl;
membar.cta;
membar.sys;
fence.sc.cta;
fence.sc.cluster;
fence.proxy.alias;
membar.proxy.alias;
fence.mbarrier_init.release.cluster;
fence.proxy.async;
fence.proxy.async.shared::cta;
fence.proxy.async.shared::cluster;
fence.proxy.async.global;

tensormap.replace.tile.global_address.global.b1024.b64   [gbl], new_addr;
fence.proxy.tensormap::generic.release.gpu;
cvta.global.u64  tmap, gbl;
fence.proxy.tensormap::generic.acquire.gpu [tmap], 128;
cp.async.bulk.tensor.1d.shared::cluster.global.tile  [addr0], [tmap, {tc0}], [mbar0];

// Acquire remote barrier state via async proxy.
barrier.cluster.wait.acquire;
fence.proxy.async::generic.acquire.sync_restrict::shared::cluster.cluster;

// Release local barrier state via async proxy.
mbarrier.init [bar];
fence.mbarrier_init.release.cluster;
fence.proxy.async::generic.release.sync_restrict::shared::cta.cluster;
barrier.cluster.arrive.relaxed;

// Acquire local shared memory via generic proxy.
mbarrier.try_wait.relaxed.cluster.shared::cta.b64 complete, [addr], parity;
fence.acquire.sync_restrict::shared::cluster.cluster;

// Release local shared memory via generic proxy.
fence.release.sync_restrict::shared::cta.cluster;
mbarrier.arrive.relaxed.cluster.shared::cluster.b64 state, [bar];

9.7.13.5. 并行同步与通信指令:atom

原子

用于线程间通信的原子归约操作。

语法

标量类型的原子操作:

atom{.sem}{.scope}{.space}.op{.level::cache_hint}.type d, [a], b{, cache-policy};
atom{.sem}{.scope}{.space}.op.type d, [a], b, c;

atom{.sem}{.scope}{.space}.cas.b16 d, [a], b, c;

atom{.sem}{.scope}{.space}.cas.b128 d, [a], b, c {, cache-policy};
atom{.sem}{.scope}{.space}.exch{.level::cache_hint}.b128 d, [a], b {, cache-policy};

atom{.sem}{.scope}{.space}.add.noftz{.level::cache_hint}.f16     d, [a], b{, cache-policy};
atom{.sem}{.scope}{.space}.add.noftz{.level::cache_hint}.f16x2   d, [a], b{, cache-policy};

atom{.sem}{.scope}{.space}.add.noftz{.level::cache_hint}.bf16    d, [a], b{, cache-policy};
atom{.sem}{.scope}{.space}.add.noftz{.level::cache_hint}.bf16x2  d, [a], b{, cache-policy};

.space =              { .global, .shared{::cta, ::cluster} };
.sem =                { .relaxed, .acquire, .release, .acq_rel };
.scope =              { .cta, .cluster, .gpu, .sys };

.op =                 { .and, .or, .xor,
                        .cas, .exch,
                        .add, .inc, .dec,
                        .min, .max };
.level::cache_hint =  { .L2::cache_hint };
.type =               { .b32, .b64, .u32, .u64, .s32, .s64, .f32, .f64 };

使用向量类型的原子操作:

atom{.sem}{.scope}{.global}.add{.level::cache_hint}.vec_32_bit.f32                  d, [a], b{, cache-policy};
atom{.sem}{.scope}{.global}.op.noftz{.level::cache_hint}.vec_16_bit.half_word_type  d, [a], b{, cache-policy};
atom{.sem}{.scope}{.global}.op.noftz{.level::cache_hint}.vec_32_bit.packed_type     d, [a], b{, cache-policy};

.sem =               { .relaxed, .acquire, .release, .acq_rel };
.scope =             { .cta, .cluster, .gpu, .sys };
.op =                { .add, .min, .max };
.half_word_type =    { .f16, .bf16 };
.packed_type =       { .f16x2, .bf16x2 };
.vec_16_bit =        { .v2, .v4, .v8 }
.vec_32_bit =        { .v2, .v4 };
.level::cache_hint = { .L2::cache_hint }

描述

原子性地将位置a处的原始值加载到目标寄存器d中,使用操作数b与位置a中的值执行归约操作,并将指定操作的结果存储到位置a,覆盖原始值。操作数a指定了特定状态空间中的位置。如果未指定状态空间,则使用通用寻址执行内存访问。标量类型的atom仅可与.global.shared空间以及通用寻址一起使用,其中地址指向.global.shared空间。向量类型的atom仅可与.global空间以及地址指向.global空间的通用寻址一起使用。

对于向量类型的atom,操作数db是大括号包围的向量表达式,其大小等于向量限定符的大小。

如果未为.shared状态空间指定子限定符,则默认假定为::cta

可选的.sem限定符用于指定内存同步效果,具体描述见内存一致性模型。如果未指定.sem限定符,则默认采用.relaxed模式。

可选的.scope限定符指定了能直接观察到该操作内存同步效果的线程集合,如内存一致性模型中所述。如果未指定.scope限定符,默认情况下会采用.gpu作用域。

对于具有向量类型的atom,下表展示了支持的向量限定符与类型组合,以及这些组合上支持的原子操作:

向量限定符

类型

.f16/ bf16

.f16x2/ bf16x2

.f32

.v2

.add, .min, .max

.add, .min, .max

.add

.v4

.add, .min, .max

.add, .min, .max

.add

.v8

.add, .min, .max

不支持

不支持

只有当两个原子操作(atomred)各自指定的作用域包含对方时,它们才会相对于彼此原子地执行。当不满足此条件时,每个操作观察另一个操作的执行过程,就像该操作被拆分为一个读取操作后跟一个依赖写入操作一样。

atom 指令作用于打包类型或向量类型时,会访问内存中相邻的标量元素。在这种情况下,原子性仅针对每个单独的标量元素分别得到保证;整个 atom 操作并不保证作为单次访问具有原子性。

对于sm_6x及更早的架构,.shared状态空间上的atom操作不能保证与对同一地址的常规存储指令的原子性。程序员需要负责确保使用共享内存原子指令的程序正确性,例如通过在常规存储和对同一地址的原子操作之间插入屏障,或使用atom.exch来存储被其他原子操作访问的位置。

操作数 a 支持的寻址模式和对齐要求详见 Addresses as Operands

位操作包括.and(与)、.or(或)、.xor(异或)、.cas(比较并交换)和.exch(交换)。

整数运算包括.add.inc.dec.min.max。其中.inc.dec运算返回的结果范围在[0..b]之间。

浮点运算.add操作会四舍五入到最近的偶数。当前在全局内存中实现的atom.add.f32会将次正规输入和结果刷新为保留符号的零;而共享内存中的atom.add.f32则支持次正规输入和结果,不会将它们刷新为零。

atom.add.f16, atom.add.f16x2, atom.add.bf16atom.add.bf16x2 操作需要 .noftz 限定符;它会保留次正规输入和结果,不会将它们刷新为 零。

当指定可选参数cache-policy时,限定符.level::cache_hint是必需的。64位操作数cache-policy指定了在内存访问期间可能使用的缓存逐出策略。

限定符 .level::cache_hint 仅支持 .global 状态空间以及指向 .global 状态空间的通用寻址。

cache-policy 是对缓存子系统的提示,可能不会始终被遵循。它仅被视为性能提示,不会改变程序的内存一致性行为。

语义

atomic {
    d = *a;
    *a = (operation == cas) ? operation(*a, b, c)
                            : operation(*a, b);
}
where
    inc(r, s)  = (r >= s) ? 0 : r+1;
    dec(r, s)  = (r==0 || r > s)  ? s : r-1;
    exch(r, s) =  s;
    cas(r,s,t) = (r == s) ? t : r;

注意事项

简单的归约操作可以通过使用位桶目标操作数_来指定。

PTX ISA 说明

32位原子全局操作在PTX ISA版本1.1中引入。

atom.shared 以及 PTX ISA 1.2 引入的 64位 atom.global.{add,cas,exch}

atom.add.f32 以及64位atom.shared.{add,cas,exch} 在PTX ISA 2.0中引入。

64位 atom.{and,or,xor,min,max} 在PTX ISA 3.1中引入。

atom.add.f64 在PTX ISA 5.0中引入。

.scope 限定符在PTX ISA 5.0中引入。

.sem 限定符在PTX ISA 6.0版本中引入。

atom.add.noftz.f16x2 在PTX ISA 6.2中引入。

atom.add.noftz.f16atom.cas.b16 在PTX ISA 6.3中引入。

在PTX ISA 6.3版本中明确了atom.f16x2的逐元素原子性,该特性可追溯至PTX ISA 6.2版本。

支持PTX ISA版本7.4中引入的.level::cache_hint限定符。

atom.add.noftz.bf16atom.add.noftz.bf16x2 在PTX ISA 7.8中引入。

支持在PTX ISA版本7.8中引入的.cluster作用域限定符。

支持PTX ISA版本7.8中引入的::cta::cluster子限定符。

支持PTX ISA 8.1版本中引入的向量类型。

支持PTX ISA版本8.3中引入的.b128类型。

支持在PTX ISA 8.4版本中引入的.sys作用域与.b128类型。

目标ISA注意事项

atom.global 需要 sm_11 或更高版本。

atom.shared 需要 sm_12 或更高版本。

64位 atom.global.{add,cas,exch} 需要 sm_12 或更高版本。

64位 atom.shared.{add,cas,exch} 需要 sm_20 或更高版本。

64位 atom.{and,or,xor,min,max} 需要 sm_32 或更高版本。

atom.add.f32 需要 sm_20 或更高版本。

atom.add.f64 需要 sm_60 或更高版本。

.scope 限定符需要 sm_60 或更高版本。

.sem 限定符需要 sm_70 或更高版本。

使用通用寻址需要sm_20或更高版本。

atom.add.noftz.f16x2 需要 sm_60 或更高版本。

atom.add.noftz.f16atom.cas.b16 需要 sm_70 或更高版本。

支持 .level::cache_hint 限定符需要 sm_80 或更高版本。

atom.add.noftz.bf16atom.add.noftz.bf16x2 需要 sm_90 或更高版本。

支持.cluster作用域限定符需要sm_90或更高版本。

子限定符 ::cta 需要 sm_30 或更高版本。

子限定符 ::cluster 需要 sm_90 或更高版本。

支持向量类型需要sm_90或更高版本。

支持 .b128 类型需要 sm_90 或更高版本。

示例

atom.global.add.s32  d,[a],1;
atom.shared::cta.max.u32  d,[x+4],0;
@p  atom.global.cas.b32  d,[p],my_val,my_new_val;
atom.global.sys.add.u32 d, [a], 1;
atom.global.acquire.sys.inc.u32 ans, [gbl], %r0;
atom.add.noftz.f16x2 d, [a], b;
atom.add.noftz.f16   hd, [ha], hb;
atom.global.cas.b16  hd, [ha], hb, hc;
atom.add.noftz.bf16   hd, [a], hb;
atom.add.noftz.bf16x2 bd, [b], bb;
atom.add.shared::cluster.noftz.f16   hd, [ha], hb;
atom.shared.b128.cas d, a, b, c; // 128-bit atom
atom.global.b128.exch d, a, b;   // 128-bit atom

atom.global.cluster.relaxed.add.u32 d, [a], 1;

createpolicy.fractional.L2::evict_last.b64 cache-policy, 0.25;
atom.global.add.L2::cache_hint.s32  d, [a], 1, cache-policy;

atom.global.v8.f16.max.noftz  {%hd0, %hd1, %hd2, %hd3, %hd4, %hd5, %hd6, %hd7}, [gbl],
                                              {%h0, %h1, %h2, %h3, %h4, %h5, %h6, %h7};
atom.global.v8.bf16.add.noftz  {%hd0, %hd1, %hd2, %hd3, %hd4, %hd5, %hd6, %hd7}, [gbl],
                                              {%h0, %h1, %h2, %h3, %h4, %h5, %h6, %h7};
atom.global.v2.f16.add.noftz  {%hd0, %hd1}, [gbl], {%h0, %h1};
atom.global.v2.bf16.add.noftz  {%hd0, %hd1}, [gbl], {%h0, %h1};
atom.global.v4.b16x2.min.noftz  {%hd0, %hd1, %hd2, %hd3}, [gbl], {%h0, %h1, %h2, %h3};
atom.global.v4.f32.add  {%f0, %f1, %f2, %f3}, [gbl], {%f0, %f1, %f2, %f3};
atom.global.v2.f16x2.min.noftz  {%bd0, %bd1}, [g], {%b0, %b1};
atom.global.v2.bf16x2.max.noftz  {%bd0, %bd1}, [g], {%b0, %b1};
atom.global.v2.f32.add  {%f0, %f1}, [g], {%f0, %f1};

9.7.13.6. 并行同步与通信指令:red

红色

对全局内存和共享内存的归约操作。

语法

使用标量类型的归约操作:

red{.sem}{.scope}{.space}.op{.level::cache_hint}.type          [a], b{, cache-policy};

red{.sem}{.scope}{.space}.add.noftz{.level::cache_hint}.f16    [a], b{, cache-policy};

red{.sem}{.scope}{.space}.add.noftz{.level::cache_hint}.f16x2  [a], b{, cache-policy};

red{.sem}{.scope}{.space}.add.noftz{.level::cache_hint}.bf16
                                                      [a], b {, cache-policy};

red{.sem}{.scope}{.space}.add.noftz{.level::cache_hint}.bf16x2
                                                      [a], b {, cache-policy};

.space =              { .global, .shared{::cta, ::cluster} };
.sem =                {.relaxed, .release};
.scope =              {.cta, .cluster, .gpu, .sys};

.op =                 { .and, .or, .xor,
                        .add, .inc, .dec,
                        .min, .max };
.level::cache_hint =  { .L2::cache_hint };
.type =               { .b32, .b64, .u32, .u64, .s32, .s64, .f32, .f64 };

使用向量类型的归约操作:

red{.sem}{.scope}{.global}.add{.level::cache_hint}.vec_32_bit.f32 [a], b{, cache-policy};
red{.sem}{.scope}{.global}.op.noftz{.level::cache_hint}. vec_16_bit.half_word_type [a], b{, cache-policy};
red{.sem}{.scope}{.global}.op.noftz{.level::cache_hint}.vec_32_bit.packed_type [a], b {, cache-policy};

.sem =                { .relaxed, .release };
.scope =              { .cta, .cluster, .gpu, .sys };
.op =                 { .add, .min, .max };
.half_word_type =     { .f16, .bf16 };
.packed_type =        { .f16x2,.bf16x2 };
.vec_16_bit =         { .v2, .v4, .v8 }
.vec_32_bit =         { .v2, .v4 };
.level::cache_hint =  { .L2::cache_hint }

描述

使用操作数b和位置a中的值执行归约操作,并将指定操作的结果存储在位置a,覆盖原始值。操作数a指定了特定状态空间中的一个位置。如果未给定状态空间,则使用通用寻址执行内存访问。标量类型的red只能与.global.shared空间以及通用寻址一起使用,其中地址指向.global.shared空间。向量类型的red只能与.global空间以及指向.global空间的通用寻址一起使用。

对于向量类型的red,操作数b是大括号包围的向量表达式,其大小等于向量限定符的大小。

如果未为.shared状态空间指定子限定符,则默认假定为::cta

可选的.sem限定符用于指定内存同步效果,具体描述见内存一致性模型。如果未指定.sem限定符,则默认采用.relaxed模式。

可选的.scope限定符指定了能直接观察到该操作内存同步效果的线程集合,如内存一致性模型中所述。如果未指定.scope限定符,默认情况下会采用.gpu作用域。

对于red的向量类型,下表展示了支持的向量限定符、类型组合以及这些组合上支持的归约操作:

向量限定符

类型

.f16/ bf16

.f16x2/ bf16x2

.f32

.v2

.add, .min, .max

.add, .min, .max

.add

.v4

.add, .min, .max

.add, .min, .max

.add

.v8

.add, .min, .max

不支持

不支持

只有当两个原子操作(atomred)各自指定的作用域包含对方时,它们才会相对于彼此原子地执行。当不满足此条件时,每个操作观察另一个操作的执行过程,就像该操作被拆分为一个读取操作后跟一个依赖写入操作一样。

red 指令作用于打包类型或向量类型时,会访问内存中相邻的标量元素。在这种情况下,原子性仅针对每个独立的标量元素分别得到保证;整个red操作并不保证作为单次访问具有原子性。

对于sm_6x及更早的架构,在.shared状态空间上执行的red操作不能保证与对同一地址的常规存储指令的原子性。程序员需要自行确保使用共享内存归约指令的程序正确性,例如通过在常规存储和归约操作之间插入屏障来保护共享地址,或者使用atom.exch指令来存储可能被其他归约操作访问的位置。

操作数 a 支持的寻址模式和对齐要求详见 Addresses as Operands

位运算操作包括.and.or.xor

整数运算包括.add.inc.dec.min.max。其中.inc.dec运算返回的结果范围在[0..b]之间。

浮点运算.add操作会四舍五入到最近的偶数。当前在全局内存中实现的red.add.f32会将次正规输入和结果刷新为保留符号的零;而共享内存中的red.add.f32则支持次正规输入和结果,不会将它们刷新为零。

red.add.f16, red.add.f16x2, red.add.bf16red.add.bf16x2 操作需要使用 .noftz 限定符;它会保留次正规输入和结果,不会将它们刷新为零。

当指定可选参数cache-policy时,限定符.level::cache_hint是必需的。64位操作数cache-policy指定了在内存访问期间可能使用的缓存逐出策略。

限定符 .level::cache_hint 仅支持 .global 状态空间以及指向 .global 状态空间的通用寻址。

cache-policy 是对缓存子系统的提示,可能不会始终被遵循。它仅被视为性能提示,不会改变程序的内存一致性行为。

语义

*a = operation(*a, b);

where
    inc(r, s) = (r >= s) ? 0 : r+1;
    dec(r, s) = (r==0 || r > s)  ? s : r-1;

PTX ISA 说明

在PTX ISA版本1.2中引入。

red.add.f32red.shared.add.u64 在PTX ISA 2.0中引入。

64位red.{and,or,xor,min,max}在PTX ISA 3.1中引入。

red.add.f64 在PTX ISA 5.0中引入。

.scope 限定符在PTX ISA 5.0中引入。

.sem 限定符在PTX ISA 6.0版本中引入。

red.add.noftz.f16x2 在PTX ISA 6.2版本中引入。

red.add.noftz.f16 在PTX ISA 6.3中引入。

在PTX ISA 6.3版本中明确了red.f16x2的逐元素原子性,该说明对PTX ISA 6.2版本具有追溯效力

支持PTX ISA版本7.4中引入的.level::cache_hint限定符。

red.add.noftz.bf16red.add.noftz.bf16x2 在 PTX ISA 7.8 中引入。

支持在PTX ISA版本7.8中引入的.cluster作用域限定符。

支持PTX ISA版本7.8中引入的::cta::cluster子限定符。

支持PTX ISA 8.1版本中引入的向量类型。

目标ISA注意事项

red.global 需要 sm_11 或更高版本

red.shared 需要 sm_12 或更高版本。

red.global.add.u64 需要 sm_12 或更高版本。

red.shared.add.u64 需要 sm_20 或更高版本。

64位 red.{and,or,xor,min,max} 需要 sm_32 或更高版本。

red.add.f32 需要 sm_20 或更高版本。

red.add.f64 需要 sm_60 或更高版本。

.scope 限定符需要 sm_60 或更高版本。

.sem 限定符需要 sm_70 或更高版本。

使用通用寻址需要sm_20或更高版本。

red.add.noftz.f16x2 需要 sm_60 或更高版本。

red.add.noftz.f16 需要 sm_70 或更高版本。

支持 .level::cache_hint 限定符需要 sm_80 或更高版本。

red.add.noftz.bf16red.add.noftz.bf16x2 需要 sm_90 或更高版本。

支持.cluster作用域限定符需要sm_90或更高版本。

子限定符 ::cta 需要 sm_30 或更高版本。

子限定符 ::cluster 需要 sm_90 或更高版本。

支持向量类型需要sm_90或更高版本。

示例

red.global.add.s32  [a],1;
red.shared::cluster.max.u32  [x+4],0;
@p  red.global.and.b32  [p],my_val;
red.global.sys.add.u32 [a], 1;
red.global.acquire.sys.add.u32 [gbl], 1;
red.add.noftz.f16x2 [a], b;
red.add.noftz.bf16   [a], hb;
red.add.noftz.bf16x2 [b], bb;
red.global.cluster.relaxed.add.u32 [a], 1;
red.shared::cta.min.u32  [x+4],0;

createpolicy.fractional.L2::evict_last.b64 cache-policy, 0.25;
red.global.and.L2::cache_hint.b32 [a], 1, cache-policy;

red.global.v8.f16.add.noftz  [gbl], {%h0, %h1, %h2, %h3, %h4, %h5, %h6, %h7};
red.global.v8.bf16.min.noftz [gbl], {%h0, %h1, %h2, %h3, %h4, %h5, %h6, %h7};
red.global.v2.f16.add.noftz [gbl], {%h0, %h1};
red.global.v2.bf16.add.noftz [gbl], {%h0, %h1};
red.global.v4.f16x2.max.noftz [gbl], {%h0, %h1, %h2, %h3};
red.global.v4.f32.add  [gbl], {%f0, %f1, %f2, %f3};
red.global.v2.f16x2.max.noftz {%bd0, %bd1}, [g], {%b0, %b1};
red.global.v2.bf16x2.add.noftz {%bd0, %bd1}, [g], {%b0, %b1};
red.global.v2.f32.add  {%f0, %f1}, [g], {%f0, %f1};

9.7.13.7. 并行同步与通信指令:red.async

red.async

异步归约操作。

语法

// Increment and Decrement reductions
red.async.sem.scope{.ss}.completion_mechanism.op.type [a], b, [mbar];

.sem  =                 { .relaxed };
.scope =                { .cluster };
.ss   =                 { .shared::cluster };
.op   =                 { .inc, .dec };
.type =                 { .u32 };
.completion_mechanism = { .mbarrier::complete_tx::bytes };


// MIN and MAX reductions
red.async.sem.scope{.ss}.completion_mechanism.op.type [a], b, [mbar];

.sem  = { .relaxed };
.scope = { .cluster };
.ss   = { .shared::cluster };
.op   = { .min, .max };
.type = { .u32, .s32 };
.completion_mechanism = { .mbarrier::complete_tx::bytes };

// Bitwise AND, OR and XOR reductions
red.async.sem.scope{.ss}.completion_mechanism.op.type [a], b, [mbar];

.sem  = { .relaxed };
.scope = { .cluster };
.ss   = { .shared::cluster };
.op   = { .and, .or, .xor };
.type = { .b32 };
.completion_mechanism = { .mbarrier::complete_tx::bytes };

// ADD reductions
red.async.sem.scope{.ss}.completion_mechanism.add.type [a], b, [mbar];

.sem  = { .relaxed };
.scope = { .cluster };
.ss   = { .shared::cluster };
.type = { .u32, .s32, .u64 };
.completion_mechanism = { .mbarrier::complete_tx::bytes };

red.async{.mmio}.sem.scope{.ss}{.completion_mechanism}.add.type [a], b;

.sem  = { .release };
.scope = { .gpu, .cluster };
.ss   = { .global };
.type = { .u32, .s32, .u64, .s64 };
.completion_mechanism = { };

描述

red.async 是一个非阻塞指令,用于启动由 .op 指定的异步归约操作,操作数为 b 以及由操作数 a 指定的目标共享内存位置的值。

操作数

  • a 是目标地址,必须是一个寄存器,或者是 register + immOff 的形式,如 Addresses as Operands 中所述。

  • b 是一个源值,其类型由限定符 .type 指定。

  • mbar 是一个mbarrier对象地址。

限定符

  • .mmio 表示这是否是一个 mmio操作

  • .sem 指定内存排序语义,如内存一致性模型中所述。

  • .scope 指定了该指令可以直接同步的线程集合。

  • .ss 指定了目标操作数 a 和 mbarrier 操作数 mbar 的状态空间。

  • .completion_mechanism 指定了观察异步操作完成的机制。

    • .completion_mechanism设置为.mbarrier::complete_tx::bytes时:在异步操作完成后,将在操作数mbar指定的mbarrier对象上执行complete-tx操作,其中completeCount参数等于以字节为单位存储的数据量。

    • 当未指定.completion_mechanism时:存储的完成将与CTA结束同步。

  • .op 指定了归约操作。

    • .inc.dec 操作返回的结果范围在 [0..b] 之间。

  • .type 指定源操作数 b 的类型。

条件

.sem.relaxed 时:

  • reduce操作是一种宽松的内存操作。

  • mbarrier上的complete-tx操作在.cluster范围内具有.release语义。

  • 目标操作数 a 和 mbarrier 操作数 mbar 的共享内存地址必须满足以下所有条件:

    • 它们属于同一个CTA。

    • 它们所属的CTA与执行线程的CTA不同,但必须位于同一集群内。

    否则,行为是未定义的。

  • .mmio 不可被指定。

  • 如果指定了.scope,则必须为.cluster

  • 如果未指定.scope,则默认为.cluster

  • 如果指定了.ss,则必须为.shared::cluster

  • 如果未指定.ss,则对操作数ambar使用通用寻址。 如果指定的通用地址不在.shared::cluster状态空间的地址窗口范围内,则行为是未定义的。

  • 如果指定了.completion_mechanism,则必须为.mbarrier::complete_tx::bytes

  • 如果未指定.completion_mechanism,则默认为.mbarrier::complete_tx::bytes

.sem.release 时:

  • reduce操作是一种强内存操作,具有.release语义,作用域由.scope指定。

  • 如果指定了.mmio,则.scope必须为.sys

  • 如果指定了.scope,它可以是.gpu.sys

  • 如果未指定.scope,则默认为.sys

  • 如果指定了.ss,则必须为.global

  • 如果未指定.ss,则对操作数a使用通用寻址方式。 如果指定的通用地址不在.global状态空间的地址窗口范围内,则行为是未定义的。

  • .completion_mechanism 不能指定。

PTX ISA 说明

在PTX ISA版本8.1中引入。

支持PTX ISA 8.7版本中引入的.mmio限定符、.release语义、.global状态空间以及.gpu.sys作用域。

目标ISA注意事项

需要 sm_90 或更高版本。

.mmio限定符、.release语义、.global状态空间, 以及.gpu.sys作用域需要sm_100或更高版本。

示例

red.async.relaxed.cluster.shared::cluster.mbarrier::complete_tx::bytes.min.u32 [addr], b, [mbar_addr];

red.async.release.sys.global.add.u32 [addr], b;

9.7.13.8. 并行同步与通信指令:vote (已弃用)

vote (已弃用)

跨线程组投票。

语法

vote.mode.pred  d, {!}a;
vote.ballot.b32 d, {!}a;  // 'ballot' form, returns bitmask

.mode = { .all, .any, .uni };

弃用说明

在PTX ISA 6.0版本中,不带.sync限定符的vote指令已被弃用。

  • 对于.target低于sm_70的指令支持,可能会在未来的PTX ISA版本中被移除。

移除说明

在PTX ISA版本6.4中,针对.targetsm_70或更高版本,移除了对不带.sync限定符的vote指令的支持。

描述

对warp内所有活跃线程的源谓词执行归约操作。目标谓词值在warp的所有线程中保持一致。

归约模式包括:

.all

True 如果源谓词对warp中的所有活动线程为True。对源谓词取反可计算.none

.any

True 如果源谓词对warp中的某些活动线程为True。对源谓词取反以计算.not_all

.uni

True 如果源谓词在warp内所有活动线程中具有相同的值。对源谓词取反也会计算 .uni

ballot形式中,vote.ballot.b32只是将warp中每个线程的谓词复制到目标寄存器d的对应比特位,其中比特位对应于线程的lane id。

当参与vote.ballot.b32时,warp中的非活动线程将为其条目贡献一个0。

PTX ISA 说明

在PTX ISA版本1.2中引入。

在PTX ISA版本6.0中已弃用,推荐使用vote.sync

对于.target sm_70或更高版本,PTX ISA 6.4版本不支持。

目标ISA注意事项

vote 需要 sm_12 或更高版本。

vote.ballot.b32 需要 sm_20 或更高版本。

vote 从 PTX ISA 6.4 版本开始不再支持 sm_70 或更高架构。

版本发布说明

请注意,vote适用于单个warp内的线程,而非整个CTA。

示例

vote.all.pred    p,q;
vote.uni.pred    p,q;
vote.ballot.b32  r1,p;  // get 'ballot' across warp

9.7.13.9. 并行同步与通信指令: vote.sync

vote.sync

跨线程组投票。

语法

vote.sync.mode.pred  d, {!}a, membermask;
vote.sync.ballot.b32 d, {!}a, membermask;  // 'ballot' form, returns bitmask

.mode = { .all, .any, .uni };

描述

vote.sync 会使执行线程等待,直到与membermask对应的所有未退出线程都使用相同的限定符和相同的membermask值执行了vote.sync后,才会恢复执行。

操作数 membermask 指定一个32位整数,它是一个掩码,指示参与此指令的线程,其中位位置对应于线程的 laneid。操作数 a 是一个谓词寄存器。

mode模式下,vote.sync会对membermask中所有未退出的线程执行源谓词的归约操作。目标操作数d是一个谓词寄存器,其值在membermask中的所有线程间保持一致。

归约模式包括:

.all

True 如果源谓词对于membermask中所有未退出的线程为True。对源谓词取反可计算.none

.any

True 表示如果membermask中某些线程的源谓词为True。对源谓词取反可计算.not_all

.uni

True 如果源谓词在membermask的所有未退出线程中具有相同的值。对源谓词取反也会计算.uni

ballot形式中,目标操作数d是一个.b32寄存器。在这种形式下, vote.sync.ballot.b32简单地将membermask中每个线程的谓词复制到目标寄存器d的对应比特位, 其中比特位对应线程的车道ID。

未在membermask中指定的线程将在vote.sync.ballot.b32中为其条目贡献0值。

如果执行线程不在membermask中,vote.sync的行为将是未定义的。

注意

对于.target sm_6x 或更低版本,membermask中的所有线程必须在收敛时执行相同的vote.sync指令,并且只有当线程属于某个membermask时才能在执行vote.sync指令时处于活动状态。否则,行为将是未定义的。

PTX ISA 说明

在PTX ISA 6.0版本中引入。

目标ISA注意事项

需要 sm_30 或更高版本。

示例

vote.sync.all.pred    p,q,0xffffffff;
vote.sync.ballot.b32  r1,p,0xffffffff;  // get 'ballot' across warp

9.7.13.10. 并行同步与通信指令:match.sync

match.sync

在线程束(warp)内的线程间广播并比较一个值。

语法

match.any.sync.type  d, a, membermask;
match.all.sync.type  d[|p], a, membermask;

.type = { .b32, .b64 };

描述

match.sync 会使执行线程等待,直到 membermask 中所有未退出的线程都使用相同的限定符和相同的 membermask 值执行了 match.sync 后,才会继续执行。

操作数 membermask 指定一个32位整数,该整数是一个掩码,指示参与此指令的线程,其中位位置对应于线程的laneid。

match.sync 在所有未退出的线程中对操作数 a 执行广播和比较操作,并根据模式设置目标 d 和可选谓词 p

操作数 a 具有指令类型,而 d 具有 .b32 类型。

目标d是一个32位掩码,其中掩码中的位位置对应于线程的laneid。

匹配操作模式包括:

.all

d 会被设置为与 membermask 中未退出的线程对应的掩码,前提是 membermask 中所有未退出的线程对操作数 a 具有相同的值;否则 d 会被设置为 0。可选地,如果 membermask 中所有未退出的线程对操作数 a 具有相同的值,则谓词 p 会被设置为 true;否则 p 会被设置为 false。接收符号 '_' 可用于替代任意一个目标操作数。

.any

d 被设置为 membermask 中未退出的线程掩码,这些线程的操作数 a 具有相同的值。

如果执行线程不在membermask中,match.sync的行为将是未定义的。

PTX ISA 说明

在PTX ISA 6.0版本中引入。

目标ISA注意事项

需要 sm_70 或更高版本。

版本发布说明

请注意,match.sync适用于单个warp内的线程,而非整个CTA。

示例

match.any.sync.b32    d, a, 0xffffffff;
match.all.sync.b64    d|p, a, mask;

9.7.13.11. 并行同步与通信指令:activemask

活动掩码

查询warp内的活动线程。

语法

activemask.b32 d;

描述

activemask 查询执行线程束中处于活动状态的线程,并设置目标 d 为一个32位整数掩码,其中掩码中的位位置对应于线程的 laneid

目标 d 是一个32位目标寄存器。

活动线程将在结果中其对应条目贡献1,而已退出、非活动或被预测关闭的线程将在结果中其对应条目贡献0。

PTX ISA 说明

在PTX ISA 6.2版本中引入。

目标ISA注意事项

需要 sm_30 或更高版本。

示例

activemask.b32  %r1;

9.7.13.12. 并行同步与通信指令: redux.sync

redux.sync

对线程组中每个预测活动线程的数据执行归约操作。

语法

redux.sync.op.type dst, src, membermask;
.op   = {.add, .min, .max}
.type = {.u32, .s32}

redux.sync.op.b32 dst, src, membermask;
.op   = {.and, .or, .xor}

redux.sync.op{.abs.}{.NaN}.f32 dst, src, membermask;
.op   = { .min, .max }

描述

redux.sync 会使执行线程等待,直到与membermask对应的所有未退出的线程都使用相同的限定符和相同的membermask值执行了redux.sync后,才会恢复执行。

操作数 membermask 指定一个32位整数,该整数是一个掩码,指示参与此指令的线程,其中位位置对应于线程的 laneid

redux.sync 对32位源寄存器src执行归约操作.op,操作范围覆盖membermask中所有未退出的线程。归约操作的结果将写入32位目标寄存器dst

归约操作可以是位运算中的.and.or.xor,或者算术运算中的.add.min.max

对于.add操作,结果会被截断为32位。

对于.f32指令类型,如果输入值为0.0,则+0.0大于-0.0。

如果指定了.abs限定符,则在归约操作中将考虑输入值的绝对值。

如果指定了.NaN限定符,那么当参与归约操作的任何线程的输入为NaN时,归约操作的结果将为规范NaN。

在没有.NaN限定符的情况下,归约操作仅考虑非NaN值,当所有输入都是NaN时,结果将是标准NaN。

如果执行线程不在membermask中,redux.sync的行为将是未定义的。

PTX ISA 说明

在PTX ISA版本7.0中引入。

在PTX ISA 8.6版本中引入了对.f32类型的支持。

.abs.NaN限定符的支持已在PTX ISA 8.6版本中引入。

目标ISA注意事项

需要 sm_80 或更高版本。

.f32 类型需要 sm_100a

限定符 .abs.NaN 需要 sm_100a

版本发布说明

请注意,redux.sync适用于单个warp内的线程,而非整个CTA。

示例

.reg .b32 dst, src, init, mask;
redux.sync.add.s32 dst, src, 0xff;
redux.sync.xor.b32 dst, src, mask;

redux.sync.min.abs.NaN.f32 dst, src, mask;

9.7.13.13. 并行同步与通信指令:griddepcontrol

网格深度控制

控制依赖网格的执行。

语法

griddepcontrol.action;

.action   = { .launch_dependents, .wait }

描述

griddepcontrol 指令允许运行时定义的依赖网格和先决网格通过以下方式控制执行:

.launch_dependents 修饰符表示运行时系统指定的特定依赖项可以在网格中所有其他CTA发出相同指令或完成后立即调度。依赖项可能在当前网格完成前启动。不保证依赖项会在当前网格完成前启动。当前CTA中线程重复调用此指令不会产生超出首次调用的额外副作用。

.wait 修饰符会使执行线程等待,直到所有正在运行的前置网格任务完成,并且这些前置网格的所有内存操作都执行完毕并对当前网格可见。

注意

如果前提网格正在使用griddepcontrol.launch_dependents,那么依赖网格必须使用griddepcontrol.wait以确保功能正确执行。

PTX ISA 说明

在PTX ISA版本7.8中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

griddepcontrol.launch_dependents;
griddepcontrol.wait;

9.7.13.14. 并行同步与通信指令: elect.sync

elect.sync

从一组线程中选举出一个领导者线程。

语法

elect.sync d|p, membermask;

描述

elect.sync 从由 membermask 指定的一组线程中选举出一个符合条件的活跃主线程。被选线程的 laneid 会返回到32位目标操作数 d 中。接收符号 '_' 可用于目标操作数 d。对于主线程,谓词目标 p 会被设为 True,而对于其他所有线程则设为 False

操作数 membermask 指定一个32位整数,用于指示要从中选举领导者的线程集合。如果执行线程不在 membermask 中,则行为未定义。

领导者线程的选举是确定性的,也就是说,每次针对相同的membermask都会选出相同的领导者线程。

强制性的 .sync 限定符表示 elect 会使执行线程等待,直到 membermask 中的所有线程都执行完 elect 指令后才会继续执行。

PTX ISA 说明

在PTX ISA版本8.0中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

elect.sync    %r0|%p0, 0xffffffff;

9.7.13.15. 并行同步与通信指令:mbarrier

mbarrier 是一个在共享内存中创建的屏障,支持:

  • 同步CTA内任意线程子集

  • 跨集群CTA的线程单向同步。如共享内存的mbarrier支持所述,线程只能执行到达操作,而不能对位于shared::cluster空间中的mbarrier执行*_wait操作。

  • 等待线程发起的异步内存操作完成,并使这些操作对其他线程可见。

一个mbarrier对象是内存中的不透明对象,可以使用以下方式进行初始化和失效:

  • mbarrier.init

  • mbarrier.inval

支持在mbarrier对象上的操作包括:

  • mbarrier.expect_tx

  • mbarrier.complete_tx

  • mbarrier.arrive

  • mbarrier.arrive_drop

  • mbarrier.test_wait

  • mbarrier.try_wait

  • mbarrier.pending_count

  • cp.async.mbarrier.arrive

对未初始化的mbarrier对象执行除mbarrier.init之外的任何mbarrier操作会导致未定义行为。 对已初始化的mbarrier对象执行任何非mbarriermbarrier.init操作会导致未定义行为。

bar{.cta}/barrier{.cta}指令(每个CTA只能访问有限数量的屏障)不同,mbarrier对象由用户自定义,仅受限于可用的共享内存总大小。

mbarrier 操作允许线程在到达 mbarrier 后、等待 mbarrier 完成前执行有效工作。

9.7.13.15.1. mbarrier对象的大小与对齐方式

mbarrier对象是一种不透明对象,具有以下类型和对齐要求:

类型

对齐(字节)

内存空间

.b64

8

.shared

9.7.13.15.2. mbarrier对象的内容

一个不透明的mbarrier对象用于跟踪以下信息:

  • mbarrier对象的当前阶段

  • 当前阶段mbarrier对象的待处理到达计数

  • mbarrier对象下一阶段的预期到达数量

  • 当前阶段mbarrier对象跟踪的待处理异步内存操作(或事务)计数。这也被称为tx-count

一个mbarrier对象会经历一系列阶段,每个阶段由线程执行预期数量的arrive-on操作来定义。

每个计数的有效范围如下所示:

计数名称

最小值

最大值

预期到达数量

1

220 - 1

待处理到达计数

0

220 - 1

交易计数

-(220 - 1)

220 - 1

9.7.13.15.3. mbarrier对象的生命周期

在使用之前,必须初始化mbarrier对象

一个mbarrier对象用于同步线程和异步内存操作。

一个mbarrier对象可用于执行一系列此类同步操作。

必须使mbarrier对象失效,才能将其内存重新用于任何目的,包括将其重新用作另一个mbarrier对象。

9.7.13.15.4. mbarrier对象的阶段

mbarrier对象的阶段是指该对象被用于同步线程和cp.async操作的次数。在每个阶段{0, 1, 2, ...}中,线程按程序顺序执行:

  • arrive-on 操作完成当前阶段并

  • test_wait / try_wait 操作来检查当前阶段是否完成。

一个mbarrier对象在当前阶段完成后会自动重新初始化,以便立即在下一阶段使用。当前阶段尚未完成,而所有先前阶段均已完成。

对于mbarrier对象的每个阶段,必须至少执行一次test_waittry_wait操作,该操作需为waitComplete返回True,然后才能在后续阶段执行arrive-on操作。

9.7.13.15.5. 通过mbarrier对象追踪异步操作

从Hopper架构(sm_9x)开始,mbarrier对象支持一个称为tx-count的新计数器,用于追踪异步内存操作或事务的完成情况。tx-count以异步内存操作指定的单位来追踪尚未完成的异步事务数量。

mbarrier对象tx-count必须设置为当前阶段需要跟踪的异步内存操作总量,单位由异步操作指定。每当一个异步操作完成时,系统将在mbarrier对象上执行complete-tx操作,从而推动mbarrier向当前阶段的完成状态前进。

9.7.13.15.5.1. expect-tx operation

expect-tx 操作通过 expectCount 参数,将 mbarrier对象tx-count 增加指定数值。这使得 mbarrier对象 的当前阶段能够预期并追踪额外异步事务的完成情况。

9.7.13.15.5.2. complete-tx operation

mbarrier对象执行带有completeCount参数的complete-tx操作包含以下步骤:

mbarrier signaling

标记由当前阶段跟踪的异步事务已完成。因此,tx-count会减少completeCount

mbarrier potentially completing the current phase

如果当前阶段已完成,则mbarrier将过渡到下一阶段。有关阶段完成要求和阶段过渡过程的详细信息,请参阅 mbarrier对象的阶段完成

9.7.13.15.6. mbarrier对象的阶段完成

当前阶段完成的要求如下所述。完成当前阶段后,该阶段将按照以下描述转入后续阶段。

Current phase completion requirements

当满足以下所有条件时,mbarrier对象将完成当前阶段:

  • 待处理到达的数量已降至零。

  • tx-count 已降为零。

Phase transition

当一个mbarrier对象完成当前阶段时,以下操作会以原子方式执行:

  • mbarrier对象 过渡到下一阶段。

  • 待处理的到达计数被重新初始化为预期的到达计数。

9.7.13.15.7. 在mbarrier对象上执行到达操作

mbarrier对象上执行的到达操作(带有可选的计数参数)包含以下2个步骤:

  • mbarrier信号:

    表示执行线程已到达或由执行线程在mbarrier对象上发起的到达操作所对应的cp.async指令已完成。这将使待处理的到达计数减少count值。如果未指定count参数,则默认值为1。

  • mbarrier可能完成当前阶段:

    如果当前阶段已完成,则mbarrier将过渡到下一阶段。有关阶段完成要求和阶段转换过程的详细信息,请参阅 mbarrier对象的阶段完成

9.7.13.15.8. 共享内存的mbarrier支持

下表总结了位于不同共享内存位置的mbarrier对象对各种mbarrier操作的支持情况:

内存屏障操作

.shared::cta

.shared::cluster

mbarrier.arrive

已支持

支持,但无法返回结果

mbarrier.expect_tx

已支持

已支持

mbarrier.complete_tx

已支持

已支持

其他mbarrier操作

支持

不支持

9.7.13.15.9. 并行同步与通信指令:mbarrier.init

mbarrier.init

初始化mbarrier对象

语法

mbarrier.init{.shared{::cta}}.b64 [addr], count;

描述

mbarrier.init 在由地址操作数 addr 指定的位置初始化 mbarrier对象,并使用无符号32位整数 count。操作数count的值必须在mbarrier对象的内容中指定的范围内。

mbarrier对象的初始化包括:

  • 将当前阶段初始化为0。

  • 将预期到达计数初始化为count

  • 将待到达计数初始化为 count

  • tx-count初始化为0。

如果未指定状态空间,则使用通用寻址。如果addr指定的地址不在.shared::cta状态空间的地址窗口内,则行为是未定义的。

操作数addr支持的寻址模式如地址作为操作数中所述。操作数addr的对齐方式如mbarrier对象的大小和对齐中所述。

对包含有效mbarrier对象的内存位置执行mbarrier.init操作的行为是未定义的;在将该内存位置重新用于任何其他用途(包括另一个mbarrier对象)之前,应首先使用mbarrier.inval使mbarrier对象失效。

PTX ISA 说明

在PTX ISA版本7.0中引入。

在PTX ISA 7.8版本中引入了对.shared上子限定符::cta的支持。

目标ISA注意事项

需要 sm_80 或更高版本。

示例

.shared .b64 shMem, shMem2;
.reg    .b64 addr;
.reg    .b32 %r1;

cvta.shared.u64          addr, shMem2;
mbarrier.init.b64        [addr],   %r1;
bar.cta.sync             0;
// ... other mbarrier operations on addr

mbarrier.init.shared::cta.b64 [shMem], 12;
bar.sync                 0;
// ... other mbarrier operations on shMem
9.7.13.15.10. 并行同步与通信指令:mbarrier.inval

mbarrier.inval

使mbarrier对象失效。

语法

mbarrier.inval{.shared{::cta}}.b64 [addr];

描述

mbarrier.inval 使地址操作数 addr 指定位置的 mbarrier 对象 失效。

在使用mbarrier对象的内存位置用于任何其他用途之前,必须使其失效。

在未包含有效mbarrier对象的内存位置上执行除mbarrier.init之外的任何mbarrier操作,将导致未定义行为。

如果未指定状态空间,则使用通用寻址。如果addr指定的地址不在.shared::cta状态空间的地址窗口内,则行为是未定义的。

操作数addr支持的寻址模式如地址作为操作数中所述。操作数addr的对齐方式如mbarrier对象的大小与对齐中所述。

PTX ISA 说明

在PTX ISA版本7.0中引入。

在PTX ISA 7.8版本中引入了对.shared上子限定符::cta的支持。

目标ISA注意事项

需要 sm_80 或更高版本。

示例

.shared .b64 shmem;
.reg    .b64 addr;
.reg    .b32 %r1;
.reg    .pred t0;

// Example 1 :
bar.sync                      0;
@t0 mbarrier.init.b64     [addr], %r1;
// ... other mbarrier operations on addr
bar.sync                      0;
@t0 mbarrier.inval.b64    [addr];


// Example 2 :
bar.cta.sync                  0;
mbarrier.init.shared.b64           [shmem], 12;
// ... other mbarrier operations on shmem
bar.cta.sync                  0;
@t0 mbarrier.inval.shared.b64      [shmem];

// shmem can be reused here for unrelated use :
bar.cta.sync                  0;
st.shared.b64                      [shmem], ...;

// shmem can be re-initialized as mbarrier object :
bar.cta.sync                  0;
@t0 mbarrier.init.shared.b64       [shmem], 24;
// ... other mbarrier operations on shmem
bar.cta.sync                  0;
@t0 mbarrier.inval.shared::cta.b64 [shmem];
9.7.13.15.11. 并行同步与通信指令:mbarrier.expect_tx

mbarrier.expect_tx

mbarrier对象执行expect-tx操作。

语法

mbarrier.expect_tx{.sem}{.scope}{.space}.b64 [addr], txCount;

.sem   = { .relaxed }
.scope = { .cta, .cluster }
.space = { .shared{::cta}, .shared::cluster }

描述

执行mbarrier.expect_tx的线程会在地址操作数addr指定的位置对mbarrier对象执行expect-tx操作。32位无符号整数操作数txCount指定了expect-tx操作的expectCount参数。

如果未指定状态空间,则使用通用寻址。如果addr指定的地址不在.shared::cta.shared::cluster状态空间的地址窗口内,则行为未定义。

操作数addr支持的寻址模式如地址作为操作数中所述。操作数addr的对齐方式遵循mbarrier对象的大小与对齐中的描述。

此操作不提供任何内存排序语义,因此是一个宽松操作。

PTX ISA 说明

在PTX ISA版本8.0中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

mbarrier.expect_tx.b64                       [addr], 32;
mbarrier.expect_tx.relaxed.cta.shared.b64    [mbarObj1], 512;
mbarrier.expect_tx.relaxed.cta.shared.b64    [mbarObj2], 512;
9.7.13.15.12. 并行同步与通信指令:mbarrier.complete_tx

mbarrier.complete_tx

mbarrier对象上执行complete-tx操作。

语法

mbarrier.complete_tx{.sem}{.scope}{.space}.b64 [addr], txCount;

.sem   = { .relaxed }
.scope = { .cta, .cluster }
.space = { .shared{::cta}, .shared::cluster }

描述

执行mbarrier.complete_tx的线程会对地址操作数addr指定的mbarrier对象执行complete-tx操作。32位无符号整数操作数txCount指定了complete-tx操作的completeCount参数。

mbarrier.complete_tx 不涉及任何异步内存操作,仅模拟异步内存操作的完成及其向mbarrier对象发出信号的副作用。

如果未指定状态空间,则使用通用寻址。如果addr指定的地址不在.shared::cta.shared::cluster状态空间的地址窗口内,则行为是未定义的。

操作数addr支持的寻址模式如地址作为操作数中所述。操作数addr的对齐方式遵循mbarrier对象的大小与对齐中的描述。

此操作不提供任何内存排序语义,因此是一个宽松操作。

PTX ISA 说明

在PTX ISA版本8.0中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

mbarrier.complete_tx.b64             [addr],     32;
mbarrier.complete_tx.shared.b64      [mbarObj1], 512;
mbarrier.complete_tx.relaxed.cta.b64 [addr2],    32;
9.7.13.15.13. 并行同步与通信指令:mbarrier.arrive

mbarrier.arrive

mbarrier对象执行到达操作

语法

mbarrier.arrive{.sem}{.scope}{.shared{::cta}}.b64           state, [addr]{, count};
mbarrier.arrive{.sem}{.scope}{.shared::cluster}.b64         _, [addr] {,count}
mbarrier.arrive.expect_tx{.sem}{.scope}{.shared{::cta}}.b64 state, [addr], txCount;
mbarrier.arrive.expect_tx{.sem}{.scope}{.shared::cluster}.b64   _, [addr], txCount;
mbarrier.arrive.noComplete{.release}{.cta}{.shared{::cta}}.b64  state, [addr], count;

.sem   = { .release, .relaxed }
.scope = { .cta, .cluster }

描述

执行mbarrier.arrive的线程会在地址操作数addr指定的位置对mbarrier对象执行arrive-on操作。32位无符号整数操作数count指定了arrive-on操作的count参数。

如果未指定状态空间,则使用通用寻址。如果addr指定的地址不在.shared::cta状态空间的地址窗口内,则行为是未定义的。

操作数addr支持的寻址模式如Addresses as Operands中所述。操作数addr的对齐方式遵循Size and alignment of mbarrier object中的描述。

可选限定符.expect_tx指定在执行arrive-on操作前会先执行expect-tx操作。32位无符号整数操作数txCount指定了expect-tx操作的expectCount参数。当同时指定.arrive.expect_tx限定符时,arrive-on操作的count参数默认为1。

带有.noComplete限定符的mbarrier.arrive操作不得导致mbarrier完成其当前阶段,否则行为将是未定义的。

操作数count的值必须在mbarrier对象内容中指定的范围内。

注意:对于sm_8x,当指定参数count时,必须使用修饰符.noComplete

在位于.shared::cta中的mbarrier对象上执行mbarrier.arrive操作时,会返回一个不透明的64位寄存器值,该值记录了执行到达操作mbarrier对象的阶段状态到目标操作数state.中。state操作数的具体内容由实现决定。可选地,可以使用占位符'_'作为state参数。

mbarrier.arrive 操作位于 .shared::cluster 但不在 .shared::cta 中的 mbarrier 对象时无法返回值。对于这种情况,目标操作数必须使用下划线符号 '_'。

可选的.sem限定符用于指定内存同步效果,具体描述见内存一致性模型。如果未指定.sem限定符,则默认采用.release

.relaxed限定符不提供任何内存排序语义和可见性保证。

可选的.scope限定符表示直接观察到该操作内存同步效果的线程集合,如内存一致性模型中所述。如果未指定.scope限定符,则默认为.cta。相比之下,.shared::表示mbarrier所在的状态空间。

PTX ISA 说明

在PTX ISA版本7.0中引入。

PTX ISA 7.1版本引入了对目标操作数使用下划线符号'_'作为接收端的支持。

在PTX ISA 7.8版本中引入了对.shared上子限定符::cta的支持。

支持在PTX ISA版本7.8中引入的count参数,无需使用修饰符.noComplete

支持PTX ISA 8.0版本中引入的子限定符::cluster

对限定符.expect_tx的支持在PTX ISA 8.0版本中引入。

支持PTX ISA 8.0版本中引入的.scope.sem限定符

支持PTX ISA版本8.6中引入的.relaxed限定符。

目标ISA注意事项

需要 sm_80 或更高版本。

支持不带修饰符.noCompletecount参数需要sm_90或更高版本。

限定符 .expect_tx 需要 sm_90 或更高版本。

子限定符 ::cluster 需要 sm_90 或更高版本。

支持.cluster范围需要sm_90或更高版本。

示例

.reg .b32 cnt, remoteAddr32, remoteCTAId, addr32;
.reg .b64 %r<5>, addr, remoteAddr64;
.shared .b64 shMem, shMem2;

cvta.shared.u64            addr, shMem2;
mov.b32                    addr32, shMem2;
mapa.shared::cluster.u32   remoteAddr32, addr32, remoteCTAId;
mapa.u64                   remoteAddr64, addr,   remoteCTAId;

cvta.shared.u64          addr, shMem2;

mbarrier.arrive.shared.b64                       %r0, [shMem];
mbarrier.arrive.shared::cta.b64                  %r0, [shMem2];
mbarrier.arrive.release.cta.shared::cluster.b64  _, [remoteAddr32];
mbarrier.arrive.release.cluster.b64              _, [remoteAddr64], cnt;
mbarrier.arrive.expect_tx.release.cluster.b64    _, [remoteAddr64], tx_count;
mbarrier.arrive.noComplete.b64                   %r1, [addr], 2;
mbarrier.arrive.relaxed.cta.b64                  %r2, [addr], 4;
mbarrier.arrive.b64                              %r2, [addr], cnt;
9.7.13.15.14. 并行同步与通信指令:mbarrier.arrive_drop

mbarrier.arrive_drop

递减mbarrier对象的预期计数并执行arrive-on操作

语法

mbarrier.arrive_drop{.sem}{.scope}{.shared{::cta}}.b64 state,           [addr]{, count};
mbarrier.arrive_drop{.sem}{.scope}{.shared::cluster}.b64           _,   [addr] {,count};
mbarrier.arrive_drop.expect_tx{.shared{::cta}}{.sem}{.scope}.b64 state, [addr], tx_count;
mbarrier.arrive_drop.expect_tx{.shared::cluster}{.sem}{.scope}.b64   _, [addr], tx_count;
mbarrier.arrive_drop.noComplete{.release}{.cta}{.shared{::cta}}.b64 state,  [addr], count;

.sem   = { .release, .relaxed }
.scope = { .cta, .cluster }

描述

一个线程在由地址操作数addr指定的位置对mbarrier对象执行mbarrier.arrive_drop时,会执行以下步骤:

  • mbarrier对象的预期到达计数减少由32位整数操作数count指定的值。如果未指定count操作数,则默认为1。

  • mbarrier对象执行到达操作。操作数count指定了到达操作count参数。

mbarrier对象预期到达计数的递减操作将作用于该mbarrier对象的所有后续阶段。

如果未指定状态空间,则使用通用寻址。如果addr指定的地址不在.shared::cta.shared::cluster状态空间的地址窗口内,则行为未定义。

操作数addr支持的寻址模式如Addresses as Operands中所述。操作数addr的对齐方式遵循Size and alignment of mbarrier object中的描述。

可选限定符.expect_tx指定在执行arrive-on操作前会先执行一个expect-tx操作。32位无符号整数操作数txCount指定了expect-tx操作的expectCount参数。当同时指定.arrive.expect_tx限定符时,arrive-on操作的count参数默认为1。

mbarrier.arrive_drop操作配合.release限定符构成了内存一致性模型中描述的释放模式,并与获取模式实现同步。

可选的.sem限定符用于指定内存同步效果,如内存一致性模型中所述。如果未指定.sem限定符,则默认采用.release.relaxed限定符不提供任何内存排序语义和可见性保证。

可选的.scope限定符表示mbarrier.arrive_drop指令可以直接同步的线程集合。如果未指定.scope限定符,则默认为.cta。相比之下,.shared::表示mbarrier所在的状态空间。

带有.noComplete限定符的mbarrier.arrive_drop操作不得完成mbarrier,否则行为将是未定义的。

操作数count的值必须在mbarrier对象内容中指定的范围内。

注意:对于sm_8x,当指定参数count时,必须使用修饰符.noComplete

一个线程若希望退出或不参与arrive-on操作,可以使用mbarrier.arrive_drop将自己从mbarrier中移除。

在位于.shared::cta中的mbarrier对象上执行mbarrier.arrive_drop操作时,会返回一个不透明的64位寄存器值,该值记录了执行到达操作mbarrier对象的阶段状态到目标操作数state中。返回状态的具体内容是实现相关的。可选地,可以使用占位符'_'作为state参数。

mbarrier.arrive_drop 操作在位于 .shared::cluster 但不在 .shared::cta 中的 mbarrier 对象上无法返回值。对于这种情况,目标操作数必须使用下划线符号 '_' 作为接收符号。

PTX ISA 说明

在PTX ISA版本7.0中引入。

在PTX ISA 7.8版本中引入了对.shared上子限定符::cta的支持。

支持在PTX ISA版本7.8中引入的count参数,无需使用修饰符.noComplete

对限定符.expect_tx的支持在PTX ISA 8.0版本中引入。

支持PTX ISA 8.0版本中引入的子限定符::cluster

支持PTX ISA 8.0版本中引入的.scope.sem限定符

支持PTX ISA版本8.6中引入的.relaxed限定符。

目标ISA注意事项

需要 sm_80 或更高版本。

支持不带修饰符.noCompletecount参数需要sm_90或更高版本。

限定符 .expect_tx 需要 sm_90 或更高版本。

子限定符 ::cluster 需要 sm_90 或更高版本。

支持.cluster范围需要sm_90或更高版本。

示例

.reg .b32 cnt;
.reg .b64 %r1;
.shared .b64 shMem;

// Example 1
@p mbarrier.arrive_drop.shared.b64 _, [shMem];
@p exit;
@p2 mbarrier.arrive_drop.noComplete.shared.b64 _, [shMem], %a;
@p2 exit;
..
@!p mbarrier.arrive.shared.b64   %r1, [shMem];
@!p mbarrier.test_wait.shared.b64  q, [shMem], %r1;

// Example 2
mbarrier.arrive_drop.shared::cluster.b64 _, [addr];
mbarrier.arrive_drop.shared::cta.release.cluster.b64     _, [addr], cnt;

// Example 3
mbarrier.arrive_drop.expect_tx.shared::cta.relaxed.cluster.b64 state, [addr], tx_count;
9.7.13.15.15. 并行同步与通信指令:cp.async.mbarrier.arrive

cp.async.mbarrier.arrive

使mbarrier对象跟踪执行线程发起的之前所有cp.async操作。

语法

cp.async.mbarrier.arrive{.noinc}{.shared{::cta}}.b64 [addr];

描述

当执行线程发起的全部先前cp.async操作完成后,系统会在mbarrier对象上触发一个arrive-on操作。该mbarrier对象位于操作数addr指定的位置。arrive-on操作相对于cp.async.mbarrier.arrive的执行是异步的。

当未指定.noinc修饰符时,mbarrier对象的待处理计数会在异步arrive-on操作之前增加1。这导致在当前阶段,异步arrive-on操作对待处理计数的净变化为零。递增后mbarrier对象的待处理计数不应超过mbarrier对象内容中提到的限制。否则,行为将是未定义的。

当指定.noinc修饰符时,将不会对mbarrier对象的待处理计数执行递增操作。因此,由异步arrive-on操作完成的待处理计数递减必须在mbarrier对象的初始化中予以考虑。

如果未指定状态空间,则使用通用寻址。如果addr指定的地址不在.shared::cta状态空间的地址窗口内,则行为是未定义的。

操作数addr支持的寻址模式如Addresses as Operands中所述。操作数addr的对齐方式遵循Size and alignment of mbarrier object中的描述。

PTX ISA 说明

在PTX ISA版本7.0中引入。

在PTX ISA 7.8版本中引入了对.shared上子限定符::cta的支持。

目标ISA注意事项

需要 sm_80 或更高版本。

示例

// Example 1: no .noinc
mbarrier.init.shared.b64 [shMem], threadCount;
....
cp.async.ca.shared.global [shard1], [gbl1], 4;
cp.async.cg.shared.global [shard2], [gbl2], 16;
....
// Absence of .noinc accounts for arrive-on from completion of prior cp.async operations.
// So mbarrier.init must only account for arrive-on from mbarrier.arrive.
cp.async.mbarrier.arrive.shared.b64 [shMem];
....
mbarrier.arrive.shared.b64 state, [shMem];

waitLoop:
mbarrier.test_wait.shared.b64 p, [shMem], state;
@!p bra waitLoop;



// Example 2: with .noinc

// Tracks arrive-on from mbarrier.arrive and cp.async.mbarrier.arrive.

// All threads participating in the mbarrier perform cp.async
mov.b32 copyOperationCnt, threadCount;

// 3 arrive-on operations will be triggered per-thread
mul.lo.u32 copyArrivalCnt, copyOperationCnt, 3;

add.u32 totalCount, threadCount, copyArrivalCnt;

mbarrier.init.shared.b64 [shMem], totalCount;
....
cp.async.ca.shared.global [shard1], [gbl1], 4;
cp.async.cg.shared.global [shard2], [gbl2], 16;
...
// Presence of .noinc requires mbarrier initalization to have accounted for arrive-on from cp.async
cp.async.mbarrier.arrive.noinc.shared.b64 [shMem]; // 1st instance
....
cp.async.ca.shared.global [shard3], [gbl3], 4;
cp.async.ca.shared.global [shard4], [gbl4], 16;
cp.async.mbarrier.arrive.noinc.shared::cta.b64 [shMem]; // 2nd instance
....
cp.async.ca.shared.global [shard5], [gbl5], 4;
cp.async.cg.shared.global [shard6], [gbl6], 16;
cp.async.mbarrier.arrive.noinc.shared.b64 [shMem]; // 3rd and last instance
....
mbarrier.arrive.shared.b64 state, [shMem];

waitLoop:
mbarrier.test_wait.shared.b64 p, [shMem], state;
@!p bra waitLoop;
9.7.13.15.16. 并行同步与通信指令:mbarrier.test_wait/mbarrier.try_wait

mbarrier.test_wait/mbarrier.try_wait

检查mbarrier对象是否已完成该阶段。

语法

mbarrier.test_wait{.sem}{.scope}{.shared{::cta}}.b64        waitComplete, [addr], state;
mbarrier.test_wait.parity{.sem}{.scope}{.shared{::cta}}.b64 waitComplete, [addr], phaseParity;

mbarrier.try_wait{.sem}{.scope}{.shared{::cta}}.b64         waitComplete, [addr], state
                                                               {, suspendTimeHint};

mbarrier.try_wait.parity{.sem}{.scope}{.shared{::cta}}.b64  waitComplete, [addr], phaseParity
                                                               {, suspendTimeHint};

.sem   = { .acquire, .relaxed }
.scope = { .cta, .cluster }

描述

test_waittry_wait 操作用于测试由操作数 addr 指定位置的 mbarrier 对象当前或紧邻前一个阶段的完成状态。

mbarrier.test_wait 是一个非阻塞指令,用于测试阶段是否完成。

mbarrier.try_wait 是一个可能阻塞的指令,用于测试阶段是否完成。如果阶段未完成,执行线程可能会被挂起。被挂起的线程将在指定阶段完成时恢复执行,或者在系统相关时间限制内阶段未完成前恢复执行。可选的32位无符号整数操作数 suspendTimeHint 指定了时间限制(以纳秒为单位),该限制可用于替代系统相关的时间限制。

mbarrier.test_waitmbarrier.try_wait 用于测试阶段是否完成:

  • 由操作数state指定,该操作数由当前阶段或紧接前一阶段在同一mbarrier对象上执行的mbarrier.arrive指令返回。或者

  • 由操作数phaseParity指示,该操作数是mbarrier对象当前阶段或紧邻前一个阶段的整数奇偶性。

指令的.parity变体用于测试由操作数phaseParity指示的相位是否完成,该操作数是mbarrier对象当前相位或紧邻前一相位的整数奇偶性。偶数相位的整数奇偶性为0,奇数相位的整数奇偶性为1。因此,phaseParity操作数的有效值为0和1。

注意:使用.parity变体指令需要在整个生命周期内跟踪mbarrier对象的阶段。

test_waittry_wait 操作仅对以下情况有效:

  • 当前未完成的阶段,对此waitComplete返回False

  • 紧接的前一个阶段,其中waitComplete返回True

如果未指定状态空间,则使用通用寻址。如果addr指定的地址不在.shared::cta状态空间的地址窗口内,则行为是未定义的。

操作数addr支持的寻址模式如Addresses as Operands中所述。操作数addr的对齐方式遵循Size and alignment of mbarrier object中的描述。

当带有.acquire限定符的mbarrier.test_waitmbarrier.try_wait操作返回True时,它们会形成内存一致性模型中描述的获取模式。

可选的.sem限定符用于指定内存同步效果,如内存一致性模型中所述。如果未指定.sem限定符,则默认采用.acquire.relaxed限定符不提供任何内存排序语义和可见性保证。

可选的.scope限定符表示mbarrier.test_waitmbarrier.try_wait指令可以直接同步的线程集合。如果未指定.scope限定符, 则默认为.cta。相比之下,.shared::表示mbarrier所在的状态空间。

当具有获取语义的mbarrier.test_waitmbarrier.try_wait返回True时,执行线程的内存操作遵循以下顺序:

  1. 在程序顺序中,所有在mbarrier.arrive之前请求的内存访问(除了异步操作),在CTA参与线程完成的阶段具有释放语义,这些操作会被执行并对执行线程可见。

  2. 在CTA参与线程完成阶段之前,按程序顺序请求的所有cp.async操作都会被执行,并对执行线程可见。

  3. 所有使用相同mbarrier对象cp.async.bulk异步操作,在程序顺序中,在CTA参与线程完成的阶段通过具有释放语义的mbarrier.arrive请求之前,都会被执行并对执行线程可见。

  4. mbarrier.test_waitmbarrier.try_wait之后请求的所有内存访问(按程序顺序)都不会执行,并且对于其他参与mbarrier的线程(按程序顺序)通过具有释放语义的mbarrier.arrive之前执行的内存访问不可见。

  5. 在线程以释放语义执行mbarrier.arrive之后,到程序顺序中mbarrier.test_wait之前,对于该线程请求的内存访问没有顺序性和可见性保证。

PTX ISA 说明

mbarrier.test_wait 在PTX ISA 7.0版本中引入。

修饰符 .parity 在 PTX ISA 版本 7.1 中引入。

mbarrier.try_wait 在PTX ISA版本7.8中引入。

在PTX ISA 7.8版本中引入了对.shared上子限定符::cta的支持。

支持PTX ISA 8.0版本中引入的.scope.sem限定符

支持PTX ISA版本8.6中引入的.relaxed限定符。

目标ISA注意事项

mbarrier.test_wait 需要 sm_80 或更高版本。

mbarrier.try_wait 需要 sm_90 或更高版本。

支持.cluster范围需要sm_90或更高版本。

示例

// Example 1a, thread synchronization with test_wait:

.reg .b64 %r1;
.shared .b64 shMem;

mbarrier.init.shared.b64 [shMem], N;  // N threads participating in the mbarrier.
...
mbarrier.arrive.shared.b64  %r1, [shMem]; // N threads executing mbarrier.arrive

// computation not requiring mbarrier synchronization...

waitLoop:
mbarrier.test_wait.shared.b64    complete, [shMem], %r1;
@!complete nanosleep.u32 20;
@!complete bra waitLoop;

// Example 1b, thread synchronization with try_wait :

.reg .b64 %r1;
.shared .b64 shMem;

mbarrier.init.shared.b64 [shMem], N;  // N threads participating in the mbarrier.
...
mbarrier.arrive.shared.b64  %r1, [shMem]; // N threads executing mbarrier.arrive

// computation not requiring mbarrier synchronization...

waitLoop:
mbarrier.try_wait.relaxed.cluster.shared.b64    complete, [shMem], %r1;
@!complete bra waitLoop;


// Example 2, thread synchronization using phase parity :

.reg .b32 i, parArg;
.reg .b64 %r1;
.shared .b64 shMem;

mov.b32 i, 0;
mbarrier.init.shared.b64 [shMem], N;  // N threads participating in the mbarrier.
...
loopStart :                           // One phase per loop iteration
    ...
    mbarrier.arrive.shared.b64  %r1, [shMem]; // N threads
    ...
    and.b32 parArg, i, 1;
    waitLoop:
    mbarrier.test_wait.parity.shared.b64  complete, [shMem], parArg;
    @!complete nanosleep.u32 20;
    @!complete bra waitLoop;
    ...
    add.u32 i, i, 1;
    setp.lt.u32 p, i, IterMax;
@p bra loopStart;


// Example 3, Asynchronous copy completion waiting :

.reg .b64 state;
.shared .b64 shMem2;
.shared .b64 shard1, shard2;
.global .b64 gbl1, gbl2;

mbarrier.init.shared.b64 [shMem2], threadCount;
...
cp.async.ca.shared.global [shard1], [gbl1], 4;
cp.async.cg.shared.global [shard2], [gbl2], 16;

// Absence of .noinc accounts for arrive-on from prior cp.async operation
cp.async.mbarrier.arrive.shared.b64 [shMem2];
...
mbarrier.arrive.shared.b64 state, [shMem2];

waitLoop:
mbarrier.test_wait.shared::cta.b64 p, [shMem2], state;
@!p bra waitLoop;

// Example 4, Synchronizing the CTA0 threads with cluster threads
.reg .b64 %r1, addr, remAddr;
.shared .b64 shMem;

cvta.shared.u64          addr, shMem;
mapa.u64                 remAddr, addr, 0;     // CTA0's shMem instance

// One thread from CTA0 executing the below initialization operation
@p0 mbarrier.init.shared::cta.b64 [shMem], N;  // N = no of cluster threads

barrier.cluster.arrive;
barrier.cluster.wait;

// Entire cluster executing the below arrive operation
mbarrier.arrive.release.cluster.b64              _, [remAddr];

// computation not requiring mbarrier synchronization ...

// Only CTA0 threads executing the below wait operation
waitLoop:
mbarrier.try_wait.parity.acquire.cluster.shared::cta.b64  complete, [shMem], 0;
@!complete bra waitLoop;
9.7.13.15.17. 并行同步与通信指令:mbarrier.pending_count

mbarrier.pending_count

从opaque mbarrier状态查询待处理的到达计数。

语法

mbarrier.pending_count.b64 count, state;

描述

可以使用mbarrier.pending_count从不透明的mbarrier状态中查询待处理计数。

state操作数是一个64位寄存器,必须是之前执行mbarrier.arrive.noCompletembarrier.arrive_drop.noComplete指令的结果。否则,行为将是未定义的。

目标寄存器count是一个32位无符号整数,表示在获取state寄存器的arrive-on操作之前,mbarrier对象的待处理计数。

PTX ISA 说明

在PTX ISA版本7.0中引入。

目标ISA注意事项

需要 sm_80 或更高版本。

示例

.reg .b32 %r1;
.reg .b64 state;
.shared .b64 shMem;

mbarrier.arrive.noComplete.b64 state, [shMem], 1;
mbarrier.pending_count.b64 %r1, state;

9.7.13.16. 并行同步与通信指令:tensormap.cp_fenceproxy

tensormap.cp_fenceproxy

一个融合的复制和栅栏操作。

语法

tensormap.cp_fenceproxy.cp_qualifiers.fence_qualifiers.sync.aligned  [dst], [src], size;

.cp_qualifiers    = { .global.shared::cta }
.fence_qualifiers = { .to_proxy::from_proxy.release.scope }
.to_proxy::from_proxy  = { .tensormap::generic }
.scope            = { .cta, .cluster, .gpu , .sys }

描述

tensormap.cp_fenceproxy 指令按顺序执行以下操作:

  • 在通用代理中,将共享内存中由地址操作数src指定的位置数据(大小由size参数以字节为单位指定)复制到全局内存中由地址操作数dst指定的位置。

  • 在从复制操作到随后在地址dst上的tensormap代理中执行的访问之间,建立了一个单向代理发布模式。

立即操作数size的有效值为128。

操作数 srcdst 分别指定了 shared::ctaglobal 状态空间中的非通用地址。

.scope限定符指定了可以直接观察到该操作代理同步效果的线程集合,具体描述见内存一致性模型

强制性的.sync限定符表示tensormap.cp_fenceproxy会使执行线程等待,直到warp中的所有线程都执行完相同的tensormap.cp_fenceproxy指令后才会继续执行。

强制性的.aligned限定符表示warp中的所有线程必须执行相同的tensormap.cp_fenceproxy指令。在条件执行的代码中,只有当确定warp中的所有线程对条件的评估结果完全一致时,才应使用对齐的tensormap.cp_fenceproxy指令,否则行为将是未定义的。

PTX ISA 说明

在PTX ISA版本8.3中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

// Example: manipulate a tensor-map object and then consume it in cp.async.bulk.tensor

.reg .b64 new_addr;
.global .align 128 .b8 gbl[128];
.shared .align 128 .b8 sMem[128];

cp.async.bulk.shared::cluster.global.mbarrier::complete_tx::bytes [sMem], [gMem], 128, [mbar];
...
try_wait_loop:
mbarrier.try_wait.shared.b64 p, [mbar], state;
@!p bra try_wait loop;

tensormap.replace.tile.global_address.shared.b1024.b64   [sMem], new_addr;
tensormap.cp_fenceproxy.global.shared::cta.tensormap::generic.release.gpu.sync.aligned
                                                         [gbl], [sMem], 128;
fence.proxy.tensormap::generic.acquire.gpu [gbl], 128;
cp.async.bulk.tensor.1d.shared::cluster.global.tile  [addr0], [gbl, {tc0}], [mbar0];

9.7.13.17. 并行同步与通信指令:clusterlaunchcontrol.try_cancel

clusterlaunchcontrol.try_cancel

请求取消尚未启动的集群。

语法

clusterlaunchcontrol.try_cancel.async{.space}.completion_mechanism{.multicast::cluster::all}.b128 [addr], [mbar];

.completion_mechanism = { .mbarrier::complete_tx::bytes };
.space = { .shared::cta };

描述

clusterlaunchcontrol.try_cancel指令用于原子性地取消尚未开始运行的集群启动。它会异步地将一个不透明的响应写入共享内存,指示操作成功或失败。该异步操作的完成情况通过.cluster作用域下的mbarrier完成机制进行跟踪。

成功时,不透明的响应包含已取消集群的第一个CTA的ctaid;来自同一网格的其他clusterlaunchcontrol.try_cancel操作的其他成功响应将不会包含该ID。

强制性的 .async 限定符表示该指令将异步启动取消操作,并在请求的操作完成前将控制权返回给执行线程。

如果指定了.space限定符,则操作数addrmbar都必须位于.shared::cta状态空间中。否则,将假定两者都使用通用寻址。如果任何地址操作数不在.shared::cta的地址窗口范围内,则结果未定义。

限定符 .completion_mechanism 指定在异步操作完成后,将执行带有参数 completeCount(等于存储数据的字节数)的 complete-tx 操作,该操作将在操作数 mbar 指定的 mbarrier 对象上执行。

执行线程随后可以使用mbarrier指令来等待异步操作的完成。内存一致性模型中描述的其他同步机制都不能用于保证异步拷贝操作的完成。

.multicast::cluster::all限定符表示响应是通过弱异步代理写入方式异步写入到请求集群中每个CTA对应的本地共享内存地址addr。对特定CTA的addr写入操作完成时,会通过在该CTA共享内存上的mbarrier对象执行complete-tx操作来发出信号。

如果集群中的任何CTA退出,带有.multicast::cluster::all限定符的指令行为将是未定义的。

操作数 addr 指定了16字节宽的共享内存位置的自然对齐地址,该地址用于写入请求的响应。

clusterlaunchcontrol.try_cancel指令的响应将是一个16字节的不透明值,该值将存储在操作数addr指定的位置。将这个响应加载到16字节寄存器后,可以使用clusterlaunchcontrol.query_cancel指令来检查请求是否成功,并获取被取消集群的第一个CTA的ctaid

如果执行的CTA已经观察到clusterlaunchcontrol.try_cancel指令以失败状态完成,那么随后发出clusterlaunchcontrol.try_cancel指令的行为将是未定义的。

PTX ISA 说明

在PTX ISA版本8.6中引入。

目标ISA注意事项

需要 sm_100 或更高版本。

限定符 .multicast::cluster::all 在以下架构上受支持:

  • sm_100a

  • sm_101a

  • sm_120a

示例

// Assumption: 1D cluster (cluster_ctaid.y/.z == 1)
// with 1 thread per CTA.

// Current Cluster to be processed, initially the
// currently launched cluster:

mov.b32 xctaid, %ctaid.x;
barrier.cluster.arrive.relaxed;
processCluster:

// Wait on all cluster CTAs completing initialization or processing of previous cluster:

barrier.cluster.wait.acquire;
mov.u32  %r0, %tid.x;
setp.u32.eq p0, %r0, 0x0;
@!p0 bra asyncWork;

// All CTAs in the cluster arrive at their local
// SMEM   barrier and set 16B handle tx count:

mbarrier.arrive.expect_tx.cluster.relaxed.shared::cta.b64 state, [mbar], 16;

// first CTA in Cluster attempts to cancel a
// not-yet-started cluster:

mov.u32  %r0, %cluster_ctaid.x;
setp.u32.eq p0, %r0, 0x0;
@p0 clusterlaunchcontrol.try_cancel.async.mbarrier::complete_tx::bytes.multicast::cluster::all.b128 [addr], [mbar];

asyncWork:
// ...process xctaid while cancellation request completes
// asynchronously...

// All CTAs in Cluster wait on cancellation responses on their local SMEM:

waitLoop:
// .acquire prevents the load of the handle from overtaking this read:

mbarrier.try_wait.cluster.acquire.shared::cta.b64   complete, [mbar], state;
@!complete bra waitLoop;

// Load response into 16-byte wide register after unblocking
// from mbarrier:

ld.shared.b128 handle, [addr];

// Check whether cancellation succeeded:

clusterlaunchcontrol.query_cancel.is_canceled.pred.b128 p, handle;
@!p ret; // If failed, we are don end exit:

// Otherwise, read ctaid of first CTA of cancelled Cluster for next iteration...

@p clusterlaunchcontrol.query_cancel.get_first_ctaid.v4.b32.b128 {xctaid, _, _, _},  handle;

// ...and signal CTA0 that we are done reading from handle:
// Fence generic->async

fence.proxy.async.shared::cta;
barrier.cluster.arrive.relaxed;

bra processCluster;

9.7.13.18. 并行同步与通信指令:clusterlaunchcontrol.query_cancel

clusterlaunchcontrol.query_cancel

查询clusterlaunchcontrol.try_cancel操作的响应结果。

语法

clusterlaunchcontrol.query_cancel.is_canceled.pred.b128 pred, try_cancel_response;

clusterlaunchcontrol.query_cancel.get_first_ctaid.v4.b32.b128 {xdim, ydim, zdim, _},  try_cancel_response;

clusterlaunchcontrol.query_cancel.get_first_ctaid{::dimension}.b32.b128 reg, try_cancel_response;

::dimension = { ::x, ::y, ::z };

描述

指令 clusterlaunchcontrol.query_cancel 可用于解码由指令 clusterlaunchcontrol.try_cancel 生成的不透明响应。

将来自clusterlaunchcontrol.try_cancel指令的响应加载到16字节寄存器后,可以使用clusterlaunchcontrol.query_cancel指令进一步查询,如下所示:

clusterlaunchcontrol.query_cancel.is_canceled.pred.b128: 如果集群成功取消,谓词p将被设为true;否则设为false

如果请求成功,指令clusterlaunchcontrol.query_cancel.get_first_ctaid 会提取已取消集群中第一个CTA的CTA ID。默认情况下,该指令 返回一个.v4向量,其前三个元素是已取消集群中第一个CTA的xyz坐标。 第四个元素的内容未指定。可以使用显式的.get_first_ctaid::x.get_first_ctaid::y.get_first_ctaid::z 限定符将单独的xyz坐标提取到32位寄存器中。

如果请求失败,clusterlaunchcontrol.query_cancel.get_first_ctaid的行为将是未定义的。

PTX ISA 说明

在PTX ISA版本8.6中引入。

目标ISA注意事项

需要 sm_100 或更高版本。

示例

clusterlaunchcontrol.query_cancel.is_canceled pred.b128 p, handle;

@p clusterlaunchcontrol.query_cancel.get_first_ctaid.v4.b32.b128 {xdim, ydim, zdim, ignr}  handle;

clusterlaunchcontrol.query_cancel.get_first_ctaid::x.b32.b128 reg0, handle;

clusterlaunchcontrol.query_cancel.get_first_ctaid::y.b32.b128 reg1, handle;

clusterlaunchcontrol.query_cancel.get_first_ctaid::z.b32.b128 reg2, handle;

9.7.14. Warp Level Matrix Multiply-Accumulate Instructions

矩阵乘加运算具有以下形式:

D = A * B + C

其中 DC 被称为累加器,可能指向同一个矩阵。

PTX提供了两种执行矩阵乘加计算的方法:

  • 使用 wmma 指令:

    • 这个warp级别的计算由warp中的所有线程共同执行,具体如下:

      • 使用wmma.load操作将矩阵A、B和C从内存加载到寄存器中。当操作完成后,每个线程中的目标寄存器将保存所加载矩阵的一个片段。

      • 使用wmma.mma操作对已加载的矩阵执行矩阵乘加运算。当操作完成时,每个线程中的目标寄存器将保存由wmma.mma操作返回的结果矩阵片段。

      • 使用wmma.store操作将结果矩阵D存储回内存。或者,结果矩阵D也可以作为后续wmma.mma操作的参数C使用。

      wmma.loadwmma.store 指令在从内存加载输入矩阵用于 wmma.mma 操作以及将结果存储回内存时,会隐式处理矩阵元素的组织方式。

  • 使用 mma 指令:

    • wmma类似,mma同样要求由warp中的所有线程共同执行计算,但在调用mma操作前,需要显式地将矩阵元素分配到warp内的不同线程中。mma指令同时支持稠密矩阵和稀疏矩阵A。当A是Sparse matrix storage中描述的结构化稀疏矩阵时,可以使用稀疏变体。

9.7.14.1. 矩阵形状

矩阵乘加运算对操作数矩阵A、B和C的形状支持有限。所有三个矩阵操作数的形状由元组MxNxK共同描述,其中A是MxK矩阵,B是KxN矩阵,而C和D是MxN矩阵。

以下矩阵形状支持指定的类型:

指令

规模

稀疏度

乘数数据类型

形状

PTX ISA版本

wmma

不适用

密集

浮点数 - .f16

.m16n16k16, .m8n32k16, 和 .m32n8k16

PTX ISA 版本 6.0

wmma

密集

替代浮点格式 - .bf16

.m16n16k16, .m8n32k16, 和 .m32n8k16

PTX ISA 版本 7.0

wmma

密集

替代浮点格式 - .tf32

.m16n16k8

PTX ISA 版本 7.0

wmma

密集

整数 - .u8/.s8

.m16n16k16, .m8n32k16, 和 .m32n8k16

PTX ISA 版本 6.3

wmma

密集

子字节整数 - .u4/.s4

.m8n8k32

PTX ISA 版本 6.3 (预览功能)

wmma

密集

单比特 - .b1

.m8n8k128

PTX ISA 版本 6.3 (预览功能)

mma

不适用

密集

浮点数 - .f64

.m8n8k4

PTX ISA 版本 7.0

.m16n8k4, .m16n8k8, 和 .m16n8k16

PTX ISA 版本 7.8

mma

密集

浮点数 - .f16

.m8n8k4

PTX ISA 版本 6.4

.m16n8k8

PTX ISA 版本 6.5

.m16n8k16

PTX ISA 版本 7.0

mma

密集

替代浮点格式 - .bf16

.m16n8k8.m16n8k16

PTX ISA 版本 7.0

mma

密集

替代浮点格式 - .tf32

.m16n8k4.m16n8k8

PTX ISA 版本 7.0

mma

密集

整数 - .u8/.s8

.m8n8k16

PTX ISA 版本 6.5

.m16n8k16.m16n8k32

PTX ISA 版本 7.0

mma

密集

子字节整数 - .u4/.s4

.m8n8k32

PTX ISA 版本 6.5

.m16n8k32.m16n8k64

PTX ISA 版本 7.0

mma

密集

单比特 - .b1

.m8n8k128, .m16n8k128, 和 .m16n8k256

PTX ISA 版本 7.0

mma

密集

替代浮点格式 - .e4m3 / .e5m2

.m16n8k32

PTX ISA 版本 8.4

mma

密集

替代浮点格式 - .e4m3 / .e5m2

.m16n8k16

PTX ISA 版本 8.7

mma

密集

替代浮点格式 - .e3m2 / .e2m3/.e2m1

.m16n8k32

PTX ISA 版本 8.7

mma

密集

替代浮点格式 - .e4m3 / .e5m2/.e3m2/.e2m3/.e2m1 X (缩放比例) .ue8m0

.m16n8k32

PTX ISA 版本 8.7

mma

密集

替代浮点格式 - .e2m1 X (缩放比例) .ue8m0/.ue4m3

.m16n8k64

PTX ISA 版本 8.7

mma

不适用

稀疏

浮点数 - .f16

.m16n8k16.m16n8k32

PTX ISA 版本 7.1

mma

稀疏

替代浮点格式 - .bf16

.m16n8k16.m16n8k32

PTX ISA 版本 7.1

mma

稀疏

替代浮点格式 - .tf32

.m16n8k8.m16n8k16

PTX ISA 版本 7.1

mma

稀疏

整数 - .u8/.s8

.m16n8k32.m16n8k64

PTX ISA 版本 7.1

mma

稀疏

子字节整数 - .u4/.s4

.m16n8k64.m16n8k128

PTX ISA 版本 7.1

mma

稀疏

替代浮点格式 - .e4m3 / .e5m2

.m16n8k64

PTX ISA 版本 8.4

mma

稀疏 带 有序 元数据

浮点数 - .f16

.m16n8k16.m16n8k32

PTX ISA 版本 8.5

mma

稀疏 带 有序 元数据

替代浮点格式 - .bf16

.m16n8k16.m16n8k32

PTX ISA 版本 8.5

mma

稀疏 带 有序 元数据

替代浮点格式 - .tf32

.m16n8k8.m16n8k16

PTX ISA 版本 8.5

mma

稀疏 带 有序 元数据

整数 - .u8/.s8

.m16n8k32.m16n8k64

PTX ISA 版本 8.5

mma

稀疏 带 有序 元数据

子字节整数 - .u4/.s4

.m16n8k64.m16n8k128

PTX ISA 版本 8.5

mma

稀疏 带 有序 元数据

替代浮点格式 - .e4m3 / .e5m2

.m16n8k64

PTX ISA 版本 8.5

mma

稀疏 带 有序 元数据

替代浮点格式 - .e3m2 / .e2m3/.e2m1

.m16n8k64

PTX ISA 版本 8.7

mma

稀疏 带 有序 元数据

替代浮点格式 - .e4m3 / .e5m2/.e3m2/.e2m3/.e2m1 X (缩放比例) .ue8m0

.m16n8k64

PTX ISA 版本 8.7

mma

稀疏 带 有序 元数据

替代浮点格式 - .e2m1 X (缩放比例) .ue8m0/.ue4m3

.m16n8k128

PTX ISA 版本 8.7

9.7.14.2. 矩阵数据类型

矩阵乘加运算分别支持整数、浮点数、亚字节整数和单比特数据类型。所有操作数必须包含相同的基本类型类别,即整数或浮点数。

对于浮点矩阵乘加运算,不同的矩阵操作数可能具有不同的精度,具体将在后文描述。

数据类型

乘数(A或B)

累加器(C或D)

整数

.u8, .s8

.s32

浮点数

.f16

.f16, .f32

替代浮点数

.bf16

.f32

替代浮点数

.tf32

.f32

替代浮点数

.e4m3.e5m2.e3m2.e2m3.e2m1

.f16, .f32

带缩放比例的交替浮点数

.e4m3.e5m2.e3m2.e2m3.e2m1 X (缩放比例) .ue8m0

.f32

带缩放比例的交替浮点数

.e2m1 X (比例) .ue8m0.ue4m3

.f32

浮点数

.f64

.f64

子字节整数

无论是 .u4 还是 .s4

.s32

单比特整数

.b1

.s32

9.7.14.3. 块缩放

带有以下.kind限定符的mma指令:

  • .kind::mxf8f6f4

  • .kind::mxf4

  • .kind::mxf4nvf4

执行带块缩放的矩阵乘法运算。该运算具有以下形式: D = (A * scale_A) * (B * scale_B) + C

对于一个形状为M x SFA_Nscale_A矩阵,矩阵A的每一行被划分为SFA_N个数据块,每行的每个数据块与scale_A矩阵同一行中的对应元素(以下称为SF_A)相乘。

类似地,对于一个形状为SFB_M x Nscale_B矩阵,矩阵B的每一列被划分为SFB_M个数据块,每个列数据块会与scale_B矩阵同列中的对应元素(下文称为SF_B)相乘。

图41展示了一个使用scale_vec::2X块缩放的mma示例。

_images/mma-block-scaling.png

图41 使用块缩放因子.scale_vec::2Xmma

scale_Ascale_B 矩阵的形状取决于限定符 .scale_vec_size,如 表34 所示。

表 34 根据.scale_vec_size限定符确定缩放矩阵的形状

.scale_vec_size

scale_A的形状

scale_B的形状

.scale_vec::1X

M x 1

1 x N

.scale_vec::2X

M x 2

2 x N

.scale_vec::4X

M x 4

4 x N

精确元素类型与.scale_vec_size的有效组合列于 表35中。

表 35 .scale_vec_size.kind 限定符的有效组合

.kind::*

元素数据类型 .atype 和 .btype

缩放数据类型 .stype

.scale_vec_size

.kind::mxf8f6f4

.e4m3, .e5m2 .e3m2, .e2m3 .e2m1

.ue8m0

.scale_vec::1X

.kind::mxf4

.e2m1

.ue8m0

.scale_vec::2X

.kind::mxf4nvf4

.e2m1

.ue8m0

.scale_vec::2X

.e2m1

.ue4m3

.scale_vec::4X

scale-a-datascale-b-data 参数分别为 scale_Ascale_B 矩阵提供元数据。元组 {byte-id-a, thread-id-a}{byte-id-b, thread-id-b} 提供了选择器信息,用于从对应的元数据参数 scale-a-datascale-b-data 中选择元素 SF_ASF_B。 元组 {byte-id-a, thread-id-a} 允许从 scale-a-data 中选择缩放矩阵元素 SF_A。 类似地,元组 {byte-id-b, thread-id-b} 允许从 scale-b-data 中选择 缩放矩阵元素 SF_B

组件 thread-id-athread-id-b 决定了四边形中的哪些线程将贡献 SF_ASF_B 值。以下列表描述了线程选择器组件 thread-id-athread-id-b 的影响:

  • thread-id-a确定的四边形内的一个线程对贡献SF_A值。值为0选择四边形中的下方两个线程,值为1选择上方两个线程。换句话说,当thread-id-a设为0时,满足条件的线程对:%laneid % 4 == 0 或 1 提供SF_A;相反当thread-id-a设为1时,满足条件的线程对:%laneid % 4 == 2 或 3 提供SF_A。更多细节请参考图42

    _images/mma-scaling-thread-id-a-selection.png

    图42 基于thread-id-a选择SF_A值集合

  • 四边形内的一个线程,由thread-id-b确定,提供SF_B值。换句话说,每个满足条件的线程:%laneid % 4 == thread-id-b将提供SF_B。更多细节请参阅图43

    _images/mma-scaling-thread-id-b-selection.png

    图43 基于thread-id-b选择SF_B值集合

参数 byte-id-abyte-id-b 用于选择 scale-a-datascale-b-data 中的哪些字节贡献 SF_ASF_B 值。以下列表描述了 .scale_vec_size 限定符对字节选择器组件 byte-id-abyte-id-b 的影响:

  • .scale_vec_size.scale_vec::1X

    • scale-a-datascale-b-data中,由byte-id-abyte-id-b分别确定的每个字节贡献了SF_ASF_B值。

  • .scale_vec_size.scale_vec::2X

    • scale-a-datascale-b-data中由byte-id-abyte-id-b确定的一个字节对(两个字节)提供SF_ASF_B值。值为0时选择对应元数据值的低两个字节,值为2时选择对应元数据值的高两个字节。

  • .scale_vec_size.scale_vec::4X

    • scale-a-datascale-b-data 中的所有四个字节都会贡献数值。因此,byte-id-abyte-id-b 必须为零。

更多详情请参阅图44

_images/mma-scaling-byte-id-selection.png

图44 基于byte-id-abyte-id-bSF_ASF_B选择值集合

表36列举了各种选择器组件的有效值。任何其他值都会导致未定义行为。

表 36 各种选择器组件的有效值

.scale_vec_size

选择器组件

字节ID-A

线程ID-A

字节ID-B

线程ID-B

scale_vec::1X

[0, 1, 2, 3]

[0, 1]

[0, 1, 2, 3]

[0, 1, 2, 3]

scale_vec::2X

[0, 2]

[0, 2]

scale_vec::4X

0

0

9.7.14.4. 使用wmma指令的矩阵乘加运算

本节介绍warp级别的wmma.load, wmma.mmawmma.store指令,以及这些指令中涉及的各种矩阵的组织方式。

9.7.14.4.1. WMMA的矩阵片段

warp中的每个线程持有矩阵的一个片段。warp内线程加载的片段分布是未指定的,且依赖于目标架构,因此片段在矩阵中的标识也是未指定的且依赖于目标架构。如果底层矩阵的形状、布局和元素类型匹配,wmma操作返回的片段可以用作另一个wmma操作的操作数。由于片段布局依赖于架构,如果两个函数被链接在一起但为不同的链接兼容SM架构编译,则在一个函数中使用wmma操作返回的片段作为另一个函数中wmma操作的操作数可能无法按预期工作。注意将wmma片段传递给具有.weak链接的函数是不安全的,因为在链接时对此类函数的引用可能会解析到不同编译模块中的函数。

每个片段都是一个向量表达式,其内容按以下方式确定。片段中各个矩阵元素的身份未指定。

Integer fragments

乘数(A 或 B):

数据类型

形状

矩阵

片段

.u8.s8

.m16n16k16

A

由两个.b32寄存器组成的向量表达式,每个寄存器包含矩阵中的四个元素。

B

由两个.b32寄存器组成的向量表达式,每个寄存器包含矩阵中的四个元素。

.m8n32k16

A

一个向量表达式,包含一个.b32寄存器,其中存储了矩阵中的四个元素。

B

四个.b32寄存器的向量表达式,每个寄存器包含来自矩阵的四个元素。

.m32n8k16

A

四个.b32寄存器的向量表达式,每个寄存器包含来自矩阵的四个元素。

B

一个包含单个.b32寄存器的向量表达式,其中每个寄存器包含来自矩阵的四个元素。

累加器 (C 或 D):

数据类型

形状

片段

.s32

.m16n16k16

八个.s32寄存器的向量表达式。

.m8n32k16

.m32n8k16

浮点片段

数据类型

矩阵

片段

.f16

A 或 B

八个.f16x2寄存器的向量表达式。

.f16

C 或 D

四个.f16x2寄存器的向量表达式。

.f32

由八个.f32寄存器组成的向量表达式。

.bf16数据格式的浮点片段

乘数(A 或 B):

数据类型

形状

矩阵

片段

.bf16

.m16n16k16

A

四个.b32寄存器的向量表达式,每个寄存器包含来自矩阵的两个元素。

B

.m8n32k16

A

一个包含两个.b32寄存器的向量表达式,其中包含来自矩阵的两个元素。

B

一个由八个.b32寄存器组成的向量表达式,每个寄存器包含来自矩阵的两个元素。

.m32n8k16

A

一个由八个.b32寄存器组成的向量表达式,每个寄存器包含矩阵中的两个元素。

B

一个包含两个.b32寄存器的向量表达式,每个寄存器包含来自矩阵的两个元素。

累加器 (C 或 D):

数据类型

矩阵

片段

.f32

C 或 D

一个包含八个.f32寄存器的向量表达式。

.tf32数据格式的浮点片段

乘数(A 或 B):

数据类型

形状

矩阵

片段

.tf32

.m16n16k8

A

四个.b32寄存器的向量表达式。

B

四个.b32寄存器的向量表达式。

累加器 (C 或 D):

数据类型

形状

矩阵

片段

.f32

.m16n16k8

C 或 D

一个包含八个.f32寄存器的向量表达式。

双精度浮点片段

乘数(A 或 B):

数据类型

形状

矩阵

片段

.f64

.m8n8k4

A 或 B

单个.f64寄存器的向量表达式。

累加器 (C 或 D):

数据类型

形状

矩阵

片段

.f64

.m8n8k4

C 或 D

包含单个.f64寄存器的向量表达式。

子字节整数与单比特片段

乘数(A 或 B):

数据类型

形状

片段

.u4.s4

.m8n8k32

一个向量表达式,包含单个.b32寄存器,其中存储了矩阵的八个元素。

.b1

.m8n8k128

一个向量表达式,包含单个.b32寄存器,其中存储着来自矩阵的32个元素。

累加器 (C 或 D):

数据类型

形状

片段

.s32

.m8n8k32

两个.s32寄存器的向量表达式。

.m8n8k128

两个.s32寄存器的向量表达式。

操作片段内容

矩阵片段的内容可以通过读取和写入片段中的单个寄存器来操作,前提是满足以下条件:

  • 片段中的所有矩阵元素在线程间统一操作,使用相同的参数。

  • 矩阵元素的顺序保持不变。

例如,如果给定矩阵的每个寄存器都乘以一个统一的常数值,那么结果矩阵就是原始矩阵的缩放版本。

请注意,.f16.f32累加器片段之间的类型转换在任一方向上都不受支持。即使片段中元素的顺序保持不变,结果也是未定义的。

9.7.14.4.2. WMMA的矩阵存储

每个矩阵在内存中可以采用行优先列优先的布局方式存储。在行优先格式中,矩阵每行的连续元素存储在相邻的内存位置,此时行被称为矩阵的主维度。而在列优先格式中,每列的连续元素存储在相邻的内存位置,此时列被称为矩阵的主维度

连续实例的主维度(行或列)不需要在内存中连续存储。wmma.loadwmma.store操作接受一个可选参数stride,该参数以矩阵元素(而非字节)为单位指定每行(或列)起始点到下一行的偏移量。例如,被wmma操作访问的矩阵可能是存储在内存中的较大矩阵的子矩阵。这使得程序员可以对大于wmma操作支持形状的矩阵进行乘加运算。

地址对齐:

每个实例的前导维度(行或列)的起始地址必须与对应片段的字节大小对齐。请注意,起始地址由基指针和可选的stride决定。

以下指令作为示例:

wmma.load.a.sync.aligned.row.m16n16k16.f16 {x0,...,x7}, [p], s;
  • 片段大小(字节)= 32(八个类型为.f16x2的元素)

  • 实际的stride字节数 = 2 * s (因为stride是以.f16元素为单位指定的,而非字节)

  • 要使此矩阵的每一行在片段大小上对齐,必须满足以下条件:

    1. p 是32的倍数。

    2. 2*s 是32的倍数。

步长的默认值:

stride的默认值是矩阵主维度的大小。例如,对于一个MxK矩阵,stride行优先布局中为K,在列优先布局中为M。具体来说,支持的矩阵形状的默认步长如下:

形状

A (行)

A (列)

B (行)

B (列)

累加器 (行)

累加器 (列)

16x16x16

16

16

16

16

16

16

8x32x16

16

8

32

16

32

8

32x8x16

16

32

8

16

8

32

8x8x32

32

8

8

32

8

8

8x8x128

128

8

8

128

8

8

16x16x8

8

16

16

8

16

16

8x8x4

4

8

8

4

8

8

9.7.14.4.3. Warp级矩阵加载指令:wmma.load

wmma.load

为WMMA从内存中集体加载矩阵

语法

浮点格式 .f16 加载:

wmma.load.a.sync.aligned.layout.shape{.ss}.atype r, [p] {, stride};
wmma.load.b.sync.aligned.layout.shape{.ss}.btype r, [p] {, stride};
wmma.load.c.sync.aligned.layout.shape{.ss}.ctype r, [p] {, stride};

.layout = {.row, .col};
.shape  = {.m16n16k16, .m8n32k16, .m32n8k16};
.ss     = {.global, .shared{::cta}};
.atype  = {.f16, .s8, .u8};
.btype  = {.f16, .s8, .u8};
.ctype  = {.f16, .f32, .s32};

替代浮点格式 .bf16 加载:

wmma.load.a.sync.aligned.layout.shape{.ss}.atype r, [p] {, stride}
wmma.load.b.sync.aligned.layout.shape{.ss}.btype r, [p] {, stride}
wmma.load.c.sync.aligned.layout.shape{.ss}.ctype r, [p] {, stride}
.layout = {.row, .col};
.shape  = {.m16n16k16, .m8n32k16, .m32n8k16};
.ss     = {.global, .shared{::cta}};
.atype  = {.bf16 };
.btype  = {.bf16 };
.ctype  = {.f32 };

替代浮点格式 .tf32 加载:

wmma.load.a.sync.aligned.layout.shape{.ss}.atype r, [p] {, stride}
wmma.load.b.sync.aligned.layout.shape{.ss}.btype r, [p] {, stride}
wmma.load.c.sync.aligned.layout.shape{.ss}.ctype r, [p] {, stride}
.layout = {.row, .col};
.shape  = {.m16n16k8 };
.ss     = {.global, .shared{::cta}};
.atype  = {.tf32 };
.btype  = {.tf32 };
.ctype  = {.f32 };

双精度浮点数 .f64 加载:

wmma.load.a.sync.aligned.layout.shape{.ss}.atype r, [p] {, stride}
wmma.load.b.sync.aligned.layout.shape{.ss}.btype r, [p] {, stride}
wmma.load.c.sync.aligned.layout.shape{.ss}.ctype r, [p] {, stride}
.layout = {.row, .col};
.shape  = {.m8n8k4 };
.ss     = {.global, .shared{::cta}};
.atype  = {.f64 };
.btype  = {.f64 };
.ctype  = {.f64 };

子字节加载:

wmma.load.a.sync.aligned.row.shape{.ss}.atype r, [p] {, stride}
wmma.load.b.sync.aligned.col.shape{.ss}.btype r, [p] {, stride}
wmma.load.c.sync.aligned.layout.shape{.ss}.ctype r, [p] {, stride}
.layout = {.row, .col};
.shape  = {.m8n8k32};
.ss     = {.global, .shared{::cta}};
.atype  = {.s4, .u4};
.btype  = {.s4, .u4};
.ctype  = {.s32};

单比特加载:

wmma.load.a.sync.aligned.row.shape{.ss}.atype r, [p] {, stride}
wmma.load.b.sync.aligned.col.shape{.ss}.btype r, [p] {, stride}
wmma.load.c.sync.aligned.layout.shape{.ss}.ctype r, [p] {, stride}
.layout = {.row, .col};
.shape  = {.m8n8k128};
.ss     = {.global, .shared{::cta}};
.atype  = {.b1};
.btype  = {.b1};
.ctype  = {.s32};

描述

将矩阵从地址操作数p指示的位置(在指定状态空间中)共同加载到目标寄存器r中,该操作由warp中的所有线程共同完成。

如果未指定状态空间,则使用通用寻址执行内存访问。wmma.load操作仅可用于.global.shared空间以及通用寻址,其中地址指向.global.shared空间。

互斥限定符.a.b.c分别表示矩阵A、B或C是否正在为wmma计算加载。

目标操作数 r 是一个大括号包围的向量表达式,能够容纳加载操作返回的片段,具体描述见Matrix Fragments for WMMA

.shape限定符表示参与预期wmma计算的所有矩阵参数的维度。

.layout限定符指示要加载的矩阵是以行优先还是列优先格式存储的。

stride 是一个可选的32位整数操作数,用于提供连续主维度(行或列)实例起始位置之间的矩阵元素偏移量。stride的默认值在WMMA的矩阵存储中有详细说明,若实际值大于默认值则必须显式指定。例如,若当前矩阵是更大矩阵的子矩阵,则stride值应为原大矩阵的主维度。若指定值小于默认值将导致未定义行为。

p 地址和 stride 所需的对齐方式在 Matrix Storage for WMMA 中有详细说明。

强制性的 .sync 限定符表示 wmma.load 会使执行线程等待,直到warp中的所有线程都执行相同的 wmma.load 指令后才会继续执行。

强制性的.aligned限定符表示warp中的所有线程必须执行相同的wmma.load指令。在条件执行的代码中,只有当确定warp中的所有线程对条件的评估结果完全一致时,才应使用wmma.load指令,否则行为将是未定义的。

如果所有线程未使用相同的限定符和相同的pstride值,或warp中任意线程已退出,则wmma.load的行为是未定义的。

wmma.load内存一致性模型中被视为内存操作。

PTX ISA 说明

在PTX ISA 6.0版本中引入。

.m8n32k16.m32n8k16 在PTX ISA 6.1版本中引入。

PTX ISA 6.3版本中引入的整数、子字节整数和单比特wmma

.m8n8k4.m16n16k8 在 PTX ISA 7.0 版本引入的 wmma 上。

双精度和替代浮点精度 wmma 在PTX ISA 7.0版本中引入。

从PTX ISA版本6.3开始需要修饰符.aligned,而在低于6.3的PTX ISA版本中则被视为隐式存在。

支持在PTX ISA版本7.8中引入的::cta子限定符。

Preview Feature:

子字节wmma和单比特wmma是PTX ISA 6.3版本中的预览功能。所有细节都可能发生变化,不保证在未来的PTX ISA版本或SM架构上保持向后兼容性。

目标ISA注意事项

浮点wmma需要sm_70或更高版本。

整数 wmma 需要 sm_72 或更高版本。

子字节和单比特wmma需要sm_75或更高版本。

双精度和替代浮点精度 wmma 需要 sm_80 或更高版本。

示例

// Load elements from f16 row-major matrix B
.reg .b32 x<8>;

wmma.load.b.sync.aligned.m16n16k16.row.f16 {x0,x1,x2,x3,x4,x5,x,x7}, [ptr];
// Now use {x0, ..., x7} for the actual wmma.mma

// Load elements from f32 column-major matrix C and scale the values:
.reg .b32 x<8>;

wmma.load.c.sync.aligned.m16n16k16.col.f32
                 {x0,x1,x2,x3,x4,x5,x6,x7}, [ptr];

mul.f32 x0, x0, 0.1;
// repeat for all registers x<8>;
...
mul.f32 x7, x7, 0.1;
// Now use {x0, ..., x7} for the actual wmma.mma

// Load elements from integer matrix A:
.reg .b32 x<4>
// destination registers x<4> contain four packed .u8 values each
wmma.load.a.sync.aligned.m32n8k16.row.u8 {x0,x1,x2,x3}, [ptr];

// Load elements from sub-byte integer matrix A:
.reg .b32 x0;
// destination register x0 contains eight packed .s4 values
wmma.load.a.sync.aligned.m8n8k32.row.s4 {x0}, [ptr];

// Load elements from .bf16 matrix A:
.reg .b32 x<4>;
wmma.load.a.sync.aligned.m16n16k16.row.bf16
                {x0,x1,x2,x3}, [ptr];

// Load elements from .tf32 matrix A:
.reg .b32 x<4>;
wmma.load.a.sync.aligned.m16n16k8.row.tf32
                {x0,x1,x2,x3}, [ptr];

// Load elements from .f64 matrix A:
.reg .b32 x<4>;
wmma.load.a.sync.aligned.m8n8k4.row.f64
                {x0}, [ptr];
9.7.14.4.4. Warp级矩阵存储指令:wmma.store

wmma.store

为WMMA将矩阵集体存储到内存中

语法

wmma.store.d.sync.aligned.layout.shape{.ss}.type [p], r {, stride};

.layout = {.row, .col};
.shape  = {.m16n16k16, .m8n32k16, .m32n8k16};
.ss     = {.global, .shared{::cta}};
.type   = {.f16, .f32, .s32};

wmma.store.d.sync.aligned.layout.shape{.ss}.type [p], r {, stride}
.layout = {.row, .col};
.shape  = {.m8n8k32, .m8n8k128};
.ss     = {.global, .shared{::cta}};
.type   = {.s32};

wmma.store.d.sync.aligned.layout.shape{.ss}.type [p], r {, stride}
.layout = {.row, .col};
.shape  = {.m16n16k8};
.ss     = {.global, .shared{::cta}};
.type   = {.f32};

wmma.store.d.sync.aligned.layout.shape{.ss}.type [p], r {, stride}
.layout = {.row, .col};
.shape  = {.m8n8k4 };
.ss     = {.global, .shared{::cta}};
.type   = {.f64};

描述

将矩阵集体存储到由地址操作数p指示的位置,该位置位于指定状态空间中,数据来自源寄存器r,所有线程束中的线程共同参与此操作。

如果未指定状态空间,则使用通用寻址执行内存访问。wmma.load操作仅适用于.global.shared空间以及通用寻址,其中地址指向.global.shared空间。

源操作数 r 是一个大括号包裹的向量表达式,其形状与存储操作预期的矩阵片段相匹配,具体描述见Matrix Fragments for WMMA

.shape限定符表示参与预期wmma计算的所有矩阵参数的维度。它必须与生成待存储D矩阵的wmma.mma指令中指定的.shape限定符相匹配。

.layout限定符指示要加载的矩阵是以行优先还是列优先格式存储的。

stride 是一个可选的32位整数操作数,用于在主导维度(行或列)的连续实例起始位置之间提供以矩阵元素为单位的偏移量。默认情况下,stride的值在WMMA的矩阵存储中有描述,如果实际值大于默认值则必须明确指定。例如,如果该矩阵是一个更大矩阵的子矩阵,那么stride的值就是这个更大矩阵的主导维度。指定一个低于默认值的值会导致未定义行为。

p 地址和 stride 所需的对齐方式在 Matrix Storage for WMMA 中有详细说明。

强制性的 .sync 限定符表示 wmma.store 会使执行线程等待,直到warp中的所有线程都执行相同的 wmma.store 指令后才会继续执行。

强制性的.aligned限定符表示warp中的所有线程必须执行相同的wmma.store指令。在条件执行代码中,只有当确定warp中的所有线程对条件的评估结果完全一致时,才应使用wmma.store指令,否则行为将是未定义的。

如果所有线程未使用相同的限定符和相同的pstride值,或者warp中有任何线程已退出,则wmma.store的行为是未定义的。

wmma.store内存一致性模型中被视为内存操作。

PTX ISA 说明

在PTX ISA 6.0版本中引入。

.m8n32k16.m32n8k16 在PTX ISA 6.1版本中引入。

PTX ISA 6.3版本中引入的整数、子字节整数和单比特wmma

.m16n16k8 在PTX ISA版本7.0中引入。

双精度wmma在PTX ISA 7.0版本中引入。

从PTX ISA版本6.3开始需要修饰符.aligned,而在低于6.3的PTX ISA版本中则被视为隐式存在。

支持在PTX ISA版本7.8中引入的::cta子限定符。

Preview Feature:

子字节wmma和单比特wmma是PTX ISA 6.3版本中的预览功能。所有细节都可能发生变化,不保证在未来的PTX ISA版本或SM架构上保持向后兼容性。

目标ISA注意事项

浮点wmma需要sm_70或更高版本。

整数 wmma 需要 sm_72 或更高版本。

子字节和单比特wmma需要sm_75或更高版本。

双精度 wmma 和形状 .m16n16k8 需要 sm_80 或更高版本。

示例

// Storing f32 elements computed by a wmma.mma
.reg .b32 x<8>;

wmma.mma.sync.m16n16k16.row.col.f32.f32
              {d0, d1, d2, d3, d4, d5, d6, d7}, ...;
wmma.store.d.sync.m16n16k16.row.f32
              [ptr], {d0, d1, d2, d3, d4, d5, d6, d7};

// Store s32 accumulator for m16n16k16 shape:
.reg .b32 d<8>;
wmma.store.d.sync.aligned.m16n16k16.row.s32
              [ptr], {d0, d1, d2, d3, d4, d5, d6, d7};

// Store s32 accumulator for m8n8k128 shape:
.reg .b32 d<2>
wmma.store.d.sync.aligned.m8n8k128.row.s32
[ptr], {d0, d1};

// Store f64 accumulator for m8n8k4 shape:
.reg .f64 d<2>;
wmma.store.d.sync.aligned.m8n8k4.row.f64
              [ptr], {d0, d1};
9.7.14.4.5. Warp级矩阵乘累加指令:wmma.mma

wmma.mma

在warp范围内执行单次矩阵乘加操作

语法

// Floating point (.f16 multiplicands) wmma.mma
wmma.mma.sync.aligned.alayout.blayout.shape.dtype.ctype d, a, b, c;

// Integer (.u8/.s8 multiplicands) wmma.mma
wmma.mma.sync.aligned.alayout.blayout.shape.s32.atype.btype.s32{.satfinite} d, a, b, c;

.alayout = {.row, .col};
.blayout = {.row, .col};
.shape  =  {.m16n16k16, .m8n32k16, .m32n8k16};
.dtype   = {.f16, .f32};
.atype   = {.s8, .u8};
.btype   = {.s8, .u8};
.ctype   = {.f16, .f32};

浮点格式 .bf16wmma.mma:

wmma.mma.sync.aligned.alayout.blayout.shape.f32.atype.btype.f32 d, a, b, c;
.alayout = {.row, .col};
.blayout = {.row, .col};
.shape   = {.m16n16k16, .m8n32k16, .m32n8k16};
.atype   = {.bf16 };
.btype   = {.bf16};

浮点格式 .tf32wmma.mma:

wmma.mma.sync.aligned.alayout.blayout.shape.f32.atype.btype.f32 d, a, b, c;
.alayout = {.row, .col};
.blayout = {.row, .col};
.shape   = {.m16n16k8 };
.atype   = {.tf32 };
.btype   = {.tf32};

浮点双精度 wmma.mma:

wmma.mma.sync.aligned.alayout.blayout.shape{.rnd}.f64.f64.f64.f64 d, a, b, c;
.alayout = {.row, .col};
.blayout = {.row, .col};
.shape   = {.m8n8k4 };
.rnd = { .rn, .rz, .rm, .rp };

子字节(.u4/.s4 乘数) wmma.mma:

wmma.mma.sync.aligned.row.col.shape.s32.atype.btype.s32{.satfinite} d, a, b, c;
.shape  = {.m8n8k32};
.atype  = {.s4, .u4};
.btype  = {.s4, .u4};

单比特(.b1 乘数) wmma.mma:

wmma.mma.op.popc.sync.aligned.row.col.shape.s32.atype.btype.s32 d, a, b, c;
.shape  = {.m8n8k128};
.atype  = {.b1};
.btype  = {.b1};
.op     = {.xor, .and}

描述

执行一个warp级别的矩阵乘加运算D = A * B + C,使用分别加载在寄存器abc中的矩阵A、B和C,并将结果矩阵存储在寄存器d中。寄存器参数abcd持有对应矩阵的未指定片段,如Matrix Fragments for WMMA中所述

限定符 .dtype.atype.btype.ctype 分别表示矩阵 D、A、B 和 C 中元素的数据类型。

对于没有明确指定.atype.btypewmma.mma操作:.atype.btype会被隐式设置为.f16

对于整数wmma,必须将.ctype.dtype指定为.s32。此外,.atype.btype的值必须相同,即要么都是.s8,要么都是.u8

对于子字节单比特wmma,必须将.ctype.dtype指定为.s32。此外,.atype.btype的值必须相同;即要么都是.s4,要么都是.u4,或者都是.b1

对于单比特wmma,乘法运算被替换为一系列逻辑操作;具体来说,wmma.xor.popcwmma.and.popc分别计算A的128位行与B的128位列的异或(XOR)和与(AND)操作,然后统计结果中置位比特的数量(popc)。该结果会被加到C的对应元素上,并写入D中。

限定符 .alayout.blayout 必须与生成操作数 ab 内容的 wmma.load 指令所指定的布局相匹配。同样地,限定符 .atype.btype.ctype 也必须分别与生成操作数 abc 内容的 wmma.load 指令上的对应限定符相匹配。

.shape限定符必须与生成三个输入操作数abc内容的wmma.load指令所使用的.shape限定符相匹配。

目标操作数 d 是一个大括号包围的向量表达式,它与 wmma.mma 指令计算出的片段 .shape 相匹配。

Saturation at the output:

可选限定符 .satfinite 表示目标寄存器中的最终值按以下方式进行饱和处理:

  • 输出会被限制在32位有符号整数的最小或最大值范围内。否则,如果累加会导致溢出,数值将回绕。

Precision and rounding for .f16 floating point operations:

矩阵A和B的逐元素乘法运算至少采用单精度执行。当.ctype.dtype.f32时,中间值的累加运算至少采用单精度执行。当.ctype.dtype同时指定为.f16时,累加运算至少采用半精度执行。

累加顺序、舍入处理以及对非规格化输入的处理方式未作明确规定。

Precision and rounding for .bf16, .tf32 floating point operations:

矩阵A和B的逐元素乘法以指定精度执行。中间值的累加至少以单精度进行。

累加顺序、舍入处理以及对次正规输入的处理方式未作明确规定。

双精度wmma.mma的舍入修饰符(默认为.rn):

.rn

尾数最低有效位向最近的偶数舍入

.rz

尾数最低有效位向零舍入

.rm

尾数最低有效位向负无穷方向舍入

.rp

尾数最低有效位向正无穷方向舍入

强制性的.sync限定符表示wmma.mma会使执行线程等待,直到warp中的所有线程都执行相同的wmma.mma指令后才会继续执行。

强制性的.aligned限定符表示warp中的所有线程必须执行相同的wmma.mma指令。在条件执行的代码中,只有当确定warp中的所有线程对条件的评估结果完全一致时,才应使用wmma.mma指令,否则行为将是未定义的。

如果同一warp中的所有线程未使用相同的限定符,或者warp中有任何线程已退出,则wmma.mma的行为是未定义的。

PTX ISA 说明

在PTX ISA 6.0版本中引入。

.m8n32k16.m32n8k16 在PTX ISA 6.1版本中引入。

PTX ISA 6.3版本中引入的整数、子字节整数和单比特wmma

双精度和替代浮点精度 wmma 在PTX ISA 7.0版本中引入。

在PTX ISA 7.1版本中引入的对单比特wmma.and操作的支持。

从PTX ISA版本6.3开始需要修饰符.aligned,而在低于6.3的PTX ISA版本中则被视为隐式存在。

在PTX ISA 6.4版本中已弃用对浮点wmma.mma.satfinite支持,并从PTX ISA 6.5版本中移除该功能。

Preview Feature:

子字节wmma和单比特wmma是PTX ISA中的预览功能。所有细节在未来PTX ISA版本或SM架构中都可能发生变化,不保证向后兼容性。

目标ISA注意事项

浮点wmma需要sm_70或更高版本。

整数 wmma 需要 sm_72 或更高版本。

子字节和单比特wmma需要sm_75或更高版本。

双精度和替代浮点精度wmma需要sm_80或更高版本。

.and 单比特 wmma 操作需要 sm_80 或更高版本。

示例

.global .align 32 .f16 A[256], B[256];
.global .align 32 .f32 C[256], D[256];
.reg .b32 a<8> b<8> c<8> d<8>;

wmma.load.a.sync.aligned.m16n16k16.global.row.f16
        {a0, a1, a2, a3, a4, a5, a6, a7}, [A];
wmma.load.b.sync.aligned.m16n16k16.global.col.f16
        {b0, b1, b2, b3, b4, b5, b6, b7}, [B];

wmma.load.c.sync.aligned.m16n16k16.global.row.f32
        {c0, c1, c2, c3, c4, c5, c6, c7}, [C];

wmma.mma.sync.aligned.m16n16k16.row.col.f32.f32
        {d0, d1, d2, d3, d4, d5, d6, d7},
        {a0, a1, a2, a3, a4, a5, a6, a7},
        {b0, b1, b2, b3, b4, b5, b6, b7},
        {c0, c1, c2, c3, c4, c5, c6, c7};

wmma.store.d.sync.aligned.m16n16k16.global.col.f32
        [D], {d0, d1, d2, d3, d4, d5, d6, d7};

// Compute an integer WMMA:
.reg .b32  a, b<4>;
.reg .b32 c<8>, d<8>;
wmma.mma.sync.aligned.m8n32k16.row.col.s32.s8.s8.s32
        {d0, d1, d2, d3, d4, d5, d6, d7},
        {a}, {b0, b1, b2,  b3},
        {c0, c1, c2, c3, c4, c5, c6, c7};

// Compute sub-byte WMMA:
.reg .b32 a, b, c<2> d<2>
wmma.mma.sync.aligned.m8n8k32.row.col.s32.s4.s4.s32
        {d0, d1}, {a}, {b}, {c0, c1};

// Compute single-bit type WMMA:
.reg .b32 a, b, c<2> d<2>
wmma.mma.xor.popc.sync.aligned.m8n8k128.row.col.s32.b1.b1.s32
        {d0, d1}, {a}, {b}, {c0, c1};

// Compute double precision wmma
.reg .f64 a, b, c<2>, d<2>;
wmma.mma.sync.aligned.m8n8k4.row.col.f64.f64.f64.f64
        {d0, d1}, {a}, {b}, {c0, c1};

// Compute alternate floating point precision wmma
.reg .b32 a<2>, b<2>, c<8>, d<8>;
wmma.mma.sync.aligned.m16n16k8.row.col.f32.tf32.tf32.f32
        {d0, d1, d2, d3, d4, d5, d6, d7},
        {a0, a1, a2, a3}, {b0, b1, b2, b3},
        {c0, c1, c2, c3, c4, c5, c6, c7};

9.7.14.5. 使用mma指令实现矩阵乘加运算

本节介绍warp级别的mmaldmatrixstmatrixmovmatrix指令,以及这些指令中涉及的各种矩阵的组织方式。

9.7.14.5.1. 针对.f16浮点类型的mma.m8n8k4矩阵片段

一个执行mma.m8n8k4的warp,使用.f16浮点类型时,将计算4个形状为.m8n8k4的MMA操作。

需要将4个矩阵的元素分布到一个warp中的线程之间。下表展示了MMA操作中矩阵的分布情况。

MMA计算

参与MMA计算的线程

MMA计算1

线程的%laneid为0-3(低组)和16-19(高组)

MMA计算2

线程的%laneid为4-7(低组)和20-23(高组)

MMA计算3

线程的%laneid为8-11(低组)和24-27(高组)

MMA计算4

线程的%laneid为12-15(低组)和28-31(高组)

对于上面展示的每个独立的MMA计算,执行mma操作所需的每个线程都持有矩阵的一个片段,如下所示:

  • 被乘数 A:

    .atype

    片段

    元素(从低到高)

    .f16

    一个包含两个.f16x2寄存器的向量表达式,每个寄存器包含来自矩阵A的两个.f16元素。

    a0, a1, a2, a3

    不同线程持有的片段布局如下所示:

    • 行主序矩阵A的分片布局如图45所示。

      _images/mma-884-A-row-f16.png

      图45 针对行主序矩阵A的MMA .m8n8k4分片布局(使用.f16类型)

      矩阵分片的行和列可按如下方式计算:

      row =            %laneid % 4          if %laneid < 16
                      (%laneid % 4) + 4     otherwise
      
      col =            i                    for ai where i = {0,..,3}
      
    • 列主序矩阵A的分片布局如图46所示。

      不同线程持有的分片布局如下:

      _images/mma-884-A-col-f16.png

      图46 针对列主序矩阵A的.m8n8k4 MMA分片布局(.f16类型)

      矩阵分片的行和列可按以下方式计算:

      row =        i % 4            for ai  where i = {0,..,3}   if %laneid < 16
                  (i % 4) + 4       for ai  where i = {0,..,3}   otherwise
      
      col =        %laneid % 4
      
  • 被乘数 B:

    .btype

    片段

    元素(从低到高)

    .f16

    一个包含两个.f16x2寄存器的向量表达式,其中每个寄存器包含来自矩阵B的两个.f16元素。

    b0, b1, b2, b3

    不同线程持有的片段布局如下所示:

    • 行主序矩阵B的分片布局如图47所示。

      _images/mma-884-B-row-f16.png

      图47 针对行主序矩阵B的MMA .m8n8k4分片布局(使用.f16类型)

      矩阵分片的行和列可按如下方式计算:

      row =        %laneid % 4
      
      col =         i      for bi   where i = {0,..,3}   if %laneid < 16
                   i+4     for bi   where i = {0,..,3}   otherwise
      
    • 列主序矩阵B的分片布局如图48所示。

      _images/mma-884-B-col-f16.png

      图48 针对列主序矩阵B的MMA .m8n8k4分片布局(使用.f16类型)

      矩阵分片的行和列可按如下方式计算:

      row =       i                 对于 bi   其中 i = {0,..,3}
      
      col =      %laneid % 4        如果 %laneid < 16
                (%laneid % 4) + 4   否则
      
  • 累加器 C (或 D):

    .ctype / .dtype

    片段

    元素(从低到高)

    .f16

    一个包含四个.f16x2寄存器的向量表达式,其中每个寄存器包含来自矩阵C(或D)的两个.f16元素。

    c0, c1, c2, c3, c4, c5, c6, c7

    .f32

    由八个.f32寄存器组成的向量表达式。

    不同线程持有的片段布局如下所示:

    • .ctype.f16时,累加矩阵的分段布局如图49所示。

      _images/mma-884-C-f16.png

      图49 .ctype = .f16时,矩阵C/D的MMA .m8n8k4分段布局

      矩阵分段的行列可按以下方式计算:

      row =       %laneid % 4         if %laneid < 16
                 (%laneid % 4) + 4    otherwise
      
      col =          i                for ci   where i = {0,..,7}
      
    • .ctype.f32时,累加矩阵的分片布局如图50图51所示。

      _images/mma-884-C-f32-2.png

      图50 MMA .m8n8k4计算1和2中矩阵C/D的分片布局(.ctype = .f32

      _images/mma-884-C-f32-2.png

      图51 MMA .m8n8k4计算3和4中矩阵C/D的分片布局(.ctype = .f32

      矩阵分片的行和列可按如下方式计算:

      row =     X           if %laneid < 16
              X + 4         otherwise
      
                where X = (%laneid & 0b1) + (i & 0b10)  for ci where i = {0,..,7}
      
      col = (i & 0b100) + (%laneid & 0b10) + (i & 0b1)  for ci where i = {0,..,7}
      
9.7.14.5.2. 针对.f64浮点类型的mma.m8n8k4矩阵片段

一个执行mma.m8n8k4的warp,使用.f64浮点类型时,将计算形状为.m8n8k4的MMA操作。

矩阵的元素分布在warp中的线程之间,因此warp的每个线程持有矩阵的一个片段。

  • 乘数 A:

    .atype

    片段

    元素(从低到高)

    .f64

    包含单个.f64寄存器的向量表达式,其中包含来自矩阵A的单个.f64元素。

    a0

    不同线程持有的片段布局如图52所示。

    _images/mma-884-A-f64.png

    图52 矩阵A的MMA .m8n8k4片段布局(.f64类型)

    矩阵片段的行和列可通过以下公式计算:

    row =        %laneid >> 2
    
    col =        %laneid % 4
    
  • 乘数 B:

    .btype

    片段

    元素(从低到高)

    .f64

    包含单个.f64寄存器的向量表达式,其中包含来自矩阵B的单个.f64元素。

    b0

    不同线程持有的片段布局如图53所示。

    _images/mma-884-B-f64.png

    图53 矩阵B的MMA .m8n8k4片段布局(使用.f64类型)

    矩阵片段的行和列可按以下方式计算:

    row =        %laneid % 4
    
    col =        %laneid >> 2
    
  • 累加器 (C 或 D):

    .ctype / .dtype

    片段

    元素 (从低到高)

    .f64

    一个包含两个.f64寄存器的向量表达式,其中包含来自矩阵C的两个.f64元素。

    c0, c1

    不同线程持有的片段布局如图54所示。

    _images/mma-884-C-f64.png

    图54 累加器矩阵C/D的MMA .m8n8k4片段布局(.f64类型)

    矩阵片段的行和列可按如下方式计算:

    groupID           = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =      groupID
    
    col =      (threadID_in_group * 2) + (i & 0x1)       for ci   where i = {0, 1}
    
9.7.14.5.3. 用于mma.m8n8k16的矩阵片段

执行mma.m8n8k16的warp将计算形状为.m8n8k16的MMA操作。

矩阵的元素分布在warp中的线程之间,因此warp的每个线程都持有矩阵的一个片段。

  • 乘数A:

    .atype

    片段

    元素(从低到高)

    .s8 / .u8

    一个包含单个.b32寄存器的向量表达式,其中包含来自矩阵A的四个.s8.u8元素。

    a0, a1, a2, a3

    不同线程持有的片段布局如图55所示。

    _images/mma-8816-A-i8.png

    图55 针对.u8/.s8类型矩阵A的MMA .m8n8k16片段布局

    矩阵片段的行和列可按如下方式计算:

    groupID           = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row = groupID
    
    col =  (threadID_in_group * 4) + i       for ai    where i = {0,..,3}
    
  • 乘数 B:

    .btype

    片段

    元素(从低到高)

    .s8 / .u8

    包含单个.b32寄存器的向量表达式,其中包含来自矩阵B的四个.s8.u8元素

    b0, b1, b2, b3

    不同线程持有的片段布局如图56所示。

    _images/mma-8816-B-i8.png

    图56 矩阵B的MMA .m8n8k16片段布局(.u8/.s8类型)

    矩阵片段的行和列可按如下方式计算:

    groupID           = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =  (threadID_in_group * 4) + i         for bi    where i = {0,..,3}
    
    col =    groupID
    
  • 累加器(C或D):

    .ctype / .dtype

    片段

    元素(从低到高)

    .s32

    包含两个.s32寄存器的向量表达式。

    c0, c1

    不同线程持有的片段布局如图57所示。

    _images/mma-8816-C-i8.png

    图57 使用.s32类型的累加器矩阵C/D的MMA .m8n8k16片段布局

    矩阵片段的行和列可按如下方式计算:

    groupID           = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row = groupID
    
    col = (threadID_in_group * 2) + i         for ci    where i = {0, 1}
    
9.7.14.5.4. 用于mma.m8n8k32的矩阵片段

执行mma.m8n8k32的warp将计算形状为.m8n8k32的MMA操作。

矩阵的元素分布在warp中的线程之间,因此warp的每个线程持有矩阵的一个片段。

  • 乘数 A:

    .atype

    片段

    元素(从低到高)

    .s4 / .u4

    一个包含单个.b32寄存器的向量表达式,其中包含来自矩阵A的八个.s4.u4元素。

    a0, a1, a2, a3, a4, a5, a6, a7

    不同线程持有的片段布局如图58所示。

    _images/mma-8832-A-i4.png

    图58 针对.u4/.s4类型矩阵A的MMA .m8n8k32片段布局

    矩阵片段的行和列可按如下方式计算:

    groupID           = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =      groupID
    
    col = (threadID_in_group * 8) + i         for ai    where i = {0,..,7}
    
  • 乘数 B:

    .btype

    片段

    元素(从低到高)

    .s4 / .u4

    包含单个.b32寄存器的向量表达式,其中包含来自矩阵B的八个.s4.u4元素

    b0, b1, b2, b3, b4, b5, b6, b7

    不同线程持有的片段布局如图59所示。

    _images/mma-8832-B-i4.png

    图59 矩阵B的MMA .m8n8k32片段布局(.u4/.s4类型)

    矩阵片段的行和列可按如下方式计算:

    groupID           = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row = (threadID_in_group * 8) + i         for bi   where i = {0,..,7}
    
    col = groupID
    
  • 累加器 (C 或 D):

    .ctype / .dtype

    片段

    元素 (从低到高)

    .s32

    两个.s32寄存器的向量表达式。

    c0, c1

    不同线程持有的片段布局如图60所示:

    _images/mma-8832-C-i4.png

    图60 累加器矩阵C/D的MMA .m8n8k32片段布局(.s32类型)

    矩阵片段的行和列可按如下方式计算:

    groupID           = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =   groupID
    col = (threadID_in_group * 2) + i         for ci   where i = {0, 1}
    
9.7.14.5.5. 用于mma.m8n8k128的矩阵片段

执行mma.m8n8k128的warp将计算一个形状为.m8n8k128的MMA操作。

矩阵的元素分布在warp中的线程之间,因此warp的每个线程持有矩阵的一个片段。

  • 乘数A:

    .atype

    片段

    元素(从低到高)

    .b1

    包含单个.b32寄存器的向量表达式,其中包含来自矩阵A的32个.b1元素。

    a0, a1, … a30, a31

    不同线程持有的片段布局如图61所示。

    _images/mma-88128-A.png

    图61 针对.b1类型矩阵A的MMA .m8n8k128片段布局。

    矩阵片段的行和列可按如下方式计算:

    groupID           = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =  groupID
    
    col =  (threadID_in_group * 32) + i       for ai where i = {0,..,31}
    
  • 乘数 B:

    .btype

    片段

    元素(从低到高)

    .b1

    包含单个.b32寄存器的向量表达式,其中包含来自矩阵B的32个.b1元素。

    b0, b1, ..., b30, b31

    不同线程持有的片段布局如图62所示。

    _images/mma-88128-B.png

    图62 针对.b1类型矩阵B的MMA .m8n8k128片段布局。

    矩阵片段的行和列可按如下方式计算:

    groupID           = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row = (threadID_in_group * 32) + i         for bi where i = {0,..,31}
    
    col = groupID
    
  • 累加器 (C 或 D):

    .ctype / .dtype

    片段

    元素 (从低到高)

    .s32

    包含两个.s32寄存器的向量表达式,其中包含来自矩阵C(或D)的两个.s32元素。

    c0, c1

    不同线程持有的片段布局如图63所示。

    _images/mma-88128-C.png

    图63 MMA .m8n8k128片段布局,用于.s32类型的累加器矩阵C/D

    矩阵片段的行和列可按如下方式计算:

    groupID = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =      groupID
    
    col =  (threadID_in_group * 2) + i    for ci where i = {0, 1}
    
9.7.14.5.6. Matrix Fragments for mma.m16n8k4

执行mma.m16n8k4的warp将计算形状为.m16n8k4的MMA操作。

矩阵的元素分布在warp中的线程之间,因此warp的每个线程持有矩阵的一个片段。

  • 被乘数 A:

    • .tf32:

      .atype

      片段

      元素(从低到高)

      .tf32

      一个包含两个.b32寄存器的向量表达式,其中包含来自矩阵A的两个.tf32元素。

      a0, a1

      不同线程持有的片段布局如图64所示。

      _images/mma-1684-A.png

      图64 使用.tf32类型的矩阵A的MMA .m16n8k4片段布局。

      矩阵片段的行和列可按如下方式计算:

      groupID           = %laneid >> 2
      threadID_in_group = %laneid % 4
      
      row =      groupID            for a0
                 groupID + 8        for a1
      
      col =  threadID_in_group
      
    • .f64:

      数据类型

      片段

      元素(从低到高)

      .f64

      包含两个.f64寄存器的向量表达式,其中包含来自矩阵A的两个.f64元素

      a0, a1

      不同线程持有的片段布局如图65所示。

      _images/mma-1684-A.png

      图65 使用.f64类型的矩阵A的MMA .m16n8k4片段布局

      矩阵片段的行和列可按以下方式计算:

      groupID           = %laneid >> 2
      threadID_in_group = %laneid % 4
      
      row =      groupID            对应 a0
                 groupID + 8        对应 a1
      
      col =  threadID_in_group
      
  • 被乘数 B:

    • .tf32:

      .btype

      片段

      元素(从低到高)

      .tf32

      单个.b32寄存器的向量表达式,包含来自矩阵B的单个.tf32元素

      b0

      不同线程持有的片段布局如图66所示。

      _images/mma-1684-B.png

      图66 使用.tf32类型的矩阵B的MMA .m16n8k4片段布局

      矩阵片段的行列可按如下方式计算:

      groupID           = %laneid >> 2
      threadID_in_group = %laneid % 4
      
      row =  threadID_in_group
      
      col =  groupID
      
    • .f64:

      .btype

      片段

      元素(从低到高)

      .f64

      单个.f64寄存器的向量表达式,包含来自矩阵B的单个.f64元素

      b0

      不同线程持有的片段布局如图67所示。

      _images/mma-1684-B.png

      图67 矩阵B的MMA .m16n8k4片段布局(.f64类型)

      矩阵片段的行和列可按如下方式计算:

      groupID           = %laneid >> 2
      threadID_in_group = %laneid % 4
      
      row =  threadID_in_group
      
      col =  groupID
      
  • 累加器 (C 或 D):

    • .tf32:

      .ctype / .dtype

      Fragment

      元素排列(从低到高)

      .f32

      包含四个.f32寄存器的向量表达式,其中包含来自矩阵C(或D)的四个.f32元素

      c0, c1, c2, c3

      不同线程持有的片段布局如图68所示。

      _images/mma-1684-C.png

      图68 使用.f32类型的累加器矩阵C/D的MMA .m16n8k4片段布局

      矩阵片段的行和列可按如下方式计算:

      groupID           = %laneid >> 2
      threadID_in_group = %laneid % 4
      
      row =      groupID                            对应 c0  c1
               groupID + 8                          对应 c2  c3
      
      col =  (threadID_in_group * 2) + (i & 0x1)    对应 ci   其中 i = {0,..,3}
      
    • .f64:

      .ctype / .dtype

      片段

      元素(从低到高)

      .f64

      一个包含四个.f64寄存器的向量表达式,其中包含来自矩阵C(或D)的四个.f64元素

      c0, c1, c2, c3

      不同线程持有的片段布局如图69所示。

      _images/mma-1684-C.png

      图69 使用.f64类型的累加器矩阵C/D的MMA .m16n8k4片段布局。

      矩阵片段的行和列可按如下方式计算:

      groupID           = %laneid >> 2
      threadID_in_group = %laneid % 4
      
      row =      groupID                            for c0 and c1
               groupID + 8                          for c2 and c3
      
      col =  (threadID_in_group * 2) + (i & 0x1)    for ci   where i = {0,..,3}
      
9.7.14.5.7. Matrix Fragments for mma.m16n8k8

执行mma.m16n8k8的warp将计算形状为.m16n8k8的MMA操作。

矩阵的元素分布在warp中的线程之间,因此warp的每个线程都持有矩阵的一个片段。

  • 被乘数 A:

    • .f16.bf16 :

      .atype

      片段

      元素(从低到高)

      .f16 / .bf16

      包含两个.f16x2寄存器的向量表达式,每个寄存器包含来自矩阵A的两个.f16 / .bf16元素

      a0, a1, a2, a3

      不同线程持有的片段布局如图70所示。

      _images/mma-1688-A-f16.png

      图70 使用.f16 / .bf16类型的矩阵A的MMA .m16n8k8片段布局

      矩阵片段的行和列可按如下方式计算:

      groupID           = %laneid >> 2
      threadID_in_group = %laneid % 4
      
      row =      groupID            for a0 and a1
                 groupID + 8        for a2 and a3
      
      col =  threadID_in_group * 2 + (i & 0x1)    for ai     where i = {0,..,3}
      
    • .tf32 :

      .atype

      片段

      元素(从低到高)

      .tf32

      包含四个.b32寄存器的向量表达式,其中包含来自矩阵A的四个.tf32元素

      a0, a1, a2, a3

      不同线程持有的片段布局如图71所示。

      _images/mma-1688-A-tf32.png

      图71 使用.tf32类型的矩阵A的MMA .m16n8k8片段布局

      矩阵片段的行和列可按如下方式计算:

      groupID           = %laneid >> 2
      threadID_in_group = %laneid % 4
      
      row =      groupID            for a0 and a2
                 groupID + 8        for a1 and a3
      
      col =  threadID_in_group       for a0 and a1
             threadID_in_group + 4   for a2 and a3
      
    • .f64 :

      .atype

      片段

      元素(从低到高)

      .f64

      包含四个.f64寄存器的向量表达式,其中包含来自矩阵A的四个.f64元素。

      a0, a1, a2, a3

      不同线程持有的片段布局如图72所示。

      _images/mma-1688-A-tf32.png

      图72 针对.f64类型矩阵A的MMA .m16n8k8片段布局。

      矩阵片段的行和列可按如下方式计算:

      groupID           = %laneid >> 2
      threadID_in_group = %laneid % 4
      
      row =      groupID            对应 a0  a2
                 groupID + 8        对应 a1  a3
      
      col =  threadID_in_group       对应 a0  a1
             threadID_in_group + 4   对应 a2  a3
      
  • 被乘数 B:

    • .f16.bf16 :

      .btype

      片段

      元素(从低到高)

      .f16 / .bf16

      包含单个.f16x2寄存器的向量表达式,其中包含来自矩阵B的两个.f16 / .bf16元素

      b0, b1

      不同线程持有的片段布局如图73所示。

      _images/mma-1688-B-f16.png

      图73 使用.f16 / .bf16类型的矩阵B的MMA .m16n8k8片段布局

      矩阵片段的行和列可按以下方式计算:

      groupID           = %laneid >> 2
      threadID_in_group = %laneid % 4
      
      row = (threadID_in_group * 2) + i       for bi    where i = {0, 1}
      
      col =  groupID
      
    • .tf32 :

      .btype

      片段

      元素(从低到高)

      .tf32

      一个包含两个.b32寄存器的向量表达式,其中包含来自矩阵B的两个.tf32元素

      b0, b1

      不同线程持有的片段布局如图74所示。

      _images/mma-1688-B-tf32.png

      图74 使用.tf32类型的矩阵B的MMA .m16n8k8片段布局

      矩阵片段的行和列可按以下方式计算:

      groupID           = %laneid >> 2
      threadID_in_group = %laneid % 4
      
      row =    threadID_in_group         for b0
             threadID_in_group + 4       for b1
      
      col =  groupID
      
    • .f64 :

      .btype

      片段

      元素(从低到高)

      .f64

      包含两个.f64寄存器的向量表达式,其中包含来自矩阵B的两个.f64元素

      b0, b1

      不同线程持有的片段布局如图75所示。

      _images/mma-1688-B-tf32.png

      图75 针对.f64类型矩阵B的MMA .m16n8k8片段布局

      矩阵片段的行和列可按如下方式计算:

      groupID           = %laneid >> 2
      threadID_in_group = %laneid % 4
      
      row =    threadID_in_group         for b0
             threadID_in_group + 4       for b1
      
      col =  groupID
      
  • 累加器 (C 或 D):

    • .f16, .bf16.tf32:

      .ctype / .dtype

      片段

      元素(从低到高)

      .f16

      一个包含两个.f16x2寄存器的向量表达式,每个寄存器包含来自矩阵C(或D)的两个.f16元素。

      c0, c1, c2, c3

      .f32

      四个.f32寄存器的向量表达式。

      不同线程持有的片段布局如图76所示。

      _images/mma-1688-C.png

      图76 MMA .m16n8k8累加器矩阵C/D的片段布局,使用.f16x2/.f32类型。

      矩阵片段的行和列可按如下方式计算:

      groupID           = %laneid >> 2
      threadID_in_group = %laneid % 4
      
      row =      groupID                            for c0 and c1
               groupID + 8                          for c2 and c3
      
      col =  (threadID_in_group * 2) + (i & 0x1)    for ci   where i = {0,..,3}
      
    • .f64 :

      .ctype / .dtype

      片段

      元素(从低到高)

      .f64

      包含来自矩阵C(或D)的四个.f64元素的四个.f64寄存器的向量表达式

      c0, c1, c2, c3

      不同线程持有的片段布局如图77所示。

      _images/mma-1688-C.png

      图77 累加器矩阵C/D的MMA .m16n8k8片段布局(使用.f64类型)

      矩阵片段的行和列可按以下方式计算:

      groupID           = %laneid >> 2
      threadID_in_group = %laneid % 4
      
      row =      groupID                            for c0 and c1
               groupID + 8                          for c2 and c3
      
      col =  (threadID_in_group * 2) + (i & 0x1)    for ci   where i = {0,..,3}
      
9.7.14.5.8. 浮点类型mma.m16n8k16的矩阵片段

执行mma.m16n8k16浮点类型的warp将计算形状为.m16n8k16的MMA操作。

矩阵的元素分布在warp中的线程之间,因此warp的每个线程都持有矩阵的一个片段。

  • 被乘数 A:

    • .f16 and .bf16 :

      .atype

      Fragment

      Elements (low to high)

      .f16 / .bf16

      A vector expression containing four .f16x2 registers, with each register containing two .f16 / .bf16 elements from the matrix A.

      a0, a1, a2, a3, a4, a5, a6, a7

      The layout of the fragments held by different threads is shown in Figure 78.

      _images/mma-16816-A-f16.png

      Figure 78 MMA .m16n8k16 fragment layout for matrix A with .f16 / .bf16 type.

      The row and column of a matrix fragment can be computed as:

      groupID           = %laneid >> 2
      threadID_in_group = %laneid % 4
      
      row =      groupID            for ai where  0 <= i < 2 || 4 <= i < 6
                groupID + 8         Otherwise
      
      col =  (threadID_in_group * 2) + (i & 0x1)          for ai where i <  4
      (threadID_in_group * 2) + (i & 0x1) + 8      for ai where i >= 4
      
    • .f64 :

      .atype

      片段

      元素(从低到高)

      .f64

      一个包含八个.f64寄存器的向量表达式,每个寄存器包含来自矩阵A的一个.f64元素

      a0, a1, a2, a3, a4, a5, a6, a7

      不同线程持有的片段布局如图79所示。

      _images/mma-16816-A-f64.png

      图79 针对.f64类型矩阵A的MMA .m16n8k16片段布局

      矩阵片段的行和列可按如下方式计算:

      groupID           = %laneid >> 2
      threadID_in_group = %laneid % 4
      
      row =  groupID                                ai 满足 i % 2 = 0
             groupID + 8                           其他情况
      
      col =  (i * 2) + threadID_in_group            ai 满足 i % 2 = 0
             (i * 2) - 2 + (threadID_in_group      其他情况
      
  • 被乘数 B:

    • .f16.bf16 :

      .btype

      片段

      元素(从低到高)

      .f16 / .bf16

      一个包含两个.f16x2寄存器的向量表达式,每个寄存器包含来自矩阵B的两个.f16 / .bf16元素

      b0, b1, b2, b3

      不同线程持有的片段布局如图80所示。

      _images/mma-16816-B-f16.png

      图80 针对.f16 / .bf16类型矩阵B的MMA .m16n8k16片段布局。

      其中矩阵片段的行和列可通过以下公式计算:

      groupID           = %laneid >> 2
      threadID_in_group = %laneid % 4
      
      row =  (threadID_in_group * 2) + (i & 0x1)           for bi where i <  2
             (threadID_in_group * 2) + (i & 0x1) + 8       for bi where i >= 2
      
      col = groupID
      
    • .f64 :

      数据类型

      片段

      元素(从低到高)

      .f64

      一个包含四个.f64寄存器的向量表达式,每个寄存器包含矩阵B中的一个.f64元素

      b0, b1, b2, b3

      不同线程持有的片段布局如图81所示。

      _images/sparse-mma-16816-tf32-B.png

      图81 使用.f64类型的矩阵B的MMA .m16n8k16片段布局

      矩阵片段的行和列可按如下方式计算:

      groupID           = %laneid >> 2
      threadID_in_group = %laneid % 4
      
      row =  threadID_in_group + (i * 4)           for bi where  i < 4
      
      col =  groupID
      
  • 累加器 (C 或 D):

    .ctype / .dtype

    片段

    元素(从低到高)

    .f64

    包含四个.f64寄存器的向量表达式,其中包含来自矩阵C(或D)的.f64元素

    c0, c1, c2, c3

    .f32

    包含四个.f32寄存器的向量表达式,其中包含来自矩阵C(或D)的四个.f32元素

    .f16

    包含两个.f16x2寄存器的向量表达式,每个寄存器包含来自矩阵C(或D)的两个.f16元素

    不同线程持有的片段布局如图82所示。

    _images/mma-16816-C-f16.png

    图82 累加器矩阵C/D的MMA .m16n8k16片段布局

    矩阵片段的行和列可按以下方式计算:

    groupID           = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =      groupID                               for ci where i <  2
             groupID + 8                             for ci where i >= 2
    
    col =  (threadID_in_group * 2) + (i & 0x1)        for ci where i = {0,..,3}
    
9.7.14.5.9. Matrix Fragments for mma.m16n8k16

执行mma.m16n8k16的warp将计算形状为.m16n8k16的MMA操作。

矩阵的元素分布在warp中的线程之间,因此warp的每个线程都持有矩阵的一个片段。

  • Multiplicand A:

    .atype

    Fragment

    Elements (low to high)

    .u8 / .s8

    A vector expression containing two .b32 registers, with each register containing four .u8 / .s8 elements from the matrix A.

    a0, a1, a2, a3, a4, a5, a6, a7

    .e4m3 / .e5m2

    A vector expression containing two .b32 registers, with each register containing four .e4m3 / .e5m2 elements from the matrix A.

    a0, a1, a2, a3, a4, a5, a6, a7

    The layout of the fragments held by different threads is shown in Figure 83.

    _images/mma-16816-A-i8.png

    Figure 83 MMA .m16n8k16 fragment layout for matrix A with .u8 / .s8 type.

    The row and column of a matrix fragment can be computed as:

    groupID           = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =      groupID                            for ai where i < 4
             groupID + 8                          for ai where i >= 4
    
    col =  (threadID_in_group * 4) + (i & 0x3)    for ai where i = {0,..,7}
    
  • 乘数 B:

    .btype

    片段

    元素(从低到高)

    .u8 / .s8

    包含单个.b32寄存器的向量表达式,其中包含来自矩阵B的四个.u8 / .s8元素

    b0, b1, b2, b3

    .e4m3 / .e5m2

    包含单个.b32寄存器的向量表达式,其中包含来自矩阵B的四个.e4m3 / .e5m2元素

    b0, b1. b2. b3

    不同线程持有的片段布局如图84所示。

    _images/mma-16816-B-i8.png

    图84 使用.u8 / .s8类型的矩阵B的MMA .m16n8k16片段布局

    矩阵片段的行列可按如下方式计算:

    groupID           = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =  (threadID_in_group * 4) + i         for bi where i = {0,..,3}
    
    col = groupID
    
  • Accumulators (C or D):

    .ctype / .dtype

    Fragment

    Elements (low to high)

    .s32

    A vector expression containing four .s32 registers, containing four .s32 elements from the matrix C (or D).

    c0, c1, c2, c3

    .f32

    A vector expression containing four .f32 registers, containing four .f32 elements from the matrix C (or D).

    c0, c1, c2, c3

    .f16

    A vector expression containing two .f16x2 registers, with each register containing two .f16 elements from the matrix C (or D).

    c0, c1, c1, c2

    The layout of the fragments held by different threads is shown in Figure 85.

    _images/mma-16816-C-i8.png

    Figure 85 MMA .m16n8k16 fragment layout for accumulator matrix C/D with .s32 type.

    The row and column of a matrix fragment can be computed as:

    groupID           = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =      groupID                           for ci where i <  2
             groupID + 8                         for ci where i >= 2
    
    col =  (threadID_in_group * 2) + (i & 0x1)    for ci where i = {0,..,3}
    
9.7.14.5.10. 用于mma.m16n8k32的矩阵片段

执行mma.m16n8k32的warp将计算形状为.m16n8k32的MMA操作。

矩阵的元素分布在warp中的线程之间,因此warp的每个线程都持有矩阵的一个片段。

  • 被乘数 A:

    • .s4.u4 :

      .atype

      片段

      元素(从低到高)

      .s4 / .u4

      一个包含两个.b32寄存器的向量表达式,每个寄存器包含来自矩阵A的八个.u4 / .s4元素。

      a0, a1, ..., a14, a15

      不同线程持有的片段布局如图86所示。

      _images/mma-16832-A-i4.png

      图86 针对.u4/.s4类型矩阵A的MMA .m16n8k32片段布局。

      矩阵片段的行和列可按如下方式计算:

      groupID           = %laneid >> 2
      threadID_in_group = %laneid % 4
      
      row =      groupID                           当i<8时对应ai
               groupID + 8                         当i>=8时对应ai
      
      col =  (threadID_in_group * 8) + (i & 0x7)    当i={0,..,15}时对应ai
      
    • .s8 or .u8 or .e4m3 or .e5m2 or .e3m2 or .e2m3 or .e2m1:

      .atype

      Fragment

      Elements (low to high)

      .s8 / .u8

      A vector expression containing four .b32 registers, with each register containing four .s8 / .u8 elements from the matrix A.

      a0, a1, .., a14, a15

      .e4m3 / .e5m2 / .e3m2 / .e2m3 / .e2m1

      A vector expression containing four .b32 registers, with each register containing four .e4m3 / .e5m2 / .e3m2 / .e2m3 / .e2m1 elements from the matrix A.

      a0, a1, …, a14, a15

      The layout of the fragments held by different threads is shown in Figure 87.

      _images/mma-16832-A-i8.png

      Figure 87 MMA .m16n8k32 fragment layout for matrix A with .u8 / .s8 / .e4m3 / .e5m2 / .e3m2 / .e2m3 / .e2m1 type.

      The row and column of a matrix fragment can be computed as:

      groupID = %laneid >> 2
      threadID_in_group = %laneid % 4
      
      row =   groupID                                        for ai where 0 <= i < 4 || 8 <= i < 12
             groupID + 8                                     otherwise
      
      col =    (threadID_in_group * 4) + (i & 0x3)           for ai where i < 8
               (threadID_in_group * 4) + (i & 0x3) + 16      for ai where i >= 8
      
  • 被乘数 B:

    • .s4.u4 :

      .btype

      片段

      元素(从低到高)

      .s4 / .u4

      包含单个.b32寄存器的向量表达式,其中包含来自矩阵B的八个.s4 / .u4元素

      b0, b1, b2, b3, b4, b5, b6, b7

      不同线程持有的片段布局如图88所示。

      _images/mma-16832-B-i4.png

      图88 针对.u4 / .s4类型矩阵B的MMA .m16n8k32片段布局

      矩阵片段的行和列可按如下方式计算:

    groupID = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =    (threadID_in_group * 8) + (i & 0x7)      for bi where i = {0,..,7}
    col =     groupID
    
    • .s8 or .u8 or .e4m3 or .e5m2 or .e3m2 or .e2m3 or .e2m1:

      .btype

      Fragment

      Elements (low to high)

      .s8 / .u8

      A vector expression containing two .b32 registers, with each register containing four .s8 / .u8 elements from the matrix B.

      b0, b1, b2, b3, b4, b5, b6, b7

      .e4m3 / .e5m2 / .e3m2 / .e2m3 / .e2m1

      A vector expression containing two .b32 registers, with each register containing four .e4m3 / .e5m2 / .e3m2 / .e2m3 / .e2m1 elements from the matrix B.

      b0, b1, b2, b3, b4, b5, b6, b7

      The layout of the fragments held by different threads is shown in Figure 89 and Figure 90.

      _images/mma-16832-B-i8_1.png

      Figure 89 MMA .m16n8k32 fragment layout for rows 0–15 of matrix B with .u8 / .s8 / .e4m3 / .e5m2 / .e3m2 / .e2m3 / .e2m1 type.

      _images/mma-16832-B-i8_2.png

      Figure 90 MMA .m16n8k32 fragment layout for rows 16–31 of matrix B with .u8 / .s8 / .e4m3 / .e5m2 / .e3m2 / .e2m3 / .e2m1 type.

      The row and column of a matrix fragment can be computed as:

      groupID           = %laneid >> 2
      threadID_in_group = %laneid % 4
      
      row =      (threadID_in_group * 4) + (i & 0x3)           for bi where i < 4
                 (threadID_in_group * 4) + (i & 0x3) + 16      for bi where i >= 4
      
      col =   groupID
      
  • Accumulators (C or D):

    .ctype / .dtype

    Fragment

    Elements (low to high)

    .s32

    A vector expression containing four .s32 registers, containing four .s32 elements from the matrix C (or D).

    c0, c1, c2, c3

    .f32

    A vector expression containing four .f32 registers, containing four .f32 elements from the matrix C (or D).

    c0, c1, c2, c3

    .f16

    A vector expression containing two .f16x2 registers, with each register containing two .f16 elements from the matrix C (or D).

    c0, c1, c2, c3

    The layout of the fragments held by different threads is shown in Figure 91.

    _images/mma-16832-C.png

    Figure 91 MMA .m16n8k32 fragment layout for accumulator matrix C/D with .s32 / .f32 / .f16 type.

    The row and column of a matrix fragment can be computed as:

    groupID           = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =      groupID                           for ci where i <  2
             groupID + 8                         for ci where i >= 2
    
    col =  (threadID_in_group * 2) + (i & 0x1)    for ci where i = {0,..,3}
    
9.7.14.5.11. Matrix Fragments for mma.m16n8k64

执行mma.m16n8k64的warp将计算形状为.m16n8k64的MMA操作。

矩阵的元素分布在warp中的线程之间,因此warp的每个线程都持有矩阵的一个片段。

  • Multiplicand A:

    .atype

    Fragment

    Elements (low to high)

    .s4 / .u4

    A vector expression containing four .b32 registers, with each register containing eight .s4 / .u4 elements from the matrix A.

    a0, a1, …, a30, a31

    .e2m1

    A vector expression containing four .b32 registers, with each register containing eight .e2m1 elements from the matrix A.

    a0, a1, …, a30, a31

    The layout of the fragments held by different threads is shown in Figure 92.

    _images/mma-16864-A.png

    Figure 92 MMA .m16n8k64 fragment layout for matrix A with .u4 / .s4 / .e2m1 type.

    The row and column of a matrix fragment can be computed as:

    groupID           = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =     groupID                                     for ai where 0 <= i < 8 || 16 <= i < 24
            groupID + 8                                   otherwise
    
    col =      (threadID_in_group * 8) + (i & 0x7)        for ai where i < 16
               (threadID_in_group * 8) + (i & 0x7) + 32   for ai where i >= 16
    
  • Multiplicand B:

    .btype

    Fragment

    Elements (low to high)

    .s4 / .u4

    A vector expression containing two .b32 registers, with each register containing eight .s4 / .u4 elements from the matrix B.

    b0, b1, …, b14, b15

    .e2m1

    A vector expression containing two .b32 registers, with each register containing eight .e2m1 elements from the matrix B.

    b0, b1, …, b14, b15

    The layout of the fragments held by different threads is shown in Figure 93 and Figure 94.

    _images/mma-16864-B_1.png

    Figure 93 MMA .m16n8k64 fragment layout for rows 0–31 of matrix B with .u4 / .s4 / .e2m1 type.

    _images/mma-16864-B_2.png

    Figure 94 MMA .m16n8k64 fragment layout for rows 32–63 of matrix B with .u4 / .s4 / .e2m1 type.

    The row and column of a matrix fragment can be computed as:

    groupID           = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =      (threadID_in_group * 8) + (i & 0x7)          for bi where i < 8
               (threadID_in_group * 8) + (i & 0x7) + 32     for bi where i >= 8
    
    col =   groupID
    
  • 累加器(C或D):

    .ctype / .dtype

    片段

    元素(从低到高)

    .s32

    包含四个.s32寄存器的向量表达式,其中包含来自矩阵C(或D)的四个.s32元素。

    c0, c1, c2, c3

    .f32

    包含四个.f32寄存器的向量表达式,其中包含来自矩阵C(或D)的四个.f32元素。

    c0, c1, c2, c3

    不同线程持有的片段布局如图95所示。

    _images/mma-16864-C.png

    图95 累加器矩阵C/D的MMA .m16n8k64片段布局,使用.s32/.f32类型。

    矩阵片段的行和列可按如下方式计算:

    groupID           = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =      groupID                           for ci where i <  2
               groupID + 8                       for ci where i >= 2
    
    col =  (threadID_in_group * 2) + (i & 0x1)    for ci  where i = {0,..,3}
    
9.7.14.5.12. 用于mma.m16n8k128的矩阵片段

执行mma.m16n8k128的warp将计算形状为.m16n8k128的MMA操作。

矩阵的元素分布在warp中的线程之间,因此warp的每个线程都持有矩阵的一个片段。

  • 乘数A:

    .atype

    片段

    元素(从低到高)

    .b1

    包含两个.b32寄存器的向量表达式,每个寄存器包含来自矩阵A的32个.b1元素。

    a0, a1, ..., a62, a63

    不同线程持有的片段布局如图96所示。

    _images/mma-168128-A.png

    图96 针对.b1类型矩阵A的MMA .m16n8k128片段布局。

    矩阵片段的行和列可按以下方式计算:

    groupID = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =      groupID                               ai  i < 32
              groupID + 8                            ai  i >= 32
    
    col =  (threadID_in_group * 32) + (i & 0x1F)      ai  i = {0, ...,63}
    
  • 乘数 B:

    .btype

    片段

    元素(从低到高)

    .b1

    一个包含单个.b32寄存器的向量表达式,该寄存器包含来自矩阵B的32个.b1元素

    b0, b1, ... , b30, b31

    不同线程持有的片段布局如图97所示。

    _images/mma-168128-B.png

    图97 矩阵B使用.b1类型的MMA .m16n8k128片段布局

    矩阵片段的行和列可按如下方式计算:

    groupID           = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =  (threadID_in_group * 32) + i         for bi where i = {0,...,31}
    col = groupID
    
  • 累加器(C或D):

    .ctype / .dtype

    片段

    元素(从低到高)

    .s32

    一个包含四个.s32寄存器的向量表达式,其中包含来自矩阵C(或D)的四个.s32元素。

    c0, c1, c2, c3

    不同线程持有的片段布局如图98所示。

    _images/mma-168128-C.png

    图98 用于.s32类型累加器矩阵C/D的MMA .m16n8k128片段布局。

    矩阵片段的行和列可按如下方式计算:

    groupID           = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =      groupID                           for ci where i <  2
              groupID + 8                        for ci where i >= 2
    
    col =  (threadID_in_group * 2) + (i & 0x1)    for ci where i = {0, 1, 2, 3}
    
9.7.14.5.13. Matrix Fragments for mma.m16n8k256

执行mma.m16n8k256的warp将计算形状为.m16n8k256的MMA操作。

矩阵的元素分布在warp中的线程之间,因此warp的每个线程都持有矩阵的一个片段。

  • Multiplicand A:

    .atype

    Fragment

    Elements (low to high)

    .b1

    A vector expression containing four .b32 registers, with each register containing thirty two .b1 elements from the matrix A.

    a0, a1, …, a126, a127

    The layout of the fragments held by different threads is shown in Figure 99.

    _images/mma-168256-A.png

    Figure 99 MMA .m16n8k256 fragment layout for matrix A with .b1 type.

    The row and column of a matrix fragment can be computed as:

    groupID = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =   groupID                                            for ai where 0 <= i < 32 || 64 <= i < 96
            groupID + 8                                        otherwise
    
    col =      (threadID_in_group * 32) + i                    for ai where i < 64
               (threadID_in_group * 32) + (i & 0x1F) + 128     for ai where i >= 64
    
  • 乘数 B:

    .btype

    片段

    元素(从低到高)

    .b1

    一个包含两个.b32寄存器的向量表达式,每个寄存器包含来自矩阵B的32个.b1元素。

    b0, b1, ..., b62, b63

    不同线程持有的片段布局如图100图101所示。

    _images/mma-168256-B_2.png

    图100 矩阵B第0-127行使用.b1类型的MMA .m16n8k256片段布局。

    _images/mma-168256-B_2.png

    图101 矩阵B第128-255行使用.b1类型的MMA .m16n8k256片段布局。

    矩阵片段的行和列可按如下公式计算:

    groupID           = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =      (threadID_in_group * 32) + (i & 0x1F)              bi  i < 32
               (threadID_in_group * 32) + (i & 0x1F) + 128        bi  i >= 32
    
    col =      groupID
    
  • 累加器(C或D):

    .ctype / .dtype

    片段

    元素(从低到高)

    .s32

    一个包含四个.s32寄存器的向量表达式,包含来自矩阵C(或D)的四个.s32元素。

    c0, c1, c2, c3

    不同线程持有的片段布局如图102所示。

    _images/mma-168256-C.png

    图102 用于累加器矩阵C/D的MMA .m16n8k256片段布局(.s32类型)。

    矩阵片段的行和列可按如下方式计算:

    groupID           = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =        groupID                         当i<2时对应ci
               groupID + 8                       当i>=2时对应ci
    
    col =  (threadID_in_group * 2) + (i & 0x1)    当i={0,1,2,3}时对应ci
    
9.7.14.5.14. 乘积累加指令:mma

mma

执行矩阵乘加运算

语法

半精度浮点类型:

mma.sync.aligned.m8n8k4.alayout.blayout.dtype.f16.f16.ctype  d, a, b, c;
mma.sync.aligned.m16n8k8.row.col.dtype.f16.f16.ctype  d, a, b, c;
mma.sync.aligned.m16n8k16.row.col.dtype.f16.f16.ctype d, a, b, c;

.alayout = {.row, .col};
.blayout = {.row, .col};
.ctype   = {.f16, .f32};
.dtype   = {.f16, .f32};

替代浮点类型:

mma.sync.aligned.m16n8k4.row.col.f32.tf32.tf32.f32        d, a, b, c;
mma.sync.aligned.m16n8k8.row.col.f32.atype.btype.f32      d, a, b, c;
mma.sync.aligned.m16n8k16.row.col.f32.bf16.bf16.f32       d, a, b, c;
mma.sync.aligned.shape.row.col.dtype.f8type.f8type.ctype  d, a, b, c;
mma.sync.aligned.m16n8k32.row.col.kind.dtype.f8f6f4type.f8f6f4type.ctype d, a, b, c;

.atype      = {.bf16, .tf32};
.btype      = {.bf16, .tf32};
.f8type     = {.e4m3, .e5m2};
.f8f6f4type = {.e4m3, .e5m2, .e3m2, .e2m3, .e2m1};
.ctype      = {.f16, .f32};
.dtype      = {.f16, .f32};
.shape      = {.m16n8k16, .m16n8k32};
.kind       = {.kind::f8f6f4};

采用块缩放的替代浮点类型:

mma.sync.aligned.m16n8k64.row.col.kind.block_scale{.scale_vec_size}.f32.e2m1.e2m1.f32.stype d, a, b, c, scale-a-data, {byte-id-a, thread-id-a}, scale-b-data, {byte-id-b, thread-id-b};

.kind           = {.kind::mxf4};
.scale_vec_size = {.scale_vec::2X};
.stype          = {.ue8m0};

mma.sync.aligned.m16n8k64.row.col.kind.block_scale.scale_vec_size.f32.e2m1.e2m1.f32.stype d, a, b, c, scale-a-data, {byte-id-a, thread-id-a}, scale-b-data, {byte-id-b, thread-id-b};

.kind           = {.kind::mxf4nvf4};
.scale_vec_size = {.scale_vec::2X, .scale_vec::4X};
.stype          = {.ue8m0, .ue4m3};

mma.sync.aligned.m16n8k32.row.col.kind.block_scale{.scale_vec_size}.f32.f8f6f4type.f8f6f4type.f32.stype d, a, b, c, scale-a-data, {byte-id-a, thread-id-a}, scale-b-data, {byte-id-b, thread-id-b};

.kind           = {.kind::mxf8f6f4};
.scale_vec_size = {.scale_vec::1X};
.f8f6f4type     = {.e4m3, .e5m2, .e3m2, .e2m3, .e2m1};
.stype          = {.ue8m0};

双精度浮点类型:

mma.sync.aligned.shape.row.col.f64.f64.f64.f64 d, a, b, c;

.shape   = {.m8n84, .m16n8k4, .m16n8k8, .m16n8k16};

整数类型:

mma.sync.aligned.shape.row.col{.satfinite}.s32.atype.btype.s32 d, a, b, c;

.shape   = {.m8n8k16, .m16n8k16, .m16n8k32}
.atype   = {.u8, .s8};
.btype   = {.u8, .s8};

mma.sync.aligned.shape.row.col{.satfinite}.s32.atype.btype.s32 d, a, b, c;

.shape   = {.m8n8k32, .m16n8k32, .m16n8k64}
.atype   = {.u4, .s4};
.btype   = {.u4, .s4};

单比特:

mma.sync.aligned.shape.row.col.s32.b1.b1.s32.bitOp.popc d, a, b, c;

.bitOp = {.xor, .and}
.shape = {.m8n8k128, .m16n8k128, .m16n8k256}

描述

执行一个MxNxK矩阵乘加运算,D = A*B+C,其中A矩阵为MxK,B矩阵为KxN,C和D矩阵为MxN

限定符 .block_scale 指定在执行矩阵乘加运算前,矩阵A和B分别使用 scale_Ascale_B 矩阵进行缩放,具体说明见章节 Block Scalingscale_AScale_B 矩阵中每个元素对应的数据类型由 .stype 指定。限定符 .scale_vec_size 指定了 scale_A 矩阵的列数和 scale_B 矩阵的行数。

.kind.stype.scale_vec_size 的有效组合在 表35中描述。对于使用 .kind::mxf4mma,当未指定 限定符 .scale_vec_size 时,默认值为 2X。相反,当 .kind 指定为 .kind::mxf8f6f4 时,限定符 .scale_vec_size 默认 为 1X。然而,对于 .kind::mxf4nvf4,必须提供有效的 .scale_vec_size

执行mma.sync.m8n8k4指令的warp会计算4次矩阵乘加运算。其余的mma.sync操作每个warp只计算一次矩阵乘加运算。

对于单比特mma.sync,乘法运算被替换为一系列逻辑操作;具体来说,mma.xor.popcmma.and.popc分别计算A的k比特行与B的k比特列的异或(XOR)和与(AND)操作,然后统计结果中置位比特的数量(popc)。该结果会与C矩阵对应元素相加,最终写入D矩阵。

操作数 ab 表示两个被乘矩阵 A 和 B,而 cd 表示累加器和目标矩阵,分布在warp的线程中。 当指定 .block_scale 限定符时,操作数 scale-a-datascale-b-data 表示 分别对应于 scale_Ascale_B 矩阵的缩放矩阵元数据。 元组 {byte-id-a, thread-id-a}{byte-id-b, thread-id-b} 表示矩阵 scale_Ascale_B 的选择器,分别来自它们对应的元数据参数 scale-a-datascale-b-data。操作数 scale-a-datascale-b-data 的类型为 .b32。操作数 byte-id-athread-id-abyte-id-bthread-id-b 是无符号16位整数值。 有关选择器参数的更多详细信息,请参阅 Block Scaling 部分。

每个线程中的寄存器保存着矩阵的一个片段,如使用mma指令的矩阵乘加操作中所述。

限定符 .dtype, .atype, .btype.ctype 分别表示矩阵 D、A、B 和 C 中元素的数据类型。限定符 .stype 表示矩阵 scale_Ascale_B 中元素的数据类型。特定形状有类型限制:

  • .m8n8k4 : 当 .ctype.f32 时,.dtype 也必须为 .f32

  • .m16n8k8 :

    • .dtype 必须与 .ctype 相同。

    • .atype 必须与 .btype 相同。

限定符 .alayout.blayout 分别表示矩阵A和矩阵B的行优先或列优先布局。

.kind.kind::mxf8f6f4.kind::f8f6f4时,单独的4位和6位浮点类型元素必须打包在8位容器中。类型为.e2m1的矩阵元素位于8位容器的中间4位,容器的高2位和低2位为填充位。当矩阵元素类型为.e3m2.e2m3时,矩阵元素位于8位容器的低6位,容器的高2位为填充位。相比之下,请注意当使用mma.kind::mxf4.kind::mxf4nvf4时,即使矩阵元素类型为.e2m1,也不需要显式填充。

Precision and rounding :
  • .f16 浮点运算:

    矩阵A和B的逐元素乘法运算至少以单精度执行。当.ctype.dtype.f32时,中间值的累加至少以单精度执行。当.ctype.dtype都指定为.f16时,累加以至少半精度执行。

    未指定累加顺序、舍入方式以及对次正规输入的处理方式。

  • .e4m3, .e5m2, .e3m2, .e2m3, .e2m1 浮点运算:

    矩阵A和B的逐元素乘法以指定精度执行。中间值的累加至少以单精度执行。

    累加顺序、舍入方式以及对次正规输入的处理方式未作明确规定。

  • .bf16.tf32 浮点运算:

    矩阵A和B的逐元素乘法以指定精度执行。中间值的累加至少以单精度执行。

    累加顺序、舍入和对次正规输入的处理未作规定。

  • .f64 浮点运算 :

    元素级乘法和加法运算的精度与.f64精度融合乘加运算相同。支持的舍入修饰符包括:

    • .rn : 尾数最低有效位向最近的偶数舍入。这是默认设置。

    • .rz : 尾数最低有效位向零舍入。

    • .rm : 尾数最低有效位向负无穷方向舍入。

    • .rp : 尾数最低有效位向正无穷方向舍入。

  • 整数运算:

    整数mma操作使用.s32累加器执行。.satfinite 限定符表示在溢出时,累加值将被限制在 MIN_INT32..MAX_INT32范围内(其中边界分别定义为最小负32位有符号整数和最大正32位有符号整数)。

    如果未指定.satfinite,则累加值将采用环绕方式处理。

强制性的 .sync 限定符表示 mma 指令会使执行线程等待,直到warp中的所有线程都执行完相同的 mma 指令后才会继续执行。

强制性的.aligned限定符表示warp中的所有线程必须执行相同的mma指令。在条件执行的代码中,只有当确定warp中的所有线程对条件的评估结果完全一致时,才应使用mma指令,否则行为将是未定义的。

如果同一warp中的所有线程未使用相同的限定符,或者warp中有任何线程已退出,则mma指令的行为是未定义的。

注意事项

使用双精度浮点mma指令的程序,其形状为.m16n8k4.m16n8k8.m16n8k16时,编译至少需要64个寄存器。

PTX ISA 说明

在PTX ISA版本6.4中引入。

.f16 浮点类型的 mma 运算,采用 .m8n8k4 形状,在 PTX ISA 6.4 版本中引入。

.f16 浮点类型的 mma 运算,采用 .m16n8k8 形状,该功能在 PTX ISA 6.5 版本中引入。

.u8/.s8 整数类型的 mma 运算,采用 .m8n8k16 形状,该功能在 PTX ISA 版本 6.5 中引入。

.u4/.s4 整数类型 mma 操作,采用 .m8n8k32 形状,在 PTX ISA 6.5 版本中引入。

.f64 浮点类型的 mma 运算,采用 .m8n8k4 形状,在 PTX ISA 7.0 版本中引入。

.f16 浮点类型 mma 运算,采用 .m16n8k16 形状,在 PTX ISA 7.0 版本中引入。

.bf16 替代浮点类型 mma 操作,支持 .m16n8k8.m16n8k16 形状,在 PTX ISA 7.0 版本中引入。

.tf32 替代浮点类型 mma 运算,支持 .m16n8k4.m16n8k8 形状, 在 PTX ISA 7.0 版本中引入。

.u8/.s8 整数类型的 mma 操作,支持 .m16n8k16.m16n8k32 形状,该功能在 PTX ISA 7.0 版本中引入。

.u4/.s4 整数类型 mma 操作,支持 .m16n8k32.m16n8k64 形状,该功能在 PTX ISA 7.0 版本中引入。

.b1 单比特整数类型 mma 运算支持 .m8n8k128.m16n8k128.m16n8k256 形状,该功能在PTX ISA 7.0版本中引入。

支持在PTX ISA 7.1版本中引入的单比特mma操作中的.and运算。

.f64 浮点类型的 mma 操作,支持 .m16n8k4.m16n8k8.m16n8k16 形状,该功能在 PTX ISA 7.8 版本中引入。

支持PTX ISA 8.4版本中引入的.e4m3.e5m2替代浮点类型的mma运算。

支持在PTX ISA 8.7版本中引入的形状.m16n8k16.f16 dtype/ctype,以及.e4m3/.e5m2替代浮点类型的矩阵乘法累加运算。

支持在PTX ISA 8.7版本中引入的.e3m2.e2m3.e2m1替代浮点类型的mma操作。

支持PTX ISA版本8.7中引入的.kind.block_scale.scale_vec_size限定符。

目标ISA注意事项

需要 sm_70 或更高版本。

.f16 浮点类型的 mma 运算,使用 .m8n8k4 形状,需要 sm_70 或更高版本。

注意

mma.sync.m8n8k4 针对目标架构 sm_70 进行了优化,在其他目标架构上性能可能会大幅降低。

.f16 浮点类型的 mma 运算,使用 .m16n8k8 形状,需要 sm_75 或更高版本。

.u8/.s8 整数类型的 mma 运算,使用 .m8n8k16 形状需要 sm_75 或更高版本。

.u4/.s4 整数类型 mma 操作,使用 .m8n8k32 形状,需要 sm_75 或更高版本。

.b1 单比特整数类型 mma 操作,使用 .m8n8k128 形状,需要 sm_75 或更高版本。

.f64 浮点类型的 mma 运算,使用 .m8n8k4 形状需要 sm_80 或更高版本。

.f16 浮点类型的 mma 运算,使用 .m16n8k16 形状需要 sm_80 或更高版本。

.bf16 替代浮点类型 mma 操作,支持 .m16n8k8.m16n8k16 形状 需要 sm_80 或更高版本。

.tf32 替代浮点类型 mma 运算,支持 .m16n8k4.m16n8k8 形状 需要 sm_80 或更高版本。

.u8/.s8 整数类型的 mma 运算,使用 .m16n8k16.m16n8k32 形状时,需要 sm_80 或更高版本。

.u4/.s4 整数类型 mma 操作使用 .m16n8k32.m16n8k64 形状需要 sm_80 或更高版本。

.b1 单比特整数类型的 mma 运算,使用 .m16n8k128.m16n8k256 形状, 需要 sm_80 或更高版本。

.and 单比特 mma 操作需要 sm_80 或更高版本。

.f64 浮点类型的 mma 操作,使用 .m16n8k4.m16n8k8.m16n8k16 形状时,需要 sm_90 或更高版本。

.e4m3.e5m2 替代浮点类型的 mma 操作需要 sm_89 或更高版本。

.e3m2.e2m3.e2m1这些交替浮点类型的mma运算需要sm_120a支持。

支持 .kind.block_scale.scale_vec_size 限定符需要 sm_120a

半精度浮点类型示例

// f16 elements in C and D matrix
.reg .f16x2 %Ra<2> %Rb<2> %Rc<4> %Rd<4>
mma.sync.aligned.m8n8k4.row.col.f16.f16.f16.f16
{%Rd0, %Rd1, %Rd2, %Rd3},
{%Ra0, %Ra1},
{%Rb0, %Rb1},
{%Rc0, %Rc1, %Rc2, %Rc3};


// f16 elements in C and f32 elements in D
.reg .f16x2 %Ra<2> %Rb<2> %Rc<4>
.reg .f32 %Rd<8>
mma.sync.aligned.m8n8k4.row.col.f32.f16.f16.f16
{%Rd0, %Rd1, %Rd2, %Rd3, %Rd4, %Rd5, %Rd6, %Rd7},
{%Ra0, %Ra1},
{%Rb0, %Rb1},
{%Rc0, %Rc1, %Rc2, %Rc3};

 // f32 elements in C and D
.reg .f16x2 %Ra<2>, %Rb<1>;
.reg .f32 %Rc<4>, %Rd<4>;
mma.sync.aligned.m16n8k8.row.col.f32.f16.f16.f32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1},
  {%Rb0},
  {%Rc0, %Rc1, %Rc2, %Rc3};

.reg .f16x2 %Ra<4>, %Rb<2>, %Rc<2>, %Rd<2>;
mma.sync.aligned.m16n8k16.row.col.f16.f16.f16.f16
  {%Rd0, %Rd1},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1};

.reg .f16 %Ra<4>, %Rb<2>;
.reg .f32 %Rc<2>, %Rd<2>;
mma.sync.aligned.m16n8k16.row.col.f32.f16.f16.f32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1, %Rc2, %Rc3};

替代浮点类型的示例

.reg .b32 %Ra<2>, %Rb<1>;
.reg .f32 %Rc<4>, %Rd<4>;
mma.sync.aligned.m16n8k4.row.col.f32.tf32.tf32.f32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1},
  {%Rb0},
  {%Rc0, %Rc1, %Rc2, %Rc3};

.reg .f16x2 %Ra<2>, %Rb<1>;
.reg .f32 %Rc<4>, %Rd<4>;
mma.sync.aligned.m16n8k8.row.col.f32.bf16.bf16.f32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1},
  {%Rb0},
  {%Rc0, %Rc1, %Rc2, %Rc3};

.reg .b32 %Ra<2>, %Rb<1>;
.reg .f32 %Rc<4>, %Rd<4>;
mma.sync.aligned.m16n8k8.row.col.f32.tf32.tf32.f32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Rb2, %Rb3},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1, %Rc2, %Rc3};

.reg .f16x2 %Ra<2>, %Rb<1>;
.reg .f32 %Rc<4>, %Rd<4>;
mma.sync.aligned.m16n8k16.row.col.f32.bf16.bf16.f32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1, %Rc2, %Rc3};

.reg .b32 %Ra<4>, %Rb<4>;
.reg .f32 %Rc<4>, %Rd<4>;
mma.sync.aligned.m16n8k32.row.col.f32.e4m3.e5m2.f32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1, %Rc2, %Rc3};

.reg .b32 %Ra<4>, %Rb<4>;
.reg .f32 %Rc<4>, %Rd<4>;
mma.sync.aligned.m16n8k16.row.col.f32.e5m2.e4m3.f32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1},
  {%Rb0},
  {%Rc0, %Rc1, %Rc2, %Rc3};

.reg .b32 %Ra<4>, %Rb<4>;
.reg .b32 %Rc<4>, %Rd<4>;
mma.sync.aligned.m16n8k32.row.col.f16.e4m3.e5m2.f16
  {%Rd0, %Rd1},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1};

.reg .b32 %Ra<4>, %Rb<4>;
.reg .b32 %Rc<4>, %Rd<4>;
mma.sync.aligned.m16n8k16.row.col.f16.e5m2.e5m2.f16
  {%Rd0, %Rd1},
  {%Ra0, %Ra1},
  {%Rb0},
  {%Rc0, %Rc1};

.reg .b32 %Ra<4>, %Rb<4>;
.reg .f32 %Rc<4>, %Rd<4>;
mma.sync.aligned.m16n8k32.row.col.kind::f8f6f4.f32.e3m2.e2m3.f32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1, %Rc2, %Rc3};

.reg .b32 %Ra<4>, %Rb<4>;
.reg .b32 %Rc<4>, %Rd<4>;
mma.sync.aligned.m16n8k32.row.col.kind::f8f6f4.f16.e2m3.e2m1.f16
  {%Rd0, %Rd1},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1};

整数类型示例

.reg .b32 %Ra, %Rb, %Rc<2>, %Rd<2>;

// s8 elements in A and u8 elements in B
mma.sync.aligned.m8n8k16.row.col.satfinite.s32.s8.u8.s32
  {%Rd0, %Rd1},
  {%Ra},
  {%Rb},
  {%Rc0, %Rc1};

// u4 elements in A and B matrix
mma.sync.aligned.m8n8k32.row.col.satfinite.s32.u4.u4.s32
  {%Rd0, %Rd1},
  {%Ra},
  {%Rb},
  {%Rc0, %Rc1};

// s8 elements in A and u8 elements in B
.reg .b32 %Ra<2>, %Rb, %Rc<4>, %Rd<4>;
mma.sync.aligned.m16n8k16.row.col.satfinite.s32.s8.u8.s32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1},
  {%Rb},
  {%Rc0, %Rc1, %Rc2, %Rc3};

// u4 elements in A and s4 elements in B
.reg .b32 %Ra<2>, %Rb, %Rc<4>, %Rd<4>;
mma.sync.aligned.m16n8k32.row.col.satfinite.s32.u4.s4.s32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1},
  {%Rb},
  {%Rc0, %Rc1, %Rc2, %Rc3};

// s8 elements in A and s8 elements in B
.reg .b32 %Ra<4>, %Rb<2>, %Rc<4>, %Rd<4>;
mma.sync.aligned.m16n8k32.row.col.satfinite.s32.s8.s8.s32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1, %Rc2, %Rc3};

// u8 elements in A and u8 elements in B
.reg .b32 %Ra<4>, %Rb<2>, %Rc<4>, %Rd<4>;
mma.sync.aligned.m16n8k64.row.col.satfinite.s32.u4.u4.s32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1 },
  {%Rc0, %Rc1, %Rc2, %Rc3};

单比特类型的示例

// b1 elements in A and B
.reg .b32 %Ra, %Rb, %Rc<2>, %Rd<2>;
mma.sync.aligned.m8n8k128.row.col.s32.b1.b1.s32.and.popc
  {%Rd0, %Rd1},
  {%Ra},
  {%Rb},
  {%Rc0, %Rc1};

// b1 elements in A and B
.reg .b32 %Ra, %Rb, %Rc<2>, %Rd<2>;
mma.sync.aligned.m8n8k128.row.col.s32.b1.b1.s32.xor.popc
  {%Rd0, %Rd1},
  {%Ra},
  {%Rb},
  {%Rc0, %Rc1};

.reg .b32 %Ra<2>, %Rb, %Rc<4>, %Rd<4>;
mma.sync.aligned.m16n8k128.row.col.s32.b1.b1.s32.xor.popc
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1},
  {%Rb},
  {%Rc0, %Rc1, %Rc2, %Rc3};

.reg .b32 %Ra<2>, %Rb, %Rc<4>, %Rd<4>;
mma.sync.aligned.m16n8k128.row.col.s32.b1.b1.s32.and.popc
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1},
  {%Rb},
  {%Rc0, %Rc1, %Rc2, %Rc3};

.reg .b32 %Ra<4>, %Rb<2>, %Rc<4>, %Rd<4>;
mma.sync.aligned.m16n8k256.row.col.s32.b1.b1.s32.xor.popc
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1, %Rc2, %Rc3};

.reg .b32 %Ra<4>, %Rb<2>, %Rc<4>, %Rd<4>;
mma.sync.aligned.m16n8k256.row.col.s32.b1.b1.s32.and.popc
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1, %Rc2, %Rc3};

.f64 浮点类型示例

.reg .f64 %Ra, %Rb, %Rc<2>, %Rd<2>;
mma.sync.aligned.m8n8k4.row.col.f64.f64.f64.f64
  {%Rd0, %Rd1},
  {%Ra},
  {%Rb},
  {%Rc0, %Rc1};

.reg .f64 %Ra<8>, %Rb<4>, %Rc<4>, %Rd<4>;
mma.sync.aligned.m16n8k4.row.col.f64.f64.f64.f64.rn
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1},
  {%Rb0},
  {%Rc0, %Rc1, %Rc2, %Rc3};

mma.sync.aligned.m16n8k8.row.col.f64.f64.f64.f64.rn
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1, %Rc2, %Rc3};

mma.sync.aligned.m16n8k16.row.col.f64.f64.f64.f64.rn
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2, %Ra3, %Ra4, %Ra5, %Ra6, %Ra7},
  {%Rb0, %Rb1, %Rb2, %Rb3},
  {%Rc0, %Rc1, %Rc2, %Rc3};

使用块缩放的矩阵乘法示例

.reg .b32 %Ra<4>, %Rb<4>;
.reg .f32 %Rc<4>, %Rd<4>;
.reg .b32 scaleAData, scaleBData;
mma.sync.aligned.m16n8k64.row.col kind::mxf4.block_scale.f32.e2m1.e2m1.f32.ue8m0
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1, %Rc2, %Rc3},
  scaleAData, {2, 1}, scaleBData, {2, 3};

.reg .b32 %Ra<4>, %Rb<4>;
.reg .f32 %Rc<4>, %Rd<4>;
.reg .b32 scaleAData, scaleBData;
.reg .u16 bidA, bidB, tidA, tidB;
mma.sync.aligned.m16n8k64.row.col.kind::mxf4nvf4.block_scale.scale_vec::4X.f32.e2m1.e2m1.f32.ue4m3
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1, %Rc2, %Rc3},
  scaleAData, {bidA, tidA}, scaleBData, {bidB, tidB};

.reg .b32 %Ra<4>, %Rb<4>;
.reg .f32 %Rc<4>, %Rd<4>;
.reg .b32 scaleAData, scaleBData;
mma.sync.aligned.m16n8k32.row.col.kind::mxf8f6f4.block_scale.scale_vec::1X.f32.e3m2.e2m1.f32.ue8m0
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1, %Rc2, %Rc3},
  scaleAData, {0, 1}, scaleBData, {0, 1};

.reg .b32 %Ra<4>, %Rb<4>;
.reg .f32 %Rc<4>, %Rd<4>;
.reg .b32 scaleAData, scaleBData;
mma.sync.aligned.m16n8k32.row.col.kind::mxf8f6f4.block_scale.scale_vec::1X.f32.e4m3.e5m2.f32.ue8m0
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2,  %Ra3},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1, %Rc2, %Rc3},
  scaleAData, {0, 1}, scaleBData, {0, 0};
9.7.14.5.15. Warp-level matrix load instruction: ldmatrix

ldmatrix

mma指令从共享内存中批量加载一个或多个矩阵

语法

ldmatrix.sync.aligned.shape.num{.trans}{.ss}.type r, [p];

ldmatrix.sync.aligned.m8n16.num{.ss}.dst_fmt.src_fmt        r, [p];
ldmatrix.sync.aligned.m16n16.num.trans{.ss}.dst_fmt.src_fmt r, [p];

.shape   = {.m8n8, .m16n16};
.num     = {.x1, .x2, .x4};
.ss      = {.shared{::cta}};
.type    = {.b16, .b8};
.dst_fmt = { .b8x16 };
.src_fmt = { .b6x16_p32, .b4x16_p64 };

描述

将地址操作数p指示的位置(来自.shared状态空间)中的一个或多个矩阵,以线程束(warp)为单位集体加载到目标寄存器r中。若未指定状态空间,则使用通用寻址方式,此时p中的地址应指向.shared空间。若通用地址不属于.shared状态空间,则行为未定义。

.shape限定符表示所加载矩阵的维度。每个矩阵元素包含16位、8位、6位或4位数据。

下表展示了每个.shape对应的矩阵加载情况。

.shape

矩阵形状

元素大小

.m8n8

8x8

16位

.m16n16

16x16

8位或6位或4位

.m8n16

8x16

6位或4位

下表展示了6位或4位数据加载的有效使用方式。

.src_fmt

.shape

源数据

填充

.dst_fmt

.b6x16_p32

.m8n16

16个6位元素

32位

.b8x16 (16个8位元素)

.m16n16

.b4x16_p64

.m8n16

16个4位元素

64位

.m16n16

对于.b6x16_p32格式,源数据为16个无符号6位元素,带有32位填充。 对于.b4x16_p64格式,源数据为16个无符号4位元素,带有64位填充。

对于.num来说,值.x1.x2.x4分别表示一个、两个或四个矩阵。当.shape.m16n16时,.num的有效值只能是.x1.x2

强制性的 .sync 限定符表示 ldmatrix 会使执行线程等待,直到线程束中的所有线程都执行相同的 ldmatrix 指令后才会继续执行。

强制性的.aligned限定符表示warp中的所有线程必须执行相同的ldmatrix指令。在条件执行的代码中,只有当确定warp中的所有线程对条件的评估结果相同时才应使用ldmatrix指令,否则行为将是未定义的。

如果所有线程未使用相同的限定符,或者warp中的任何线程已退出,则ldmatrix的行为是未定义的。

目标操作数 r 是一个大括号包围的向量表达式,根据 .num 的值由1、2或4个32位寄存器组成。向量表达式的每个分量包含来自对应矩阵的一个片段。

p支持的寻址模式在地址作为操作数中有详细说明。

矩阵的连续行实例不需要在内存中连续存储。每个矩阵所需的八个地址由八个线程提供,具体取决于.num的值,如下表所示。每个地址对应矩阵行的起始位置。地址addr0–addr7对应第一个矩阵的行,地址addr8–addr15对应第二个矩阵的行,依此类推。

.num

线程0–7

线程8–15

线程16–23

线程 24–31

.x1

地址0–地址7

.x2

地址0–地址7

地址8–地址15

.x4

地址0–地址7

地址8–地址15

地址16–地址23

地址24–地址31

注意

对于目标架构sm_75或更低版本,所有线程必须包含有效地址。否则行为将是未定义的。对于.num = .x1.num = .x2的情况,可以将低位线程中的地址复制到高位线程以实现预期行为。

当读取8x8矩阵时,一组四个连续的线程会加载16字节。矩阵地址必须相应地自然对齐。

在一个warp中,每个线程加载行的片段,线程0在其寄存器r中接收第一个片段,依此类推。如图103所示,四个线程组加载矩阵的整行。

_images/mma-ldmatrix-fragments.png

图103 包含16位元素的8x8矩阵的ldmatrix片段布局

.num = .x2时,根据上表中的布局,第二个矩阵的元素会被加载到每个线程的下一个目标寄存器中。类似地,当.num = .x4时,第三个和第四个矩阵的元素会被加载到每个线程随后的目标寄存器中。

对于16x16的矩阵形状,必须指定两个目标寄存器r0r1,类型为.b32,每个寄存器将加载四个8位元素。对于4位或6位数据,8位元素将分别有4位或2位的填充。有关这些格式的更多详情,请参阅Optional Decopression

矩阵的整行可以通过一组四个连续且对齐的线程加载。 如图104所示,一个warp中的每个线程会跨2行加载4个连续的列。

_images/mma-ldmatrix-fragments-1616.png

图104 8位元素16x16矩阵的ldmatrix片段布局

对于8x16的矩阵形状,必须指定一个类型为.b32的目标寄存器r0,其中将加载四个8位元素到该寄存器中。对于4位或6位数据,8位元素将分别有4位或2位的填充。

矩阵的整行可以通过一组四个连续且对齐的线程加载。 如图105所示,一个warp中的每个线程会加载4个连续的列。

_images/mma-ldmatrix-fragments-816.png

图 105 包含4位/6位数据的8位元素组成的8x16矩阵的ldmatrix片段布局

可选限定符.trans表示矩阵以列主序格式加载。但对于16x16矩阵,.trans是强制要求的。

内存一致性模型中,ldmatrix指令被视为弱内存操作。

PTX ISA 说明

在PTX ISA 6.5版本中引入。

支持在PTX ISA版本7.8中引入的::cta子限定符。

支持PTX ISA版本8.6中引入的.m16n16.m8n16形状。

在PTX ISA 8.6版本中引入了对.b8类型与ldmatrix的支持。

支持PTX ISA版本8.6中引入的.src_fmt.dst_fmt限定符。

目标ISA注意事项

需要 sm_75 或更高版本。

以下架构支持形状 .m16n16.m8n16

  • sm_100a

  • sm_101a

  • sm_120a

支持在以下架构上使用.b8类型与ldmatrix指令:

  • sm_100a

  • sm_101a

  • sm_120a

限定符 .src_fmt.dst_fmt 在以下架构上受支持:

  • sm_100a

  • sm_101a

  • sm_120a

示例

// Load a single 8x8 matrix using 64-bit addressing
.reg .b64 addr;
.reg .b32 d;
ldmatrix.sync.aligned.m8n8.x1.shared::cta.b16 {d}, [addr];

// Load two 8x8 matrices in column-major format
.reg .b64 addr;
.reg .b32 d<2>;
ldmatrix.sync.aligned.m8n8.x2.trans.shared.b16 {d0, d1}, [addr];

// Load four 8x8 matrices
.reg .b64 addr;
.reg .b32 d<4>;
ldmatrix.sync.aligned.m8n8.x4.b16 {d0, d1, d2, d3}, [addr];

// Load one 16x16 matrices of 64-bit elements and transpose them
.reg .b64 addr;
.reg .b32 d<2>;
ldmatrix.sync.aligned.m16n16.x1.trans.shared.b8 {d0, d1}, [addr];

// Load two 16x16 matrices of 64-bit elements and transpose them
.reg .b64 addr;
.reg .b32 d<4>;
ldmatrix.sync.aligned.m16n16.x2.trans.shared::cta.b8 {d0, d1, d2, d3}, [addr];

// Load two 16x16 matrices of 6-bit elements and transpose them
.reg .b64 addr;
.reg .b32 d<4>;
ldmatrix.sync.aligned.m16n16.x2.trans.shared::cta.b8x16.b6x16_p32 {d0, d1, d2, d3}, [addr];
9.7.14.5.16. Warp级矩阵存储指令:stmatrix

stmatrix

将一或多个矩阵共同存储到共享内存中。

语法

stmatrix.sync.aligned.shape.num{.trans}{.ss}.type [p], r;

.shape  = {.m8n8, .m16n8};
.num    = {.x1, .x2, .x4};
.ss     = {.shared{::cta}};
.type   = {.b16, .b8};

描述

将warp中所有线程的一个或多个矩阵共同存储到由地址操作数p指示的位置,位于.shared状态空间。如果未提供状态空间,则使用通用寻址方式,使得p中的地址指向.shared空间。如果通用地址不在.shared状态空间内,则行为未定义。

.shape限定符表示所加载矩阵的维度。每个矩阵元素根据.type限定符指示,保存16位或8位数据。

.m16n8 形状仅适用于 .b8 类型。

对于.num来说,值.x1.x2.x4分别表示一个、两个或四个矩阵。

强制性的 .sync 限定符表示 stmatrix 会使执行线程等待,直到warp中的所有线程都执行相同的 stmatrix 指令后才会继续执行。

强制性的.aligned限定符表示warp中的所有线程必须执行相同的stmatrix指令。在条件执行的代码中,只有当确定warp中的所有线程对条件的评估结果相同时才应使用stmatrix指令,否则行为将是未定义的。

如果所有线程未使用相同的限定符,或者warp中的任何线程已退出,则stmatrix的行为是未定义的。

源操作数 r 是一个大括号包围的向量表达式,根据 .num 的值由1、2或4个32位寄存器组成。向量表达式的每个分量包含来自对应矩阵的一个片段。

p支持的寻址模式在地址作为操作数中有详细说明。

矩阵的连续行实例不需要在内存中连续存储。每个矩阵所需的八个地址由八个线程提供,具体取决于.num的值,如下表所示。每个地址对应矩阵行的起始位置。地址addr0–addr7对应第一个矩阵的行,地址addr8–addr15对应第二个矩阵的行,依此类推。

.num

线程0–7

线程8–15

线程16–23

线程 24–31

.x1

地址0–地址7

.x2

地址0–地址7

地址8–地址15

.x4

地址0–地址7

地址8–地址15

地址16–地址23

地址24–地址31

当存储8x8矩阵时,四个连续的线程组会存储16字节。矩阵地址必须相应地自然对齐。

warp中的每个线程存储一行的片段,线程0从其寄存器r中存储第一个片段,依此类推。如图106所示,四个线程组存储矩阵的整行。

_images/mma-stmatrix-fragments.png

图 106 一个8x8矩阵(16位元素)的stmatrix片段布局

.num = .x2时,根据上表中的布局,第二个矩阵的元素将从每个线程中的下一个源寄存器开始存储。同样地,当.num = .x4时,第三个和第四个矩阵的元素将从每个线程中后续的源寄存器开始存储。

对于16x8的矩阵形状,warp中的32个线程每个提供四个矩阵数据元素。

源操作数r中的每个元素类型为.b32,包含四个8位元素e0e1e2e3,其中e0e3分别存放寄存器r的最低有效位和最高有效位。

_images/mma-stmatrix-fragments-168.png

图107 一个16x8矩阵(8位元素)的stmatrix片段布局

可选修饰符 .trans 表示矩阵以列优先格式存储。但对于16x8矩阵,.trans 是强制要求的。

内存一致性模型中,stmatrix指令被视为弱内存操作。

PTX ISA 说明

在PTX ISA版本7.8中引入。

在PTX ISA版本8.6中引入了对.m16n8形状的支持。

在PTX ISA 8.6版本中引入了对.b8类型与stmatrix的支持。

目标ISA注意事项

需要 sm_90 或更高版本。

形状 .m16n8 在以下架构上受支持:

  • sm_100a

  • sm_101a

  • sm_120a

类型 .b8stmatrix 在以下架构上受支持:

  • sm_100a

  • sm_101a

  • sm_120a

示例

// Store a single 8x8 matrix using 64-bit addressing
.reg .b64 addr;
.reg .b32 r;
stmatrix.sync.aligned.m8n8.x1.shared.b16 [addr], {r};

// Store two 8x8 matrices in column-major format
.reg .b64 addr;
.reg .b32 r<2>;
stmatrix.sync.aligned.m8n8.x2.trans.shared::cta.b16 [addr], {r0, r1};

// Store four 8x8 matrices
.reg .b64 addr;
.reg .b32 r<4>;
stmatrix.sync.aligned.m8n8.x4.b16 [addr], {r0, r1, r2, r3};

// Store a single 16x8 matrix using generic addressing
.reg .b64 addr;
.reg .b32 r;
stmatrix.sync.aligned.m16n8.x1.trans.shared.b8 [addr], {r};

// Store two 16x8 matrices
.reg .b64 addr;
.reg .b32 r<2>;
stmatrix.sync.aligned.m16n8.x2.trans.shared::cta.b8 [addr],{r0, r1};

// Store four 16x8 matrices
.reg .b64 addr;
.reg .b32 r<4>;
stmatrix.sync.aligned.m16n8.x4.b8 [addr], {r0, r1, r2, r3};
9.7.14.5.17. Warp级矩阵转置指令:movmatrix

movmatrix

在寄存器中跨warp转置矩阵。

语法

movmatrix.sync.aligned.shape.trans.type d, a;

.shape  = {.m8n8};
.type   = {.b16};

描述

将行主序矩阵跨warp中的所有线程移动,从源a读取元素,并将转置后的元素写入目标d

.shape限定符表示被转置矩阵的维度。每个矩阵元素包含16位数据,如.type限定符所示。

强制性的 .sync 限定符表示 movmatrix 会使执行线程等待,直到线程束中的所有线程都执行相同的 movmatrix 指令后才会继续执行。

强制性的 .aligned 限定符表示warp中的所有线程必须执行相同的 movmatrix 指令。在条件执行的代码中,只有当确定warp中的所有线程对条件的评估结果相同时, 才应使用 movmatrix 指令,否则行为将是未定义的。

操作数 ad 是32位寄存器,分别包含输入矩阵和结果矩阵的片段。强制限定符 .trans 表示 d 中的结果矩阵是由 a 指定的输入矩阵的转置。

在一个warp中的每个线程持有输入矩阵一行的片段,其中线程0持有寄存器a中的第一个片段,依此类推。如图108所示,四个线程组成的组持有输入矩阵的完整一行。

_images/mma-movmatrix-fragments-src.png

图108 movmatrix源矩阵片段布局

warp中的每个线程持有结果矩阵一列的片段,线程0持有寄存器d中的第一个片段,依此类推。如图109所示,四个线程组成的组持有结果矩阵的整列。

_images/mma-movmatrix-fragments-dst.png

图 109 movmatrix结果矩阵片段布局

PTX ISA 说明

在PTX ISA版本7.8中引入。

目标ISA注意事项

需要 sm_75 或更高版本。

示例

.reg .b32 d, a;
movmatrix.sync.aligned.m8n8.trans.b16 d, a;

9.7.14.6. 使用mma.sp指令执行矩阵乘累加操作(稀疏矩阵A)

本节介绍针对稀疏矩阵A的warp级mma.sp{::ordered_metadata}指令。 当A是结构化稀疏矩阵且每行具有50%零值(按特定形状粒度分布)时,可使用此mma运算变体。对于MxNxK稀疏mma.sp{::ordered_metadata}运算,MxK矩阵A会被压缩为MxK/2个元素。 矩阵A的每个K宽度行中,50%元素为零值,剩余的K/2个非零元素被压缩到表示矩阵A的操作数中。这些K/2元素与对应K宽度行的映射关系通过元数据显式提供。

9.7.14.6.1. 稀疏矩阵存储

稀疏矩阵A的粒度定义为矩阵行中子块的非零元素数量与该子块总元素数量的比值,其中子块的大小取决于具体形状。例如,在一个16x16的矩阵A中,稀疏度预期为2:4粒度,即矩阵行中每个4元素向量(即4个连续元素组成的子块)包含2个零值。子块中每个非零元素的索引存储在元数据操作数中。值0b00000b01010b10100b1111是元数据的无效值,将导致未定义行为。在四个连续线程组成的组中,根据矩阵形状,一个或多个线程会存储整个组的元数据。这些线程通过额外的稀疏选择器操作数来指定。

图110展示了一个16x16稀疏矩阵A的示例,该矩阵采用稀疏格式存储,并通过稀疏选择器指示连续四个线程组中哪个线程存储元数据。

_images/sparse-mma-storage-example.png

图110 稀疏MMA存储示例

不同矩阵形状和数据类型的粒度描述如下。

稀疏 mma.sp{::ordered_metadata} 支持半精度和 .bf16 类型

对于.m16n8k16.m16n8k32mma.sp{::ordered_metadata}操作,矩阵A采用2:4粒度结构化稀疏存储。也就是说,矩阵A每行中每四个相邻元素组成的块包含两个零元素和两个非零元素。只有这两个非零元素会被存储在表示矩阵A的操作数中,而它们在矩阵A四元素块中的位置由元数据操作数中的两个2位索引指示。对于mma.sp::ordered_metadata0b01000b10000b10010b11000b11010b1110是有效的索引值;其他任何值都会导致未定义行为。

_images/f16-metadata-example.png

图111 针对.f16/.bf16类型的稀疏MMA元数据示例。

稀疏选择器指示贡献元数据的线程,如下所列:

  • m16n8k16: 在四个连续线程组成的一个组中,其中一个线程负责为整个组提供元数据。该线程由{0, 1, 2, 3}中的一个值表示。

  • m16n8k32: 在四个连续线程组成的组中,一个线程对负责提供稀疏性元数据。因此,稀疏性选择器必须为0(线程T0、T1)或1(线程T2、T3);任何其他值都会导致未定义行为。

稀疏 mma.sp{::ordered_metadata} 使用 .tf32 类型

当矩阵A具有.tf32元素时,矩阵A以1:2的粒度呈现结构化稀疏。换句话说,在矩阵A的每一行中,每两个相邻元素组成的块包含一个零元素和一个非零元素。只有非零元素会被存储在矩阵A的操作数中,并且它们在矩阵A两宽块中的位置由元数据中的4位索引指示。0b11100b0100是唯一有意义的索引值;任何其他值都会导致未定义行为。

_images/tf32-metadata-example.png

图112 针对.tf32类型的稀疏MMA元数据示例。

稀疏选择器指示贡献如下所列元数据的线程:

  • m16n8k8: 在四个连续线程组成的组中,其中一个线程负责为整个组提供元数据。该线程由{0, 1, 2, 3}中的一个值表示。

  • m16n8k16: 在四个连续线程组成的组中,每个线程对负责提供稀疏性元数据。因此,稀疏选择器必须为0(线程T0、T1)或1(线程T2、T3);任何其他值将导致未定义行为。

稀疏 mma.sp{::ordered_metadata} 整数类型

当矩阵A和B具有.u8/.s8元素时,矩阵A以2:4的粒度进行结构化稀疏。换句话说,矩阵A每行中相邻的四个元素块包含两个零元素和两个非零元素。稀疏矩阵仅存储这两个非零元素,它们在四元素块中的位置由元数据中的两个2位索引指示。对于mma.sp::ordered_metadata0b01000b10000b10010b11000b11010b1110是索引的有效值;任何其他值都将导致未定义行为。

_images/u8s8-metadata-example.png

图 113 针对.u8/.s8类型的稀疏MMA元数据示例。

当矩阵A和B具有.u4/.s4元素时,矩阵A在4:8的粒度上呈现成对结构化稀疏。换句话说,矩阵A每行中相邻的八个元素块包含四个零值和四个非零值。此外,这些零值和非零值在八个元素的块内被分组为两个元素的子块。即,八个元素块内的每个两元素子块必须全为零或全为非零。稀疏矩阵中仅存储四个非零值,并且矩阵A每行八个元素块中两个包含非零值的两元素子块的位置由元数据中的两个2位索引指示。对于mma.sp::ordered_metadata0b01000b10000b10010b11000b11010b1110是索引的有效值;任何其他值将导致未定义行为。

_images/u4s4-metadata-example.png

图114 针对.u4/.s4类型的稀疏MMA元数据示例。

稀疏选择器指示贡献如下所列元数据的线程:

  • m16n8k32 搭配 .u8/.s8 类型以及 m16n8k64 搭配 .u4/.s4 类型:在四个连续线程组成的组中,一个线程对负责提供稀疏元数据。因此,稀疏选择器必须为0(线程T0、T1)或1(线程T2、T3);任何其他值将导致未定义行为。

  • m16n8k64 搭配 .u8/.s8 类型以及 m16n8k128 搭配 .u4/.s4 类型:每组四个连续线程中的所有线程都会提供稀疏元数据。因此,此情况下的稀疏选择器必须为0。任何其他稀疏选择器值将导致未定义行为。

稀疏 mma.sp{::ordered_metadata} 操作于 .e4m3/.e5m2/.e3m2/.e2m3/.e2m1 类型,使用 .kind::f8f6f4 .kind::mxf8f6f4

当矩阵A和B具有.e4m3/.e5m2/.e3m2/.e2m3/.e2m1元素时,矩阵A以2:4的粒度呈现结构化稀疏。换句话说,矩阵A每行中每四个相邻元素组成的块包含两个零元素和两个非零元素。稀疏矩阵中仅存储这两个非零元素,它们在四元素块中的位置由元数据中的两个2位索引表示。0b01000b10000b10010b11000b11010b1110是索引的有效值;任何其他值都将导致未定义行为。

_images/fp8-metadata-example.png

图115 针对.e4m3/.e5m2/.e3m2/.e2m3/.e2m1类型的稀疏MMA元数据示例。

稀疏选择器指示贡献元数据的线程,如下所列:

  • m16n8k64: 每组四个连续线程中的所有线程都会贡献稀疏元数据。因此,在这种情况下,稀疏选择器必须为0。任何其他稀疏选择器值都会导致未定义行为。

稀疏 mma.sp::ordered_metadata 操作于 .e2m1 类型,使用 .kind::mxf4 .kind::mxf4nvf4

当矩阵A和B具有.e2m1元素时,矩阵A在4:8的粒度上呈现成对结构化稀疏。换句话说,矩阵A每行中相邻的八个元素块包含四个零值和四个非零值。此外,零值和非零值被聚类在八个元素块内每个由两个元素组成的子块中。也就是说,八个元素块内每个两元素宽的子块必须全为零或全为非零。稀疏矩阵中仅存储四个非零值,而矩阵A每行八个元素块内包含非零值的两个两元素宽子块的位置由元数据中的两个2位索引指示。0b01000b10000b10010b11000b11010b1110是索引的有效取值;任何其他值将导致未定义行为。

_images/fp4-metadata-example.png

图116 针对.e2m1类型(使用.kind::mxf4.kind::mxf4nvf4)的稀疏MMA元数据示例

稀疏选择器指示贡献元数据的线程,如下所列:

  • m16n8k128: 每组四个连续线程内的所有线程都会贡献稀疏元数据。因此,在这种情况下稀疏选择器必须为0。任何其他稀疏选择器值都会导致未定义行为。

9.7.14.6.2. 用于稀疏矩阵A乘加运算的矩阵片段

本节我们将介绍线程寄存器的内容如何与各类矩阵片段及稀疏元数据相关联。以下约定贯穿本节始终:

  • 对于矩阵A,仅描述了片段的布局方式,涉及寄存器向量大小及其与矩阵数据的关联。

  • 对于矩阵B,当矩阵维度与支持的数据类型组合未在使用mma指令的矩阵乘积累加操作中涵盖时,会提供矩阵分片的图示表示。

  • 对于矩阵C和D,由于所有支持的形状都具有相同的矩阵维度-数据类型组合,并且已在使用mma指令的矩阵乘加运算中涵盖,因此本节未包含矩阵片段的图示表示。

  • 对于元数据操作数,包含了矩阵A元素索引与元数据操作数内容之间关联的图示表示。Tk: [m..n]出现在单元格[x][y..z]中,表示线程%laneid=k的元数据操作数中从高位mn的位包含了矩阵A块[x][y]..[x][z]中非零元素的索引。

9.7.14.6.2.1. 针对.f16和.bf16类型的稀疏矩阵乘法指令m16n8k16的矩阵片段

一个执行稀疏mma.m16n8k16的warp,使用.f16/.bf16浮点类型时,将计算形状为.m16n8k16的MMA操作。

矩阵的元素分布在warp中的线程之间,因此warp的每个线程都持有矩阵的一个片段。

  • 乘数A:

    .atype

    片段

    元素

    .f16 / .bf16

    一个包含两个.b32寄存器的向量表达式, 每个寄存器包含来自矩阵A的4个连续元素中的两个非零.f16 / .bf16元素。

    非零元素的映射方式如稀疏矩阵存储中所述。

    不同线程持有的片段布局如图117所示。

    _images/sparse-mma-16816-f16-bf16-A.png

    图117 针对.f16/.bf16类型矩阵A的稀疏MMA .m16n8k16片段布局。

    矩阵片段的行和列可按如下方式计算:

    groupID = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =      groupID            // 对应a0和a1
               groupID + 8        // 对应a2和a3
    
    col = [firstcol ... lastcol]  // 根据非零元素的映射方式
                                  // 如稀疏矩阵存储中所述
    
    其中
    firstcol = threadID_in_group * 4
    lastcol  = firstcol + 3
    
  • 乘数B以及累加器C和D的矩阵片段与Matrix Fragments for mma.m16n8k16 with floating point type中针对.f16/.b16格式的情况相同。

  • 元数据:一个.b32寄存器,包含16个2位向量,每个向量存储矩阵A的4宽块中非零元素的索引,如图118所示。

    _images/sparse-mma-metadata-16816-f16bf16.png

    图118 针对.f16/.bf16类型的稀疏MMA .m16n8k16元数据布局。

9.7.14.6.2.2. 针对.f16和.bf16类型的稀疏矩阵乘法指令m16n8k32的矩阵分块

一个执行稀疏mma.m16n8k32的warp,使用.f16/.bf16浮点类型时,将计算形状为.m16n8k32的MMA操作。

矩阵的元素分布在warp中的线程之间,因此warp的每个线程都持有矩阵的一个片段。

  • Multiplicand A:

    .atype

    Fragment

    Elements

    .f16 / .bf16

    A vector expression containing four .b32 registers, with each register containing two non-zero .f16 / .bf16 elements out of 4 consecutive elements from matrix A.

    Mapping of the non-zero elements is as described in Sparse matrix storage.

    The layout of the fragments held by different threads is shown in Figure 119.

    _images/sparse-mma-16832-f16-bf16-A.png

    Figure 119 Sparse MMA .m16n8k32 fragment layout for matrix A with .f16/.bf16 type.

    The row and column of a matrix fragment can be computed as:

    groupID = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =      groupID            for ai where  0 <= i < 2 || 4 <= i < 6
               groupID + 8        Otherwise
    
    col = [firstcol ... lastcol]  // As per the mapping of non-zero elements
                                  // as described in Sparse matrix storage
    
    Where
    firstcol = threadID_in_group * 4          For ai where i <  4
               (threadID_in_group * 4) + 16   for ai where i >= 4
    lastcol  = firstcol + 3
    
  • 乘数 B:

    .atype

    片段

    元素(从低到高)

    .f16 / .bf16

    包含四个.b32寄存器的向量表达式,每个寄存器包含来自矩阵B的两个.f16/.bf16元素。

    b0, b1, b2, b3

    不同线程持有的片段布局如图120所示。

    _images/sparse-mma-16832-f16bf16-B.png

    图120 使用.f16/.bf16类型的矩阵B的稀疏MMA .m16n8k32片段布局。

  • 累加器矩阵C和D的片段与浮点类型的mma.m16n8k16矩阵片段情况相同,适用于.f16/.b16格式。

  • 元数据:一个.b32寄存器,包含16个2位向量,每对2位向量存储来自矩阵A的4宽块中两个非零元素的索引,如图121所示。

    _images/sparse-mma-metadata-16832-f16bf16.png

    图121 针对.f16/.bf16类型的稀疏MMA .m16n8k32元数据布局。

9.7.14.6.2.3. 稀疏矩阵片段用于支持.tf32浮点类型的mma.m16n8k16运算

一个执行稀疏mma.m16n8k16的warp,使用.tf32浮点类型,将计算形状为.m16n8k16的MMA操作。

矩阵的元素分布在warp中的线程之间,因此warp的每个线程都持有矩阵的一个片段。

  • 乘数A:

    .atype

    片段

    元素

    .tf32

    一个包含四个.b32寄存器的向量表达式,每个寄存器包含来自矩阵A的两个连续元素中的一个非零.tf32元素。

    非零元素的映射方式如稀疏矩阵存储中所述。

    不同线程持有的片段布局如图122所示。

    _images/sparse-mma-16816-tf32-A.png

    图122 使用.tf32类型的矩阵A的稀疏MMA .m16n8k16片段布局。

    矩阵片段的行和列可按如下方式计算:

    groupID = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =      groupID            for a0 and a2
               groupID + 8        for a1 and a3
    
    col = [firstcol ... lastcol]  // 根据非零元素的映射方式
                                  // 如稀疏矩阵存储中所述
    
    Where
    firstcol = threadID_in_group * 2          for a0 and a1
               (threadID_in_group * 2) + 8    for a2 and a3
    lastcol  = firstcol + 1
    
  • 乘数 B:

    .atype

    片段

    元素(从低到高)

    .tf32

    一个包含四个.b32寄存器的向量表达式,每个寄存器包含来自矩阵B的四个.tf32元素。

    b0, b1, b2, b3

    不同线程持有的片段布局如图123所示。

    _images/sparse-mma-16816-tf32-B.png

    图123 使用.tf32类型的矩阵B的稀疏MMA .m16n8k16片段布局。

  • 累加器C和D的矩阵片段与浮点类型的mma.m16n8k16矩阵片段情况相同。

  • 元数据:一个.b32寄存器,包含8个4位向量,每个向量存储矩阵A的2宽块中非零元素的索引,如图124所示。

    _images/sparse-mma-metadata-16816-tf32.png

    图124 针对.tf32类型的稀疏MMA .m16n8k16元数据布局。

9.7.14.6.2.4. 针对稀疏矩阵乘法累加运算m16n8k8的.tf32浮点类型矩阵片段

一个执行稀疏mma.m16n8k8的warp,使用.tf32浮点类型时,将计算形状为.m16n8k8的MMA操作。

矩阵的元素分布在warp中的线程之间,因此warp的每个线程都持有矩阵的一个片段。

  • 乘数A:

    .atype

    片段

    元素

    .tf32

    一个包含两个.b32寄存器的向量表达式,每个寄存器包含来自矩阵A的2个连续元素中的一个非零.tf32元素。

    非零元素的映射方式如稀疏矩阵存储中所述。

    不同线程持有的片段布局如图125所示。

    _images/sparse-mma-1688-tf32-A.png

    图125 使用.tf32类型的矩阵A的稀疏MMA .m16n8k8片段布局。

    矩阵片段的行和列可按如下方式计算:

    groupID = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =      groupID            对应 a0
               groupID + 8        对应 a1
    
    col = [firstcol ... lastcol]  // 根据非零元素的映射方式
                                  // 如稀疏矩阵存储中所述
    
    其中
    firstcol = threadID_in_group * 2
    lastcol  = firstcol + 1
    
  • 乘数B以及累加器C和D的矩阵片段与Matrix Fragments for mma.m16n8k8.tf32格式的情况相同。

  • 元数据:一个.b32寄存器,包含8个4位向量,每个向量存储矩阵A的2宽块中非零元素的索引,如图126所示。

    _images/sparse-mma-metadata-1688-tf32.png

    图126 针对.tf32类型的稀疏MMA .m16n8k8元数据布局。

9.7.14.6.2.5. 针对稀疏矩阵乘法指令m16n8k32的矩阵片段(支持.u8/.s8整数类型)

执行稀疏mma.m16n8k32的warp使用.u8/.s8整数类型时,将计算形状为.m16n8k32的MMA运算。

矩阵的元素分布在warp中的线程之间,因此warp的每个线程都持有矩阵的一个片段。

  • 乘数A:

    .atype

    片段

    元素

    .u8 / .s8

    一个包含两个.b32寄存器的向量表达式,每个寄存器包含来自矩阵A的8个连续元素中的四个非零.u8 / .s8元素。

    非零元素的映射方式如稀疏矩阵存储中所述。

    不同线程持有的片段布局如图127所示。

    _images/sparse-mma-16832-u8s8-A.png

    图127 针对.u8/.s8类型矩阵A的稀疏MMA .m16n8k32片段布局。

    groupID = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =      groupID            for ai where  0 <= i < 4
               groupID + 8        Otherwise
    
    col = [firstcol ... lastcol]  // 根据非零元素的映射方式
                                  // 如稀疏矩阵存储中所述
    
    Where
    firstcol = threadID_in_group * 8
    lastcol  = firstcol + 7
    
  • 乘数B矩阵片段以及累加器C和D的矩阵片段与Matrix Fragments for mma.m16n8k32中的情况相同。

  • 元数据:一个.b32寄存器,包含16个2位向量,每对2位向量存储来自矩阵A的4宽块中两个非零元素的索引,如图128所示。

    _images/sparse-mma-metadata-16832-u8s8.png

    图128 稀疏MMA .m16n8k32元数据布局(适用于.u8/.s8类型)

9.7.14.6.2.6. 稀疏矩阵乘法m16n8k64的矩阵片段,支持.u8/.s8/.e4m3/.e5m2/.e3m2/.e2m3/.e2m1数据类型

执行稀疏mma.m16n8k64的warp将计算形状为.m16n8k64的MMA操作,支持.u8/.s8/.e4m3/.e5m2/.e3m2/.e2m3/.e2m1数据类型。

矩阵的元素分布在warp中的线程之间,因此warp的每个线程都持有矩阵的一个片段。

  • Multiplicand A:

    .atype

    Fragment

    Elements

    .u8 / .s8

    A vector expression containing four .b32 registers, with each register containing four non-zero .u8 / .s8 elements out of 8 consecutive elements from matrix A.

    Mapping of the non-zero elements is as described in Sparse matrix storage.

    .e4m3 / .e5m2 / .e3m2 / .e2m3 / .e2m1

    A vector expression containing four .b32 registers, with each register containing four non-zero .e4m3 / .e5m2 / .e3m2 / .e2m3 / .e2m1 elements out of 8 consecutive elements from matrix A.

    The layout of the fragments held by different threads is shown in Figure 129 and Figure 130.

    _images/sparse-mma-16864-u8s8-A-first32col.png

    Figure 129 Sparse MMA .m16n8k64 fragment layout for columns 0–31 of matrix A with .u8/.s8/.e4m3/.e5m2/.e3m2/.e2m3/.e2m1 type.

    _images/sparse-mma-16864-u8s8-A-last32col.png

    Figure 130 Sparse MMA .m16n8k64 fragment layout for columns 32–63 of matrix A with .u8/.s8/.e4m3/.e5m2/.e3m2/.e2m3/.e2m1 type.

    groupID = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =      groupID            for ai where  0 <= i < 4 || 8 <= i < 12
               groupID + 8        Otherwise
    
    col = [firstcol ... lastcol]  // As per the mapping of non-zero elements
                                  // as described in Sparse matrix storage
    
    Where
    firstcol = threadID_in_group * 8           For ai where i <  8
               (threadID_in_group * 8) + 32    For ai where i >= 8
    lastcol  = firstcol + 7
    
  • Multiplicand B:

    .btype

    Fragment

    Elements (low to high)

    .u8 / .s8

    A vector expression containing four .b32 registers, each containing four .u8 / .s8 elements from matrix B.

    b0, b1, b2, b3, …, b15

    .e4m3 / .e5m2 / .e3m2 / .e2m3 / .e2m1

    A vector expression containing four .b32 registers, each containing four .e4m3 / .e5m2 / .e3m2 / .e2m3 / .e2m1 elements from matrix B.

    The layout of the fragments held by different threads is shown in Figure 131, Figure 132, Figure 133 and Figure 134.

    _images/sparse-mma-16864-u8s8-B1.png

    Figure 131 Sparse MMA .m16n8k64 fragment layout for rows 0–15 of matrix B with .u8/.s8/.e4m3/.e5m2/.e3m2/.e2m3/.e2m1 type.

    _images/sparse-mma-16864-u8s8-B2.png

    Figure 132 Sparse MMA .m16n8k64 fragment layout for rows 16–31 of matrix B with .u8/.s8/.e4m3/.e5m2/.e3m2/.e2m3/.e2m1 type.

    _images/sparse-mma-16864-u8s8-B3.png

    Figure 133 Sparse MMA .m16n8k64 fragment layout for rows 32–47 of matrix B with .u8/.s8/.e4m3/.e5m2/.e3m2/.e2m3/.e2m1 type.

    _images/sparse-mma-16864-u8s8-B4.png

    Figure 134 Sparse MMA .m16n8k64 fragment layout for rows 48–63 of matrix B with .u8/.s8/.e4m3/.e5m2/.e3m2/.e2m3/.e2m1 type.

  • 累加器C和D的矩阵片段与Matrix Fragments for mma.m16n8k16的情况相同。

  • 元数据:一个.b32寄存器,包含16个2位向量,每对2位向量存储来自矩阵A的4宽块中两个非零元素的索引,如图135图136所示。

    _images/sparse-mma-metadata-16864-u8s8-last32col.png

    图135 稀疏MMA .m16n8k64元数据布局(0-31列),适用于.u8/.s8/.e4m3/.e5m2/.e3m2/.e2m3/.e2m1类型。

    _images/sparse-mma-metadata-16864-u8s8-last32col.png

    图136 稀疏MMA .m16n8k64元数据布局(32-63列),适用于.u8/.s8/.e4m3/.e5m2/.e3m2/.e2m3/.e2m1类型。

9.7.14.6.2.7. 针对.u4/.s4整数类型的稀疏矩阵乘法m16n8k64的矩阵片段

执行稀疏mma.m16n8k64的warp,使用.u4/.s4整数类型时,将计算形状为.m16n8k64的MMA操作。

矩阵的元素分布在warp中的线程之间,因此warp的每个线程都持有矩阵的一个片段。

  • 乘数A:

    .atype

    片段

    元素

    .u4 / .s4

    一个包含两个.b32寄存器的向量表达式,每个寄存器包含来自矩阵A的16个连续元素中的8个非零.u4 / .s4元素

    非零元素的映射方式如稀疏矩阵存储中所述

    不同线程持有的片段布局如图137所示

    _images/sparse-mma-16864-u4s4-A.png

    图137 使用.u4/.s4类型的矩阵A的稀疏MMA .m16n8k64片段布局

    groupID = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =      groupID            当ai满足0 <= i < 8时
               groupID + 8        其他情况
    
    col = [firstcol ... lastcol]  // 按照非零元素的映射方式
                                  // 如稀疏矩阵存储中所述
    
    其中
    firstcol = threadID_in_group * 16
    lastcol  = firstcol + 15
    
  • 乘数B矩阵片段以及累加器C和D矩阵片段与Matrix Fragments for mma.m16n8k64中的情况相同。

  • 元数据:一个.b32寄存器,包含16个2位向量,每对2位向量存储来自矩阵A的8宽块中四个非零元素的索引,如图138所示。

    _images/sparse-mma-metadata-16864-u4s4.png

    图138 针对.u4/.s4类型的稀疏MMA .m16n8k64元数据布局。

9.7.14.6.2.8. 针对.u4/.s4/.e2m1类型的稀疏矩阵乘法m16n8k128的矩阵分块

一个执行稀疏mma.m16n8k128的warp,使用.u4/.s4/.e2m1整数类型时,将计算形状为.m16n8k128的MMA操作。

矩阵的元素分布在warp中的线程之间,因此warp的每个线程都持有矩阵的一个片段。

  • Multiplicand A:

    .atype

    Fragment

    Elements

    .u4 / .s4

    A vector expression containing four .b32 registers, with each register containing eight non-zero .u4 / .s4 elements out of 16 consecutive elements from matrix A.

    Mapping of the non-zero elements is as described in Sparse matrix storage.

    .e2m1

    A vector expression containing four .b32 registers, with each register containing eight non-zero .e2m1 elements out of 16 consecutive elements from matrix A.

    The layout of the fragments held by different threads is shown in Figure 139 and Figure 140.

    _images/sparse-mma-168128-u4s4-A-first64col.png

    Figure 139 Sparse MMA .m16n8k128 fragment layout for columns 0–63 of matrix A with .u4/.s4/.e2m1 type.

    _images/sparse-mma-168128-u4s4-A-last64col.png

    Figure 140 Sparse MMA .m16n8k128 fragment layout for columns 64–127 of matrix A with .u4/.s4/.e2m1 type.

    groupID = %laneid >> 2
    threadID_in_group = %laneid % 4
    
    row =      groupID            for ai where  0 <= i < 8 || 16 <= i < 24
               groupID + 8        Otherwise
    
    col = [firstcol ... lastcol]  // As per the mapping of non-zero elements
                                  // as described in Sparse matrix storage
    
    Where
    firstcol = threadID_in_group * 16           For ai where i <  16
               (threadID_in_group * 16) + 64    For ai where i >= 16
    lastcol  = firstcol + 15
    
  • 乘数 B:

    .atype

    片段

    元素(从低到高)

    .u4 / .s4

    一个包含四个.b32寄存器的向量表达式,每个寄存器包含来自矩阵B的八个.u4 / .s4元素。

    b0, b1, b2, b3, …, b31

    .e2m1

    一个包含四个.b32寄存器的向量表达式,每个寄存器包含来自矩阵B的八个.e2m1元素。

    不同线程持有的片段布局如图141图142图143图144所示。

    _images/sparse-mma-168128-u4s4-B4.png

    图141 稀疏MMA .m16n8k128片段布局(矩阵B第0-31行,.u4/.s4/.e2m1类型)

    _images/sparse-mma-168128-u4s4-B4.png

    图142 稀疏MMA .m16n8k128片段布局(矩阵B第32-63行,.u4/.s4/.e2m1类型)

    _images/sparse-mma-168128-u4s4-B4.png

    图143 稀疏MMA .m16n8k128片段布局(矩阵B第64-95行,.u4/.s4/.e2m1类型)

    _images/sparse-mma-168128-u4s4-B4.png

    图144 稀疏MMA .m16n8k128片段布局(矩阵B第96-127行,.u4/.s4/.e2m1类型)

  • 累加器C和D的矩阵片段与Matrix Fragments for mma.m16n8k64中的情况相同。

  • 元数据:一个.b32寄存器,包含16个2位向量,每对2位向量存储来自矩阵A的8宽块中四个非零元素的索引,如图145图146所示。

    _images/sparse-mma-metadata-168128-u4s4-last64col.png

    图145 稀疏MMA .m16n8k128元数据布局(针对0-63列,适用于.u4/.s4/.e2m1类型)

    _images/sparse-mma-metadata-168128-u4s4-last64col.png

    图146 稀疏MMA .m16n8k128元数据布局(针对64-127列,适用于.u4/.s4/.e2m1类型)

9.7.14.6.3. 乘加指令:mma.sp/mma.sp::ordered_metadata

mma.sp/mma.sp::ordered_metadata

使用稀疏矩阵A执行矩阵乘加操作

语法

半精度浮点类型:

mma.spvariant.sync.aligned.m16n8k16.row.col.dtype.f16.f16.ctype  d, a, b, c, e, f;
mma.spvariant.sync.aligned.m16n8k32.row.col.dtype.f16.f16.ctype  d, a, b, c, e, f;

.ctype     = {.f16, .f32};
.dtype     = {.f16, .f32};
.spvariant = {.sp, .sp::ordered_metadata};

替代浮点类型:

mma.spvariant.sync.aligned.m16n8k16.row.col.f32.bf16.bf16.f32     d, a, b, c, e, f;
mma.spvariant.sync.aligned.m16n8k32.row.col.f32.bf16.bf16.f32     d, a, b, c, e, f;
mma.spvariant.sync.aligned.m16n8k8.row.col.f32.tf32.tf32.f32      d, a, b, c, e, f;
mma.spvariant.sync.aligned.m16n8k16.row.col.f32.tf32.tf32.f32     d, a, b, c, e, f;
mma.spvariant.sync.aligned.m16n8k64.row.col.f32.f8type.f8type.f32 d, a, b, c, e, f;
mma.sp::ordered_metadata.sync.aligned.m16n8k64.row.col.kind.dtype.f8f6f4type.f8f6f4type.ctype d, a, b, c, e, f;

.f8type     = {.e4m3, .e5m2};
.spvariant  = {.sp, .sp::ordered_metadata};
.f8f6f4type = {.e4m3, .e5m2, .e3m2, .e2m3, .e2m1};
.kind       = {kind::f8f6f4};
.ctype      = {.f16, .f32};
.dtype      = {.f16, .f32};

采用块缩放的替代浮点类型:

mma.spvariant.sync.aligned.m16n8k128.row.col.kind.block_scale{.scale_vec_size}.f32.e2m1.e2m1.f32.stype d, a, b, c, e, f, scale-a-data, {byte-id-a, thread-id-a}, scale-b-data, {byte-id-b, thread-id-b};

.spvariant      = {.sp::ordered_metadata};
.kind           = {.kind::mxf4};
.scale_vec_size = {.scale_vec::2X};
.stype          = {.ue8m0};

mma.spvariant.sync.aligned.m16n8k128.row.col.kind.block_scale.scale_vec_size.f32.e2m1.e2m1.f32.stype d, a, b, c, e, f, scale-a-data, {byte-id-a, thread-id-a}, scale-b-data, {byte-id-b, thread-id-b};

.spvariant      = {.sp::ordered_metadata};
.kind           = {.kind::mxf4nvf4};
.scale_vec_size = {.scale_vec::2X, .scale_vec::4X};
.stype          = {.ue8m0, .ue4m3};

mma.spvariant.sync.aligned.m16n8k64.row.col.kind.block_scale{.scale_vec_size}.f32.f8f6f4type.f8f6f4type.f32.stype d, a, b, c, e, f, scale-a-data, {byte-id-a, thread-id-a}, scale-b-data, {byte-id-b, thread-id-b};

.spvariant      = {.sp::ordered_metadata};
.kind           = {.kind::mxf8f6f4};
.scale_vec_size = {.scale_vec::1X};
.f8f6f4type     = {.e4m3, .e5m2, .e3m2, .e2m3, .e2m1};
.stype          = {.ue8m0};

整数类型:

mma.spvariant.sync.aligned.shape.row.col{.satfinite}.s32.atype.btype.s32 d, a, b, c, e, f;

.shape     = {.m16n8k32, .m16n8k64}
.atype     = {.u8, .s8};
.btype     = {.u8, .s8};
.spvariant = {.sp, .sp::ordered_metadata};

mma.spvariant.sync.aligned.shape.row.col{.satfinite}.s32.atype.btype.s32 d, a, b, c, e, f;

.shape     = {.m16n8k64, .m16n8k128}
.atype     = {.u4, .s4};
.btype     = {.u4, .s4};
.spvariant = {.sp, .sp::ordered_metadata};

描述

执行一个MxNxK矩阵乘加运算,D = A*B+C,其中A矩阵为MxK,B矩阵为KxN,C和D矩阵为MxN

执行mma.sp.sync/mma.sp::ordered_metadata.sync指令的warp会计算单个矩阵乘加运算。

限定符 .block_scale 指定在执行矩阵乘加运算前,矩阵 AB 将分别与 scale_Ascale_B 矩阵进行缩放,具体说明见章节 Block Scaling。矩阵 scale_Ascale_B 中每个元素对应的数据类型由 .stype 指定。限定符 .scale_vec_size 则指定了 scale_A 矩阵的列数和 scale_B 矩阵的行数。

.kind.stype.scale_vec_size 的有效组合在 表35中有详细说明。对于使用 .kind::mxf4mma,当未指定限定符 .scale_vec_size 时,默认值为 2X。相反,当 .kind 指定为 .kind::mxf8f6f4 时, 限定符 .scale_vec_size 的默认值为 1X。然而,对于 .kind::mxf4nvf4,必须提供有效的 .scale_vec_size 值。

操作数 ab 表示两个被乘矩阵 A 和 B,而 cd 表示累加器和目标矩阵,这些矩阵分布在warp中的线程间。矩阵A采用Sparse matrix storage中描述的结构化稀疏存储方式。操作数 ef 分别表示稀疏元数据和稀疏选择器。操作数 e 是32位整数,操作数 f 是取值范围为0..3的32位整数常量。 当指定 .block_scale 限定符时,操作数 scale-a-datascale-b-data 分别表示与 scale_Ascale_B 矩阵对应的缩放矩阵元数据。 元组 {byte-id-a, thread-id-a}{byte-id-b, thread-id-b} 表示从相应的元数据参数 scale-a-datascale-b-data 中选择 scale_Ascale_B 矩阵的选择器。操作数 scale-a-datascale-b-data 的类型为 .b32。操作数 byte-id-athread-id-abyte-id-bthread-id-b 是无符号16位整数值。有关选择器参数的更多详细信息,请参阅Block Scaling部分。

指令 mma.sp::ordered_metadata 要求稀疏元数据中的索引必须按从最低有效位(LSB)开始递增的顺序排序,否则行为将是未定义的。

每个线程中的寄存器保存着矩阵的一个片段,如稀疏矩阵A的乘积累加操作矩阵片段中所述。

限定符 .dtype.atype.btype.ctype 分别表示矩阵 D、A、B 和 C 中元素的数据类型。限定符 .stype 表示矩阵 scale_Ascale_B 中元素的数据类型。对于形状 .m16n8k16.m16n8k32.dtype 必须与 .ctype 相同。

.kind.kind::mxf8f6f4.kind::f8f6f4时,单独的4位和6位浮点类型元素必须打包在8位容器中。类型为.e2m1的矩阵元素位于8位容器的中间4位,容器的高2位和低2位为填充位。当矩阵元素类型为.e3m2.e2m3时,矩阵元素位于8位容器的低6位,容器的高2位为填充位。需要注意的是,当使用mma.kind::mxf4.kind::mxf4nvf4时,即使矩阵元素类型为.e2m1,也不需要显式填充。

Precision and rounding :
  • .f16 浮点运算:

    矩阵A和B的逐元素乘法运算至少以单精度执行。当.ctype.dtype.f32时,中间值的累加至少以单精度执行。当.ctype.dtype都指定为.f16时,累加以至少半精度执行。

    未指定累加顺序、舍入方式以及对次正规输入的处理。

  • .e4m3, .e5m2, .e3m2, .e2m3, .e2m1 浮点运算:

    矩阵A和B的逐元素乘法以指定精度执行。中间值的累加至少以单精度执行。

    累加顺序、舍入方式以及对次正规输入的处理方式未作明确规定。

  • .bf16.tf32 浮点运算:

    矩阵A和B的逐元素乘法以指定精度执行。中间值的累加至少以单精度执行。

    累加顺序、舍入和对次正规输入的处理未作规定。

  • 整数运算:

    整数运算 mma.sp/mma.sp::ordered_metadata 使用 .s32 累加器执行。 .satfinite 限定符表示在溢出时,累加值将被限制在范围 MIN_INT32..MAX_INT32 内(其中边界分别定义为最小负32位有符号整数和最大正32位有符号整数)。

    如果未指定 .satfinite,则累加值将进行环绕处理。

强制性的.sync限定符表示mma.sp/mma.sp::ordered_metadata指令会使执行线程等待,直到warp中的所有线程都执行相同的mma.sp/mma.sp::ordered_metadata指令后才会继续执行。

强制性的.aligned限定符表示warp中的所有线程必须执行相同的mma.sp/mma.sp::ordered_metadata指令。在条件执行的代码中,只有当确定warp中的所有线程对条件的评估结果完全一致时,才应使用mma.sp/mma.sp::ordered_metadata指令,否则行为将是未定义的。

如果同一warp中的所有线程未使用相同的限定符,或者warp中有任何线程已退出,则mma.sp/mma.sp::ordered_metadata指令的行为是未定义的。

注意事项

mma.sp 指令在某些目标架构上性能可能大幅降低。 因此,建议使用 mma.sp::ordered_metadata 指令。

PTX ISA 说明

在PTX ISA版本7.1中引入。

支持PTX ISA 8.4版本中引入的.e4m3.e5m2替代浮点类型的mma运算。

mma.sp::ordered_metadata 在PTX ISA 8.5版本中引入。

支持在PTX ISA 8.7版本中引入的.m16n8k32形状和.f16数据类型/计算类型,以及.e4m3/.e5m2替代浮点类型的mma矩阵乘法累加运算。

支持在PTX ISA 8.7版本中引入的.e3m2.e2m3.e2m1替代浮点类型的mma操作。

支持PTX ISA版本8.7中引入的.kind.block_scale.scale_vec_size限定符。

目标ISA注意事项

需要 sm_80 或更高版本。

.e4m3.e5m2 替代浮点类型的 mma 操作需要 sm_89 或更高版本。

mma.sp::ordered_metadata 需要 sm_80 或更高版本。

支持形状.m16n8k32.f16数据类型/计算类型,使用.e4m3/.e5m2替代浮点类型的mma操作需要sm_120a架构支持。

.e3m2.e2m3.e2m1这些交替浮点类型的mma运算需要sm_120a支持。

支持 .kind.block_scale.scale_vec_size 限定符需要 sm_120a

半精度浮点类型示例

// f16 elements in C and D matrix
.reg .f16x2 %Ra<2> %Rb<2> %Rc<2> %Rd<2>
.reg .b32 %Re;
mma.sp.sync.aligned.m16n8k16.row.col.f16.f16.f16.f16
  {%Rd0, %Rd1},
  {%Ra0, %Ra1},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1}, %Re, 0x1;

.reg .f16x2 %Ra<2> %Rb<2> %Rc<2> %Rd<2>
.reg .b32 %Re;

mma.sp::ordered_metadata.sync.aligned.m16n8k16.row.col.f16.f16.f16.f16
  {%Rd0, %Rd1},
  {%Ra0, %Ra1},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1}, %Re, 0x1;

替代浮点类型的示例

.reg .b32 %Ra<2>, %Rb<2>;
.reg .f32 %Rc<4>, %Rd<4>;
.reg .b32 %Re;
mma.sp.sync.aligned.m16n8k8.row.col.f32.tf32.tf32.f32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1, %Rc2, %Rc3}, %Re, 0x1;

.reg .b32 %Ra<2>, %Rb<2>;
.reg .f32 %Rc<4>, %Rd<4>;
.reg .b32 %Re;
mma.sp.sync.aligned.m16n8k16.row.col.f32.bf16.bf16.f32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1, %Rc2, %Rc3}, %Re, 0x1;

.reg .b32 %Ra<4>, %Rb<4>;
.reg .f32 %Rc<4>, %Rd<4>;
.reg .b32 %Re;
mma.sp.sync.aligned.m16n8k32.row.col.f32.bf16.bf16.f32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1, %Rb2, %Rb3},
  {%Rc0, %Rc1, %Rc2, %Rc3}, %Re, 0x1;

.reg .b32 %Ra<4>, %Rb<4>;
.reg .f32 %Rc<4>, %Rd<4>;
.reg .b32 %Re;
mma.sp.sync.aligned.m16n8k64.row.col.f32.e5m2.e4m3.f32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1, %Rb2, %Rb3},
  {%Rc0, %Rc1, %Rc2, %Rc3}, %Re, 0;

.reg .b32 %Ra<2>, %Rb<2>;
.reg .f32 %Rc<4>, %Rd<4>;
.reg .b32 %Re;
mma.sp::ordered_metadata.sync.aligned.m16n8k16.row.col.f32.bf16.bf16.f32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1, %Rc2, %Rc3}, %Re, 0x1;

.reg .b32 %Ra<4>, %Rb<4>;
.reg .f32 %Rc<4>, %Rd<4>;
.reg .b32 %Re;
mma.sp::ordered_metadata.sync.aligned.m16n8k64.row.col.kind::f8f6f4.f32.e3m2.e2m3.f32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1, %Rb2, %Rb3},
  {%Rc0, %Rc1, %Rc2, %Rc3}, %Re, 0;

.reg .b32 %Ra<4>, %Rb<4>;
.reg .b32 %Rc<4>, %Rd<4>;
.reg .b32 %Re;
mma.sp::ordered_metadata.sync.aligned.m16n8k64.row.col.kind::f8f6f4.f16.e2m3.e2m1.f16
  {%Rd0, %Rd1},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1, %Rb2, %Rb3},
  {%Rc0, %Rc1}, %Re, 0;

整数类型示例

.reg .b32 %Ra<4>, %Rb<4>, %Rc<4>, %Rd<4>;
.reg .u32 %Re;

// u8 elements in A and B matrix
mma.sp.sync.aligned.m16n8k32.row.col.satfinite.s32.u8.u8.s32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1, %Rc2, %Rc3}, %Re, 0x1;

// s8 elements in A and B matrix
mma.sp.sync.aligned.m16n8k64.row.col.satfinite.s32.s8.s8.s32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1, %Rb2, %Rb3},
  {%Rc0, %Rc1, %Rc2, %Rc3}, %Re, 0x0;

// s8 elements in A and B matrix with ordered metadata
mma.sp::ordered_metadata.sync.aligned.m16n8k64.row.col.satfinite.s32.s8.s8.s32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1, %Rb2, %Rb3},
  {%Rc0, %Rc1, %Rc2, %Rc3}, %Re, 0x0;

// u4 elements in A and B matrix
mma.sp.sync.aligned.m16n8k64.row.col.s32.s4.s4.s32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1},
  {%Rb0, %Rb1},
  {%Rc0, %Rc1, %Rc2, %Rc3}, %Re, 0x1;

// u4 elements in A and B matrix
mma.sp.sync.aligned.m16n8k128.row.col.satfinite.s32.u4.u4.s32
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1, %Rb2, %Rb3},
  {%Rc0, %Rc1, %Rc2, %Rc3}, %Re, 0x0;

使用块缩放的矩阵乘法示例

.reg .b32 %Ra<4>, %Rb<4>;
.reg .f32 %Rc<4>, %Rd<4>;
.reg .b32 scaleAData, scaleBData;
.reg .b32 %Re;
mma.sp::ordered_metadata.sync.aligned.m16n8k128.row.col.kind::mxf4.block_scale.f32.e2m1.e2m1.f32.ue8m0
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1, %Rb2, %Rb3},
  {%Rc0, %Rc1, %Rc2, %Rc3},
  %Re, 0,
  scaleAData, {2, 1}, scaleBData, {2, 3};

.reg .b32 %Ra<4>, %Rb<4>;
.reg .f32 %Rc<4>, %Rd<4>;
.reg .b32 scaleAData, scaleBData;
.reg .u16 bidA, bidB, tidA, tidB;
.reg .b32 %Re;
mma.sp::ordered_metadata.sync.aligned.m16n8k128.row.col.kind::mxf4nvf4.block_scale.scale_vec::4X.f32.e2m1.e2m1.f32.ue4m3
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1, %Rb2, %Rb3},
  {%Rc0, %Rc1, %Rc2, %Rc3},
  %Re, 0,
  scaleAData, {bidA, tidA}, scaleBData, {bidB, tidB};

.reg .b32 %Ra<4>, %Rb<4>;
.reg .f32 %Rc<4>, %Rd<4>;
.reg .b32 scaleAData, scaleBData;
.reg .b32 %Re;
mma.sp::ordered_metadata.sync.aligned.m16n8k64.row.col.kind::mxf8f6f4.block_scale.scale_vec::1X.f32.e3m2.e2m1.f32.ue8m0
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2, %Ra3},
  {%Rb0, %Rb1, %Rb2, %Rb3},
  {%Rc0, %Rc1, %Rc2, %Rc3},
  %Re, 0,
  scaleAData, {0, 1}, scaleBData, {0, 1};

.reg .b32 %Ra<4>, %Rb<4>;
.reg .f32 %Rc<4>, %Rd<4>;
.reg .b32 scaleAData, scaleBData;
.reg .b32 %Re;
mma.sp::ordered_metadata.sync.aligned.m16n8k64.row.col.kind::mxf8f6f4.block_scale.scale_vec::1X.f32.e4m3.e5m2.f32.ue8m0
  {%Rd0, %Rd1, %Rd2, %Rd3},
  {%Ra0, %Ra1, %Ra2,  %Ra3},
  {%Rb0, %Rb1, %Rb2, %Rb3},
  {%Rc0, %Rc1, %Rc2, %Rc3},
  %Re, 0,
  scaleAData, {0, 1}, scaleBData, {0, 0};

9.7.15. 异步Warpgroup级矩阵乘累加指令

warpgroup级别的矩阵乘加运算具有以下任一形式,其中矩阵D被称为累加器:

  • D = A * B + D

  • D = A * B,其中来自累加器D的输入被禁用。

wgmma指令通过让整个warpgroup中的所有线程协同执行以下操作,实现warpgroup级别的矩阵乘加运算:

  1. 将矩阵A、B和D加载到寄存器或共享内存中。

  2. 执行以下fence操作:

    • wgmma.fence 操作用于表明跨warpgroup的寄存器/共享内存已完成写入。

    • fence.proxy.async 操作用于使通用代理操作对异步代理可见。

  3. 使用wgmma.mma_async操作对输入矩阵执行异步矩阵乘加运算。wgmma.mma_async操作在异步代理中执行。

  4. 创建一个wgmma-group并通过wgmma.commit_group操作将所有先前未完成的wgmma.mma_async操作提交到该组中。

  5. 等待所需的wgmma-group完成。

  6. 一旦wgmma-group完成,所有的wgmma.mma_async操作都已执行并完成。

9.7.15.1. Warpgroup

一个warpgroup是由四个连续的warp组成的集合,其中第一个warp的warp-rank是4的倍数。

warp的rank定义为:

(%tid.x + %tid.y * %ntid.x  + %tid.z * %ntid.x * %ntid.y) / 32

9.7.15.2. 矩阵形状

矩阵乘加运算对操作数矩阵A、B和D的形状支持有限。所有三个矩阵操作数的形状由元组MxNxK共同描述,其中A是MxK矩阵,B是KxN矩阵,而D是MxN矩阵。

以下矩阵形状支持wgmma.mma_async操作指定的类型:

被乘数数据类型

稀疏度

形状

浮点数 - .f16

密集

.m64n8k16, .m64n16k16, .m64n24k16, .m64n32k16, .m64n40k16, .m64n48k16, .m64n56k16, .m64n64k16, .m64n72k16, .m64n80k16, .m64n88k16, .m64n96k16, .m64n104k16, .m64n112k16, .m64n120k16, .m64n128k16, .m64n136k16, .m64n144k16, .m64n152k16, .m64n160k16, .m64n168k16, .m64n176k16, .m64n184k16, .m64n192k16, .m64n200k16, .m64n208k16, .m64n216k16, .m64n224k16, .m64n232k16, .m64n240k16, .m64n248k16, .m64n256k16

替代浮点格式 - .bf16

替代浮点格式 - .tf32

稀疏

替代浮点格式 - .tf32

密集

.m64n8k8, .m64n16k8, .m64n24k8, .m64n32k8, .m64n40k8, .m64n48k8, .m64n56k8, .m64n64k8, .m64n72k8, .m64n80k8, .m64n88k8, .m64n96k8, .m64n104k8, .m64n112k8, .m64n120k8, .m64n128k8, .m64n136k8, .m64n144k8, .m64n152k8, .m64n160k8, .m64n168k8, .m64n176k8, .m64n184k8, .m64n192k8, .m64n200k8, .m64n208k8, .m64n216k8, .m64n224k8, .m64n232k8, .m64n240k8, .m64n248k8, .m64n256k8

替代浮点格式 - .e4m3/ .e5m2

密集

.m64n8k32, .m64n16k32, .m64n24k32, .m64n32k32, .m64n40k32, .m64n48k32, .m64n56k32, .m64n64k32, .m64n72k32, .m64n80k32, .m64n88k32, .m64n96k32, .m64n104k32, .m64n112k32, .m64n120k32, .m64n128k32, .m64n136k32, .m64n144k32, .m64n152k32, .m64n160k32, .m64n168k32, .m64n176k32, .m64n184k32, .m64n192k32, .m64n200k32, .m64n208k32, .m64n216k32, .m64n224k32, .m64n232k32, .m64n240k32, .m64n248k32, .m64n256k32

浮点数 - .f16

稀疏

替代浮点格式 - .bf16

整数 - .u8/.s8

密集

.m64n8k32, .m64n16k32, .m64n24k32, .m64n32k32, .m64n48k32, .m64n64k32, .m64n80k32, .m64n96k32, .m64n112k32, .m64n128k32, .m64n144k32, .m64n160k32, .m64n176k32, .m64n192k32, .m64n208k32, .m64n224k32, .m64n240k32, .m64n256k32

替代浮点格式 - .e4m3/ .e5m2

稀疏

.m64n8k64, .m64n16k64, .m64n24k64, .m64n32k64, .m64n40k64, .m64n48k64, .m64n56k64, .m64n64k64, .m64n72k64, .m64n80k64, .m64n88k64, .m64n96k64, .m64n104k64, .m64n112k64, .m64n120k64, .m64n128k64, .m64n136k64, .m64n144k64, .m64n152k64, .m64n160k64, .m64n168k64, .m64n176k64, .m64n184k64, .m64n192k64, .m64n200k64, .m64n208k64, .m64n216k64, .m64n224k64, .m64n232k64, .m64n240k64, .m64n248k64, .m64n256k64

整数 - .u8/.s8

稀疏

.m64n8k64, .m64n16k64, .m64n24k64, .m64n32k64, .m64n48k64, .m64n64k64, .m64n80k64, .m64n96k64, .m64n112k64, .m64n128k64, .m64n144k64, .m64n160k64, .m64n176k64, .m64n192k64, .m64n208k64, .m64n224k64, .m64n240k64, .m64n256k64

单比特 - .b1

密集

.m64n8k256, .m64n16k256, .m64n24k256, .m64n32k256, .m64n48k256, .m64n64k256, .m64n80k256, .m64n96k256, .m64n112k256, .m64n128k256, .m64n144k256, .m64n160k256, .m64n176k256, .m64n192k256, .m64n208k256, .m64n224k256, .m64n240k256, .m64n256k256

9.7.15.3. 矩阵数据类型

矩阵乘加运算分别支持整数、浮点数、亚字节整数和单比特数据类型。所有操作数必须包含相同的基本类型类别,即整数或浮点数。

对于浮点矩阵乘加运算,不同的矩阵操作数可能具有不同的精度,具体将在后文描述。

对于整数矩阵的乘积累加运算,两个被乘矩阵(A和B)必须具有相同数据类型的元素,例如均为有符号整数或均为无符号整数。

数据类型

乘数(A或B)

累加器(D)

整数

两者都是 .u8 或两者都是 .s8

.s32

浮点数

.f16

.f16,.f32

替代浮点数

.bf16

.f32

替代浮点数

.tf32

.f32

替代浮点数

.e4m3,.e5m2

.f16,.f32

单比特整数

.b1

.s32

9.7.15.4. Async Proxy

wgmma.mma_async操作在异步代理(或async proxy)中执行。

跨多个代理访问同一内存位置需要使用跨代理栅栏。对于异步代理,应使用fence.proxy.async来同步通用代理与异步代理之间的内存。

wgmma.mma_async操作完成后会隐式触发一个通用异步代理栅栏。因此异步操作的结果在其完成状态被观测到时,会立即对通用代理可见。必须使用wgmma.commit_groupwgmma.wait_group操作来等待wgmma.mma_async指令的完成。

9.7.15.5. 使用wgmma.mma_async指令实现异步Warpgroup级矩阵乘加运算

本节介绍warpgroup级别的wgmma.mma_async指令以及该指令中涉及的各种矩阵的组织方式。

9.7.15.5.1. 注册片段和共享内存矩阵布局

warpgroup宽MMA操作的输入矩阵A可以位于寄存器或共享内存中。warpgroup宽MMA操作的输入矩阵B必须位于共享内存中。本节描述了warpgroup MMA指令所期望的寄存器片段和共享内存的布局。

当矩阵位于共享内存中时,它们的起始地址必须对齐到16字节。

9.7.15.5.1.1. 注册片段

本节介绍位于wgmma.mma_async指令寄存器操作数中的各类矩阵的组织结构。

9.7.15.5.1.1.1.wgmma.mma_async.m64nNk16 的矩阵片段

一个执行wgmma.mma_async.m64nNk16的warpgroup将计算形状为.m64nNk16的MMA操作,其中N是矩阵形状中列出的有效n维度。

矩阵的元素被分布在一个warpgroup的线程中,因此warpgroup的每个线程都持有矩阵的一个片段。

  • 寄存器中的被乘数A:

    .atype

    片段

    元素(从低到高)

    .f16/.bf16

    包含四个.f16x2寄存器的向量表达式,每个寄存器包含来自矩阵A的两个.f16/.bf16元素。

    a0, a1, a2, a3, a4, a5, a6, a7

    不同线程持有的片段布局如图147所示。

    _images/wgmma-64N16-A.png

    图147 WGMMA .m64nNk16矩阵A的寄存器片段布局。

  • 累加器 D:

    .dtype

    片段

    元素(从低到高)

    .f16

    一个包含N/4个.f16x2寄存器的向量表达式,每个寄存器包含来自矩阵D的两个.f16元素。

    d0, d1, d2, d3, ..., dX, dY, dZ, dW

    其中 X = N/2  -  4

    Y = N/2  -  3

    Z = N/2  -  2

    W = N/2  -  1

    N = 8*i where i = {1, 2, ... , 32}

    .f32

    一个包含N/2个.f32寄存器的向量表达式。

    不同线程持有的片段布局如图148所示。

    _images/wgmma-64N16-D.png

    图148 WGMMA .m64nNk16累加器矩阵D的寄存器片段布局

9.7.15.5.1.1.2. wgmma.mma_async.m64nNk8 的矩阵片段

一个执行wgmma.mma_async.m64nNk8的warpgroup将计算形状为.m64nNk8的MMA操作,其中N是矩阵形状中列出的有效n维度。

矩阵的元素被分布在一个warpgroup的线程中,因此warpgroup的每个线程都持有矩阵的一个片段。

  • 寄存器中的被乘数A:

    .atype

    片段

    元素(从低到高)

    .tf32

    一个包含四个.b32寄存器的向量表达式,其中包含来自矩阵A的四个.tf32元素。

    a0, a1, a2, a3

    不同线程持有的片段布局如图149所示。

    _images/wgmma-64N8-A.png

    图149 WGMMA .m64nNk8矩阵A的寄存器片段布局。

  • 累加器 D:

    .dtype

    片段

    元素(从低到高)

    .f32

    包含N/2个.f32寄存器的向量表达式。

    d0, d1, d2, d3, ..., dX, dY, dZ, dW

    其中 X = N/2  -  4

    Y = N/2  -  3

    Z = N/2  -  2

    W = N/2  -  1

    N = 8*i where i = {1, 2, ... , 32}

    不同线程持有的片段布局如图150所示。

    _images/wgmma-64N8-D.png

    图150 WGMMA .m64nNk8寄存器片段布局(累加器矩阵D)

9.7.15.5.1.1.3. wgmma.mma_async.m64nNk32 的矩阵片段

一个执行wgmma.mma_async.m64nNk32的warpgroup将计算形状为.m64nNk32的MMA操作,其中N是Matrix Shape中列出的有效n维度。

矩阵的元素被分布在一个warpgroup的线程中,因此warpgroup的每个线程都持有矩阵的一个片段。

  • 寄存器中的被乘数A:

    .atype

    片段

    元素(从低到高)

    .s8/.u8

    包含四个.b32寄存器的向量表达式,每个寄存器包含来自矩阵A的四个.u8/.s8元素。

    a0, a1, a2, a3, … , a14, a15

    .e4m3/.e5m2

    包含四个.b32寄存器的向量表达式,每个寄存器包含来自矩阵A的四个.e4m3/.e5m2元素。

    不同线程持有的片段布局如图151所示。

    _images/wgmma-64N32-A.png

    图151 WGMMA .m64nNk32矩阵A的寄存器片段布局。

  • 累加器 D:

    .dtype

    片段

    元素(从低到高)

    其他信息

    .s32

    包含N/2个.s32寄存器的向量表达式

    d0, d1, d2, d3, ..., dX, dY, dZ, dW

    其中 X = N/2  -  4

    Y = N/2  -  3

    Z = N/2  -  2

    W = N/2  -  1

    N取决于.dtype,如右栏所述

    N = 8*i 其中 i = {1, 2, 3, 4}

    = 16*i 其中 i = {3, 4, ..., 15, 16}

    .f32

    包含N/2个.f32寄存器的向量表达式

    N = 8*i 其中 i = {1, 2, ... , 32}

    .f16

    包含N/4个.f16x2寄存器的向量表达式,每个寄存器包含来自矩阵D的两个.f16元素

    不同线程持有的片段布局如图152所示。

    _images/wgmma-64N32-D.png

    图152 WGMMA .m64nNk32累加器矩阵D的寄存器片段布局

9.7.15.5.1.1.4. wgmma.mma_async.m64nNk256 的矩阵片段

一个执行wgmma.mma_async.m64nNk256的warpgroup将计算形状为.m64nNk256的MMA操作,其中N是Matrix Shape中列出的有效n维度。

矩阵的元素被分布在一个warpgroup的线程中,因此warpgroup的每个线程都持有矩阵的一个片段。

  • 寄存器中的被乘数A:

    .atype

    片段

    元素(从低到高)

    .b1

    一个包含四个.b32寄存器的向量表达式,每个寄存器包含来自矩阵A的32个.b1元素。

    a0, a1, a2, …, a127

    不同线程持有的片段布局如图153所示。

    _images/wgmma-64N256-A.png

    图153 WGMMA .m64nNk256矩阵A的寄存器片段布局。

  • 累加器 D:

    .dtype

    片段

    元素(从低到高)

    .s32

    包含N/2个.s32寄存器的向量表达式。

    d0, d1, d2, d3, ..., dX, dY, dZ, dW

    其中 X = N/2  -  4

    Y = N/2  -  3

    Z = N/2  -  2

    W = N/2  -  1

    N = 8*i 其中 i = {1, 2, 3, 4}

    = 16*i 其中 i = {3, 4, ..., 15, 16}

    不同线程持有的片段布局如图154所示。

    _images/wgmma-64N256-D.png

    图154 WGMMA .m64nNk256累加器矩阵D的寄存器片段布局。

9.7.15.5.1.2. 共享内存矩阵布局

如果指令wgmma.mma_async{.sp}的参数imm-trans-a / imm-trans-b为0,则矩阵A / B分别使用K主序。如果参数imm-trans-a的值为1,则矩阵A使用M主序。如果参数imm-trans-b的值为1,则矩阵B使用N主序

在默认按列主序的BLAS库(如cuBLAS)中,矩阵AB无论是否转置,都可以归类为K主序M/N主序,如下表所示:

非转置

转置

A

K主序

M主序

B

K主序

N主序

为避免与AB行优先列优先转置非转置等术语混淆,本节将统一使用MN主序K主序的表述方式。

共享内存中的矩阵由一个或多个“交错布局原子”组成。这些交错原子的具体布局取决于交错模式、交错原子性以及前导维度。交错布局的具体形式如表37所示。

表 37 各种交换模式、前导维度和交换原子布局的组合

Swizzling 模式

主导维度/主序性

Swizzle 原子布局(128b元素)

128B 交换模式

M/N

8x8

K

8x8

64B 交换模式

M/N

4x8

K

8x4

32B 交换模式

M/N

2x8

K

8x2

M/N

1x8

K

8x1

上述形状适用于128位大小的元素。对于更小的元素尺寸,相同的形状会沿着前导维度乘以一个因子128/sizeof_bits(Element)。例如,128B MN主序swizzle原子对于tf32张量核心输入将具有(8*(128/32))x8 = 32x8的形状。

示例

以下是MxKKxN矩阵在不同swizzling模式下的示例布局,单位为128b元素,如图155图156图157图158图159图160图161图162中每个彩色单元格所示。

_images/async-warpgroup-smem-layout-128B-mn.png

图 155 MN主128B数据重排

_images/async-warpgroup-smem-layout-128B-k.png

图156 K major 128B 数据交错模式

_images/async-warpgroup-smem-layout-64B-mn.png

图157 MN主64B交换模式

_images/async-warpgroup-smem-layout-64B-k.png

图 158 K major 64B 数据重组

_images/async-warpgroup-smem-layout-32B-mn.png

图 159 MN主32B交换模式

_images/async-warpgroup-smem-layout-32B-k.png

图160 K主32B交织模式

_images/async-warpgroup-smem-layout-mn-interleaved.png

图161 MN主交错模式

_images/async-warpgroup-smem-layout-k-interleaved.png

图 162 K主交织

以下是tf32元素类型的128B swizzling布局的一些示例。

9.7.15.5.1.2.1. Strides支持的主要特性

从共享内存访问矩阵时涉及两个步幅:

  1. 前导维度字节偏移量

  2. 步幅维度字节偏移量

9.7.15.5.1.2.1.1. 前导维度字节偏移量

对于转置和非转置矩阵,前导维度字节偏移量的定义有所不同。对于元素类型归一化为128位的矩阵,前导字节偏移量定义如下:

重要性

定义

K-Major

  • 无交换(No-Swizzling): 在128位元素类型的归一化矩阵中,从8x2图块的第一列到第二列的偏移量。

  • 交换布局(Swizzled layouts): 未使用,假定为1。

MN-Major

  • 交错布局:从前8列到后8列的偏移量。

  • 混洗布局:从前(混洗字节大小/16)行到后(混洗字节大小/16)行的偏移量。

9.7.15.5.1.2.1.2. 步幅维度字节偏移量

对于转置和非转置矩阵,步长维度字节偏移量的定义方式不同。对于元素类型归一化为128位的矩阵,步长维度字节偏移量定义如下:

重要性

定义

K-Major

从首8行到次8行的偏移量。

MN-Major

  • 交错存储:从第一行到下一行的偏移量。

  • Swizzled布局:从前8列到后8列的偏移量

9.7.15.5.1.2.1.3. 规范布局

CuTe布局方面,标准布局可以表示如下:

主序性

混洗模式

未混洗的标准布局

Swizzling 应用于前一列

MN-主序

无交换或交错

((T,1,m),(8,k)):((1,T,SBO),(1T,LBO))

Swizzle<0, 4, 3>

32B 数据重排

((T,2,m),(8,k)):((1,T,LBO),(2T,SBO))

Swizzle<1, 4, 3>

64B 数据重排

((T,4,m),(8,k)):((1,T,LBO),(4T,SBO))

Swizzle<2, 4, 3>

128B 数据重排

((T,8,m),(8,k)):((1,T,LBO),(8T,SBO))

Swizzle<3, 4, 3>

K-主序

无交换或交错

((8,m),(T,2k)):((1T,SBO),(1,LBO))

Swizzle<0, 4, 3>

32B 数据重排

((8,m),(T,2k)):((2T,SBO),(1,T))

Swizzle<1, 4, 3>

64B 数据重排

((8,m),(T,2k)):((4T,SBO),(1,T))

Swizzle<2, 4, 3>

128B 数据重排

((8,m),(T,2k)):((8T,SBO),(1,T))

Swizzle<3, 4, 3>

其中

  • T = 128 / 元素大小(以位为单位) T代表比例因子,用于将矩阵元素类型归一化到128位。

  • m 表示行中重复模式的数量。

  • k表示列中重复模式的数量。

示例

  • K主序、无交换和tf32类型:图165

    _images/async-warpgroup-k-no-swizzle-tf32.png

    图165 K主序、无交换和tf32类型

    步长及相关细节如下:

    精确布局:Swizzle<0,4,3> o ((8,2),(4,4)):((4,32),(1,64))

    规范布局:Swizzle<0,4,3> o ((8,m),(T,2k)):((1T,SBO),(1,LBO))

    参数

    T

    4

    m

    2

    k

    2

    LBO

    32*sizeof(tf32)

    SBO

    64*sizeof(tf32)

    描述符中LBO的编码

    (LBO) >> 4 = 16

    描述符中SBO的编码

    (SBO) >> 4 = 8

  • K主序、32B交换和tf32类型:图166

    _images/async-warpgroup-k-32B-swizzle-tf32.png

    图166 K主序、32B交换和tf32类型

    步长及相关细节如下:

    精确布局:Swizzle<1,4,3> o ((8,2),(4,4)):((8,64),(1,4))

    规范布局:Swizzle<1,4,3> o ((8,m),(T,2k)):((2T,SBO),(1,T))

    参数

    T

    4

    m

    2

    k

    2

    LBO

    NA

    SBO

    64*sizeof(tf32)

    描述符中LBO的编码

    1 (假设)

    描述符中SBO的编码

    (SBO) >> 4 = 16

  • MN主序、无交换和bf16类型:图167

    _images/async-warpgroup-mn-no-swizzle-bf16.png

    图167 MN主序、无交换和bf16类型

    步长及相关细节如下:

    精确布局:Swizzle<0,4,3> o ((8,1,2),(8,2)):((1,8,64),(8,128))

    规范布局:Swizzle<0,4,3> o ((T,1,m),(8,k)):((1,T,SBO),(1T,LBO))

    参数

    T

    8

    m

    2

    k

    2

    LBO

    128*sizeof(bf16)

    SBO

    64*sizeof(bf16)

    描述符中LBO的编码

    (LBO) >> 4 = 16

    描述符中SBO的编码

    (SBO) >> 4 = 8

  • MN主序,32B交换和bf16类型:图168

    _images/async-warpgroup-mn-32B-swizzle-bf16.png

    图168 MN主序,32B交换和bf16类型

    步幅及相关细节如下:

    精确布局:Swizzle<1,4,3> o ((8,2,2),(8,2)):((1,8,128),(16,256))

    规范布局:Swizzle<1,4,3> o ((T,2,m),(8,k)):((1,T,LBO),(2T,SBO))

    参数

    T

    8

    m

    2

    k

    2

    LBO

    128*sizeof(bf16)

    SBO

    256*sizeof(bf16)

    描述符中LBO的编码

    (LBO) >> 4 = 16

    描述符中SBO的编码

    (SBO) >> 4 = 32

  • MN主序,64B交换和bf16类型:图169

    _images/async-warpgroup-mn-64B-swizzle-bf16.png

    图169 MN主序,64B交换和bf16类型

    步幅及相关细节如下:

    精确布局:Swizzle<2,4,3> o ((8,4,2),(8,2)):((1,8,256),(32,512))

    规范布局:Swizzle<2,4,3> o ((T,4,m),(8,k)):((1,T,LBO),(4T,SBO))

    参数

    T

    8

    m

    2

    k

    2

    LBO

    256*sizeof(bf16)

    SBO

    512*sizeof(bf16)

    描述符中LBO的编码

    (LBO) >> 4 = 32

    描述符中SBO的编码

    (SBO) >> 4 = 64

9.7.15.5.1.2.2.矩阵描述符格式

矩阵描述符指定了共享内存中矩阵的属性,该矩阵是矩阵乘加运算中的一个乘数。它是一个64位值,存储在寄存器中,布局如下:

位域

位大小

描述

13–0

14

矩阵描述符编码(矩阵起始地址)

29–16

14

matrix-descriptor-encode (前导维度字节偏移量)

45–32

14

matrix-descriptor-encode (步长维度字节偏移量)

51–49

3

矩阵基础偏移量。该参数适用于除无交换模式外的所有交换模式。

63–62

2

指定要使用的交换模式:

  • 0: 无交换

  • 1: 128字节交换

  • 2: 64字节交换

  • 3: 32字节交换

其中

matrix-descriptor-encode(x) = (x & 0x3FFFF) >> 4

根据下表,当指定交换模式的重复模式开始时,基础偏移量的值为0:

混洗模式

重复模式的起始地址

128字节交换

1024字节边界

64字节交换

512字节边界

32字节交换

256字节边界

否则,基准偏移量必须是一个非零值,使用以下公式计算:

base offset = (pattern start addr >> 0x7) & 0x7
9.7.15.5.2. 异步乘加指令: wgmma.mma_async

wgmma.mma_async

在warpgroup范围内执行矩阵乘加操作

语法

半精度浮点类型:

wgmma.mma_async.sync.aligned.shape.dtype.f16.f16  d, a-desc, b-desc, scale-d, imm-scale-a, imm-scale-b, imm-trans-a, imm-trans-b;

wgmma.mma_async.sync.aligned.shape.dtype.f16.f16  d, a, b-desc, scale-d, imm-scale-a, imm-scale-b, imm-trans-b;

.shape   = {.m64n8k16, .m64n16k16, .m64n24k16, .m64n32k16,
            .m64n40k16, .m64n48k16, .m64n56k16, .m64n64k16,
            .m64n72k16, .m64n80k16, .m64n88k16, .m64n96k16,
            .m64n104k16, .m64n112k16, .m64n120k16, .m64n128k16,
            .m64n136k16, .m64n144k16, .m64n152k16, .m64n160k16,
            .m64n168k16, .m648176k16, .m64n184k16, .m64n192k16,
            .m64n200k16, .m64n208k16, .m64n216k16, .m64n224k16,
            .m64n232k16, .m64n240k16, .m64n248k16, .m64n256k16};
.dtype   = {.f16, .f32};

替代浮点类型:

.bf16 floating point type:

wgmma.mma_async.sync.aligned.shape.dtype.bf16.bf16  d, a-desc, b-desc, scale-d, imm-scale-a, imm-scale-b, imm-trans-a, imm-trans-b;

wgmma.mma_async.sync.aligned.shape.dtype.bf16.bf16  d, a, b-desc, scale-d, imm-scale-a, imm-scale-b, imm-trans-b;

.shape   = {.m64n8k16, .m64n16k16, .m64n24k16, .m64n32k16,
            .m64n40k16, .m64n48k16, .m64n56k16, .m64n64k16,
            .m64n72k16, .m64n80k16, .m64n88k16, .m64n96k16,
            .m64n104k16, .m64n112k16, .m64n120k16, .m64n128k16,
            .m64n136k16, .m64n144k16, .m64n152k16, .m64n160k16,
            .m64n168k16, .m648176k16, .m64n184k16, .m64n192k16,
            .m64n200k16, .m64n208k16, .m64n216k16, .m64n224k16,
            .m64n232k16, .m64n240k16, .m64n248k16, .m64n256k16};
.dtype  = {.f32};

.tf32 floating point type:

wgmma.mma_async.sync.aligned.shape.dtype.tf32.tf32  d, a-desc, b-desc, scale-d, imm-scale-a, imm-scale-b;

wgmma.mma_async.sync.aligned.shape.dtype.tf32.tf32  d, a, b-desc, scale-d, imm-scale-a, imm-scale-b;

.shape   = {.m64n8k8, .m64n16k8, .m64n24k8, .m64n32k8,
            .m64n40k8, .m64n48k8, .m64n56k8, .m64n64k8,
            .m64n72k8, .m64n80k8, .m64n88k8, .m64n96k8,
            .m64n104k8, .m64n112k8, .m64n120k8, .m64n128k8,
            .m64n136k8, .m64n144k8, .m64n152k8, .m64n160k8,
            .m64n168k8, .m648176k8, .m64n184k8, .m64n192k8,
            .m64n200k8, .m64n208k8, .m64n216k8, .m64n224k8,
            .m64n232k8, .m64n240k8, .m64n248k8, .m64n256k8};
.dtype  = {.f32};

FP8 floating point type

wgmma.mma_async.sync.aligned.shape.dtype.atype.btype  d, a-desc, b-desc, scale-d, imm-scale-a, imm-scale-b;

wgmma.mma_async.sync.aligned.shape.dtype.atype.btype  d, a, b-desc, scale-d, imm-scale-a, imm-scale-b;

.shape   = {.m64n8k32, .m64n16k32, .m64n24k32, .m64n32k32,
            .m64n40k32, .m64n48k32, .m64n56k32, .m64n64k32,
            .m64n72k32, .m64n80k32, .m64n88k32, .m64n96k32,
            .m64n104k32, .m64n112k32, .m64n120k32, .m64n128k32,
            .m64n136k32, .m64n144k32, .m64n152k32, .m64n160k32,
            .m64n168k32, .m648176k32, .m64n184k32, .m64n192k32,
            .m64n200k32, .m64n208k32, .m64n216k32, .m64n224k32,
            .m64n232k32, .m64n240k32, .m64n248k32, .m64n256k32};
.atype  = {.e4m3, .e5m2};
.btype  = {.e4m3, .e5m2};
.dtype  = {.f16, .f32};

整数类型:

wgmma.mma_async.sync.aligned.shape{.satfinite}.s32.atype.btype  d, a-desc, b-desc, scale-d;

wgmma.mma_async.sync.aligned.shape{.satfinite}.s32.atype.btype  d, a, b-desc, scale-d;

.shape   = {.m64n8k32, .m64n16k32, .m64n24k32, .m64n32k32,
            .m64n48k32, .m64n64k32, .m64n80k32, .m64n96k32,
            .m64n112k32, .m64n128k32, .m64n144k32, .m64n160k32,
            .m648176k32, .m64n192k32, .m64n208k32, .m64n224k32};
.atype  = {.s8, .u8};
.btype  = {.s8, .u8};

单比特:

wgmma.mma_async.sync.aligned.shape.s32.b1.b1.op.popc  d, a-desc, b-desc, scale-d;

wgmma.mma_async.sync.aligned.shape.s32.b1.b1.op.popc  d, a, b-desc, scale-d;

.shape   = {.m64n8k256, .m64n16k256, .m64n24k256, .m64n32k256,
            .m64n48k256, .m64n64k256, .m64n80k256, .m64n96k256,
            .m64n112k256, .m64n128k256, .m64n144k256, .m64n160k256,
            .m64n176k256, .m64n192k256, .m64n208k256, .m64n224k256,
            .m64n240k256, .m64n256k256};
.op  = {.and};

描述

指令 wgmma.mma_async 执行一个 MxNxK 矩阵乘加运算 D = A*B+D,其中矩阵A为 MxK,矩阵B为 KxN,矩阵D为 MxN

当输入谓词参数scale-d为false时,会执行形式为D = A*B的运算。

wgmma.fence 指令必须用于隔离 wgmma.mma_async 指令的寄存器访问与其之前的访问。否则,行为将是未定义的。

wgmma.commit_groupwgmma.wait_group 操作必须用于在访问结果之前等待异步矩阵乘加运算完成。

寄存器操作数 d 表示累加器矩阵以及目标矩阵,分布在参与线程中。寄存器操作数 a 表示乘数矩阵A,以寄存器形式分布在参与线程中。64位寄存器操作数 a-descb-desc 是矩阵描述符,分别表示共享内存中的乘数矩阵A和B。矩阵描述符的内容必须在warpgroup中的所有warp中保持一致。矩阵描述符的格式详见 Matrix Descriptor Format

矩阵A和B分别以行优先和列优先格式存储。对于某些浮点变体,可以通过为立即整型参数imm-trans-aimm-trans-b分别指定值1来转置输入矩阵A和B。值0可用于避免转置操作。imm-trans-aimm-trans-b的有效值为0和1。转置操作仅支持使用矩阵描述符从共享内存访问的、带有.f16/.bf16类型的wgmma.mma_async变体。

对于wgmma.mma_async操作的浮点变体,可以通过为操作数imm-scale-aimm-scale-b分别指定值-1来对输入矩阵A和B的每个元素取反。使用值1可以避免取反操作。imm-scale-aimm-scale-b的有效值为-1和1。

限定符 .dtype.atype.btype 分别表示矩阵 D、A 和 B 中元素的数据类型。对于所有浮点类型的 wgmma.mma_async 变体(FP8 浮点变体除外),.atype.btype 必须相同。在 wgmma.mma_async 操作的替代浮点变体中,矩阵 A 和 B 的单个数据元素大小如下:

  • .atype/.btype.e4m3/.e5m2时,矩阵A和B具有8位数据元素。

  • .atype/.btype.bf16时,矩阵A和B具有16位数据元素。

  • .atype/.btype.tf32时,矩阵A和B具有32位数据元素。

精度与舍入:

  • 浮点运算:

    矩阵A和B的逐元素乘法运算至少使用单精度执行。当 .dtype.f32时,中间值的累加至少使用单 精度执行。当.dtype.f16时,累加至少使用半 精度执行。

    未指定累加顺序、舍入和对次正规输入的处理方式。

  • .bf16.tf32 浮点运算:

    矩阵A和B的逐元素乘法以指定精度执行。涉及.tf32类型的wgmma.mma_async操作会在乘法运算前截断32位输入数据的低13位。中间值的累加以至少单精度执行。

    累加顺序、舍入方式以及对次正规输入的处理方式未作明确规定。

  • 整数运算:

    整数运算wgmma.mma_async使用.s32累加器执行。 .satfinite限定符表示在溢出时,累加值将被限制在 MIN_INT32..MAX_INT32范围内(其中边界分别定义为最小负值有符号 32位整数和最大正值有符号32位整数)。

    如果未指定.satfinite,则累加值将采用回绕方式处理。

强制性的.sync限定符表示wgmma.mma_async指令会使执行线程等待,直到warp中的所有线程都执行相同的wgmma.mma_async指令后才会继续执行。

强制性的.aligned限定符表示warpgroup中的所有线程必须执行相同的wgmma.mma_async指令。在条件执行的代码中,只有当确认warpgroup中的所有线程对条件的评估结果完全一致时,才应使用wgmma.mma_async指令,否则行为将是未定义的。

PTX ISA 说明

在PTX ISA版本8.0中引入。

支持在PTX ISA 8.4版本中引入的.u8.s8.s8.u8作为.atype.btype格式。

目标ISA注意事项

需要 sm_90a

半精度浮点类型示例

.reg .f16x2 f16a<40>, f16d<40>;
.reg .f32   f32d<40>;
.reg .b64   descA, descB;
.reg .pred  scaleD;
wgmma.mma_async.sync.aligned.m64n8k16.f32.f16.f16
  {f32d0, f32d1, f32d2, f32d3},
  {f16a0, f16a1, f16a2, f16a3},
  descB,
  1, -1, -1, 1;

wgmma.mma_async.sync.aligned.m64n72k16.f16.f16.f16
  {f16d0, f16d1,  f16d2,  f16d3,  f16d4,  f16d5,  f16d6,  f16d7,  f16d8,
   f16d9, f16d10, f16d11, f16d12, f16d13, f16d14, f16d15, f16d16, f16d17},
  descA,
  descB,
  scaleD, -1, 1, 1, 0;

替代浮点类型的示例

.reg .f32   f32d<40>;
.reg .b32   bf16a<40>
.reg .b64   descA, descB;

wgmma.mma_async.sync.aligned.m64n120k16.f32.bf16.bf16
  {f32d0, f32d1, f32d2, f32d3, f32d4, f32d5, f32d6, f32d7, f32d8, f32d9,
   f32d10, f32d11, f32d12, f32d13, f32d14, f32d15, f32d16, f32d17, f32d18, f32d19,
   f32d20, f32d21, f32d22, f32d23, f32d24, f32d25, f32d26, f32d27, f32d28, f32d29,
   f32d30, f32d31, f32d32, f32d33, f32d34, f32d35, f32d36, f32d37, f32d38, f32d39,
   f32d40, f32d41, f32d42, f32d43, f32d44, f32d45, f32d46, f32d47, f32d48, f32d49,
   f32d50, f32d51, f32d52, f32d53, f32d54, f32d55, f32d56, f32d57, f32d58, f32d59},
  {bf16a0, bf16a1, bf16a2, bf16a3},
  descB,
  scaleD, -1, -1, 0;

.reg .f32   f32d<40>;
.reg .b64   descA, descB;

wgmma.mma_async.sync.aligned.m64n16k8.f32.tf32.tf32
  {f32d0, f32d1, f32d2, f32d3, f32d4, f32d5, f32d6, f32d7},
  descA,
  descB,
  0, -1, -1;

.reg .b32 f16d<8>, f16a<8>;
.reg .f32 f32d<8>;
.reg .b64   descA, descB;

wgmma.mma_async.sync.aligned.m64n8k32.f16.e4m3.e5m2
  {f16d0, f16d1},
  descA,
  descB,
  scaleD, -1, 1;

wgmma.mma_async.sync.aligned.m64n8k32.f32.e5m2.e4m3
  {f32d0, f32d1, f32d2, f32d3},
  {f16a0, f16a1, f16a2, f16a3},
  descB,
  1, -1, -1;

整数类型示例

.reg .s32 s32d<8>, s32a<8>;
.reg .u32 u32a<8>;
.reg .pred scaleD;
.reg .b64   descA, descB;

wgmma.mma_async.sync.aligned.m64n8k32.s32.s8.s8.satfinite
  {s32d0, s32d1, s32d2, s32d3},
  {s32a0, s32a1, s32a2, s32a3},
  descB,
  1;

wgmma.mma_async.sync.aligned.m64n8k32.s32.u8.u8
  {s32d0, s32d1, s32d2, s32d3},
  descA,
  descB,
  scaleD;

wgmma.mma_async.sync.aligned.m64n8k32.s32.s8.u8.satfinite
  {s32d0, s32d1, s32d2, s32d3},
  {s32a0, s32a1, s32a2, s32a3},
  descB,
  scaleD;

wgmma.mma_async.sync.aligned.m64n8k32.s32.u8.s8
  {s32d0, s32d1, s32d2, s32d3},
  descA,
  descB,
  scaleD;

单比特类型的示例

.reg .s32 s32d<4>;
.reg .b32 b32a<4>;
.reg .pred scaleD;
.reg .b64   descA, descB;


wgmma.mma_async.sync.aligned.m64n8k256.s32.b1.b1.and.popc
  {s32d0, s32d1, s32d2, s32d3},
  {b32a0, b32a1, b32a2, b32a3},
  descB,
  scaleD;

9.7.15.6. 使用wgmma.mma_async.sp指令实现异步Warpgroup级乘加操作

本节介绍针对稀疏矩阵A的warp级wgmma.mma_async.sp指令。当矩阵A是每行具有50%零值且按特定形状粒度分布的结构化稀疏矩阵时,可使用此变体的wgmma.mma_async运算。对于MxNxK稀疏wgmma.mma_async.sp运算,MxK矩阵A会被压缩为MxK/2个元素。矩阵A的每个K宽度行中,50%元素为零值,其余K/2个非零元素被压缩在表示矩阵A的操作数中。这些K/2元素到对应K宽度行的映射关系通过元数据显式提供。

9.7.15.6.1. 稀疏矩阵存储

稀疏矩阵A的粒度定义为矩阵行中子块的非零元素数量与该子块总元素数量的比值,其中子块大小取决于具体形状。例如,在用于浮点wgmma.mma_async操作的64x32矩阵A中,稀疏度预期为2:4粒度,即矩阵行中每个4元素向量(即4个连续元素组成的子块)包含2个零值。每个子块中非零元素的索引存储在元数据操作数中。值0b00000b01010b10100b1111是元数据的无效值,将导致未定义行为。在四个连续线程组成的组中,根据矩阵形状,一个或多个线程存储整个组的元数据。这些线程通过额外的稀疏选择器操作数指定。

矩阵A及其对应的稀疏wgMMA输入操作数类似于图110所示的示意图,具有适当的矩阵尺寸。

不同矩阵形状和数据类型的粒度描述如下。

稀疏 wgmma.mma_async.sp 支持半精度和 .bf16 类型

对于.f16.bf16类型,在所有支持的64xNx32形状中,矩阵A采用2:4粒度的结构化稀疏存储。也就是说,矩阵A每行中每四个相邻元素的块包含两个零元素和两个非零元素。只有这两个非零元素会被存储在矩阵A中,而它们在四元素块中的位置由元数据操作数中的两个2位索引来指示。

_images/f16-metadata-example.png

图170 针对.f16/.bf16类型的稀疏WGMMA元数据示例。

稀疏选择器指示在四个连续线程组中贡献稀疏元数据的线程对。因此,稀疏选择器必须为0(线程T0、T1)或1(线程T2、T3);任何其他值都会导致未定义行为。

稀疏 wgmma.mma_async.sp 支持 .tf32 类型

对于.tf32类型,在所有支持的64xNx16形状中,矩阵A以1:2的粒度呈现结构化稀疏性。换句话说,矩阵A每行中相邻的两个元素块包含一个零元素和一个非零元素。只有非零元素会被存储在矩阵A的操作数中,元数据中的4位索引指示了非零元素在双元素块中的位置。0b1110和0b0100是该索引仅有的有效值,其余值将导致未定义行为。

_images/tf32-metadata-example.png

图171 针对.tf32类型的稀疏WGMMA元数据示例。

稀疏选择器指示在四个连续线程组中贡献稀疏元数据的线程对。因此,稀疏选择器必须为0(线程T0、T1)或1(线程T2、T3);任何其他值都会导致未定义行为。

稀疏 wgmma.mma_async.sp 支持 .e4m3.e5m2 浮点类型

对于.e4m3.e5m2类型,在所有支持的64xNx64形状中,矩阵A以2:4的粒度进行结构化稀疏。换句话说,矩阵A每行中四个相邻元素的每个块包含两个零元素和两个非零元素。只有这两个非零元素会被存储在矩阵A中,并且它们在矩阵A四元素块中的位置由元数据操作数中的两个2位索引指示。

_images/u8s8-metadata-example.png

图172 针对.e4m3/.e5m2类型的稀疏WGMMA元数据示例。

所有线程都贡献稀疏元数据,稀疏选择器必须为0;任何其他值将导致未定义行为。

稀疏 wgmma.mma_async.sp 整数类型

对于整数类型,在所有支持的64xNx64形状中,矩阵A以2:4的粒度进行结构化稀疏。换句话说,矩阵A每行中四个相邻元素的块包含两个零元素和两个非零元素。只有这两个非零元素会被存储在矩阵A中,而元数据中的两个2位索引指示了这两个非零元素在四元素块中的位置。

_images/u8s8-metadata-example.png

图173 针对.u8/.s8类型的稀疏WGMMA元数据示例。

所有线程都贡献稀疏元数据,稀疏选择器必须为0;任何其他值都会导致未定义行为。

9.7.15.6.2. 针对稀疏矩阵A的warpgroup级乘加运算的矩阵片段

本节我们将介绍线程寄存器的内容如何与矩阵A的片段及稀疏元数据相关联。

warp组中的每个warp为矩阵A的16行提供稀疏性信息。下表显示了warp到矩阵A行的分配情况:

Warp

矩阵A各行的稀疏度信息

%warpid % 4 = 3

48-63

%warpid % 4 = 2

32-47

%warpid % 4 = 1

16-31

%warpid % 4 = 0

0-15

本节通篇使用以下约定:

  • 对于矩阵A,仅描述了片段的布局方式,涉及寄存器向量大小及其与矩阵数据的关联。

  • 对于矩阵D,由于所有支持形状的矩阵维度-数据类型组合都相同,且已在使用wgmma指令的矩阵乘加运算中涵盖,因此本节未包含矩阵片段的图示表示。

  • 对于元数据操作数,包含了矩阵A元素索引与元数据操作数内容之间关联的图示表示。Tk: [m..n]出现在单元格[x][y..z]中,表示线程%laneid=k的元数据操作数中从高位mn的位包含了矩阵A块[x][y]..[x][z]中非零元素的索引。

9.7.15.6.2.1. 稀疏wgmma.mma_async.m64nNk32的矩阵分块

一个执行稀疏wgmma.mma_async.m64nNk32的warpgroup将计算形状为.m64nNk32的MMA操作,其中N是Matrix shape中列出的有效n维度。

矩阵的元素被分布在一个warpgroup的线程中,因此warpgroup的每个线程都持有矩阵的一个片段。

  • 乘数A,来自共享内存的文档记录在Shared Memory Layout for wgmma.mma_async.m64nNk32中。

  • 乘数A,来自寄存器:

    .atype

    片段

    元素

    .f16 /
    .bf16
    一个包含四个.b32的向量表达式
    每个寄存器包含两个
    非零的.f16/.bf16元素,来自矩阵A的4个
    连续元素
    非零元素:
    a0, a1, a2, a3, a4, a5, a6, a7
    非零元素的映射方式

    不同线程持有的片段布局如图174所示。

    _images/sparse-wgmma-64N32-f16-bf16-A.png

    图174 稀疏WGMMA .m64nNk32矩阵A的片段布局(.f16/.bf16类型)

  • 累加器 D:

    累加器 D 的矩阵片段与 Matrix Fragments for wgmma.m64nNk32 with floating point type 中相同 .dtype 格式的情况一致。

  • 乘数 B:

    矩阵 B 的共享内存布局文档详见 Shared Memory Layout for wgmma.mma_async.m64nNk32

  • 元数据操作数是一个.b32寄存器,其中包含16个2位向量,每个向量存储矩阵A的4宽块中非零元素的索引。

    图175展示了一个warp中元数据位与矩阵A元素的映射关系。图中变量i表示稀疏选择器操作数的值。

    _images/sparse-mma-metadata-16832-f16bf16.png

    图175 针对.f16/.bf16类型的稀疏WGMMA .m64nNk32元数据布局。

9.7.15.6.2.2. 稀疏wgmma.mma_async.m64nNk16的矩阵分片

一个执行稀疏wgmma.mma_async.m64nNk16的warpgroup将计算形状为.m64nNk16的MMA操作,其中N是Matrix shape中列出的有效n维度。

矩阵的元素被分布在一个warpgroup的线程中,因此warpgroup的每个线程都持有矩阵的一个片段。

  • 乘数A,在共享内存中的布局记录在Shared Memory Layout for wgmma.mma_async.m64nNk16中。

  • 乘数A,来自寄存器:

    .atype

    片段

    元素

    .tf32
    一个包含四个.b32的向量表达式
    寄存器,包含来自矩阵A的八个连续元素中的
    四个非零.tf32元素
    非零元素:
    a0, a1, a2, a3

    非零元素的映射方式如

    不同线程持有的片段布局如图176所示。

    _images/sparse-wgmma-64N16-tf32-A.png

    图176 针对.tf32类型矩阵A的稀疏WGMMA .m64nNk16片段布局。

  • 累加器 D:

    累加器 D 的矩阵片段与 Matrix Fragments for wgmma.m64nNk8 with floating point type 中相同 .dtype 格式的情况一致。

  • 乘数 B:

    矩阵 B 的共享内存布局文档详见 Shared Memory Layout for wgmma.mma_async.m64nNk16

  • 元数据操作数是一个.b32寄存器,其中包含八个4位向量,每个向量存储矩阵A的2宽块中非零元素的索引。

    图177展示了一个warp中元数据位与矩阵A元素的映射关系。图中变量i表示稀疏选择器操作数的值。

    _images/sparse-mma-metadata-16816-tf32.png

    图177 针对.tf32类型的稀疏WGMMA .m64nNk16元数据布局。

9.7.15.6.2.3. 稀疏wgmma.mma_async.m64nNk64的矩阵片段

一个执行稀疏wgmma.mma_async.m64nNk64的warpgroup将计算形状为.m64nNk64的MMA操作,其中N是Matrix shape中列出的有效n维度。

矩阵的元素被分布在一个warpgroup的线程中,因此warpgroup的每个线程都持有矩阵的一个片段。

  • 乘数A,来自共享内存的文档位于Shared Memory Layout for wgmma.mma_async.m64nNk64

  • 乘数A,来自寄存器:

    .atype

    片段

    元素

    .e4m3 /
    .e5m2
    一个包含四个.b32的向量表达式
    寄存器,每个寄存器包含四个
    非零.e4m3 /.e5m2元素,来自
    矩阵A的八个连续元素

    非零元素:
    a0, a1, a2, … , a15

    非零元素的映射方式
    稀疏矩阵存储中所述
    .s8 /
    .u8
    一个包含四个.b32的向量表达式
    寄存器,每个寄存器包含四个
    非零.s8 /.u8元素,来自
    矩阵A的八个连续元素

    不同线程持有的片段布局如图178所示。

    _images/sparse-wgmma-64N64-e4m3-e5m2-s8-u8-A.png

    图178 稀疏WGMMA .m64nNk64矩阵A的片段布局(.e4m3/ .e5m2/ .s8/ .u8类型)

  • 累加器 D:

    累加器 D 的矩阵片段与 Matrix Fragments for wgmma.m64nNk32 with floating point type 中相同 .dtype 格式的情况一致。

  • 乘数 B:

    矩阵 B 的共享内存布局文档位于 Shared Memory Layout for wgmma.mma_async.m64nNk64

  • 元数据操作数是一个.b32寄存器,包含16个4位向量,每个向量存储矩阵A的4宽块中两个非零元素的索引。

    图179展示了元数据位与矩阵A第0-31列元素的映射关系。

    _images/sparse-mma-metadata-16864-u8s8-last32col.png

    图179 稀疏WGMMA .m64nNk64元数据布局,适用于第0-31列的.e4m3/.e5m2/.s8/.u8类型

    图180展示了元数据位与矩阵A第32-63列元素的映射关系。

    _images/sparse-mma-metadata-16864-u8s8-last32col.png

    图180 稀疏WGMMA .m64nNk64元数据布局,适用于第32-63列的.e4m3/.e5m2/.s8/.u8类型

9.7.15.6.3. 异步乘加指令: wgmma.mma_async.sp

wgmma.mma_async.sp

在warpgroup范围内执行稀疏矩阵A的矩阵乘加操作

语法

半精度浮点类型:

wgmma.mma_async.sp.sync.aligned.shape.dtype.f16.f16  d, a-desc, b-desc, sp-meta, sp-sel, scale-d, imm-scale-a, imm-scale-b, imm-trans-a, imm-trans-b;

wgmma.mma_async.sp.sync.aligned.shape.dtype.f16.f16  d, a, b-desc, sp-meta, sp-sel, scale-d, imm-scale-a, imm-scale-b, imm-trans-b;

.shape   = {.m64n8k32, .m64n16k32, .m64n24k32, .m64n32k32,
            .m64n40k32, .m64n48k32, .m64n56k32, .m64n64k32,
            .m64n72k32, .m64n80k32, .m64n88k32, .m64n96k32,
            .m64n104k32, .m64n112k32, .m64n120k32, .m64n128k32,
            .m64n136k32, .m64n144k32, .m64n152k32, .m64n160k32,
            .m64n168k32, .m648176k32, .m64n184k32, .m64n192k32,
            .m64n200k32, .m64n208k32, .m64n216k32, .m64n224k32,
            .m64n232k32, .m64n240k32, .m64n248k32, .m64n256k32};
.dtype   = {.f16, .f32};

替代浮点类型:

.bf16 floating point type:

wgmma.mma_async.sp.sync.aligned.shape.dtype.bf16.bf16  d, a-desc, b-desc, sp-meta, sp-sel, scale-d, imm-scale-a, imm-scale-b, imm-trans-a, imm-trans-b;

wgmma.mma_async.sp.sync.aligned.shape.dtype.bf16.bf16  d, a, b-desc, sp-meta, sp-sel, scale-d, imm-scale-a, imm-scale-b, imm-trans-b;

.shape   = {.m64n8k32, .m64n16k32, .m64n24k32, .m64n32k32,
            .m64n40k32, .m64n48k32, .m64n56k32, .m64n64k32,
            .m64n72k32, .m64n80k32, .m64n88k32, .m64n96k32,
            .m64n104k32, .m64n112k32, .m64n120k32, .m64n128k32,
            .m64n136k32, .m64n144k32, .m64n152k32, .m64n160k32,
            .m64n168k32, .m648176k32, .m64n184k32, .m64n192k32,
            .m64n200k32, .m64n208k32, .m64n216k32, .m64n224k32,
            .m64n232k32, .m64n240k32, .m64n248k32, .m64n256k32};
.dtype  = {.f32};

.tf32 floating point type:

wgmma.mma_async.sp.sync.aligned.shape.dtype.tf32.tf32  d, a-desc, b-desc, sp-meta, sp-sel, scale-d, imm-scale-a, imm-scale-b;

wgmma.mma_async.sp.sync.aligned.shape.dtype.tf32.tf32  d, a, b-desc, sp-meta, sp-sel, scale-d, imm-scale-a, imm-scale-b;

.shape   = {.m64n8k16, .m64n16k16, .m64n24k16, .m64n32k16,
            .m64n40k16, .m64n48k16, .m64n56k16, .m64n64k16,
            .m64n72k16, .m64n80k16, .m64n88k16, .m64n96k16,
            .m64n104k16, .m64n112k16, .m64n120k16, .m64n128k16,
            .m64n136k16, .m64n144k16, .m64n152k16, .m64n160k16,
            .m64n168k16, .m648176k16, .m64n184k16, .m64n192k16,
            .m64n200k16, .m64n208k16, .m64n216k16, .m64n224k16,
            .m64n232k16, .m64n240k16, .m64n248k16, .m64n256k16};
.dtype  = {.f32};

FP8 floating point type

wgmma.mma_async.sp.sync.aligned.shape.dtype.atype.btype  d, a-desc, b-desc, sp-meta, sp-sel, scale-d, imm-scale-a, imm-scale-b;

wgmma.mma_async.sp.sync.aligned.shape.dtype.atype.btype  d, a, b-desc, sp-meta, sp-sel, scale-d, imm-scale-a, imm-scale-b;

.shape   = {.m64n8k64, .m64n16k64, .m64n24k64, .m64n32k64,
            .m64n40k64, .m64n48k64, .m64n56k64, .m64n64k64,
            .m64n72k64, .m64n80k64, .m64n88k64, .m64n96k64,
            .m64n104k64, .m64n112k64, .m64n120k64, .m64n128k64,
            .m64n136k64, .m64n144k64, .m64n152k64, .m64n160k64,
            .m64n168k64, .m648176k64, .m64n184k64, .m64n192k64,
            .m64n200k64, .m64n208k64, .m64n216k64, .m64n224k64,
            .m64n232k64, .m64n240k64, .m64n248k64, .m64n256k64};
.atype  = {.e4m3, .e5m2};
.btype  = {.e4m3, .e5m2};
.dtype  = {.f16, .f32};

整数类型:

wgmma.mma_async.sp.sync.aligned.shape{.satfinite}.s32.atype.btype  d, a-desc, b-desc, sp-meta, sp-sel, scale-d;

wgmma.mma_async.sp.sync.aligned.shape{.satfinite}.s32.atype.btype  d, a, b-desc, sp-meta, sp-sel, scale-d;

.shape   = {.m64n8k64, .m64n16k64, .m64n24k64, .m64n32k64,
            .m64n48k64, .m64n64k64, .m64n80k64, .m64n96k64,
            .m64n112k64, .m64n128k64, .m64n144k64, .m64n160k64,
            .m648176k64, .m64n192k64, .m64n208k64, .m64n224k64,
            .m64n240k64, .m64n256k64};
.atype  = {.s8, .u8};
.btype  = {.s8, .u8};

描述

指令 wgmma.mma_async 执行一个 MxNxK 矩阵乘加运算 D = A*B+D,其中矩阵A为 MxK,矩阵B为 KxN,矩阵D为 MxN

矩阵A以Mx(K/2)的压缩格式存储,具体描述请参见使用wgmma.mma_async.sp指令对稀疏矩阵A执行矩阵乘积累加操作

当输入谓词参数scale-d为false时,会执行形式为D = A*B的运算。

wgmma.fence 指令必须用于隔离 wgmma.mma_async 指令的寄存器访问与其之前的访问。否则,行为将是未定义的。

wgmma.commit_groupwgmma.wait_group 操作必须用于在访问结果之前等待异步矩阵乘加运算完成。

寄存器操作数 d 表示累加器矩阵以及目标矩阵,分布在参与线程中。寄存器操作数 a 表示乘数矩阵A,以寄存器形式分布在参与线程中。64位寄存器操作数 a-descb-desc 是矩阵描述符,分别表示共享内存中的乘数矩阵A和B。矩阵描述符的内容必须在warpgroup中的所有warp中保持一致。矩阵描述符的格式详见Matrix Descriptor Format。矩阵A采用结构化稀疏存储,如Sparse matrix storage所述。操作数 sp-metasp-sel 分别表示稀疏元数据和稀疏选择器。操作数 sp-meta 是32位整数,操作数 sp-sel 是取值范围为0..3的32位整数常量。

每个形状对应的sp-metasp-sel有效值在使用wgmma.mma_async.sp指令对稀疏矩阵A执行矩阵乘累加操作中有详细说明,并在此处进行总结:

矩阵形状

.atype

sp-meta的有效值

sp-sel的有效值

.m64nNk16

.tf32

0b1110 , 0b0100

0(线程 T0、T1)或 1(线程 T2、T3)

.m64nNk32

.f16/ .bf16

0b00, 0b01, 0b10, 0b11

0 (线程 T0, T1) 或 1 (线程 T2, T3)

.m64nNk64

.e4m3 / .e5m2 / .s8 / .u8

0b00, 0b01, 0b10, 0b11

0 (所有线程都参与贡献)

矩阵A和B分别以行优先和列优先格式存储。对于某些浮点变体,可以通过为立即整型参数imm-trans-aimm-trans-b分别指定值1来转置输入矩阵A和B。值0可用于避免转置操作。imm-trans-aimm-trans-b的有效值为0和1。转置操作仅支持使用矩阵描述符从共享内存访问的、带有.f16/.bf16类型的wgmma.mma_async变体。

对于wgmma.mma_async操作的浮点变体,可以通过为操作数imm-scale-aimm-scale-b分别指定值-1来对输入矩阵A和B的每个元素取反。使用值1可以避免取反操作。imm-scale-aimm-scale-b的有效值为-1和1。

限定符 .dtype.atype.btype 分别表示矩阵 D、A 和 B 中元素的数据类型。对于所有浮点类型的 wgmma.mma_async 变体(FP8 浮点变体除外),.atype.btype 必须相同。在 wgmma.mma_async 操作的替代浮点变体中,矩阵 A 和 B 的单个数据元素大小如下:

  • .atype/.btype.e4m3/.e5m2时,矩阵A和B具有8位数据元素。

  • .atype/.btype.bf16时,矩阵A和B具有16位数据元素。

  • .atype/.btype.tf32时,矩阵A和B具有32位数据元素。

精度与舍入:

  • 浮点运算:

    矩阵A和B的逐元素乘法运算至少使用单精度执行。当 .dtype.f32时,中间值的累加至少使用单精度执行。当.dtype.f16时,累加至少使用半精度执行。

    未指定累加顺序、舍入方式以及对次正规输入的处理方式。

  • .bf16.tf32 浮点运算:

    矩阵A和B的逐元素乘法以指定精度执行。涉及 .tf32 类型的 wgmma.mma_async 操作会在乘法运算前截断32位输入数据的低13位。中间值的累加以至少单精度执行。

    累加顺序、舍入方式以及对次正规输入的处理方式未作规定。

  • 整数运算:

    整数运算 wgmma.mma_async 使用 .s32 累加器执行。 .satfinite 限定符表示在溢出时,累加值将被限制在 MIN_INT32..MAX_INT32 范围内(其中边界分别定义为最小负32位有符号整数和最大正32位有符号整数)。

    如果未指定 .satfinite,则累加值将采用回绕方式处理。

强制性的.sync限定符表示wgmma.mma_async指令会使执行线程等待,直到warp中的所有线程都执行相同的wgmma.mma_async指令后才会继续执行。

强制性的.aligned限定符表示warpgroup中的所有线程必须执行相同的wgmma.mma_async指令。在条件执行的代码中,只有当确认warpgroup中的所有线程对条件的评估结果完全一致时,才应使用wgmma.mma_async指令,否则行为将是未定义的。

PTX ISA 说明

在PTX ISA版本8.2中引入。

支持在PTX ISA 8.4版本中引入的.u8.s8.s8.u8作为.atype.btype格式。

目标ISA注意事项

需要 sm_90a

整数类型示例

wgmma.fence.sync.aligned;
wgmma.mma_async.sp.sync.aligned.m64n8k64.s32.u8.u8  {s32d0, s32d1, s32d2, s32d3},
                                                    descA, descB, spMeta, 0, scaleD;
wgmma.mma_async.sp.sync.aligned.m64n8k64.s32.s8.u8  {s32d0, s32d1, s32d2, s32d3},
                                                    descA, descB, spMeta, 0, scaleD;
wgmma.commit_group.sync.aligned;
wgmma.wait_group.sync.aligned 0;

9.7.15.7. 异步wgmma代理操作

本节介绍warpgroup级别的wgmma.fencewgmma.commit_groupwgmma.wait_group指令。

9.7.15.7.1. 异步乘加指令:wgmma.fence

wgmma.fence

wgmma.mma_async和其他操作之间强制执行寄存器访问的顺序。

语法

wgmma.fence.sync.aligned;

描述

wgmma.fence 指令用于在之前对任何warpgroup寄存器的访问与后续通过wgmma.mma_async指令对相同寄存器的访问之间建立顺序关系。只有累加器寄存器和包含矩阵A片段的输入寄存器需要这种顺序保证。

wgmma.fence指令必须由warpgroup中的所有warps在以下位置发出:

  • 在warpgroup中的第一个wgmma.mma_async操作之前。

  • 在线程束组中的线程访问寄存器与任何访问相同寄存器的wgmma.mma_async指令之间(无论这些寄存器是作为累加器还是包含矩阵A片段的输入寄存器),除非这些是跨多个相同形状的wgmma.mma_async指令的累加器寄存器访问。在后一种情况下,默认会提供顺序保证。

否则,行为是未定义的。

必须使用异步代理栅栏来确保在共享内存矩阵的先前写入与后续在wgmma.mma_async指令中对相同矩阵的读取之间建立顺序关系。

强制性的.sync限定符表示wgmma.fence指令会使执行线程等待,直到warp中的所有线程都执行相同的wgmma.fence指令后才会继续执行。

强制性的.aligned限定符表示warpgroup中的所有线程必须执行相同的wgmma.fence指令。在条件执行代码中,只有当确定warpgroup中的所有线程对条件的评估结果完全一致时,才应使用wgmma.fence指令,否则行为将是未定义的。

PTX ISA 说明

在PTX ISA版本8.0中引入。

目标ISA注意事项

需要 sm_90a

示例

// Example 1, first use example:
wgmma.fence.sync.aligned;    // Establishes an ordering w.r.t. prior accesses to the registers s32d<0-3>
wgmma.mma_async.sync.aligned.m64n8k32.s32.u8.u8  {s32d0, s32d1, s32d2, s32d3},
                                                  descA, descB, scaleD;
wgmma.commit_group.sync.aligned;
wgmma.wait_group.sync.aligned 0;

// Example 2, use-case with the input value updated in between:
wgmma.fence.sync.aligned;
wgmma.mma_async.sync.aligned.m64n8k32.s32.u8.u8  {s32d0, s32d1, s32d2, s32d3},
                                                  descA, descB, scaleD;
...
mov.b32 s32d0, new_val;
wgmma.fence.sync.aligned;
wgmma.mma_async.sync.aligned.m64n8k32.s32.u8.u8  {s32d4, s32d5, s32d6, s32d7},
                                                 {s32d0, s32d1, s32d2, s32d3},
                                                  descB, scaleD;
wgmma.commit_group.sync.aligned;
wgmma.wait_group.sync.aligned 0;
9.7.15.7.2. 异步乘加指令: wgmma.commit_group

wgmma.commit_group

将所有先前未提交的wgmma.mma_async操作提交到一个wgmma-group中。

语法

wgmma.commit_group.sync.aligned;

描述

wgmma.commit_group 指令为每个线程束组创建一个新的wgmma-group,并将执行线程发起但尚未提交到任何wgmma-group的所有先前wgmma.mma_async指令批量归入新的wgmma-group。如果不存在未提交的wgmma.mma_async指令,则wgmma.commit_group会产生一个空的wgmma-group。

执行线程可以通过使用wgmma.wait_group来等待wgmma组中所有wgmma.mma_async操作的完成。

强制性的.sync限定符表示wgmma.commit_group指令会使执行线程等待,直到warp中的所有线程都执行相同的wgmma.commit_group指令后才会继续执行。

强制性的.aligned限定符表示warpgroup中的所有线程必须执行相同的wgmma.commit_group指令。在条件执行代码中,只有当确定warpgroup中的所有线程对条件的评估结果完全一致时,才应使用wgmma.commit_group指令,否则行为将是未定义的。

PTX ISA 说明

在PTX ISA版本8.0中引入。

目标ISA注意事项

需要 sm_90a

示例

wgmma.commit_group.sync.aligned;
9.7.15.7.3. 异步乘加指令:wgmma.wait_group

wgmma.wait_group

表示前一个warpgroup操作已完成。

语法

wgmma.wait_group.sync.aligned N;

描述

wgmma.wait_group 指令会使执行线程等待,直到最近提交的wgmma-group中只有N个或更少处于挂起状态,并且该执行线程之前提交的所有wgmma-group都已完成。例如,当N为0时,执行线程会等待之前所有的wgmma-group完成。操作数N是一个整数常量。

在未先执行等待包含该wgmma.mma_async指令的wgmma-groupwgmma.wait_group指令的情况下,访问累加器寄存器或包含矩阵A片段的输入寄存器是未定义行为。

强制性的.sync限定符表示wgmma.wait_group指令会使执行线程等待,直到warp中的所有线程都执行相同的wgmma.wait_group指令后才会继续执行。

强制性的.aligned限定符表示warpgroup中的所有线程必须执行相同的wgmma.wait_group指令。在条件执行代码中,只有当确定warpgroup中的所有线程对条件的评估结果完全一致时,才应使用wgmma.wait_group指令,否则行为将是未定义的。

PTX ISA 说明

在PTX ISA版本8.0中引入。

目标ISA注意事项

需要 sm_90a

示例

wgmma.fence.sync.aligned;

wgmma.mma_async.sync.aligned.m64n8k32.s32.u8.u8  {s32d0, s32d1, s32d2, s32d3},
                                                  descA, descB, scaleD;
wgmma.commit_group.sync.aligned;

wgmma.mma_async.sync.aligned.m64n8k16.f32.f16.f16 {f32d0, f32d1, f32d2, f32d3},
                                                  {f16a0, f16a1, f16a2, f16a3},
                                                   descB, 1, -1, -1, 1;
wgmma.commit_group.sync.aligned;

wgmma.wait_group.sync.aligned 0;

9.7.16. TensorCore 第五代系列指令集

9.7.16.1. Tensor Memory

第五代TensorCore配备了专为TensorCore运算优化的专用片上内存。该Tensor内存被组织为一个二维矩阵,其中水平行称为通道,垂直列称为列。

sm_100a架构上,第五代TensorCore的张量内存采用二维结构,每个CTA包含512列和128行,每个单元大小为32位。

通过加载和存储操作访问张量内存的线程限制在加载/存储访问限制中进行了规定。

9.7.16.1.1. 张量内存寻址

张量内存地址为32位宽,并指定两个组成部分。

  1. 车道索引

  2. 列索引

布局如下:

31 16

15 0

车道索引

列索引

图181展示了CTA内部的张量内存布局视图。

_images/tensor-memory-layout.png

图181 张量内存布局与寻址

9.7.16.1.2. 张量内存分配

张量内存是动态分配的。张量内存必须由CTA中的单个warp使用Tensor Memory分配管理指令进行分配。

Tensor Memory的分配和释放是按列进行的。分配的基本单位是32列,且分配的列数必须是2的幂次方。当分配一列时,该列的128个通道都会被同时分配。

9.7.16.2. 矩阵与数据移动形态

涉及两种形状。

  1. 数据移动操作中的形状

  2. MMA操作中的形状

9.7.16.2.1. 矩阵形状

矩阵乘加运算对操作数矩阵ABD支持有限的形状集合。这三个矩阵操作数的形状由元组MxNxK共同描述,其中AMxK矩阵,BKxN矩阵,DMxN矩阵。

表38展示了tcgen05.mma操作所支持的指定类型的矩阵形状。

表 38 各种.kind和形状的组合

多种组合

支持的形状

.kind::*

包含 .ws

CTA 分组

稀疏度

数据类型

atype/btype

kind::f16

没有 .ws

1

密集

.f16

.f16

64xN1xK

128xN2xK

N1 = {8, 16, 24, … 256} 步长8

N2 = {16, 32, … 256} 步长间隔为16

K = 16

.f32

.f16, .bf16

稀疏

.f16

.f16

K = 32

.f32

.f16, .bf16

2

密集

.f16

.f16

128xNxK

256xNxK

N = {32, 64, … 256} 步长32

K = 16

.f32

.f16, .bf16

稀疏

.f16

.f16

K = 32

.f32

.f16, .bf16

.ws

1

密集

.f16

.f16

32xNxK

64xNxK

128xNxK

N = {64, 128, 256}

K = 16

.f32

.f16, .bf16

稀疏

.f16

.f16

N = {64, 128}

K = 32

.f32

.f16, .bf16

2

或者

.f16

.f16

无效

.f32

.f16, .bf16

.kind::tf32

没有 .ws

1

密集

.f32

.tf32

64xN1xK

128xN2xK

N1 = {8, 16, 24, … 256} 步长8

N2 = {16, 32, … 256} 步长间隔为16

K = 8

稀疏

K = 16

2

Dense

128xNxK

256xNxK

N = {32, 64, … 256} 步长为32

K = 8

稀疏

K = 16

.ws

1

密集

32xNxK 64xNxK 128xNxK

N= {64, 128, 256}

K = 8

稀疏

N= {64, 128}

K = 16

2

Dense

无效

稀疏

.kind::f8f6f4

没有 .ws

1

密集

.f32

.f16

.e4m3,

.e5m2,

.e2m3,

.e3m2,

.e2m1

64xN1xK

128xN2xK

N1 = {8, 16, … 256} 步长间隔为8

N2 = {16, 32, … 256} 步长间隔为16

K = 32

稀疏

K = 64

2

Dense

128xNxK

256xNxK

N = {32, 64, … 256} 步长为32

K = 32

稀疏

K = 64

.ws

1

密集

32xNxK 64xNxK 128xNxK

N= {64, 128, 256}

K = 32

稀疏

N= {64, 128}

K = 64

2

Dense

无效

稀疏

.kind::mxf8f6f4

没有 .ws

1

密集

.f32

.e4m3,

.e5m2,

.e2m3,

.e3m2,

.e2m1

X

(规模)

.ue8m0

128xNxK

N = {8, 16, … 256} 步长间隔为8

K = 32

稀疏

K = 64

2

Dense

128xNxK

256xNxK

N = {16, 32, … 256} 步长为16

K = 32

稀疏

256xNxK

K = 64

.ws

1

密集

无效

稀疏

2

Dense

稀疏

.kind::i8

没有 .ws

1

密集

.s32

.s8, .u8

64xNxK

128xNxK

N = {8, 16, 24, 32, 48, … 256}

当N大于32时,步长为16

K = 32

稀疏

K = 64

2

Dense

128xNxK

256xNxK

N = {32, 64, … 256} 步长为32

K = 32

稀疏

K = 64

.ws

1

密集

32xNxK 64xNxK 128xNxK

N= {64, 128, 256}

K = 32

稀疏

N= {64, 128}

K = 64

2

Dense

无效

稀疏

.kind::mxf4

没有 .ws

1

密集

.f32

.e2m1

X

(规模)

.ue8m0

128xNxK

N = {8, 16, … 256} 步长间隔为8

K = 64

稀疏

K = 128

2

Dense

128xNxK

256xNxK

N = {16, 32, … 256} 步长为16

K = 64

稀疏

256xNxK

K = 128

.ws

1 / 2

或者

无效

.kind::mxf4nvf4

没有 .ws

1

密集

.f32

.e2m1

X

(规模)

.ue8m0, .ue4m3

128xNxK

N = {8, 16, … 256} 步长间隔为8

K = 64

稀疏

K = 128

2

Dense

128xNxK

256xNxK

N = {16, 32, … 256} 步长为16

K = 64

稀疏

256xNxK

K = 128

.ws

1 / 2

或者

无效

9.7.16.2.2. 指定矩阵形状

MN 可以在 Instruction descriptor 中指定。

K 不能显式指定,而是由 MMA 类型和稀疏度隐式决定,如 表 38 所示。

9.7.16.2.3. 数据移动形态

数据移动形状表示要移入或移出Tensor Memory的数据维度。这些形状被描述为一个元组lane x size,其中:

以下形状受各种tcgen05操作支持:

形状

tcgen05.

.16x64b, .16x128b, .16x256b, .16x32bx2, .32x32b

.ld / .st

.4x256b, .32x128b, .64x128b, .128x256b, .128x128b

.cp

.31x256b (隐式)

.shift

9.7.16.2.3.1. 内存布局

以下展示了矩阵片段在warp线程间的分布布局。

9.7.16.2.3.1.1.形状为32x32b的矩阵片段

一条tcgen05{.ld,.st}.32x32b指令拥有以下数据向量寄存器。

片段

元素(从低到高)

一个向量表达式,包含表46中提到的.num数量的.b32寄存器。

r0, r1, …

执行tcgen05{.ld,.st}.32x32b的warp将访问Tensor Memory的32个通道。 它会根据图182所示,对每个通道加载或存储(32 * .num)位的数据。

_images/tcgen05-mma-fragment-3232b.png

图 182 形状为 32x32b 的矩阵片段

9.7.16.2.3.1.2. 形状为16x64b的矩阵片段

一条tcgen05{.ld,.st}.16x64b指令包含以下数据向量寄存器。

片段

元素(从低到高)

一个向量表达式,包含表46中提到的.num数量的.b32寄存器。

r0, r1, …

执行tcgen05{.ld,.st}.16x64b的warp将访问Tensor Memory的16个通道。 它会根据图183所示,对每个通道加载或存储(64 * .num)位的数据。

_images/tcgen05-mma-fragment-1664b.png

图183 形状为16x64b的矩阵片段

9.7.16.2.3.1.3. 形状为16x128b的矩阵片段

一条tcgen05{.ld,.st}.16x128b指令拥有以下数据向量寄存器。

片段

元素(从低到高)

一个向量表达式,包含表46中提到的.num数量的.b32寄存器。

r0, r1, …

执行tcgen05{.ld,.st}.16x128b的warp将访问Tensor Memory的16个通道。 它会从每个通道加载或存储(128 * .num)位的数据,如图184所示。

_images/tcgen05-mma-fragment-16128b.png

图 184 形状为 16x128b 的矩阵片段

9.7.16.2.3.1.4. 形状为16x256b的矩阵片段

一条tcgen05{.ld,.st}.16x256b指令包含以下数据向量寄存器。

片段

元素(从低到高)

一个向量表达式,包含表46中提到的.num.b32寄存器。

r0, r1, r2, r3, …

执行tcgen05{.ld,.st}.16x256b的warp将访问Tensor Memory的16个通道。 如图185所示,它会从每个通道加载或存储(256 * .num)位的数据。

_images/tcgen05-mma-fragment-16256b.png

图185 形状为16x256b的矩阵片段

9.7.16.2.3.1.5. 形状为16x32bx2的矩阵片段

一条tcgen05{.ld,.st}.16x32bx2指令拥有以下数据向量寄存器。

片段

元素(从低到高)

一个向量表达式,包含表46中提到的.num数量的.b32寄存器。

r0, r1, …

执行tcgen05{.ld,.st}.16x32bx2的warp将访问Tensor Memory的16个通道。 如图186所示,它会从每个通道加载或存储(32 * .num)位的数据。

_images/tcgen05-mma-fragment-1632b2.png

图186 形状为16x32bx2的矩阵片段

9.7.16.3. 矩阵描述符

tcgen05 指令系列使用了三种类型的矩阵描述符。

9.7.16.3.1. 共享内存描述符

共享内存描述符描述了共享内存中乘数矩阵的属性,包括其在当前CTA共享内存中的位置。它是一个64位的寄存器值,具有以下布局:

表 39 共享内存描述符布局

位域

位大小

描述

0-13

14

矩阵描述符编码(矩阵起始地址)

16-29

14

matrix-descriptor-encode (前导维度字节偏移量)

32-45

14

matrix-descriptor-encode (步长维度字节偏移量)

46-48

3

固定常量值0b001

49-51

3

矩阵基址偏移

52

1

固定常量值0xb0

53-60

8

固定常量值0xb00000000

61-63

3

指定要使用的交换模式: 0. 无交换 1. 128字节带32B原子交换 2. 128字节交换 4. 64字节交换 6. 32字节交换

注意:值3、5和7无效

其中 matrix-descriptor-encode(x) = (x & 0x3FFFF) >> 4

当指定交换模式的重复模式开始时,基础偏移值为0,如表40所示。

表40 各种交换模式的重复模式起始地址

混洗模式

重复模式的起始地址

128字节交换

1024字节边界

64字节交换

512字节边界

32字节交换

256字节边界

否则,基础偏移量必须是一个非零值,使用以下公式计算: base offset = (pattern start addr >> 0x7) & 0x7

以下内容必须16字节对齐:

  1. 矩阵起始地址

  2. 前导维度字节偏移量

  3. 步幅维度字节偏移量

9.7.16.3.2. 指令描述符

指令描述符描述了所有矩阵的形状、类型及其他细节,以及矩阵乘加运算的操作。它是一个存储在寄存器中的32位值,具体布局取决于MMA-Kind类型:

表41 用于.kind::tf32、.kind::f16、.kind::f8f6f4和.kind::i8的指令描述符格式

位数

大小

(比特)

描述

取值

.kind::tf32

.kind::f16

.kind::f8f6f4

.kind::i8

0-1

2

Sparsity selector, 如果启用了稀疏性

0-3

2

1

稀疏度

稠密 = 0 稀疏 = 1

3

1

整数类型的饱和处理

0 (不适用)

不饱和 = 0 饱和 = 1

4-5

2

dtype (矩阵D类型)

F32 = 1

F16 = 0 F32 = 1

S32 = 2

6

1

保留

0

7-9

3

atype (矩阵A类型)

TF32 = 2

F16 = 0

BF16 = 1

E4M3 = 0 E5M2 = 1 E2M3 = 3 E3M2 = 4 E2M1 = 5

无符号8位 = 0

有符号8位 = 1

10-12

3

btype (矩阵B类型)

13

1

矩阵取反

不取反 = 0

取反 = 1

不取反 = 0

14

1

矩阵B取反

15

1

矩阵转置

不转置 = 0

转置 = 1

16

1

转置B矩阵

17-22

6

N,矩阵B的维度 (最低3位不包括在内)

N >> 3

23

1

保留

0

24-28

5

M,矩阵A的维度 (不包括最低4位)

M >> 4

29

1

保留

0

30-31

2

尝试在.ws中重用B矩阵时的最大偏移量

无偏移 = 0 最大偏移8 = 1 最大偏移16 = 2 最大偏移32 = 3

表42.kind::mxf8f6f4的指令描述符格式

位数

大小

(比特)

描述

取值

.kind::mxf8f6f4

0-1

2

保留

0

2

1

稀疏度

稠密 = 0 稀疏 = 1

3

1

保留

0

4-5

2

Matrix B Scale Factor Data ID

0-3

6

1

保留

0

7-9

3

atype (矩阵A类型)

E4M3 = 0 E5M2 = 1 E2M3 = 3 E3M2 = 4 E2M1 = 5

10-12

3

btype (矩阵B类型)

13

1

矩阵取反

不取反 = 0

取反 = 1

14

1

矩阵B取反

15

1

矩阵转置

不转置 = 0

转置 = 1

16

1

转置B矩阵

17-22

6

N,矩阵B的维度 (最低3位不包含)

N >> 3

23

1

缩放矩阵类型,适用于 scale_A / scale_B

UE8M0 = 1

24-26

3

保留

0

27-28

5

M,矩阵A的维度 (最低7位不包括在内)

M >> 7

29-30

2

Matrix A Scale Factor Data ID

0-3

31

1

保留

0

表43 .kind::mxf4 和 .kind::mxf4nvf4 的指令描述符格式

位数

大小

(比特)

描述

取值

.kind::mxf4

.kind::mxf4nvf4

0-1

2

保留

0

2

1

稀疏度

稠密 = 0 稀疏 = 1

3

1

保留

0

4-5

2

Matrix B Scale Factor Data ID

0 或 2

6

1

保留

0

7-9

3

atype (矩阵A类型)

E2M1 = 1

10-11

2

btype (矩阵B类型)

12

1

保留

0

13

1

矩阵取反

不取反 = 0

取反 = 1

14

1

矩阵B取反

15

1

矩阵转置

不转置 = 0

16

1

转置B矩阵

17-22

6

N, 矩阵B的维度 (最低3位不包含)

N >> 3

23

1

缩放矩阵类型,适用于scale_A/scale_B

UE8M0 = 1

UE4M3 = 0 UE8M0 = 1

24-26

3

保留

0

27-28

5

M,矩阵A的维度 (不包括7个最低有效位)

M >> 7

29-30

2

Matrix A Scale Factor Data ID

0 或 2

31

1

保留

0

9.7.16.3.3. 零列掩码描述符

零列掩码描述符用于生成一个掩码,该掩码指定在MMA操作中B矩阵的哪些列将具有零值,而不管共享内存中存在的实际值如何。生成的掩码总大小为N位。

掩码中的0位表示矩阵B对应列的值应被用于MMA运算。掩码中的1位表示整个列必须使用0值进行MMA运算。

零列掩码描述符是一个64位的寄存器值,其布局如下:

表 44 零列掩码描述符布局

位数

大小(比特)

字段名称

描述

0-7

8

起始计数0 (sc0)

指定必须跳过的LSB位

用于子掩码mask-i

8-15

8

起始计数1 (sc1)

16-23

8

起始计数2 (sc2)

24-31

8

起始计数3 (sc3)

32

1

第一跨度0 (fs0)

指定子掩码mask-i的起始值

33

1

第一个跨度1 (fs1)

34

1

第一跨度2 (fs2)

35

1

第一跨度3 (fs3)

36-38

3

保留

39

1

非零掩码

值为0表示生成的掩码将全为0 值为1表示需要生成掩码

40-47

8

跳过跨度

(使用B矩阵的连续列数) - 1

48-55

8

使用Span

(连续使用0的列数) - 1

56-61

6

Column Shift

将列按指定数量进行位移。 这样可以在非0起始列上执行MMA操作。 当M=32时最大位移量=16 其他情况下最大位移量=32

零列掩码由一个或多个子掩码组成,具体取决于M,如下表所示:

M

零列掩码拆分

子掩码

使用的首个跨度

使用的起始列

128

N位大小的单子掩码

mask0

fs0

sc0

64

两个子掩码,每个大小为N/2位

mask0, mask1

fs0, fs1

sc0, sc1

32

四个子掩码,每个大小为N/4位

mask0, mask1 mask2, mask3

fs0, fs1, fs2, fs3

sc0, sc1, sc2, sc3

下表展示了子掩码在N维空间中的覆盖范围:

子掩码

M

128

64

32

mask0

列 [0, N-1]

列 [0, N/2-1]

列 [0, N/4-1]

mask1

列 [N/2, N-1]

列 [N/4, N/2-1]

mask2

列 [N/2, (N/4*3)-1]

mask3

列 [(N/4*3), N-1]

以下示例展示了零列掩码描述符及其生成的对应掩码:

  1. 示例1:M = 128

    输入全零列掩码描述符:

    起始计数

    首个跨度

    非零掩码

    跳过跨度

    使用跨度

    位移

    {0, 0, 0, 0}

    {0, 0, 0, 0}

    0

    4

    3

    0

    输出全零列掩码:0x0

    由于非零掩码字段为0,掩码值为0x0。矩阵B的所有列都将用于MMA运算。

  2. 示例2: M = 128

    输入零列掩码描述符:

    起始计数

    首个跨度

    非零掩码

    跳过跨度

    使用跨度

    位移

    {-, -, -, 0}

    {-, -, -, 0}

    1

    2

    3

    0

    输出掩码0: 0b ... 111 0000 111 0000 (大小 = N)

  3. 示例3: M = 64

    输入零列掩码描述符:

    起始计数 {.., sc1, sc0}

    首个跨度 {.., fs1, fs0}

    非零掩码

    跳过跨度

    使用跨度

    位移

    {-, -, 0, 0}

    {-, -, 0, 1}

    1

    2

    3

    0

    输出掩码0: 0b ... 111 0000 111 0000 111

    输出掩码1: 0b ... 0000 111 0000 111 0000

  4. 示例4:M = 32

    输入零列掩码描述符:

    起始计数 {sc3, sc2, sc1, sc0}

    首个跨度 {fs3, fs2, fs1, fs0}

    非零掩码

    跳过跨度

    使用跨度

    位移

    {1, 2, 1, 0}

    {0, 0, 1, 1}

    1

    2

    3

    2

    输出掩码0:0b ... 0000 111 0000 111

    输出掩码1:0b ... 0000 111 0000 11

    输出掩码2:0b ... 111 0000 111 00

    输出掩码3:0b ... 111 0000 111 000

    若N = 128,则由于位移为2,MMA操作将使用从第2列到第129列的B矩阵。

9.7.16.4. 问题粒度

每个tcgen05操作对需要执行它们的线程/线程束数量有不同的要求。

下表列出了每个tcgen05操作的执行粒度要求:

表45tcgen05操作的执行粒度要求

tcgen05 操作

.cta_group

问题粒度

.mma,
.cp,
.shift,
.commit

::1

当前CTA中单个线程的问题将触发基础操作。

::2

来自CTA-Pair中单个线程的问题将触发基础操作。

.alloc,
.dealloc,
.relinquish_alloc_permit

::1

来自当前CTA中单个warp的请求会触发分配管理指令。

::2

从两个线程束发出指令,分别位于当前CTA及其Peer CTA中,需要共同执行该操作。

.ld,
.st,
.wait::{ld, st}

N/A

当前CTA中的一个warp只能访问当前CTA张量内存的1/4。因此,需要warpgroup才能访问当前CTA的整个张量内存。

.fence::*

N/A

线程需要对其想要与其他线程对张量内存的访问进行排序的所有张量内存访问进行栅栏操作。

9.7.16.4.1. CTA Pair

集群中任意两个CTA,其%cluster_ctarank仅最后一位不同的,称为一个CTA对。

在CTA对中,%cluster_ctarank的最后一位为:

  • 0被称为CTA对中的偶数编号CTA。

  • 1被称为CTA对中的奇数编号CTA。

大多数tcgen05操作可以在单个CTA级别粒度执行,也可以在CTA对级别粒度执行。当tcgen05操作以CTA对粒度执行时,将访问CTA对内两个CTA的张量内存。需要发出tcgen05操作的线程集列在Issue Granularity中。

9.7.16.4.2. Peer CTA

CTA对中奇数CTA的对等CTA是同一对中的偶数CTA。 同样,CTA对中偶数CTA的对等CTA是同一对中的奇数CTA。

9.7.16.5. 第五代TensorCore操作的内存一致性模型

tcgen05指令的排序通过两个关键概念来描述:

  1. 流水线式 tcgen05 指令

  2. 专为tcgen05设计的线程间同步机制。

这些概念组合形成了四种典型的同步模式,如下文进一步描述。

9.7.16.5.1. 异步操作

tcgen05指令系列分为2类:

  1. 异步指令:

    这些tcgen05操作与同一线程中的其他tcgen05操作之间没有内在的先后顺序(除非如下文所述采用流水线方式)。

  2. 同步指令:

    这些tcgen05操作在同一序列中与其他tcgen05操作具有内在的顺序关系。

    访问共享内存的张量内存分配相关指令,对于非tcgen05指令保持相同地址顺序。

下表列出了tcgen05指令的每个类别:

tcgen05.* 操作

类别

.alloc

同步

使用说明

.dealloc

.relinquish_alloc_permit

.fence::*

.wait::*

.commit

.mma

异步

使用说明

.cp

.shift

.ld

.st

9.7.16.5.2. 流水线式tcgen05指令

异步的tcgen05操作可能以不同于它们被发出的顺序执行和完成。然而,某些特定的异步tcgen05指令对会形成tcgen05流水线,其中这两个异步操作保证按照发出它们的指令顺序执行。具体的配对如下:

  1. tcgen05.mma.cta_group::N -> tcgen05.mma.cta_group::N (相同的N、累加器和形状)

  2. tcgen05.copy.cta_group::N -> tcgen05.mma.cta_group::N (相同的N)

  3. tcgen05.shift.cta_group::N -> tcgen05.mma.cta_group::N (相同的N)

  4. tcgen05.shift.cta_group::N -> tcgen05.cp.4x256b.cta_group::N (相同的N)

  5. tcgen05.mma.cta_group::N -> tcgen05.shift.cta_group::N (相同的N)

9.7.16.5.2.1. 隐式流水线化的tcgen05指令

指令 tcgen05.committcgen05.wait 会隐式地与先前从同一线程发出的 tcgen05.{mma,cp,shift}tcgen05.{ld,st} 指令分别形成流水线关系。

9.7.16.5.2.1.1. 基于mbarrier的完成机制

通过基于mbarrier的等待机制来观察以下指令异步操作的完成情况:

  1. tcgen05.mma

  2. tcgen05.cp

  3. tcgen05.shift

tcgen05.commit 用于追踪上述异步指令的完成情况。

以下是隐式流水线化的tcgen05指令配对,该配对使用基于mbarrier的完成机制:

  • tcgen05.mma.cta_group::N -> tcgen05.commit.cta_group::N (相同的N)

  • tcgen05.cp.cta_group::N -> tcgen05.commit.cta_group::N (相同的N)

  • tcgen05.shift.cta_group::N -> tcgen05.commit.cta_group::N (相同的N)

9.7.16.5.2.1.2. 基于tcgen05.wait指令的完成机制

通过基于tcgen05.wait的等待机制来观察以下指令异步操作的完成情况:

  1. tcgen05.ld

  2. tcgen05.st

tcgen05.wait::ldtcgen05.wait::st 用于追踪 tcgen05.ldtcgen05.st 异步指令的完成状态。

以下是隐式流水线化的tcgen05指令配对,它使用基于tcgen05.wait的完成机制:

  • tcgen05.ld -> tcgen05.wait::ld

  • tcgen05.st -> tcgen05.wait::st

9.7.16.5.3. 针对tcgen05指令的专用线程间同步机制

tcgen05指令支持一种专为tcgen05指令系列优化的线程间同步机制。标准内存一致性模型的同步机制同样适用于tcgen05指令系列。

部分包含针对tcgen05指令的专用线程间同步机制。

tcgen05.fence::before_thread_synctcgen05.fence::after_thread_sync 与执行排序指令(如道德上严格的 ld/st/atom 指令、mbarrier 指令、barrier 指令等)组合使用,以在线程间建立 tcgen05 操作之间的顺序关系。这些在线程间排序的异步 tcgen05 指令也构成了一个 tcgen05 流水线。

tcgen05.fence::before_thread_sync之前的异步tcgen05操作会被排序在所有后续tcgen05操作和执行排序操作之前。

tcgen05.fence::after_thread_sync之后执行的异步tcgen05操作,将被排序在所有先前的tcgen05和执行排序操作之后。

9.7.16.5.4. Canonical synchronization patterns

根据上述规则,以下是五种标准同步模式:

9.7.16.5.4.1. 流水线指令,同一线程

在这种模式下,无需显式的排序机制,流水线指令配对即可提供顺序保证。

示例:

tcgen05.mma
tcgen05.mma (same shape and accumulator)

这两条指令将按程序顺序执行。

9.7.16.5.4.2. 非流水线指令,同一线程

在此模式中,使用了显式等待机制来等待异步tcgen05操作的完成。

示例 1:

tcgen05.st
tcgen05.wait::st
tcgen05.ld

tcgen05.wait::st 用于等待先前的异步指令 tcgen05.st 完成。

示例 2:

tcgen05.mma [d], ...
tcgen05.commit.mbarrier::arrive::one
mbarrier.try_wait.relaxed.cluster (loop until successful)
tcgen05.fence::after_thread_sync
tcgen05.ld [d], ...

为了完成异步的tcgen05.mma,使用了tcgen05.commit

由于tcgen05.ld是一个异步操作,因此需要指令tcgen05.fence::after_thread_sync

不需要显式的tcgen05.fence::before_thread_sync,因为这会由tcgen05.commit隐式执行。tcgen05.mmatcgen05.commit的组合形成了一个概念上的异步流水线,并建立了执行顺序。

tcgen05.mma [d], ...
tcgen05.fence::before_thread_sync
mbarrier::arrive
9.7.16.5.4.3. 流水线指令,不同线程

在这种模式下,不需要显式的等待机制,但需要线程之间的适当同步。

示例:

线程 0

线程 1

tcgen05.cp
tcgen05.fence::before_thread_sync
mbarrier.arrive.relaxed.cluster
mbarrier.try_wait.relaxed.cluster // 循环直到成功
tcgen05.fence::after_thread_sync
tcgen05.mma
9.7.16.5.4.4. 非流水线指令,不同线程

在这种模式下,发出异步tcgen05指令的生产者线程必须在与消费者线程同步之前,显式等待指令完成。

示例 1:

线程 0

线程 1

tcgen05.ld
tcgen05.wait::ld
tcgen05.fence::before_thread_sync
mbarrier.arrive.relaxed.cluster
mbarrier.try_wait.relaxed.cluster // 循环直到成功
tcgen05.fence::after_thread_sync
tcgen05.mma

示例 1:

线程 0

线程 1

tcgen05.mma
tcgen05.commit.mbarrier::arrive::one [mbar]
mbarrier.try_wait.relaxed.cluster [mbar] // 循环直到成功
tcgen05.fence::after_thread_sync
tcgen05.ld

同步机制也可以相互组合使用。例如:

线程 0

线程 1

tcgen05.mma
tcgen05.commit.mbarrier::arrive::one [bar1]
mbarrier.try_wait.relaxed.cluster [bar1] // 循环
...
tcgen05.fence::after_thread_sync
...// 完成已确保
tcgen05.fence::before_thread_sync
mbarrier.arrive.relaxed.cluster   [bar2] // 循环
...
mbarrier.try_wait.relaxed.cluster [bar2] // 循环
...
tcgen05.fence::after_thread_sync
tcgen05.ld
9.7.16.5.4.5. 注册依赖项,同一线程

对于tcgen05.ld,无论是否存在其他形式的同步,通过真实寄存器依赖实现的线程内顺序都将被遵守。这种形式的寄存器依赖并不意味着任何其他形式的顺序。例如,寄存器依赖并不意味着被依赖指令的内存访问将在依赖指令的内存访问之前执行。为了强制执行此类内存顺序并避免围绕tcgen05.ld的反依赖风险,必须使用tcgen05.wait::ld

示例:

tcgen05.ld %r1, ...;
tcgen05.mma ..., %r1, ...;
9.7.16.5.5. 共享内存访问

tcgen05.mmatcgen05.cp 操作的共享内存访问是在异步代理(async proxy)中执行的。

跨多个代理访问相同的内存位置需要一个跨代理的栅栏。 对于异步代理,应使用fence.proxy.async来同步通用代理和异步代理之间的内存。

9.7.16.6. 张量内存分配与管理指令

9.7.16.6.1. 第五代Tensor核心指令:tcgen05.alloc, tcgen05.dealloc, tcgen05.relinquish_alloc_permit

tcgen05.alloc, tcgen05.dealloc, tcgen05.relinquish_alloc_permit

动态Tensor Memory分配管理指令

语法

tcgen05.alloc.cta_group.sync.aligned{.shared::cta}.b32  [dst], nCols;

tcgen05.dealloc.cta_group.sync.aligned.b32              taddr, nCols;

tcgen05.relinquish_alloc_permit.cta_group.sync.aligned;

.cta_group = { .cta_group::1, .cta_group::2 }

描述

tcgen05.alloc 是一个可能阻塞的指令,它会动态分配Tensor Memory中指定数量的列,并将分配的Tensor Memory地址写入共享内存中由目标操作数dst指定的位置。当请求的Tensor Memory数量不可用时,tcgen05.alloc会阻塞,一旦请求的Tensor Memory数量变为可分配状态,指令就会解除阻塞。

指令 tcgen05.dealloc 释放由 Tensor Memory 地址 taddr 指定的 Tensor Memory。操作数 taddr 必须指向之前分配的 Tensor Memory

无符号32位操作数nCols指定要分配或释放的列数。分配和释放的单位是32列及每列的所有通道。列数必须是2的幂次方。操作数nCols必须在[32, 512]范围内。在CTA执行顺序内,任何两次分配之间分配的列数不应增加。操作数nCols必须是2的幂次方。

指令 tcgen05.relinquish_alloc_permit 规定执行线程的CTA将放弃分配张量内存的权限。因此,在CTA的任何组成线程执行tcgen05.relinquish_alloc_permit后,该CTA再执行tcgen05.alloc操作将被视为非法。

如果未指定状态空间,则使用通用寻址。如果dst指定的地址不在.shared::cta状态空间的地址窗口内,则行为是未定义的。

限定符 .cta_group 指定参与分配和释放操作的CTA数量。当指定 .cta_group::1 时,必须由CTA中的一个warp执行分配和释放操作。当指定 .cta_group::2 时,必须由每个peer CTAs中的一个warp共同执行分配和释放操作。请参阅Issue Granularity部分。 当指定 .cta_group::2 时,发起warp必须确保peer CTA已启动且仍处于活动状态。

强制性的 .sync 限定符表示该指令会使执行线程等待,直到线程束中的所有线程都执行完相同指令后才会继续执行。

强制性的.aligned限定符表示warp中的所有线程必须执行相同的指令。在条件执行的代码中,只有当确定warp中的所有线程对条件的评估结果完全一致时才能使用该指令,否则行为将是未定义的。

如果warp中的所有线程未使用相同的nCols值,或者warp中任何线程已退出,则该指令的行为是未定义的。

tcgen05.alloc中的存储操作在Memory Consistency Model中被视为弱内存操作。

PTX ISA 说明

在PTX ISA版本8.6中引入。

目标ISA注意事项

支持以下架构:

  • sm_100a

  • sm_101a

示例

// Example 1:

tcgen05.alloc.cta_group::1.sync.aligned.shared::cta.b32 [sMemAddr1], 32;
ld.shared.b32 taddr, [sMemAddr1];
// use taddr ...
// more allocations and its usages ...
tcgen05.dealloc.cta_group::1.sync.aligned.b32  taddr, 32;
// more deallocations ...
tcgen05.relinquish_alloc_permit.cta_group::1.sync.aligned;

// Example 2:

// Following instructions are performed by current warp and the warp in the peer-CTA:
tcgen05.alloc.cta_group::2.sync.aligned.shared::cta.b32 [sMemAddr2], 32;
ld.shared.b32 taddr, [sMemAddr2];
// use taddr ...
// more allocations and its usages ...
tcgen05.dealloc.cta_group::2.sync.aligned.b32  taddr, 32;
// more deallocations ...
tcgen05.relinquish_alloc_permit.cta_group::2.sync.aligned;

9.7.16.7. 张量内存与寄存器加载/存储指令

CTA的线程可以对CTA的张量内存执行加载和存储操作,并在寄存器和张量内存之间移动数据。数据的加载和存储可以按照矩阵和数据移动形状部分中指定的特定形状执行。

9.7.16.7.1. 访问限制

并非所有CTA线程都能通过tcgen05.ldtcgen05.st操作访问整个张量内存。

CTA的张量内存被划分为4个相等的块,使得warpgroup中的每个warp都可以访问张量内存的一个块。张量内存的所有列都可以被warpgroup中的四个warp访问。张量内存的一个通道只能被warpgroup中的一个warp访问。下表描述了访问限制。

warp组内warp的ID

可访问通道

0

0-31

1

32-63

2

64-95

3

96-127

9.7.16.7.2. Packing and Unpacking

可选地,在加载和存储过程中可以执行以下打包(pack)和解包(unpack)操作:

  1. 打包:可以将两个16位数据块打包成寄存器中的一个32位数据块,位于tcgen05.ld

  2. 解包:寄存器中的单个32位数据块可以在tcgen05.st中被解包为两个16位数据块

图187所示。

_images/tcgen05-ld-st-pack-unpack.png

图 187 tcgen05加载/存储操作的打包/解包操作

9.7.16.7.3. Tensorcore 第五代指令集: tcgen05.ld

tcgen05.ld

将张量内存中的数据异步集体加载到寄存器中。

语法

// Base load instruction:

tcgen05.ld.sync.aligned.shape1.num{.pack}.b32    r, [taddr];

tcgen05.ld.sync.aligned.shape2.num{.pack}.b32    r, [taddr], immHalfSplitoff;

.shape1 = { .16x64b, .16x128b, .16x256b, .32x32b }
.shape2 = { .16x32bx2 }
.num    = { .x1, .x2, .x4, .x8, .x16, .x32, .x64, .x128 }
.pack   = { .pack::16b }

描述

指令 tcgen05.ld 异步地从 Tensor Memory 加载数据,数据位置由32位地址操作数 taddr 指定,加载到目标寄存器 r 中,该操作在所有线程束的线程中共同执行。

warp中的所有线程必须指定相同的taddr值,该值必须是集体加载操作的基础地址。否则,行为将是未定义的。

.shape限定符和.num限定符共同决定了从Tensor Memory加载的数据总维度。.shape限定符表示要访问数据的基础维度,如Data Movement Shape中所述。.num限定符表示基础维度的重复因子,最终形成所访问数据的总维度。

形状 .16x32bx2 对形状为 .16x32b 的张量内存执行两次访问。 第一次访问的基地址由 taddr 指定,第二次访问的基地址由 taddr+immHalfSplitoff 指定,其中 immHalfSplitoff 是一个立即数参数。

目标操作数 r 是一个大括号包围的向量表达式,根据 .shape.num 的值由一个或多个32位寄存器组成。不同 .num.shape 组合对应的向量大小如 表46 所示。

表46 num和shape的各种组合

.num

.shape

.16x32bx2 / .16x64b / .32x32b

.16x128b

.16x256b

.x1

1

2

4

.x2

2

4

8

.x4

4

8

16

.x8

8

16

32

.x16

16

32

64

.x32

32

64

128

.x64

64

128

不适用

.x128

128

不适用

不适用

可选限定符 .pack::16b 可用于在加载过程中将相邻列的两个16位元素打包成单个32位元素,如章节Packing and Unpacking所示。

强制性的 .sync 限定符表示 tcgen05.ld 会使执行线程等待,直到线程束中的所有线程都执行相同的 tcgen05.ld 指令后才会继续执行。

强制性的.aligned限定符表示warp中的所有线程必须执行相同的tcgen05.ld指令。在条件执行的代码中,只有当确定warp中的所有线程对条件的评估结果完全一致时,才应使用tcgen05.ld指令,否则行为将是未定义的。

如果所有线程未使用相同的taddr值,或者warp中有任何线程已退出,则tcgen05.ld的行为是未定义的。

指令 tcgen05.ld 是异步执行的,更多详细信息请参阅章节 第五代TensorCore操作的内存一致性模型

PTX ISA 说明

在PTX ISA版本8.6中引入。

目标ISA注意事项

支持以下架构:

  • sm_100a

  • sm_101a

示例

tcgen05.ld.sync.aligned.32x32b.x2.b32     {r0, r1}, [taddr1];

tcgen05.ld.sync.aligned.16x128b.x4.b32    {r0, r1, r2, r3, r4, r5, r6, r7}, [taddr2];
9.7.16.7.4. Tensorcore 第五代指令集: tcgen05.st

tcgen05.st

将寄存器中的张量内存异步集体存储。

语法

tcgen05.st.sync.aligned.shape1.num{.unpack}.b32    [taddr], r;

tcgen05.st.sync.aligned.shape2.num{.unpack}.b32    [taddr], immHalfSplitoff, r;

.shape1 = { .16x64b, .16x128b, .16x256b, .32x32b }
.shape2 = { .16x32bx2 }
.num    = { .x1, .x2, .x4, .x8, .x16, .x32, .x64, .x128 }
.unpack = { .unpack::16b }

描述

指令 tcgen05.st 异步地将源寄存器 r 中的数据存储到由32位地址操作数 taddr 指定的Tensor Memory位置,该操作在所有线程束的线程间协同执行。

warp中的所有线程必须指定相同的taddr值,该值必须是集体存储操作的基础地址。否则,行为将是未定义的。

.shape限定符和.num限定符共同决定了存储在张量内存中的数据总维度。.shape限定符表示待访问数据的基础维度,如数据移动形状中所述。.num限定符则表示基础维度的重复因子,最终形成所访问数据的总维度。

形状.16x32bx2表示对形状为.16x32b的张量内存执行两次访问。第一次访问的基地址由taddr指定,第二次访问的基地址由taddr+immHalfSplitoff指定,其中immHalfSplitoff是一个立即数参数。

源操作数 r 是一个大括号包围的向量表达式,根据 .shape.num 的值由一个或多个32位寄存器组成。不同 .num.shape 组合对应的向量大小如 表47 所示。

表47 num和shape的各种组合

.num

.shape

.16x32bx2 / .16x64b / .32x32b

.16x128b

.16x256b

.x1

1

2

4

.x2

2

4

8

.x4

4

8

16

.x8

8

16

32

.x16

16

32

64

.x32

32

64

128

.x64

64

128

不适用

.x128

128

不适用

不适用

可选限定符 .unpack::16b 可用于将寄存器中的32位元素解包为两个16位元素,并将它们存储到相邻列中,如Packing and Unpacking章节所示。

强制性的.sync限定符表示tcgen05.st会使执行线程等待,直到warp中的所有线程都执行相同的tcgen05.st指令后才会继续执行。

强制性的.aligned限定符表示warp中的所有线程必须执行相同的tcgen05.st指令。在条件执行代码中,只有当确定warp中的所有线程对条件的评估结果完全一致时,才应使用tcgen05.st指令,否则行为将是未定义的。

如果所有线程未使用相同的taddr值,或者warp中有任何线程已退出,则tcgen05.st的行为是未定义的。

指令 tcgen05.st 是异步执行的,更多细节请参阅章节 Memory Consistency Model for 5th generation of TensorCore operations

PTX ISA 说明

在PTX ISA版本8.6中引入。

目标ISA注意事项

支持以下架构:

  • sm_100a

  • sm_101a

示例

tcgen05.st.sync.aligned.16x64b.x4.b32               [taddr0], {r0,  r1,  r2,  r3};

tcgen05.st.sync.aligned.16x128b.x1.unpack::16b.b32  [taddr1], {r0,  r1};
9.7.16.7.5. Tensorcore 第五代指令: tcgen05.wait

tcgen05.wait

等待所有先前的异步tcgen05.ld / tcgen05.st指令完成。

语法

tcgen05.wait_operation.sync.aligned;

.wait_operation = { .wait::ld, .wait::st }

描述

指令 tcgen05.wait::st 会使执行线程阻塞,直到该线程之前发出的所有 tcgen05.st 操作都完成。

指令 tcgen05.wait::ld 会使执行线程阻塞,直到该线程之前发出的所有 tcgen05.ld 操作都已完成。

强制性的.sync限定符表示tcgen05.wait_operation会使执行线程等待,直到线程束中的所有线程都执行相同的tcgen05.wait_operation指令后才会继续执行。

强制性的 .aligned 限定符表示线程束中的所有线程必须执行相同的 tcgen05.wait_operation 指令。

PTX ISA 说明

在PTX ISA版本8.6中引入。

目标ISA注意事项

支持以下架构:

  • sm_100a

  • sm_101a

示例

Example 1:

tcgen05.ld.sync.aligned.32x32b.x2.b32     {r0, r1}, [taddr0];

// Prevents subsequent tcgen05.mma from racing ahead of the tcgen05.ld

tcgen05.wait::ld.sync.aligned;

tcgen05.mma.cta_group::1.kind::f16   [taddr0],  a-desc,  b-desc, idesc, p;

Example 2:

tcgen05.st.sync.aligned.32x32b.x2.b32     [taddr0], {r0, r1};

// Prevents the write to taddr0 in tcgen05.mma from racing ahead of the tcgen05.st

tcgen05.wait::st.sync.aligned;

tcgen05.mma.cta_group::1.kind::f16   [taddr0],  a-desc,  b-desc, idesc, p;

9.7.16.8. 张量内存数据移动指令

可以使用tcgen05.cp操作将共享内存中的数据异步复制到Tensor Memory

9.7.16.8.1. 可选解压

可选地,在复制过程中,可以将4位和6位自定义浮点类型的向量解压缩为8位类型。

9.7.16.8.1.1. 4位浮点数解压至8位类型

一组连续的16个4位元素,后跟8字节的填充,可以转换为16个8位元素,如图188所示。

_images/tcgen05-decompression-4b8b.png

图 188 从4位解压到8位

单个4位到8位的解压缩过程如图189所示。

_images/tcgen05-decompression-4b8b-individual.png

图 189 从4位到8位的单独解压

9.7.16.8.1.2. 6位浮点数解压至8位类型

一组连续的16个6位元素,后跟4字节的填充数据,将被解压缩为16个8位元素,如图190所示。

_images/tcgen05-decompression-6b8b.png

图 190 从6位到8位的解压缩

针对E3M2E2M3类型的6位到8位单独解压缩过程分别展示在 Figure 191Figure 192中。

_images/tcgen05-decompression-6b8b-individual1.png

图191 E3M2类型的6位到8位单独解压

_images/tcgen05-decompression-6b8b-individual2.png

图192 E2M3类型的6位到8位单独解压

9.7.16.8.2. Tensorcore 第五代指令集:tcgen05.cp

tcgen05.cp

启动从共享内存到Tensor Memory的异步复制操作。

语法

tcgen05.cp.cta_group.shape{.multicast}{.dst_fmt.src_fmt} [taddr], s-desc;

.cta_group = { .cta_group::1, .cta_group::2 }
.src_fmt   = { .b6x16_p32 , .b4x16_p64 }
.dst_fmt   = { .b8x16 }
.shape     = { .128x256b, .4x256b, .128x128b, .64x128b**, .32x128b*** }
.multicast = { .warpx2::02_13** , .warpx2::01_23**, .warpx4*** }

描述

指令 tcgen05.cp 启动一个从共享内存到Tensor Memory中由地址操作数 taddr 指定位置的异步拷贝操作。

64位寄存器操作数s-desc是矩阵描述符,表示需要复制的共享内存中的源矩阵。矩阵描述符的格式详见Matrix Descriptor Format

.shape限定符表示要复制的数据维度,如Data Movement Shape中所述。

限定符 .cta_group 指定了当单个CTA的单个线程执行 tcgen05.cp 指令时,有多少个CTA的Tensor Memory会被访问。当指定 .cta_group::1 时,数据会被复制到当前CTA的Tensor Memory中。当指定 .cta_group::2 时,数据会被复制到当前CTA及其peer CTAsTensor Memory中。

当指定限定符.dst_fmt.src_fmt时,数据将通过复制操作从共享内存中的源格式.src_fmt解压缩到Tensor Memory中的目标格式.dst_fmt。源格式和目标格式的详细信息如章节Optional Decompression所述。

某些.shape限定符需要特定的.multicast限定符。

  1. .64x128b 需要 .warpx2::02_13.warpx2::01_23

  2. .32x128b 需要 .warpx4

当指定.multicast限定符为.warpx2::02_13.warpx2::01_23时,被复制的数据将被多播到warp对中,且warp对中的每个warp接收一半数据。warp对按以下方式形成:

  1. .warpx2::02_13 : warp 0和2组成一对;warp 1和3组成一对。

  2. .warpx2::01_23 : warp 0和1组成一对;warp 2和3组成一对。

.multicast修饰符指定为.warpx4时,被复制的数据将被多播到所有4个warp中。

PTX ISA 说明

在PTX ISA版本8.6中引入。

目标ISA注意事项

支持以下架构:

  • sm_100a

  • sm_101a

示例

tcgen05.cp.cta_group::1.128x256b                 [taddr0], sdesc0;
tcgen05.cp.cta_group::2.128x128b.b8x16.b6x16_p32 [taddr1], sdesc1;
tcgen05.cp.cta_group::1.64x128b.warpx2::02_13    [taddr2], sdesc2;
9.7.16.8.3. Tensorcore 第五代指令集:tcgen05.shift

tcgen05.shift

异步地将矩阵的行向下移动,用于Tensor Memory中的一个warp。

语法

tcgen05.shift.cta_group.down  [taddr];

.cta_group = { .cta_group::1, .cta_group::2 }

描述

指令 tcgen05.shift 是一个异步指令,它会启动将32字节元素向下移动一个行的操作(最后一行除外)。地址操作数 taddr 指定了Tensor Memory中矩阵的基地址,该矩阵的行必须向下移动。

地址操作数 taddr 的通道必须对齐到32位。

限定符 .cta_group 指定了当单个CTA的单个线程执行 tcgen05.shift 指令时,会访问多少个CTA的张量内存。当指定 .cta_group::1 时,移位操作在当前CTA的张量内存中执行。当指定 .cta_group::2 时,移位操作会在当前CTA和对等CTA张量内存中同时执行。

PTX ISA 说明

在PTX ISA版本8.6中引入。

目标ISA注意事项

支持以下架构:

  • sm_100a

  • sm_101a

示例

tcgen05.shift.down.cta_group::1 [taddr0];
tcgen05.shift.down.cta_group::2 [taddr1];

9.7.16.9. TensorCore 第五代矩阵乘加运算

第五代TensorCore运算支持MxNxK形状的矩阵乘法累加操作,其运算形式为:

D = A*B+D

其中:

  • A矩阵的形状为MxK,可以存储在张量内存或共享内存中

  • B矩阵的形状为KxN,存储在当前CTA的共享内存中,也可选择存储在peer CTA中

  • 矩阵 D 的形状为 MxN,位于张量内存中

可选地,可以使用输入谓词来禁用来自累加器矩阵的输入,并执行以下操作:

D = A*B

矩阵乘法和累加运算根据输入类型和乘法运算的吞吐量被分为多种类别。以下是所支持的不同类型的MMA运算:

  1. f16 : 支持 f16bf16 输入类型。

  2. tf32 : 支持 tf32 输入类型。

  3. f8f6f4 : 支持 f8f6f4 类型的所有输入组合。

  4. i8 : 支持有符号和无符号8位整数输入类型。

  5. mxf8f6f4/mxf4 : 支持mx浮点数输入类型。

  6. mxf4nvf4 : 支持 mxf4 类型以及一种自定义的NVIDIA浮点类型,用于输入向量元素为4位且需要共用缩放因子来构成完整浮点类型的情况,与其他mx类型类似。

可选地,第5代TensorCore MMA支持稠密和稀疏矩阵ASparse Matrices详细描述了稀疏矩阵的相关细节。

某些MMA类型需要在执行MMA操作前对输入矩阵进行内存缩放以形成矩阵A和矩阵BBlock Scaling详细描述了矩阵缩放的具体细节。

下表展示了MMA操作中涉及的各种矩阵及其可以驻留的内存位置:

矩阵类型

内存

A

张量内存或共享内存

B

共享内存

D

张量内存

稀疏 数据

A-Scale / B-Scale

一系列MMA指令可能会重复使用相同的A矩阵与一系列B矩阵进行运算,或者重复使用相同的B矩阵与一系列A矩阵进行运算。在这些模式中,TensorCore可能只需加载一次未改变的矩阵,并在整个序列中重复使用,而无需多次重新加载。AB矩阵会被加载到TensorCore的收集器缓冲区(即特殊缓存)中。

MMA指令有一个可选的collector限定符,用于指定AB矩阵何时是序列中新出现的并应被加载、在序列中保持不变并应被重用、或是序列中的最后一次使用并应被丢弃。collector限定符用于授予TensorCore重用先前加载的AB矩阵的权限;然而重用是机会性的,因为TensorCore即使有重用该矩阵的权限也可能重新加载矩阵。因此,在使用这些矩阵的MMA指令完成之前,AB矩阵的源内存不得被修改——无论collector限定符权限如何。

第五代TensorCore MMA可用于通用矩阵乘法或卷积运算。在卷积运算中,激活值可以存储在矩阵A或矩阵B中,而权重将存储在另一个矩阵中。

激活 矩阵

权重 矩阵

操作 名称

指令 名称

收集器缓冲区适用性

A

B

激活 静止

(默认 tcgen05.mma)

收集器缓冲区适用于矩阵 A

B

A

权重 静态

.ws

收集器缓冲区适用于矩阵 B

9.7.16.9.1. 转置与取反操作

矩阵 AB 可以通过在指令描述符中分别设置转置 A 矩阵和转置 B 矩阵的标志位来进行转置操作。

矩阵AB的元素可以通过在指令描述符中分别设置Negate A Matrix和Negate B Matrix位来进行取反操作。

对各种MMA-Kind的转置(Transpose)和取反(Negate)操作的支持情况如 表48所示。

表 48 针对各类MMA类型的转置与取反操作

MMA类型

是否支持转置A/B

是否支持取反A/B

.kind::tf32

.kind::f16

.kind::f8f6f4

.kind::mxf8f6f4

.kind::i8

.kind::mxf4

.kind::mxf4nvf4

对于.kind::tf32,矩阵AB的转置操作仅支持128B交换模式配合32B交换原子性。

对于所有其他MMA类型,在128B交换模式和32B交换原子性下,不支持对矩阵AB进行转置操作。

9.7.16.9.2. 矩阵布局组织

表49 描述了用于不同矩阵的主要性度量。

表 49 不同矩阵的主要性

矩阵

驻留在内存中

默认主序

D

张量内存

行优先

A

张量内存

共享内存

取决于交换模式。 参考 共享内存布局与交换

B

共享内存

9.7.16.9.3. 类型大小、主序与混洗的有效组合
表 50 类型大小、主序性和Swizzling的有效组合

类型-大小

主次性

矩阵

支持的交换模式

4位, 6位, 8位, 16位, 32位

A

所有交换模式

B

8位

16位

列(转置)

A

除128B外全部
使用32B原子性
进行交换

行(转置)

B

32位

列(转置)

A

仅支持128B交换 32B原子性

行(转置)

B

9.7.16.9.4. 张量内存与共享内存中元素的打包格式
9.7.16.9.4.1. 张量内存中矩阵D的打包格式

矩阵D的子字元素预计不会被封装在32位张量内存字中。 例如,如果矩阵D的元素类型是16位,那么一个张量内存字 将在其低16位中包含单个16位元素。

9.7.16.9.4.2. 矩阵A和B的打包格式

6位和4位浮点类型在张量内存和共享内存中针对不同MMA种类有不同的打包格式要求。具体要求如下。

9.7.16.9.4.3. Tensor Memory中.kind::mxf8f6f4用于矩阵A的打包格式

单独的4位和6位浮点类型元素必须按照如下方式打包在张量内存的8位容器中。这些8位容器必须连续打包在一个32位的张量内存字中。例如,如果矩阵A的元素类型为6位,那么4个连续的A元素应打包在一个32位的张量内存字中。

  • 4位打包格式如图193所示

    _images/tcgen05-packing-formats-mxf8f6f4-tmem-dig1.png

    图193 采用E2M1类型的4位打包格式

  • 6位打包格式

    • 图194所示输入E3M2类型

      _images/tcgen05-packing-formats-mxf8f6f4-tmem-dig2.png

      图194 使用E3M2类型的6位打包格式

    • 按照图195所示输入E2M3类型

      _images/tcgen05-packing-formats-mxf8f6f4-tmem-dig3.png

      图195 采用E2M3类型的6位打包格式

9.7.16.9.4.4. 矩阵A和B在共享内存中通过.kind::mxf8f6f4使用的打包格式

共享内存中的4位和6位浮点元素必须按照以下方式连续打包并填充。

  • 4-bit打包格式如图196所示

    _images/tcgen05-packing-formats-mxf8f6f4-smem-dig1.png

    图196 4-bit打包格式

  • 6位打包格式如图197所示

_images/tcgen05-packing-formats-mxf8f6f4-smem-dig2.png

图197 6位打包格式

9.7.16.9.4.5. Tensor Memory中.kind::mxf4和.kind::mxf4nvf4使用的矩阵A打包格式

两个4位浮点类型元素必须按照图198所示的方式打包在Tensor内存的8位容器中,适用于mxf4

_images/tcgen05-packing-formats-mxf4-tmem-dig1.png

图198 采用E2M1类型的4位压缩格式

9.7.16.9.4.6. 矩阵A和B在共享内存中通过.kind::mxf4和.kind::mxf4nvf4使用的打包格式

共享内存中4位浮点元素的打包格式是将两个4位元素打包到一个8位容器中,不进行填充。

9.7.16.9.5. 数据路径布局组织

不同的MMA变体以不同的布局组织形式访问张量内存。 下表列出了各种布局:

M

cta_group

A-稀疏度

是否为.ws模式

数据路径组织

布局ID

张量内存数据路径通道对齐

32

::1

两者任一

1x4

Layout G

0

64

::1

两者皆可

2x3

Layout E

0

64

::1

两者均可

4x1 (仅使用1/2数据路径)

Layout F

0或16

128

::1

Either

Either

4x1

Layout D

0

128

::2

密集

不适用

2x2

Layout B

0

128

::2

稀疏

不适用

4x1 (1/2 数据路径利用率)

布局 C

0 或 16

256

::2

Either

N/A

4x1

Layout A

0

仅利用一半数据通道的布局,即Layout FLayout C,必须在矩阵AD和稀疏元数据矩阵之间保持相同的张量内存通道对齐。

以下展示了可以通过tcgen05.ld / tcgen05.st访问张量内存区域的线程束,以及不同张量内存布局对应的地址。

9.7.16.9.5.1. 布局A (M = 256)

M = 256 的布局组织如图 199所示。

_images/tcgen05-data-path-layout-a1.png

图 199 M = 256 时的布局结构

上述区域在tcgen05.ld / tcgen05.st中使用的地址显示在图200

_images/tcgen05-data-path-layout-a2.png

图200 tcgen05.ld / tcgen05.st中使用的地址

9.7.16.9.5.2. 布局B (M = 128 + cta-group::2 + 密集A矩阵)

M = 128 + .cta_group::2 + 密集A矩阵的布局组织如图201所示。

_images/tcgen05-data-path-layout-b1.png

图201 M = 128 + .cta_group::2 + 密集A矩阵的布局结构

上述区域在tcgen05.ld / tcgen05.st中使用的地址显示在图202

_images/tcgen05-data-path-layout-b2.png

图202 tcgen05.ld / tcgen05.st中使用的地址

9.7.16.9.5.3. 布局C (M = 128 + cta-group::2 + 稀疏A矩阵)

M = 128 + .cta_group::2 + 稀疏矩阵A的布局组织如图203所示。

_images/tcgen05-data-path-layout-c1.png

图203 M = 128 + .cta_group::2 + 稀疏A矩阵的布局组织

上述区域在tcgen05.ld / tcgen05.st中使用的地址显示在图204

_images/tcgen05-data-path-layout-c2.png

图204 tcgen05.ld / tcgen05.st中使用的地址

9.7.16.9.5.4. 布局 D (M = 128 + cta-group::1)

M = 128 + .cta_group::1 的布局组织如 图 205 所示。

_images/tcgen05-data-path-layout-d1.png

图205 M = 128 + .cta_group::1的布局组织结构

上述区域在tcgen05.ld / tcgen05.st中使用的地址如图206所示

_images/tcgen05-data-path-layout-d2.png

图206 tcgen05.ld / tcgen05.st中使用的地址

9.7.16.9.5.5. 布局 E (M = 64 + .ws 模式)

M = 64 + .ws 模式的布局组织如图207所示。

_images/tcgen05-data-path-layout-e1.png

图207 M = 64 + .ws模式下的布局结构

上述区域在tcgen05.ld / tcgen05.st中使用的地址显示在图208

_images/tcgen05-data-path-layout-e2.png

图208 tcgen05.ld / tcgen05.st中使用的地址

9.7.16.9.5.6. 布局 F (M = 64 + 非 .ws 模式)

M = 64 + 非 .ws 模式下的布局组织如图209所示。

_images/tcgen05-data-path-layout-f1.png

图209 M = 64 + 非.ws模式下的布局组织

上述区域在tcgen05.ld / tcgen05.st中使用的地址显示在图210

_images/tcgen05-data-path-layout-f2.png

图210 tcgen05.ld / tcgen05.st中使用的地址

9.7.16.9.5.7. 布局 G (M = 32)

当M = 32时的布局结构如图211所示。

_images/tcgen05-data-path-layout-g1.png

图211 M = 32时的布局结构

上述区域在tcgen05.ld / tcgen05.st中使用的地址显示在图212

_images/tcgen05-data-path-layout-g2.png

图212 tcgen05.ld / tcgen05.st中使用的地址

9.7.16.9.6. 共享内存布局与数据重排

如果指令描述符中的Transpose A Matrix/Transpose B Matrix位为0,则矩阵A/B分别采用K主序排列。如果指令描述符中的Transpose A Matrix位为1,则矩阵A采用M主序排列。如果指令描述符中的Transpose B Matrix位为1,则矩阵B采用N主序排列。

在默认按列主序的BLAS库(如cuBLAS)中,矩阵AB无论是否转置,都可以归类为K主序M/N主序,如下表所示:

非转置

转置

A

K主序

M主序

B

K主序

N主序

为避免与AB行优先列优先转置非转置等术语混淆,本节将统一使用MN主序K主序的表述方式。

共享内存中的矩阵由一个或多个"swizzle布局原子"组成。 这些swizzle原子的具体布局取决于swizzling模式、swizzle原子性以及前导维度。 swizzle的布局如表51所示

表 51 原子交换的布局

混排模式与混排原子性

主导维度

混排原子布局(128位元素)

128B 交换与32B原子性

M/N

8x4

128B 交换操作,原子性为16B

M/N

8x8

K

8x8

64B 交换模式

M/N

4x8

K

8x4

32B 交换模式

M/N

2x8

K

8x2

M/N

1x8

K

8x1

上述形状适用于128位大小的元素。对于更小的元素尺寸,相同的形状会沿着前导维度乘以一个因子128 / sizeof_bits(Element)。例如,128B MN主序的swizzle原子对于tf32张量核心输入将具有(8*(128/32))x8 = 32x8的形状。

展示MxKKxN矩阵在不同swizzling模式下的示例布局,每个彩色单元格表示128b元素单位,如 图213图214图215图216图217图218图219图220图221所示。

_images/tcgen05-smem-layout-128B-32B-atom-mn.png

图213 MN主128B交换模式(32B原子性)

_images/tcgen05-smem-layout-128B-mn.png

图214 MN主128B交换模式

_images/tcgen05-smem-layout-128B-k.png

图215 K major 128B 数据混排

_images/tcgen05-smem-layout-64B-mn.png

图216 MN主64B交换模式

_images/tcgen05-smem-layout-64B-k.png

图217 K主64B交换模式

_images/tcgen05-smem-layout-32B-mn.png

图218 MN主32B交换模式

_images/tcgen05-smem-layout-32B-k.png

图219 K主32B交织模式

_images/tcgen05-smem-layout-no-swizzle-mn.png

图220 MN主无交换模式

_images/tcgen05-smem-layout-no-swizzle-k.png

图221 K主无交换模式

以下是tf32元素类型的128B swizzling布局的一些示例。

9.7.16.9.7. Block Scaling

带有以下.kind限定符的tcgen05.mma指令:

  • .kind::mxf8f6f4

  • .kind::mxf4

  • .kind::mxf4nvf4

执行带块缩放的矩阵乘法。该操作具有以下形式:

(A * scale_A) * (B * scale_B) + D

其中scale_Ascale_B是驻留在Tensor Memory中的矩阵。

对于一个形状为M x SFA_Nscale_A矩阵,矩阵A的每一行被划分为SFA_N个数据块,每行的每个数据块与同一行SF_A中的对应元素相乘。

类似地,对于形状为SFB_M x Nscale_B矩阵,矩阵B的每一列被划分为SFB_M个数据块,每个列数据块与同一列SF_B中的对应元素相乘。

图224展示了一个使用tcgen05.mma进行块缩放scale_vec::2X的示例。

_images/tcgen05-mma-block-scaling.png

图224 带块缩放scale_vec::2Xtcgen05.mma

9.7.16.9.7.1. scale_vec_size与类型及MMA-Kind的有效组合

scale_Ascale_B 矩阵的形状取决于 .scale_vec_size,如 表52所示。

表52scale_vec_size 和形状的有效组合

.scale_vec_size

scale_A的形状

scale_B的形状

.scale_vec::1X

M x 1

1 x N

.scale_vec::2X

M x 2

2 x N

.scale_vec::4X

M x 4

4 x N

精确元素类型与.scale_vec_size的有效组合列于表53中。

表 53 scale_vec_size与类型及MMA-Kind的有效组合

.kind::*

元素数据类型

缩放数据类型

.scale_vec_size

.kind::mxf8f6f4

E4M3, E5M2, E2M3 E3M2, E2M1

UE8M0

.scale_vec::1X

.kind::mxf4

E2M1

UE8M0

.scale_vec::2X

.kind::mxf4nvf4

E2M1

UE8M0

.scale_vec::2X

E2M1

UE4M3

.scale_vec::4X

9.7.16.9.7.2. Scale Factor A ID

比例因子A ID的值用于选择张量内存中的子列,以构成比例因子A矩阵,该矩阵用于缩放矩阵A

以下展示了不同缩放向量大小对应的缩放因子矩阵布局:

9.7.16.9.7.2.1.scale_vec::1X 的比例因子A矩阵布局

每行A矩阵对应一个缩放因子,该缩放因子必须存放在张量内存中1字节对齐的子列中。SFA_ID指定了张量内存字中必须用于缩放因子矩阵的字节偏移量。图225展示了不同SFA_ID值所选择的子列。

_images/tcgen05-mma-scale-factor-a-1x-dig.png

图225 使用scale_vec::1X的缩放因子A矩阵布局

例如,如果SFA_ID为0,则所有绿色列将被选中以构成比例因子矩阵。类似地,SFA_ID值为1、2和3时,将分别选择蓝色、黄色和红色列。

9.7.16.9.7.2.2.scale_vec::2X 的比例因子A矩阵布局

A矩阵的每一行有两个缩放因子,且缩放因子必须存放在张量内存的2字节对齐子列中。SFA_ID指定了必须用于缩放因子矩阵的张量内存字中的半字偏移量。图226展示了不同SFA_ID值对应的子列选择情况。

_images/tcgen05-mma-scale-factor-a-2x-dig.png

图226 缩放因子A矩阵的布局,缩放向量::2X

例如,如果SFA_ID为0,则选择所有绿色列构成比例因子矩阵。类似地,如果SFA_ID为2,则选择所有蓝色列构成比例因子矩阵。

9.7.16.9.7.2.3.scale_vec::4X 比例因子A矩阵的布局

A矩阵的每一行有四个缩放因子,缩放因子必须存放在张量内存中4字节对齐的子列中。SFA_ID值必须设为0,这表示所有绿色标注的列都将用作缩放因子矩阵。图227展示了不同SFA_ID值对应的子列选择情况。

_images/tcgen05-mma-scale-factor-a-4x-dig.png

图227 使用scale_vec::4X的缩放因子A矩阵布局

9.7.16.9.7.3. Scale Factor B ID

比例因子B ID的值用于选择张量内存中的子列,以构成比例因子B矩阵,该矩阵用于缩放矩阵B

以下展示了不同缩放向量大小对应的缩放因子矩阵布局:

9.7.16.9.7.3.1.scale_vec::1X 比例因子B矩阵的布局

对于B矩阵的每一行都有一个缩放因子,且该缩放因子必须存放在张量内存中按1字节对齐的子列中。SFB_ID参数用于指定张量内存字中存放缩放因子矩阵的字节偏移量。图228展示了不同SFB_ID值所对应的子列选择情况。

_images/tcgen05-mma-scale-factor-b-1x-dig.png

图228 缩放因子B矩阵的布局(scale_vec::1X)

例如,如果SFB_ID为0,则选择所有绿色列构成比例因子矩阵。类似地,SFB_ID值为1、2和3时,将分别选择蓝色、黄色和红色列。

9.7.16.9.7.3.2.scale_vec::2X 比例因子B矩阵的布局

B矩阵的每一行有两个缩放因子,且缩放因子必须存放在张量内存的2字节对齐子列中。SFB_ID指定了必须用于缩放因子矩阵的张量内存字中的半字偏移量。图229展示了不同SFB_ID值对应的子列选择情况。

_images/tcgen05-mma-scale-factor-b-2x-dig.png

图229 使用scale_vec::2X的缩放因子B矩阵布局

例如,如果SFB_ID为0,则选择所有绿色列构成比例因子矩阵。类似地,如果SFB_ID为2,则选择所有蓝色列构成比例因子矩阵。

9.7.16.9.7.3.3.scale_vec::4X 比例因子 B 矩阵的布局

B矩阵的每一行有四个缩放因子,且缩放因子必须存放在张量内存中4字节对齐的子列中。SFB_ID值必须设为0,这表示所有绿色标注的列都将用作缩放因子矩阵。 图230展示了不同SFB_ID值对应的子列选择情况。

_images/tcgen05-mma-scale-factor-b-4x-dig.png

图230 带有scale_vec::4X的缩放因子B矩阵布局

9.7.16.9.8. 稀疏矩阵

当矩阵A是结构化稀疏矩阵且每行有50%零元素按照其稀疏粒度分布时,可以使用该指令tcgen05.mma.sp

MxNxK稀疏tcgen05.mma.sp运算中,形状为MxK的矩阵A以压缩形式存储为内存中的Mx(K/2)。对于矩阵A的每个K宽行,50%的元素为零,其余K/2个非零元素存储在内存中。元数据指定了在执行MMA运算前,将K/2个非零元素映射到K个元素的对应关系。

稀疏矩阵A的粒度定义为矩阵行子块中非零元素数量与该子块总元素数量的比值,其中子块大小取决于具体形状。下表列出了不同tcgen05.mma.sp变体的粒度:

tcgen05.mma的类型

稀疏粒度

.kind::tf32

1:2

.kind::f16

2:4

.kind::f8f6f4

.kind::mxf8f6f4

.kind::i8

.kind::mxf4

4:8 (成对)

9.7.16.9.8.1. 使用.kind::tf32生成稀疏tcgen05.mma.sp

对于.kind::tf32,矩阵A1:2的粒度上是结构化稀疏的。 换句话说,矩阵A每一行中每两个相邻元素组成的块中有一个 零元素和一个非零元素。只有非零元素会被存储在内存中,而 元数据中的4位索引表示非零元素在 两元素块中的位置。该索引唯一有意义的值为:

  • 0b1110

  • 0b0100

其余值会导致未定义行为。

_images/tcgen05-sparse-mma-metadata-tf32.png

图231 tf32类型的稀疏tcgen05.mma元数据示例

9.7.16.9.8.2. Sparse tcgen05.mma.sp with .kind::f16, .kind::f8f6f4, .kind::mxf8f6f4, .kind::i8

对于以下 .kind 变体的 tcgen05.mma

  • .kind::f16

  • .kind::f8f8f4

  • .kind::mxf8f6f4

  • .kind::i8

矩阵 A2:4 的粒度上具有结构化稀疏性。换句话说,矩阵 A 每一行中每四个相邻元素组成的块中有两个零元素和两个非零元素。只有非零元素会被存储在内存中,而元数据中的两个2位索引指示了这两个非零元素在四元素块中的位置。该索引唯一有效的取值为:

  • 0b0100

  • 0b1000

  • 0b1100

  • 0b1001

  • 0b1101

  • 0b0110

  • 0b1110

_images/tcgen05-sparse-mma-metadata-f16-f8f6f4-mxf8f6f4.png

图232 针对f16/f8f6f4/mxf8f6f4类型的稀疏tcgen05.mma元数据示例

9.7.16.9.8.3. 使用.kind::mxf4和.kind::mxf4nvf4生成稀疏tcgen05.mma.sp

对于.kind::mxf4.kind::mxf4nvf4类型,矩阵A采用粒度为4:8的成对结构化稀疏存储。换句话说,矩阵A每行中连续的八个元素块包含四个零元素和四个非零元素。这些零元素和非零元素被分组存储在八个元素块内两个元素为一组的子块中,因此八个元素块内每个两元素子块必须全为零或全为非零。内存中仅存储四个非零元素,元数据中的两个2位索引值用于指示矩阵A每行八个元素块中两个包含非零值的两元素子块的位置。索引的有效取值仅限于:

  • 0b0100

  • 0b1000

  • 0b1100

  • 0b1001

  • 0b1101

  • 0b0110

  • 0b1110

其余值会导致未定义行为。

_images/tcgen05-sparse-mma-metadata-mxf4.png

图233 针对mxf4类型的稀疏tcgen05.mma元数据示例

9.7.16.9.8.4. 稀疏度选择器

稀疏选择器的值用于选择张量内存中的子列,以形成稀疏元数据矩阵,该矩阵与矩阵A一起用于构成被乘数矩阵。

以下展示了不同MMA变体在张量内存中的稀疏元数据矩阵布局:

9.7.16.9.8.4.1.针对M=64的.kind::f16稀疏元数据矩阵布局

图234展示了针对不同稀疏选择器值所选取的子列。

_images/tcgen05-sparse-matrices-sparsity-selctor-kind-f16-m64.png

图234 M = 64时的稀疏元数据布局(适用于.kind::f16

9.7.16.9.8.4.2.针对M = 128 / M = 256的.kind::f16稀疏元数据矩阵布局

图235展示了针对不同稀疏选择器值所选取的子列。

_images/tcgen05-sparse-matrices-sparsity-selctor-kind-f16-m128-256.png

图235 M = 128 / M = 256时.kind::f16的稀疏元数据布局

9.7.16.9.8.4.3.针对M=64的.kind::tf32稀疏元数据矩阵布局

图236展示了针对不同稀疏选择器值所选择的子列。

_images/tcgen05-sparse-matrices-sparsity-selctor-kind-tf32-m64.png

图236 M = 64时的稀疏元数据布局,适用于.kind::tf32

9.7.16.9.8.4.4.针对M = 128 / M = 256的.kind::tf32稀疏元数据矩阵布局

图237展示了针对不同稀疏选择器值所选取的子列。

_images/tcgen05-sparse-matrices-sparsity-selctor-kind-tf32-m128-256.png

图237 M = 128 / M = 256时.kind::tf32的稀疏元数据布局

9.7.16.9.8.4.5. 针对M=64的稀疏元数据矩阵布局,适用于.kind::f8f6f4、.kind::mxf8f6f4、.kind::i8、.kind::mxf4、.kind::mxf4nvf4类型

稀疏选择器的值:

  • 对于 .kind::i8.kind::f8f6f4 必须为 0

  • 对于 .kind::mxf8f6f4.kind::mxf4.kind::mxf4nvf4 类型,默认值为 0

所有列都被选中,如图238所示

_images/tcgen05-sparse-matrices-sparsity-selctor-kind-f8f6f4-mxf8f6f4-m64.png

图238 M = 64时的稀疏元数据布局,适用于.kind::f8f6f4, .kind::mxf8f6f4, .kind::i8, .kind::mxf4, .kind::mxf4nvf4

9.7.16.9.8.4.6.M = 128 / M = 256 的稀疏元数据矩阵布局,适用于 .kind::f8f6f4, .kind::mxf8f6f4, .kind::i8, .kind::mxf4, .kind::mxf4nvf4

稀疏选择器的值:

  • 对于 .kind::i8.kind::f8f6f4 必须为 0

  • 对于 .kind::mxf8f6f4.kind::mxf4.kind::mxf4nvf4 类型,默认值为 0

所有列都被选中,如图239所示

_images/tcgen05-sparse-matrices-sparsity-selctor-kind-f8f6f4-mxf8f6f4-m128-256.png

图239 针对M = 128 / M = 256的稀疏元数据布局,适用于.kind::f8f6f4, .kind::mxf8f6f4, .kind::i8, .kind::mxf4, .kind::mxf4nvf4

9.7.16.9.8.5. 对齐限制

仅使用数据路径通道一半的布局(如数据路径布局组织中所述),即布局F布局C,必须在矩阵A、D和稀疏元数据矩阵之间保持相同的对齐方式。

9.7.16.9.9. TensorCore 第五代 MMA 指令集
9.7.16.9.9.1. 第五代TensorCore指令集:tcgen05.mma

tcgen05.mma

执行第五代矩阵乘加运算。

语法

// 1. Floating-point type without block scaling:

tcgen05.mma.cta_group.kind   [d-tmem],  a-desc,  b-desc, idesc,
                             { disable-output-lane }, enable-input-d {, scale-input-d};

tcgen05.mma.cta_group.kind   [d-tmem], [a-tmem], b-desc, idesc,
                             { disable-output-lane }, enable-input-d {, scale-input-d};

.kind      = { .kind::f16, .kind::tf32, .kind::f8f6f4 }
.cta_group = { .cta_group::1, .cta_group::2 }

----------------------------------------------------------------------------------

// 2. Floating-point type with block scaling:

tcgen05.mma.cta_group.kind.block_scale{.scale_vec_size}
                                        [d-tmem],  a-desc,  b-desc, idesc,
                                        [scale-A-tmem], [scale-B-tmem], enable-input-d;

tcgen05.mma.cta_group.kind.block_scale{.scale_vec_size}
                                        [d-tmem], [a-tmem], b-desc, idesc,
                                        [scale-A-tmem], [scale-B-tmem], enable-input-d;

.kind = { .kind::mxf8f6f4, .kind::mxf4, .kind::mxf4nvf4 }
.cta_group      = { .cta_group::1,   .cta_group::2 }
.scale_vec_size = { .scale_vec::1X, .scale_vec::2X, .scale_vec::4X }

----------------------------------------------------------------------------------

// 3. Convolution MMA for floating-point type without block scaling:

tcgen05.mma.cta_group.kind.collector_usage [d-tmem],  a-desc,  b-desc, idesc,
                                           { disable-output-lane }, enable-input-d {, scale-input-d};

tcgen05.mma.cta_group.kind{.ashift}.collector_usage [d-tmem], [a-tmem], b-desc, idesc,
                                                    { disable-output-lane }, enable-input-d {, scale-input-d};

tcgen05.mma.cta_group.kind.ashift{.collector_usage} [d-tmem], [a-tmem], b-desc, idesc,
                                                    { disable-output-lane }, enable-input-d {, scale-input-d};

.kind      = { .kind::f16, .kind::tf32, .kind::f8f6f4 }
.cta_group = { .cta_group::1,   .cta_group::2 }
.collector_usage = { .collector::buffer::op }
::buffer         = { ::a }
::op             = { ::fill, ::use, ::lastuse, ::discard* }

----------------------------------------------------------------------------------

// 4. Activation Stationary MMA for floating-point type with block scaling:

tcgen05.mma.cta_group.kind.block_scale{.scale_vec_size}.collector_usage
                                            [d-tmem],  a-desc,  b-desc, idesc,
                                            [scale-A-tmem], [scale-B-tmem], enable-input-d;

tcgen05.mma.cta_group.kind.block_scale{.scale_vec_size}.collector_usage
                                            [d-tmem], [a-tmem], b-desc, idesc,
                                            [scale-A-tmem], [scale-B-tmem], enable-input-d;

.cta_group       = { .cta_group::1,   .cta_group::2 }
.scale_vec_size  = { .scale_vec::1X, .scale_vec::2X, .scale_vec::4X }
.kind            = { .kind::mxf8f6f4, .kind::mxf4, .kind::mxf4nvf4 }
.collector_usage = { .collector::buffer::op }
::buffer         = { ::a }
::op             = { ::fill, ::use, ::lastuse, ::discard* }

----------------------------------------------------------------------------------

// 5. Integer type:

tcgen05.mma.cta_group.kind::i8  [d-tmem],  a-desc,  b-desc, idesc,
                                { disable-output-lane }, enable-input-d;

tcgen05.mma.cta_group.kind::i8  [d-tmem], [a-tmem], b-desc, idesc,
                                { disable-output-lane }, enable-input-d;

.cta_group = { .cta_group::1,   .cta_group::2  }

----------------------------------------------------------------------------------

// 6. Convolution MMA for integer type:

tcgen05.mma.cta_group.kind::i8.collector_usage          [d-tmem],  a-desc,  b-desc, idesc,
                                                        { disable-output-lane }, enable-input-d;

tcgen05.mma.cta_group.kind::i8.ashift{.collector_usage} [d-tmem], [a-tmem], b-desc, idesc,
                                                        { disable-output-lane }, enable-input-d;

tcgen05.mma.cta_group.kind::i8{.ashift}.collector_usage [d-tmem], [a-tmem], b-desc, idesc,
                                                        { disable-output-lane }, enable-input-d;

.cta_group       = { .cta_group::1,   .cta_group::2  }
.collector_usage = { .collector::buffer::op }
::buffer         = { ::a }
::op             = { ::fill, ::use, ::lastuse, ::discard* }

描述

指令 tcgen05.mma 是一个异步指令,用于启动一个MxNxK矩阵的乘加运算, D = A*B+D 其中A矩阵是MxKB矩阵是KxN,而D矩阵是MxN

当输入谓词参数 enable-input-d 为 false 时,会执行形式为 D = A*B 的运算。

可选参数 scale-input-d 可用于按以下方式缩放输入矩阵 DD = A*B+D * (2 ^ - scale-input-d)

参数 scale-input-d 的有效取值范围为 [0, 15]。该参数 scale-input-d 仅适用于 .kind::tf32.kind::f16

32位寄存器操作数idesc是指令描述符,如Instruction descriptor中所述,它指定了输入矩阵、输出矩阵以及矩阵乘加运算的形状、精确类型、稀疏性等细节。

限定符 .cta_group::1 指定矩阵乘加运算仅在执行线程CTA的Tensor Memory上执行。限定符 .cta_group::2 指定矩阵乘加运算在执行线程CTA及其peer CTATensor Memory上执行。

指令tcgen05.mma具有单线程语义,与集体指令mma.syncwgmma.mma_async不同。因此,单个线程发出tcgen05.mma将启动整个矩阵乘加运算。请参阅章节Issue granularity

限定符 .kind 指定了被乘矩阵元素类型的通用类别。每种MMA类型的输入和输出矩阵元素的具体类型在指令描述符中进行了规定。

地址操作数 d-tmem 指定了目标地址和张量内存中累加矩阵 D 的地址。地址操作数 a-tmem 指定了张量内存中矩阵 A 的地址。64位寄存器操作数 a-descb-desc 是矩阵描述符,分别表示共享内存中的矩阵 AB。矩阵描述符的格式在矩阵描述符格式中有详细说明。

向量操作数disable-output-lane用于指定Tensor Memory中不应被结果矩阵D更新的通道。该向量操作数的各元素构成一个掩码,其中每个比特对应Tensor Memory的一个通道(语法中最左侧的向量首元素最低有效位对应Tensor Memory的通道0)。若掩码中某位为1,则结果矩阵D在Tensor Memory中的对应通道将不被更新。该向量的尺寸规定如下:

.cta_group

禁用输出通道的向量大小

::1

4

::2

8

限定符 .block_scale 指定在执行矩阵乘加运算时(如块缩放部分所述),矩阵 AB 将分别与 scale_Ascale_B 矩阵进行缩放。 地址操作数 scale-A-tmemscale-B-tmem 分别指定了矩阵 scale_Ascale_B张量内存中的基地址。

限定符 .scale_vec_size 指定了 scale_A 矩阵的列数和 scale_B 的行数。MMA类型与 .scale_vec_size 的有效组合在 表52 中有详细说明。对于 .kind::mxf4,当未指定限定符 .scale_vec_size 时,默认值为 2X。而对于 .kind::mxf4nvf4,必须明确指定限定符 .scale_vec_size

限定符 .ashiftA 矩阵的行向下移动一行,但 Tensor Memory 中的最后一行除外。限定符 .ashift 仅允许在 M = 128 或 M = 256 时使用。

限定符 .collector_usage 指定了矩阵 A 收集器缓冲区的用途。 可以指定以下收集器缓冲区操作:

.collector_usage

语义

.collector::a::fill

指定从内存读取的A矩阵应填充到收集器缓冲区中。

.collector::a::use

指定可以从收集器缓冲区读取A矩阵。这要求之前对收集器缓冲区的填充仍然有效。

.collector::a::lastuse

指定可以从收集器缓冲区读取A矩阵,并且可以丢弃收集器缓冲区的内容。这要求之前对收集器缓冲区的填充在读取收集器缓冲区之前保持有效。

.collector::a::discard

指定可以丢弃A收集器缓冲区的内容。

如果未指定.collector_usage限定符,则默认为.collector::a::discard。 同时指定.collector::a::use.collector::a::fill.ashift是无效的。

PTX ISA 说明

在PTX ISA版本8.6中引入。

限定符 .kind::mxf4nvf4 在 PTX ISA 8.7 版本中引入。

目标ISA注意事项

支持以下架构:

  • sm_100a

  • sm_101a

参数 scale-input-d 需要 sm_100a

示例

tcgen05.mma.cta_group::1.kind::tf32      [taddr0],  adesc,  bdesc, idesc, {m0, m1, m2, m3}, p;
tcgen05.mma.cta_group::1.kind::mxf8f6f4  [taddr2],  [taddr1],  bdesc, idesc,
                                         [tmem_scaleA], [tmem_scaleB], p;

tcgen05.commit.cta_group::1.mbarrier::arrive::one.b64 [mbarObj0];

loop:
mbarrier.try_wait.parity.b64 p, [mbarObj0], 0;
@!p bra loop;
9.7.16.9.9.2. 第五代TensorCore指令集:tcgen05.mma.sp

tcgen05.mma.sp

使用稀疏矩阵A执行第五代矩阵乘加运算。

语法

// 1. Floating-point type without block scaling:

tcgen05.mma.sp.cta_group.kind  [d-tmem],  a-desc,  b-desc, [sp-meta-tmem] ,  idesc,
                               { disable-output-lane }, enable-input-d{, scale-input-d};

tcgen05.mma.sp.cta_group.kind  [d-tmem], [a-tmem], b-desc, [sp-meta-tmem] , idesc,
                               { disable-output-lane }, enable-input-d{, scale-input-d};

.kind       = { .kind::f16, , .kind::tf32, .kind::f8f6f4 }
.cta_group  = { .cta_group::1,  .cta_group::2 }

----------------------------------------------------------------------------------

// 2. Floating-point type with block scaling:

tcgen05.mma.sp.cta_group.kind.block_scale{.scale_vec_size}
                                         [d-tmem],  a-desc,  b-desc , [sp-meta-tmem] , idesc,
                                         [scale-A-tmem], [scale-B-tmem], enable-input-d;

tcgen05.mma.sp.cta_group.kind.block_scale{.scale_vec_size}
                                         [d-tmem], [a-tmem], b-desc , [sp-meta-tmem] , idesc,
                                         [scale-A-tmem], [scale-B-tmem], enable-input-d;

.scale_vec_size = { .scale_vec::1X, .scale_vec::2X, .scale_vec::4X }
.cta_group      = { .cta_group::1,  .cta_group::2 }
.kind = { .kind::mxf8f6f4, .kind::mxf4, .kind::mxf4nvf4 }

----------------------------------------------------------------------------------

// 3. Convolution MMA with floating-point type without block scaling:

tcgen05.mma.sp.cta_group.kind.collector_usage           [d-tmem],  a-desc,  b-desc,
                                                        [sp-meta-tmem] ,  idesc,
                                                        { disable-output-lane }, enable-input-d
                                                        {, scale-input-d};

tcgen05.mma.sp.cta_group.kind.ashift{.collector_usage}  [d-tmem], [a-tmem], b-desc,
                                                        [sp-meta-tmem] , idesc,
                                                        { disable-output-lane }, enable-input-d
                                                        {, scale-input-d};

tcgen05.mma.sp.cta_group.kind{.ashift}.collector_usage  [d-tmem], [a-tmem], b-desc,
                                                        [sp-meta-tmem] , idesc,
                                                        { disable-output-lane }, enable-input-d
                                                        {, scale-input-d};

.kind            = { .kind::f16, .kind::tf32, .kind::f8f6f4 }
.collector_usage = { .collector::buffer::op }
::buffer         = { ::a }
::op             = { ::fill, ::use, ::lastuse, ::discard* }

----------------------------------------------------------------------------------

// 4. Activation Stationary MMA with floating-point type with block scaling:

tcgen05.mma.sp.cta_group.kind.block_scale{.scale_vec_size}.collector_usage
                                         [d-tmem],  a-desc,  b-desc , [sp-meta-tmem] , idesc,
                                         [scale-A-tmem], [scale-B-tmem], enable-input-d;

tcgen05.mma.sp.cta_group.kind.block_scale{.scale_vec_size}.collector_usage
                                         [d-tmem], [a-tmem], b-desc , [sp-meta-tmem] , idesc,
                                         [scale-A-tmem], [scale-B-tmem], enable-input-d;

.kind = { .kind::mxf8f6f4, .kind::mxf4, .kind::mxf4nvf4 }
.scale_vec_size = { .scale_vec::1X, .scale_vec::2X, .scale_vec::4X }
.collector_usage = { .collector::buffer::op }
::buffer         = { ::a }
::op             = { ::fill, ::use, ::lastuse, ::discard* }

----------------------------------------------------------------------------------

// 5. Integer type:

tcgen05.mma.sp.cta_group.kind::i8 [d-tmem],  a-desc,  b-desc, [sp-meta-tmem] , idesc,
                                  { disable-output-lane }, enable-input-d;

tcgen05.mma.sp.cta_group.kind::i8 [d-tmem], [a-tmem], b-desc, [sp-meta-tmem] , idesc,
                                  { disable-output-lane }, enable-input-d;

.cta_group      = { .cta_group::1,  .cta_group::2 }

----------------------------------------------------------------------------------

// 6. Convolution MMA with Integer type:

tcgen05.mma.sp.cta_group.kind::i8.collector_usage          [d-tmem],  a-desc,  b-desc,
                                                           [sp-meta-tmem] , idesc,
                                                           { disable-output-lane }, enable-input-d;

tcgen05.mma.sp.cta_group.kind::i8.ashift{.collector_usage} [d-tmem], [a-tmem], b-desc,
                                                           [sp-meta-tmem], idesc ,
                                                           { disable-output-lane }, enable-input-d;

tcgen05.mma.sp.cta_group.kind::i8{.ashift}.collector_usage [d-tmem], [a-tmem], b-desc,
                                                           [sp-meta-tmem], idesc ,
                                                           { disable-output-lane }, enable-input-d;

.collector_usage = { .collector::buffer::op }
::buffer         = { ::a }
::op             = { ::fill, ::use, ::lastuse, ::discard* }

描述

指令 tcgen05.mma.sp 是一个异步指令,用于启动一个 MxNxK 矩阵乘加运算,其形式为 D = A*B+D。其中 A 矩阵为 Mx(K/2)B 矩阵为 KxN,而 D 矩阵为 MxNSparse Matrices 描述了稀疏性的详细信息。

当输入谓词参数 enable-input-d 为 false 时,会执行形式为 D = A*B 的运算。

可选参数 scale-input-d 可用于按以下方式缩放输入矩阵 DD = A*B+D * (2 ^ - scale-input-d)

参数 scale-input-d 的有效取值范围为 [0, 15]。该参数 scale-input-d 仅适用于 .kind::tf32.kind::f16

32位寄存器操作数idesc是指令描述符,如Instruction descriptor中所述,它指定了输入矩阵、输出矩阵以及矩阵乘加操作的形状、精确类型、稀疏性等详细信息。

限定符 .cta_group::1 指定矩阵乘加运算仅在执行线程CTA的张量内存上执行。限定符 .cta_group::2 指定矩阵乘加运算在执行线程CTA及其对等CTA张量内存上执行。

指令 tcgen05.mma.sp 具有单线程语义,这与集合指令 mma.syncwgmma.mma_async 不同。因此,单个线程发出 tcgen05.mma.sp 将导致整个矩阵乘加操作的启动。请参阅章节 Issue granularity

限定符 .kind 指定了被乘矩阵元素类型的通用类别。每种MMA类型的输入和输出矩阵元素的具体类型在指令描述符中进行了规定。

地址操作数 d-tmem 指定了目标地址以及张量内存中累加矩阵 D 的地址。地址操作数 a-tmem 指定了张量内存中矩阵 A 的地址。64位寄存器操作数 a-descb-desc 是矩阵描述符,分别表示共享内存中的矩阵 AB。矩阵描述符的格式详见矩阵描述符格式

向量操作数disable-output-lane用于指定Tensor Memory中不应使用结果矩阵D更新的通道。该向量操作数的元素构成一个掩码,其中每个位对应Tensor Memory的一个通道,向量第一个元素(语法中最左侧)的最低有效位对应Tensor Memory的通道0。如果掩码中某位为1,则结果矩阵D在Tensor Memory中的对应通道将不会被更新。该向量的大小如下:

.cta_group

禁用输出通道向量的大小

::1

4

::2

8

限定符 .block_scale 指定在执行矩阵乘加运算前,矩阵 AB 将分别与 scale_Ascale_B 矩阵进行缩放,具体如章节 Block Scaling 所述。 地址操作数 scale-A-tmemscale-B-tmem 分别指定了矩阵 scale_Ascale_BTensor Memory 中的基地址。

限定符 .scale_vec_size 指定了 scale_A 矩阵的列数和 scale_B 的行数。MMA类型与 .scale_vec_size 的有效组合详见 表52。对于 .kind::mxf4 类型,当未指定限定符 .scale_vec_size 时,默认值为 2X。而对于 .kind::mxf4nvf4 类型,必须显式指定限定符 .scale_vec_size

限定符 .ashiftA 矩阵的行向下移动一行,但 Tensor Memory 中的最后一行除外。限定符 .ashift 仅允许在 M = 128 或 M = 256 时使用。

限定符 .collector_usage 指定了矩阵 A 收集器缓冲区的用途。 可以指定以下收集器缓冲区操作:

.collector_usage

语义

.collector::a::fill

指定从内存读取的A矩阵应填充到收集器缓冲区中。

.collector::a::use

指定可以从收集器缓冲区读取A矩阵。这要求之前对收集器缓冲区的填充仍然有效。

.collector::a::lastuse

指定可以从收集器缓冲区读取A矩阵,并且可以丢弃收集器缓冲区的内容。这要求先前对收集器缓冲区的填充在读取收集器缓冲区之前保持有效。

.collector::a::discard

指定可以丢弃A收集器缓冲区的内容。

如果未指定.collector_usage限定符,则默认为.collector::a::discard。 同时指定.collector::a::use.collector::a::fill.ashift是非法的。

PTX ISA 说明

在PTX ISA版本8.6中引入。

限定符 .kind::mxf4nvf4 在 PTX ISA 8.7 版本中引入。

目标ISA注意事项

支持以下架构:

  • sm_100a

  • sm_101a

参数 scale-input-d 需要 sm_100a

示例

tcgen05.mma.sp.cta_group::1.kind::f16      [taddr0],  adesc,  bdesc, [tmem_spmeta0], idesc, p;

tcgen05.mma.sp.cta_group::1.kind::mxf8f6f4.collector::a:fill
                                           [taddr2],  [taddr1],  bdesc, [tmem_spmeta1], idesc,
                                           [tmem_scaleA], [tmem_scaleB], p;

tcgen05.commit.cta_group::1.mbarrier::arrive::one.b64 [mbarObj0];

loop:
mbarrier.try_wait.parity.b64 p, [mbarObj0], 0;
@!p bra loop;
9.7.16.9.9.3. 第五代TensorCore指令集:tcgen05.mma.ws

tcgen05.mma.ws

执行第五代权重固定的卷积矩阵乘加运算。

语法

// 1. Floating-point type without block scaling:

tcgen05.mma.ws.cta_group::1.kind{.collector_usage}    [d-tmem],  a-desc,  b-desc,  idesc,
                                                      enable-input-d {, zero-column-mask-desc };

tcgen05.mma.ws.cta_group::1.kind{.collector_usage}    [d-tmem], [a-tmem], b-desc, idesc,
                                                      enable-input-d {, zero-column-mask-desc };

.kind = { .kind::f16, .kind::tf32, .kind::f8f6f4 }

----------------------------------------------------------------------------------

// 2. Integer type:

tcgen05.mma.ws.cta_group::1.kind::i8{.collector_usage} [d-tmem],  a-desc,  b-desc, idesc,
                                                       enable-input-d {, zero-column-mask-desc};

tcgen05.mma.ws.cta_group::1.kind::i8{.collector_usage} [d-tmem], [a-tmem], b-desc, idesc,
                                                       enable-input-d {, zero-column-mask-desc};

.collector_usage = { .collector::buffer::op }
::buffer = { ::b0, ::b1, ::b2, ::b3 }
::op   = { ::fill, ::use, ::lastuse, ::discard}

描述

指令 tcgen05.mma.ws 是一个异步指令,用于启动 MxNxK 矩阵乘加运算, D = A*B+D 其中 A 矩阵为 MxKB 矩阵为 KxND 矩阵为 MxN

当输入谓词参数 enable-input-d 为 false 时,会执行形式为 D = A*B 的运算。

32位寄存器操作数idesc是指令描述符,如指令描述符中所述,它指定了输入矩阵、输出矩阵以及矩阵乘加操作的形状、精确类型、稀疏性等详细信息。

限定符 .cta_group::1 指定矩阵乘加运算仅在执行线程CTA的Tensor Memory上执行。

指令 tcgen05.mma.ws 具有单线程语义,这与集体指令 mma.syncwgmma.mma_async 不同。因此,单个线程发出 tcgen05.mma.ws 将导致整个矩阵乘加操作的启动。请参阅章节 Issue granularity

限定符 .kind 指定了被乘矩阵元素类型的通用类别。每种MMA类型的输入和输出矩阵元素的具体类型在指令描述符中进行了规定。

地址操作数 d-tmem 指定了目标地址和张量内存中累加矩阵 D 的地址。地址操作数 a-tmem 指定了张量内存中矩阵 A 的地址。64位寄存器操作数 a-descb-desc 是矩阵描述符,分别表示共享内存中的矩阵 AB。矩阵描述符的格式在矩阵描述符格式中有详细说明。

可选操作数 zero-column-mask-desc 是一个64位寄存器,用于指定 零列掩码描述符。该零列掩码描述符用于生成一个掩码,该掩码指定在矩阵乘加运算中,无论共享内存中存在什么值,B 矩阵的哪些列将具有零值。

限定符 .collector_usage 用于指定矩阵 B 的收集器缓冲区用途。 可以指定以下收集器缓冲区操作:

.collector_usage

语义

.collector::bN::fill

指定从内存读取的B矩阵应填充到收集器缓冲区#N中。

.collector::bN::use

指定可以从收集器缓冲区#N中读取B矩阵。这要求之前对收集器缓冲区#N的填充仍然有效。

.collector::bN::lastuse

指定可以从收集器缓冲区#N中读取B矩阵,之后收集器缓冲区#N的内容可以被丢弃。这要求先前对收集器缓冲区#N的填充在读取收集器缓冲区#N之前保持有效。

.collector::bN::discard

指定可以丢弃收集器缓冲区#N中的内容。

如果未指定.collector_usage限定符,则默认为.collector::b0::discard

PTX ISA 说明

在PTX ISA版本8.6中引入。

目标ISA注意事项

支持以下架构:

  • sm_100a

  • sm_101a

示例

tcgen05.mma.ws.cta_group::1.kind::i8.collector::b2:use [taddr2], [taddr1], bdesc, idesc, p;
tcgen05.commit.cta_group::1.mbarrier::arrive::one.b64 [mbarObj0];

loop:
mbarrier.try_wait.parity.b64 p, [mbarObj0], 0;
@!p bra loop;
9.7.16.9.9.4. TensorCore 第五代指令集:tcgen05.mma.ws.sp

tcgen05.mma.ws.sp

执行第5代权重固定的稀疏A矩阵卷积乘累加运算。

语法

// 1. Floating-point type without block scaling:

tcgen05.mma.ws.sp.cta_group::1.kind{.collector_usage} [d-tmem],  a-desc,  b-desc,
                                                      [sp-meta-tmem] ,  idesc,
                                                      enable-input-d {, zero-column-mask-desc};

tcgen05.mma.ws.sp.cta_group::1.kind{.collector_usage} [d-tmem], [a-tmem], b-desc,
                                                      [sp-meta-tmem] , idesc,
                                                      enable-input-d {, zero-column-mask-desc};

.kind = { .kind::f16, .kind::tf32, .kind::f8f6f4 }

----------------------------------------------------------------------------------

// 2. Integer type:

tcgen05.mma.ws.sp.cta_group::1.kind::i8{.collector_usage} [d-tmem], a-desc, b-desc,
                                                          [sp-meta-tmem] , idesc,
                                                          enable-input-d {, zero-column-mask-desc};

tcgen05.mma.ws.sp.cta_group::1.kind::i8{.collector_usage} [d-tmem], [a-tmem], b-desc,
                                                          [sp-meta-tmem] , idesc,
                                                          enable-input-d {, zero-column-mask-desc};

.collector_usage = { .collector::buffer::op }
::buffer = { ::b0, ::b1, ::b2, ::b3 }
::op   = { ::fill, ::use, ::lastuse, ::discard}

描述

指令 tcgen05.mma.ws.sp 是一个异步指令,用于启动一个MxNxK矩阵乘加运算,D = A*B+D,其中A矩阵是Mx(K/2)B矩阵是KxN,而D矩阵是MxNSparse Matrices描述了稀疏性的细节。

当输入谓词参数 enable-input-d 为 false 时,会执行形式为 D = A*B 的运算。

32位寄存器操作数idesc是指令描述符,如Instruction descriptor中所述,它指定了输入矩阵、输出矩阵以及矩阵乘加操作的形状、精确类型、稀疏性等详细信息。

限定符 .cta_group::1 指定矩阵乘加运算仅在执行线程CTA的张量内存上执行。

指令 tcgen05.mma.ws.sp 具有单线程语义,这与集体指令 mma.syncwgmma.mma_async 不同。因此,单个线程发出 tcgen05.mma.ws.sp 将导致整个矩阵乘加操作的启动。请参阅章节 Issue granularity

限定符 .kind 指定了被乘矩阵元素类型的通用类别。每种MMA类型的输入和输出矩阵元素的具体类型在指令描述符中进行了规定。

地址操作数 d-tmem 指定了目标地址和张量内存中累加矩阵 D 的地址。地址操作数 a-tmem 指定了张量内存中矩阵 A 的地址。64位寄存器操作数 a-descb-desc 是矩阵描述符,分别表示共享内存中的矩阵 AB。矩阵描述符的格式在矩阵描述符格式中有详细说明。

可选操作数 zero-column-mask-desc 是一个64位寄存器,用于指定 零列掩码描述符。该零列掩码描述符用于生成一个掩码,该掩码指定在矩阵乘加运算中,无论共享内存中存在什么值,B 矩阵的哪些列将具有零值。

限定符 .collector_usage 指定了矩阵 B 收集器缓冲区的用途。可以指定以下收集器缓冲区操作:

.collector_usage

语义

.collector::bN::fill

指定从内存读取的B矩阵应填充到收集器缓冲区#N中。

.collector::bN::use

指定可以从收集器缓冲区#N中读取B矩阵。这要求先前对收集器缓冲区#N的填充仍然有效。

.collector::bN::lastuse

指定可以从收集器缓冲区#N中读取B矩阵,之后收集器缓冲区#N的内容可以被丢弃。这要求先前对收集器缓冲区#N的填充在读取收集器缓冲区#N之前保持有效。

.collector::bN::discard

指定可以丢弃收集器缓冲区#N中的内容。

如果未指定.collector_usage限定符,则默认为.collector::b0::discard

PTX ISA 说明

在PTX ISA版本8.6中引入。

目标ISA注意事项

支持以下架构:

  • sm_100a

  • sm_101a

示例

tcgen05.mma.ws.sp.cta_group::1.kind::tf32.collector::b1::fill  [taddr1], [taddr0], bdesc,
                                                               [tmem_spmeta0], idesc, p;

tcgen05.commit.cta_group::1.mbarrier::arrive::one.b64 [mbarObj0];

loop:
mbarrier.try_wait.parity.b64 p, [mbarObj0], 0;
@!p bra loop;

9.7.16.10. TensorCore 第五代专用同步操作

9.7.16.10.1. TensorCore 第五代指令集:tcgen05.fence

tcgen05.fence

专为异步tcgen05操作设计的特殊栅栏。

语法

tcgen05.fence::before_thread_sync ;
tcgen05.fence::after_thread_sync  ;

描述

指令 tcgen05.fence::before_thread_sync 将所有先前的异步 tcgen05 操作与后续的 tcgen05 及执行排序操作进行同步排序。

指令 tcgen05.fence::after_thread_sync 会确保所有后续的异步 tcgen05 操作相对于先前的 tcgen05 操作和执行排序操作保持有序。

tcgen05.fence::* 指令在线程范围内与执行排序指令组合,并在同一范围内提供tcgen05指令之间的排序。

tcgen05.fence::before_thread_sync指令作为代码移动屏障作用于先前的tcgen05指令,因为这些指令无法被跨越提升。tcgen05.fence::after_thread_sync指令则作为代码移动屏障作用于后续的tcgen05指令,因为它们同样无法被跨越提升。

PTX ISA 说明

在PTX ISA版本8.6中引入。

目标ISA注意事项

支持以下架构:

  • sm_100a

  • sm_101a

示例

// Producer thread:

tcgen05.cp.cta_group::1.128x256b  [taddr0], sdesc0;

tcgen05.fence::before_thread_sync;
st.relaxed.b32 [flag], 1;

// Consumer thread:

loop:
ld.relaxed.b32 r, [flag];
setp.eq.u32 p, r, 1;
@!p bra loop;

tcgen05.fence::after_thread_sync;
tcgen05.mma.cta_group.kind   [taddr0], adesc, bdesc, idesc, p;

9.7.16.11. TensorCore 第五代异步同步操作

9.7.16.11.1. TensorCore第五代指令集:tcgen05.commit

tcgen05.commit

使mbarrier对象跟踪执行线程发起的之前所有async-tcgen05操作的完成情况。

语法

tcgen05.commit.cta_group.completion_mechanism{.shared::cluster}{.multicast}.b64
                                                            [mbar] {, ctaMask};

.completion_mechanism = { .mbarrier::arrive::one }
.cta_group            = { .cta_group::1, .cta_group::2 }
.multicast            = { .multicast::cluster }

描述

指令 tcgen05.commit 是一个异步指令,它使由地址操作数 mbar 指定的 mbarrier 对象跟踪执行线程发起的、所有先前的异步 tcgen05 操作的完成情况,如基于 mbarrier 的完成机制中所述。当跟踪的异步 tcgen05 操作完成时,系统会在 mbarrier 对象上触发由 .completion_mechanism 指定的信号。

指令 tcgen05.commit.cta_group::1 用于追踪当前线程发出的所有先前带有 .cta_group::1 的异步 tcgen05 操作的完成情况。 类似地,指令 tcgen05.commit.cta_group::2 用于追踪当前线程发出的所有先前带有 .cta_group::2 的异步 tcgen05 操作的完成情况。

限定符 .mbarrier::arrive::one 表示在当前线程发出的先前异步 tcgen05 操作完成后,会在 mbarrier 对象上触发一个计数参数为 1 的到达操作。该到达操作的作用域是集群范围。

可选限定符 .multicast::cluster 允许对集群中多个CTA的mbarrier对象发出信号。操作数 ctaMask 指定集群中的CTA,其中16位 ctaMask 操作数的每个比特位对应目标CTA的 %cluster_ctarank。mbarrier信号会以与 mbar 相同的偏移量多播到每个目标CTA的共享内存中。

如果未指定状态空间,则使用通用寻址。如果mbar指定的地址不在.shared::cluster状态空间的地址窗口内,则行为未定义。

PTX ISA 说明

在PTX ISA版本8.6中引入。

目标ISA注意事项

支持以下架构:

  • sm_100a

  • sm_101a

示例

Example 1:
tcgen05.cp.cta_group::1.128x256b                      [taddr0], sdesc0;
tcgen05.commit.cta_group::1.mbarrier::arrive::one.b64 [mbarObj1];

loop:
mbarrier.try_wait.parity.b64 p, [mbarObj1], 0;
@!p bra loop;

Example 2:
tcgen05.mma.cta_group::2.kind::tf32    [taddr0],  adesc,  bdesc, idesc, p;
tcgen05.commit.cta_group::2.mbarrier::arrive::one.b64 [mbarObj2];

loop:
mbarrier.try_wait.parity.b64 p, [mbarObj2], 0;
@!p bra loop;

9.7.17. 堆栈操作指令

栈操作指令可用于在当前函数的栈帧上动态分配和释放内存。

堆栈操作指令包括:

  • stacksave

  • stackrestore

  • alloca

9.7.17.1. 堆栈操作指令:stacksave

stacksave

将栈指针的值保存到寄存器中。

语法

stacksave.type  d;

.type = { .u32, .u64 };

描述

将当前栈指针的值复制到目标寄存器d中。stacksave返回的指针可用于后续的stackrestore指令来恢复栈指针。如果在stackrestore指令使用前修改了d,可能会损坏栈中的数据。

目标操作数 d 与指令类型具有相同的类型。

语义

d = stackptr;

PTX ISA 说明

在PTX ISA版本7.3中引入。

Preview Feature:

stacksave 是PTX ISA 7.3版本中的预览功能。所有细节都可能发生变化,不保证在未来的PTX ISA版本或SM架构中保持向后兼容性。

目标ISA注意事项

stacksave 需要 sm_52 或更高版本。

示例

.reg .u32 rd;
stacksave.u32 rd;

.reg .u64 rd1;
stacksave.u64 rd1;

9.7.17.2. 堆栈操作指令:stackrestore

stackrestore

使用新值更新栈指针。

语法

stackrestore.type  a;

.type = { .u32, .u64 };

描述

将当前栈指针设置为源寄存器 a

stackrestore与先前stacksave指令写入的操作数a一起使用时,它将有效地恢复栈的状态,就像执行stacksave之前一样。请注意,如果stackrestore与任意值的a一起使用,可能会导致栈指针损坏。这意味着正确使用此功能需要在stacksave.type a之后使用stackrestore.type a,且在这期间不重新定义a的值。

操作数 a 与指令类型具有相同的类型。

语义

stackptr = a;

PTX ISA 说明

在PTX ISA版本7.3中引入。

Preview Feature:

stackrestore 是PTX ISA 7.3版本中的预览功能。所有细节都可能发生变化,不保证在未来的PTX ISA版本或SM架构中保持向后兼容性。

目标ISA注意事项

stackrestore 需要 sm_52 或更高版本。

示例

.reg .u32 ra;
stacksave.u32 ra;
// Code that may modify stack pointer
...
stackrestore.u32 ra;

9.7.17.3. 栈操作指令:alloca

alloca

在栈上动态分配内存。

语法

alloca.type  ptr, size{, immAlign};

.type = { .u32, .u64 };

描述

alloca指令在当前函数的栈帧上动态分配内存,并相应更新栈指针。返回的指针ptr指向本地内存,可用于ld.localst.local指令的地址操作数。

If sufficient memory is unavailable for allocation on the stack, then execution of alloca may result in stack overflow. In such cases, attempting to access the allocated memory with ptr will result in undefined program behavior.

alloca分配的内存会通过以下方式释放:

  • 当函数退出时,它会自动释放。

  • 可以使用stacksavestackrestore指令显式释放内存: stacksave可用于在执行alloca前保存栈指针的值, 而stackrestore可在alloca后使用,将栈指针恢复到之前通过 stacksave保存的原始值。请注意,在执行stackrestore后访问已释放的内存会导致未定义行为。

size 是一个无符号值,用于指定要在栈上分配的内存大小(以字节为单位)。size = 0 可能导致无效的内存分配。

ptrsize 的类型与指令类型相同。

immAlign 是一个32位值,用于指定由 alloca 分配的内存所需的对齐字节数。它是一个整数常量,必须是2的幂次方且不得超过2^23。immAlign 是一个可选参数,默认值为8,这是保证的最小对齐值。

语义

alloca.type ptr, size, immAlign:

a = max(immAlign, frame_align); // frame_align is the minimum guaranteed alignment

// Allocate size bytes of stack memory with alignment a and update the stack pointer.
// Since the stack grows down, the updated stack pointer contains a lower address.
stackptr = alloc_stack_mem(size, a);

// Return the new value of stack pointer as ptr. Since ptr is the lowest address of the memory
// allocated by alloca, the memory can be accessed using ptr up to (ptr + size of allocated memory).
stacksave ptr;

PTX ISA 说明

在PTX ISA版本7.3中引入。

Preview Feature:

alloca 是PTX ISA 7.3版本中的预览功能。所有细节都可能发生变化,不保证在未来的PTX ISA版本或SM架构中保持向后兼容性。

目标ISA注意事项

alloca 需要 sm_52 或更高版本。

示例

.reg .u32 ra, stackptr, ptr, size;

stacksave.u32 stackptr;     // Save the current stack pointer
alloca ptr, size, 8;        // Allocate stack memory
st.local.u32 [ptr], ra;     // Use the allocated stack memory
stackrestore.u32 stackptr;  // Deallocate memory by restoring the stack pointer

9.7.18. 视频教程

所有视频指令都操作32位寄存器操作数。不过,根据其核心操作是应用于单个值还是多个值,视频指令可分为标量或SIMD两类。

视频说明如下:

  • vadd, vadd2, vadd4

  • vsub, vsub2, vsub4

  • vmad

  • vavrg2, vavrg4

  • vabsdiff, vabsdiff2, vabsdiff4

  • vmin, vmin2, vmin4

  • vmax, vmax2, vmax4

  • vshl

  • vshr

  • vset, vset2, vset4

9.7.18.1. 标量视频指令

所有标量视频指令均对32位寄存器操作数进行操作。标量视频指令包括:

  • vadd

  • vsub

  • vabsdiff

  • vmin

  • vmax

  • vshl

  • vshr

  • vmad

  • vset

标量视频指令执行以下阶段:

  1. 从源操作数中提取字节、半字或字值,并进行符号扩展或零扩展,以生成带符号的33位输入值。

  2. 执行标量算术运算以生成34位有符号结果。

  3. 可选地将结果限制在目标类型的范围内。

  4. 可选执行以下操作之一:

    • 对中间结果应用第二个操作和第三个操作数,或者

    • 将中间结果截断为一个字节或半字值,并合并到第三个操作数中的指定位置以生成最终结果。

标量视频指令的一般格式如下:

// 32-bit scalar operation, with optional secondary operation
vop.dtype.atype.btype{.sat}        d, a{.asel}, b{.bsel};
vop.dtype.atype.btype{.sat}.secop  d, a{.asel}, b{.bsel}, c;

// 32-bit scalar operation, with optional data merge
vop.dtype.atype.btype{.sat}   d.dsel, a{.asel}, b{.bsel}, c;


.dtype = .atype = .btype = { .u32, .s32 };
.dsel  = .asel  = .bsel  = { .b0, .b1, .b2, .b3, .h0, .h1 };
.secop = { .add, .min, .max };

源操作数和目标操作数均为32位寄存器。每个操作数的类型(.u32.s32)在指令类型中指定;dtypeatypebtype的所有组合均有效。通过使用atype/btypeasel/bsel说明符,输入值被提取并在内部进行符号扩展或零扩展为.s33值。然后执行主要操作以生成.s34中间结果。中间结果的符号取决于dtype。

中间结果可选择性地被限制在目标类型(有符号或无符号)的范围内,在可选数据合并的情况下会考虑子字目标大小。

.s33 optSaturate( .s34 tmp, Bool sat, Bool sign, Modifier dsel ) {
    if ( !sat )  return tmp;

    switch ( dsel ) {
        case .b0, .b1, .b2, .b3:
            if ( sign )  return CLAMP( tmp, S8_MAX, S8_MIN );
            else         return CLAMP( tmp, U8_MAX, U8_MIN );
        case .h0, .h1:
            if ( sign )  return CLAMP( tmp, S16_MAX, S16_MIN );
            else         return CLAMP( tmp, U16_MAX, U16_MIN );
        default:
            if ( sign )  return CLAMP( tmp, S32_MAX, S32_MIN );
            else         return CLAMP( tmp, U32_MAX, U32_MIN );
    }
}

这个中间结果随后可以选择性地与第三个源操作数结合,通过二次算术运算或子字数据合并,如下面的伪代码所示。第三个操作数的符号基于dtype

.s33 optSecOp(Modifier secop, .s33 tmp, .s33 c) {
    switch ( secop ) {
        .add:     return tmp + c;
        .min:     return MIN(tmp, c);
        .max      return MAX(tmp, c);
        default:  return tmp;
    }
}
.s33 optMerge( Modifier dsel, .s33 tmp, .s33 c ) {
    switch ( dsel ) {
        case .h0:  return ((tmp & 0xffff)        | (0xffff0000 & c);
        case .h1:  return ((tmp & 0xffff) << 16) | (0x0000ffff & c);
        case .b0:  return ((tmp & 0xff)          | (0xffffff00 & c);
        case .b1:  return ((tmp & 0xff) <<  8)   | (0xffff00ff & c);
        case .b2:  return ((tmp & 0xff) << 16)   | (0xff00ffff & c);
        case .b3:  return ((tmp & 0xff) << 24)   | (0x00ffffff & c);
        default:   return tmp;
    }
}

然后将低32位写入目标操作数。

9.7.18.1.1. 标量视频指令:vadd、vsub、vabsdiff、vmin、vmax

vadd, vsub

整数字节/半字/字加法/减法。

vabsdiff

整数字节/半字/字的差值绝对值。

vmin, vmax

整数字节/半字/字的最小值/最大值。

语法

// 32-bit scalar operation, with optional secondary operation
vop.dtype.atype.btype{.sat}       d, a{.asel}, b{.bsel};
vop.dtype.atype.btype{.sat}.op2   d, a{.asel}, b{.bsel}, c;

// 32-bit scalar operation, with optional data merge
vop.dtype.atype.btype{.sat}  d.dsel, a{.asel}, b{.bsel}, c;

 vop   = { vadd, vsub, vabsdiff, vmin, vmax };
.dtype = .atype = .btype = { .u32, .s32 };
.dsel  = .asel  = .bsel  = { .b0, .b1, .b2, .b3, .h0, .h1 };
.op2   = { .add, .min, .max };

描述

执行标量算术运算,可选择饱和处理,以及可选的二次算术运算或子字数据合并。

语义

// extract byte/half-word/word and sign- or zero-extend
// based on source operand type
ta = partSelectSignExtend( a, atype, asel );
tb = partSelectSignExtend( b, btype, bsel );

switch ( vop ) {
    case vadd:     tmp = ta + tb;
    case vsub:     tmp = ta - tb;
    case vabsdiff: tmp = | ta - tb |;
    case vmin:     tmp = MIN( ta, tb );
    case vmax:     tmp = MAX( ta, tb );
}
// saturate, taking into account destination type and merge operations
tmp = optSaturate( tmp, sat, isSigned(dtype), dsel );
d = optSecondaryOp( op2, tmp, c );  // optional secondary operation
d = optMerge( dsel, tmp, c );       // optional merge with c operand

PTX ISA 说明

在PTX ISA 2.0版本中引入。

目标ISA注意事项

vadd, vsub, vabsdiff, vmin, vmax 需要 sm_20 或更高版本。

示例

vadd.s32.u32.s32.sat      r1, r2.b0, r3.h0;
vsub.s32.s32.u32.sat      r1, r2.h1, r3.h1;
vabsdiff.s32.s32.s32.sat  r1.h0, r2.b0, r3.b2, c;
vmin.s32.s32.s32.sat.add  r1, r2, r3, c;
9.7.18.1.2. 标量视频指令: vshl, vshr

vshl, vshr

整数字节/半字/字的左/右移位。

语法

// 32-bit scalar operation, with optional secondary operation
vop.dtype.atype.u32{.sat}.mode       d, a{.asel}, b{.bsel};
vop.dtype.atype.u32{.sat}.mode.op2   d, a{.asel}, b{.bsel}, c;

// 32-bit scalar operation, with optional data merge
vop.dtype.atype.u32{.sat}.mode  d.dsel, a{.asel}, b{.bsel}, c;

 vop   = { vshl, vshr };
.dtype = .atype = { .u32, .s32 };
.mode  = { .clamp, .wrap };
.dsel  = .asel  = .bsel  = { .b0, .b1, .b2, .b3, .h0, .h1 };
.op2   = { .add, .min, .max };

描述

vshl

ab中的无符号数值左移,可选择饱和处理,以及可选的次级算术运算或子字数据合并。左移操作会用零填充空位。

vshr

a按无符号数b指定的位数右移,可选择饱和处理,以及可选的次级算术运算或子字数据合并。有符号移位用符号位填充,无符号移位用零填充。

语义

// extract byte/half-word/word and sign- or zero-extend
// based on source operand type
ta = partSelectSignExtend( a,atype, asel );
tb = partSelectSignExtend( b, .u32, bsel );
if ( mode == .clamp  && tb > 32 )  tb = 32;
if ( mode == .wrap )                       tb = tb & 0x1f;
switch ( vop ){
   case vshl:  tmp = ta << tb;
   case vshr:  tmp = ta >> tb;
}
// saturate, taking into account destination type and merge operations
tmp = optSaturate( tmp, sat, isSigned(dtype), dsel );
d = optSecondaryOp( op2, tmp, c );  // optional secondary operation
d = optMerge( dsel, tmp, c );       // optional merge with c operand

PTX ISA 说明

在PTX ISA 2.0版本中引入。

目标ISA注意事项

vshl, vshr 需要 sm_20 或更高版本。

示例

vshl.s32.u32.u32.clamp  r1, r2, r3;
vshr.u32.u32.u32.wrap   r1, r2, r3.h1;
9.7.18.1.3. 标量视频指令: vmad

vmad

整数字节/半字/字乘加运算。

语法

// 32-bit scalar operation
vmad.dtype.atype.btype{.sat}{.scale}     d, {-}a{.asel}, {-}b{.bsel},
                                         {-}c;
vmad.dtype.atype.btype.po{.sat}{.scale}  d, a{.asel}, b{.bsel}, c;

.dtype = .atype = .btype = { .u32, .s32 };
.asel  = .bsel  = { .b0, .b1, .b2, .b3, .h0, .h1 };
.scale = { .shr7, .shr15 };

描述

计算 (a*b) + c,支持可选操作数取反、加一模式以及缩放功能。

源操作数支持带有限制的可选取反操作。虽然PTX语法允许对ab操作数分别取反,但在内部实现中这被表示为对乘积(a*b)的取反。也就是说,当且仅当ab中恰好有一个被取反时,(a*b)才会被取反。PTX允许对(a*b)c进行取反操作。

加一模式(.po)计算(a*b) + c + 1,用于计算平均值。在.po模式下源操作数不能被取反。

如果atype和btype是无符号的且乘积(a*b)未被取反,则中间结果(a*b)是无符号的;否则,中间结果是有符号的。输入c与中间结果具有相同的符号。

如果中间结果是无符号的且c未被取反,则最终结果也是无符号的。

根据操作数ab的符号以及操作数取反情况,VMAD支持以下操作数组合:

 (u32 * u32) + u32  // intermediate unsigned; final unsigned
-(u32 * u32) + s32  // intermediate   signed; final   signed
 (u32 * u32) - u32  // intermediate unsigned; final   signed
 (u32 * s32) + s32  // intermediate   signed; final   signed
-(u32 * s32) + s32  // intermediate   signed; final   signed
 (u32 * s32) - s32  // intermediate   signed; final   signed
 (s32 * u32) + s32  // intermediate   signed; final   signed
-(s32 * u32) + s32  // intermediate   signed; final   signed
 (s32 * u32) - s32  // intermediate   signed; final   signed
 (s32 * s32) + s32  // intermediate   signed; final   signed
-(s32 * s32) + s32  // intermediate   signed; final   signed
 (s32 * s32) - s32  // intermediate   signed; final   signed

中间结果可以选择通过右移进行缩放;如果最终结果是有符号的,则该结果会进行符号扩展,否则进行零扩展。

最终结果可根据其类型(有符号或无符号)选择性地饱和到适当的32位范围内。

语义

// extract byte/half-word/word and sign- or zero-extend
// based on source operand type
ta = partSelectSignExtend( a, atype, asel );
tb = partSelectSignExtend( b, btype, bsel );
signedFinal = isSigned(atype) || isSigned(btype) ||
                                 (a.negate ^ b.negate) || c.negate;
tmp[127:0] = ta * tb;

lsb = 0;
if ( .po )                  {              lsb = 1; } else
if ( a.negate ^ b.negate )  { tmp = ~tmp;  lsb = 1; } else
if ( c.negate )             { c   = ~c;    lsb = 1; }

c128[127:0] = (signedFinal) sext32( c ) : zext ( c );
tmp = tmp + c128 + lsb;
switch( scale ) {
   case .shr7:   result = (tmp >>  7) & 0xffffffffffffffff;
   case .shr15:  result = (tmp >> 15) & 0xffffffffffffffff;
}
if ( .sat ) {
     if (signedFinal) result = CLAMP(result, S32_MAX, S32_MIN);
     else             result = CLAMP(result, U32_MAX, U32_MIN);
}

PTX ISA 说明

在PTX ISA 2.0版本中引入。

目标ISA注意事项

vmad 需要 sm_20 或更高版本。

示例

vmad.s32.s32.u32.sat    r0, r1, r2, -r3;
vmad.u32.u32.u32.shr15  r0, r1.h0, r2.h0, r3;
9.7.18.1.4. 标量视频指令: vset

vset

整数字节/半字/字比较。

语法

// 32-bit scalar operation, with optional secondary operation
vset.atype.btype.cmp       d, a{.asel}, b{.bsel};
vset.atype.btype.cmp.op2   d, a{.asel}, b{.bsel}, c;

// 32-bit scalar operation, with optional data merge
vset.atype.btype.cmp  d.dsel, a{.asel}, b{.bsel}, c;

.atype = .btype = { .u32, .s32 };
.cmp   = { .eq, .ne, .lt, .le, .gt, .ge };
.dsel  = .asel  = .bsel  = { .b0, .b1, .b2, .b3, .h0, .h1 };
.op2   = { .add, .min, .max };

描述

使用指定的比较方式对比输入值,可选择进行次级算术运算或子字数据合并。

比较的中间结果始终是无符号的,因此目标d和操作数c也是无符号的。

语义

// extract byte/half-word/word and sign- or zero-extend
// based on source operand type
ta = partSelectSignExtend( a, atype, asel );
tb = partSelectSignExtend( b, btype, bsel );
tmp = compare( ta, tb, cmp ) ? 1 : 0;
d = optSecondaryOp( op2, tmp, c );    // optional secondary operation
d = optMerge( dsel, tmp, c );         // optional merge with c operand

PTX ISA 说明

在PTX ISA 2.0版本中引入。

目标ISA注意事项

vset 需要 sm_20 或更高版本。

示例

vset.s32.u32.lt    r1, r2, r3;
vset.u32.u32.ne    r1, r2, r3.h1;

9.7.18.2. SIMD视频指令

SIMD视频指令可对16位值对和8位值四元组进行操作。

SIMD视频指令包括:

  • vadd2, vadd4

  • vsub2, vsub4

  • vavrg2, vavrg4

  • vabsdiff2, vabsdiff4

  • vmin2, vmin4

  • vmax2, vmax4

  • vset2, vset4

PTX包含针对16位值对和8位值四元组操作的SIMD视频指令。这些SIMD视频指令执行以下阶段:

  1. 通过从源操作数中提取并符号扩展或零扩展字节或半字值,形成有符号17位值对,从而构建输入向量。

  2. 对输入的数据对执行SIMD算术运算。

  3. 可选地将结果钳制到适当的有符号或无符号范围,由目标类型决定。

  4. 可选执行以下操作之一:

    1. 执行第二次SIMD合并操作,或

    2. 应用标量累加操作将中间SIMD结果缩减为单个标量。

双半字SIMD视频指令的一般格式如下:

// 2-way SIMD operation, with second SIMD merge or accumulate
vop2.dtype.atype.btype{.sat}{.add}  d{.mask}, a{.asel}, b{.bsel}, c;

.dtype = .atype = .btype = { .u32, .s32 };
.mask  = { .h0, .h1, .h10 };
.asel  = .bsel = { .hxy, where x,y are from { 0, 1, 2, 3 } };

四字节SIMD视频指令的一般格式如下:

// 4-way SIMD operation, with second SIMD merge or accumulate
vop4.dtype.atype.btype{.sat}{.add}  d{.mask}, a{.asel}, b{.bsel}, c;

.dtype = .atype = .btype = { .u32, .s32 };
.mask  = { .b0,
           .b1, .b10
           .b2, .b20, .b21, .b210,
           .b3, .b30, .b31, .b310, .b32, .b320, .b321, .b3210 };
.asel = .bsel = .bxyzw, where x,y,z,w are from { 0, ..., 7 };

源操作数和目标操作数都是32位寄存器。每个操作数的类型(.u32.s32)在指令类型中指定;dtypeatypebtype的所有组合都是有效的。使用atype/btypeasel/bsel指定符,输入值被提取并在内部进行符号扩展或零扩展为.s33值。然后执行主要操作以生成.s34中间结果。中间结果的符号取决于dtype

中间结果可选择性地被限制在目标类型(有符号或无符号)的范围内,在可选数据合并的情况下会考虑子字目标大小。

9.7.18.2.1. SIMD视频指令:vadd2, vsub2, vavrg2, vabsdiff2, vmin2, vmax2

vadd2, vsub2

整数双半字SIMD加法/减法。

vavrg2

整数双半字SIMD平均值。

vabsdiff2

整数双半字SIMD差值绝对值。

vmin2, vmax2

整数双半字SIMD最小值/最大值。

语法

// SIMD instruction with secondary SIMD merge operation
vop2.dtype.atype.btype{.sat}  d{.mask}, a{.asel}, b{.bsel}, c;

// SIMD instruction with secondary accumulate operation
vop2.dtype.atype.btype.add  d{.mask}, a{.asel}, b{.bsel}, c;

 vop2  = { vadd2, vsub2, vavrg2, vabsdiff2, vmin2, vmax2 };
.dtype = .atype = .btype = { .u32, .s32 };
.mask  = { .h0, .h1, .h10 };  // defaults to .h10
.asel  = .bsel  = { .hxy, where x,y are from { 0, 1, 2, 3 } };
   .asel defaults to .h10
   .bsel defaults to .h32

描述

支持二次操作的双向SIMD并行算术运算。

操作中每个双半字源元素的选择,是通过aselbsel修饰符从两个源操作数ab的任意四个半字中选取的。

然后对选中的半字进行并行操作。

结果可选择性地被限制在由目标类型(有符号或无符号)确定的适当范围内。饱和操作不能与次要累加操作一起使用。

对于带有辅助SIMD合并操作的指令:

  • 对于掩码中指定的半字位置,选中的半字结果会被复制到目标d。对于其他所有位置,源操作数c中对应的半字会被复制到d

对于包含次要累积操作的指令:

  • 对于掩码中指示的半字位置,选定的半字结果会与操作数c相加,最终结果存储在d中。

语义

// extract pairs of half-words and sign- or zero-extend
// based on operand type
Va = extractAndSignExt_2( a, b, .asel, .atype );
Vb = extractAndSignExt_2( a, b, .bsel, .btype );
Vc = extractAndSignExt_2( c );

for (i=0; i<2; i++) {
    switch ( vop2 ) {
       case vadd2:             t[i] = Va[i] + Vb[i];
       case vsub2:             t[i] = Va[i] - Vb[i];
       case vavrg2:            if ( ( Va[i] + Vb[i] ) >= 0 ) {
                                   t[i] = ( Va[i] + Vb[i] + 1 ) >> 1;
                               } else {
                                   t[i] = ( Va[i] + Vb[i] ) >> 1;
                               }
       case vabsdiff2:         t[i] = | Va[i] - Vb[i] |;
       case vmin2:             t[i] = MIN( Va[i], Vb[i] );
       case vmax2:             t[i] = MAX( Va[i], Vb[i] );
    }
    if (.sat) {
        if ( .dtype == .s32 )  t[i] = CLAMP( t[i], S16_MAX, S16_MIN );
        else                   t[i] = CLAMP( t[i], U16_MAX, U16_MIN );
    }
}
// secondary accumulate or SIMD merge
mask = extractMaskBits( .mask );
if (.add) {
    d = c;
    for (i=0; i<2; i++) {  d += mask[i] ? t[i] : 0;  }
} else {
    d = 0;
    for (i=0; i<2; i++)  {  d |= mask[i] ? t[i] : Vc[i];  }
}

PTX ISA 说明

在PTX ISA 3.0版本中引入。

目标ISA注意事项

vadd2, vsub2, varvg2, vabsdiff2, vmin2, vmax2 需要 sm_30 或更高版本。

示例

vadd2.s32.s32.u32.sat  r1, r2, r3, r1;
vsub2.s32.s32.s32.sat  r1.h0, r2.h10, r3.h32, r1;
vmin2.s32.u32.u32.add  r1.h10, r2.h00, r3.h22, r1;
9.7.18.2.2. SIMD视频指令集:vset2

vset2

整数双半字SIMD比较。

语法

// SIMD instruction with secondary SIMD merge operation
vset2.atype.btype.cmp  d{.mask}, a{.asel}, b{.bsel}, c;

// SIMD instruction with secondary accumulate operation
vset2.atype.btype.cmp.add  d{.mask}, a{.asel}, b{.bsel}, c;

.atype = .btype = { .u32, .s32 };
.cmp   = { .eq, .ne, .lt, .le, .gt, .ge };
.mask  = { .h0, .h1, .h10 };  // defaults to .h10
.asel  = .bsel  = { .hxy, where x,y are from { 0, 1, 2, 3 } };
   .asel defaults to .h10
   .bsel defaults to .h32

描述

支持双向SIMD并行比较及二次操作。

操作中每个双半字源的元素,通过aselbsel修饰符,从两个源操作数ab的任意四个半字中选择。

然后并行比较选中的半字。

比较的中间结果始终是无符号的,因此目标d和操作数c的半字也是无符号的。

对于包含辅助SIMD合并操作的指令:

  • 对于掩码中指定的半字位置,选中的半字结果会被复制到目标d。对于其他所有位置,源操作数b中对应的半字会被复制到d

对于包含次要累积操作的指令:

  • 对于掩码中指示的半字位置,选中的半字结果会与操作数c相加,最终在d中生成a结果。

语义

// extract pairs of half-words and sign- or zero-extend
// based on operand type
Va = extractAndSignExt_2( a, b, .asel, .atype );
Vb = extractAndSignExt_2( a, b, .bsel, .btype );
Vc = extractAndSignExt_2( c );
for (i=0; i<2; i++) {
    t[i] = compare( Va[i], Vb[i], .cmp ) ? 1 : 0;
}
// secondary accumulate or SIMD merge
mask = extractMaskBits( .mask );
if (.add) {
    d = c;
    for (i=0; i<2; i++) {  d += mask[i] ? t[i] : 0;  }
} else {
    d = 0;
    for (i=0; i<2; i++)  {  d |= mask[i] ? t[i] : Vc[i];  }
}

PTX ISA 说明

在PTX ISA 3.0版本中引入。

目标ISA注意事项

vset2 需要 sm_30 或更高版本。

示例

vset2.s32.u32.lt      r1, r2, r3, r0;
vset2.u32.u32.ne.add  r1, r2, r3, r0;
9.7.18.2.3. SIMD视频指令集:vadd4, vsub4, vavrg4, vabsdiff4, vmin4, vmax4

vadd4, vsub4

整数四字节SIMD加法/减法。

vavrg4

整数四字节SIMD平均值。

vabsdiff4

四字节整数SIMD差值绝对值。

vmin4, vmax4

整数四字节SIMD最小值/最大值。

语法

// SIMD instruction with secondary SIMD merge operation
vop4.dtype.atype.btype{.sat}  d{.mask}, a{.asel}, b{.bsel}, c;

// SIMD instruction with secondary accumulate operation
vop4.dtype.atype.btype.add  d{.mask}, a{.asel}, b{.bsel}, c;
vop4  = { vadd4, vsub4, vavrg4, vabsdiff4, vmin4, vmax4 };

.dtype = .atype = .btype = { .u32, .s32 };
.mask  = { .b0,
           .b1, .b10
           .b2, .b20, .b21, .b210,
           .b3, .b30, .b31, .b310, .b32, .b320, .b321, .b3210 };
    defaults to .b3210
.asel = .bsel = .bxyzw, where x,y,z,w are from { 0, ..., 7 };
   .asel defaults to .b3210
   .bsel defaults to .b7654

描述

支持次级操作的四路SIMD并行算术运算。

操作中每个四字节源元素都是从两个源操作数ab的任意八个字节中,通过aselbsel修饰符选择出来的。

选定的字节随后会并行处理。

结果可选择性地被限制在由目标类型(有符号或无符号)确定的适当范围内。饱和操作不能与次要累加操作一起使用。

对于包含辅助SIMD合并操作的指令:

  • 对于掩码中指定的字节位置,选中的字节结果会被复制到目标d。对于其他所有位置,源操作数c中的对应字节会被复制到d

对于包含次要累积操作的指令:

  • 对于掩码中指示的字节位置,所选字节结果会添加到操作数 c 中,最终在 d 中生成结果。

语义

// extract quads of bytes and sign- or zero-extend
// based on operand type
Va = extractAndSignExt_4( a, b, .asel, .atype );
Vb = extractAndSignExt_4( a, b, .bsel, .btype );
Vc = extractAndSignExt_4( c );
for (i=0; i<4; i++) {
    switch ( vop4 ) {
        case vadd4:            t[i] = Va[i] + Vb[i];
        case vsub4:            t[i] = Va[i] - Vb[i];
        case vavrg4:           if ( ( Va[i] + Vb[i] ) >= 0 ) {
                                   t[i] = ( Va[i] + Vb[i] + 1 ) >> 1;
                               } else {
                                   t[i] = ( Va[i] + Vb[i] ) >> 1;
                               }
        case vabsdiff4:        t[i] = | Va[i] - Vb[i] |;
        case vmin4:            t[i] = MIN( Va[i], Vb[i] );
        case vmax4:            t[i] = MAX( Va[i], Vb[i] );
    }
    if (.sat) {
        if ( .dtype == .s32 )  t[i] = CLAMP( t[i], S8_MAX, S8_MIN );
        else                   t[i] = CLAMP( t[i], U8_MAX, U8_MIN );
    }
}
// secondary accumulate or SIMD merge
mask = extractMaskBits( .mask );
if (.add) {
    d = c;
    for (i=0; i<4; i++) {  d += mask[i] ? t[i] : 0;  }
} else {
    d = 0;
    for (i=0; i<4; i++)  {  d |= mask[i] ? t[i] : Vc[i];  }
}

PTX ISA 说明

在PTX ISA 3.0版本中引入。

目标ISA注意事项

vadd4, vsub4, varvg4, vabsdiff4, vmin4, vmax4 需要 sm_30 或更高版本。

示例

vadd4.s32.s32.u32.sat  r1, r2, r3, r1;
vsub4.s32.s32.s32.sat  r1.b0, r2.b3210, r3.b7654, r1;
vmin4.s32.u32.u32.add  r1.b00, r2.b0000, r3.b2222, r1;
9.7.18.2.4. SIMD视频指令:vset4

vset4

整数四字节SIMD比较。

语法

// SIMD instruction with secondary SIMD merge operation
vset4.atype.btype.cmp  d{.mask}, a{.asel}, b{.bsel}, c;

// SIMD instruction with secondary accumulate operation
vset4.atype.btype.cmp.add  d{.mask}, a{.asel}, b{.bsel}, c;

.atype = .btype = { .u32, .s32 };
.cmp   = { .eq, .ne, .lt, .le, .gt, .ge };
.mask  = { .b0,
           .b1, .b10
           .b2, .b20, .b21, .b210,
           .b3, .b30, .b31, .b310, .b32, .b320, .b321, .b3210 };
    defaults to .b3210
.asel = .bsel = .bxyzw, where x,y,z,w are from { 0, ..., 7 };
   .asel defaults to .b3210
   .bsel defaults to .b7654

描述

四路SIMD并行比较与二次操作。

操作中每个四字节源元素都是从两个源操作数ab的任意八个字节中,通过aselbsel修饰符进行选择的。

随后并行比较选定的字节。

比较的中间结果始终是无符号的,因此目标d和操作数c的字节也是无符号的。

对于包含辅助SIMD合并操作的指令:

  • 对于掩码中指定的字节位置,选定的字节结果会被复制到目标d。对于所有其他位置,源操作数b中的对应字节会被复制到d

对于包含次要累积操作的指令:

  • 对于掩码中指定的字节位置,所选字节结果会与操作数 c 相加,最终结果存储在 d 中。

语义

// extract quads of bytes and sign- or zero-extend
// based on operand type
Va = extractAndSignExt_4( a, b, .asel, .atype );
Vb = extractAndSignExt_4( a, b, .bsel, .btype );
Vc = extractAndSignExt_4( c );
for (i=0; i<4; i++) {
    t[i] = compare( Va[i], Vb[i], cmp ) ? 1 : 0;
}
// secondary accumulate or SIMD merge
mask = extractMaskBits( .mask );
if (.add) {
    d = c;
    for (i=0; i<4; i++) {  d += mask[i] ? t[i] : 0;  }
} else {
    d = 0;
    for (i=0; i<4; i++)  {  d |= mask[i] ? t[i] : Vc[i];  }
}

PTX ISA 说明

在PTX ISA 3.0版本中引入。

目标ISA注意事项

vset4 需要 sm_30 或更高版本。

示例

vset4.s32.u32.lt      r1, r2, r3, r0;
vset4.u32.u32.ne.max  r1, r2, r3, r0;

9.7.19. Miscellaneous Instructions

杂项指令包括:

  • brkpt

  • nanosleep

  • pmevent

  • trap

  • setmaxnreg

9.7.19.1. 杂项指令:brkpt

断点

断点。

语法

brkpt;

描述

暂停执行。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

brkpt 需要 sm_11 或更高版本。

示例

    brkpt;
@p  brkpt;

9.7.19.2. 杂项指令:nanosleep

nanosleep

将线程暂停大约指定的纳秒延迟时间。

语法

nanosleep.u32 t;

描述

将线程暂停大约接近延迟t的睡眠时长,以纳秒为单位指定。t可以是寄存器值或立即数。

睡眠时长是近似值,但保证在区间[0, 2*t]内。最大睡眠时长为1毫秒。具体实现可能会减少一个warp内各线程的睡眠时长,使得该warp中所有休眠线程能同时唤醒。

PTX ISA 说明

nanosleep 在PTX ISA 6.3中引入。

目标ISA注意事项

nanosleep 需要 sm_70 或更高版本。

示例

.reg .b32 r;
.reg .pred p;

nanosleep.u32 r;
nanosleep.u32 42;
@p nanosleep.u32 r;

9.7.19.3. 杂项指令:pmevent

性能监控事件

触发一个或多个性能监视器事件。

语法

pmevent       a;    // trigger a single performance monitor event
pmevent.mask  a;    // trigger one or more performance monitor events

描述

触发一个或多个固定数量的性能监控事件,事件索引或掩码由立即操作数a指定。

pmevent(不带修饰符.mask)会触发一个由立即操作数a索引的性能监控事件,其索引范围为0..15

pmevent.mask 触发一个或多个性能监控事件。16位立即操作数 a 中的每一位控制一个事件。

编程性能监控事件可以通过布尔函数与其他硬件事件结合,以递增四个性能计数器中的一个。事件与计数器之间的关系通过主机端的API调用进行编程设置。

注意事项

目前共有16个性能监控事件,编号从0到15。

PTX ISA 说明

pmevent 在PTX ISA版本1.4中引入。

pmevent.mask 在PTX ISA 3.0版本中引入。

目标ISA注意事项

pmevent 在所有目标架构上都受支持。

pmevent.mask 需要 sm_20 或更高版本。

示例

    pmevent      1;
@p  pmevent      7;
@q  pmevent.mask 0xff;

9.7.19.4. 杂项指令:trap

陷阱

执行陷阱操作。

语法

trap;

描述

中止执行并向主机CPU生成中断。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

    trap;
@p  trap;

9.7.19.5. 杂项指令:setmaxnreg

setmaxnreg

提示更改warp拥有的寄存器数量。

语法

setmaxnreg.action.sync.aligned.u32 imm-reg-count;

.action = { .inc, .dec };

描述

setmaxnreg 向系统提供一个提示,用于将执行线程束中每个线程拥有的最大寄存器数量更新为 imm-reg-count 操作数指定的值。

修饰符 .dec 用于释放额外的寄存器,从而将每个线程的绝对最大寄存器数量从当前值减少到 imm-reg-count。修饰符 .inc 用于请求额外的寄存器,从而将每个线程的绝对最大寄存器数量从当前值增加到 imm-reg-count

每个CTA维护一个可用寄存器的资源池。通过setmaxnreg指令请求的寄存器调整,会根据.action限定符的值,通过从该池中分配额外寄存器给请求的warp,或从请求的warp释放额外寄存器到该池来完成。

setmaxnreg.inc指令会阻塞执行,直到CTA寄存器池中有足够的寄存器可用。在setmaxnreg.inc指令从CTA池获取新寄存器后,新寄存器的初始内容是未定义的。这些新寄存器在使用前必须进行初始化。

同一个setmaxnreg指令必须由warpgroup中的所有warp执行。在执行完setmaxnreg指令后,warpgroup中的所有warp必须在执行后续setmaxnreg指令前显式同步。如果warpgroup中并非所有warp都执行了setmaxnreg指令,则行为是未定义的。

操作数 imm-reg-count 是一个整数常量。imm-reg-count 的值必须在24到256之间(包含两端值),且必须是8的倍数。

对warp寄存器文件的更改总是发生在寄存器文件的末端。

setmaxnreg指令要求内核启动时已通过适当的编译选项或性能调优指令,指定了有效的每线程最大寄存器数量值。否则,setmaxnreg指令可能不会产生任何效果。

当指定限定符.dec时,在执行setmaxnreg指令前,warp拥有的每线程寄存器最大数量应大于或等于imm-reg-count。否则,行为是未定义的。

当指定限定符.inc时,在执行setmaxnreg指令前,每个线程所属warp拥有的最大寄存器数量应小于或等于imm-reg-count。否则,行为将是未定义的。

强制性的 .sync 限定符表示 setmaxnreg 指令会使执行线程等待,直到线程束中的所有线程都执行相同的 setmaxnreg 指令后才会继续执行。

强制性的.aligned限定符表示warpgroup中的所有线程必须执行相同的setmaxnreg指令。在条件执行的代码中,setmaxnreg指令应仅在已知warpgroup中所有线程对条件的评估结果一致时使用,否则行为将是未定义的。

PTX ISA 说明

在PTX ISA版本8.0中引入。

目标ISA注意事项

支持以下架构:

  • sm_90a

  • sm_100a

  • sm_101a

  • sm_120a

示例

setmaxnreg.dec.sync.aligned.u32 64;
setmaxnreg.inc.sync.aligned.u32 192;

10. 特殊寄存器

PTX包含多个预定义的只读变量,这些变量作为特殊寄存器可见,并通过movcvt指令进行访问。

特殊寄存器包括:

  • %tid

  • %ntid

  • %laneid

  • %warpid

  • %nwarpid

  • %ctaid

  • %nctaid

  • %smid

  • %nsmid

  • %gridid

  • %is_explicit_cluster

  • %clusterid

  • %nclusterid

  • %cluster_ctaid

  • %cluster_nctaid

  • %cluster_ctarank

  • %cluster_nctarank

  • %lanemask_eq, %lanemask_le, %lanemask_lt, %lanemask_ge, %lanemask_gt

  • %clock, %clock_hi, %clock64

  • %pm0, ..., %pm7

  • %pm0_64, ..., %pm7_64

  • %envreg0, ..., %envreg31

  • %globaltimer, %globaltimer_lo, %globaltimer_hi

  • %reserved_smem_offset_begin, %reserved_smem_offset_end, %reserved_smem_offset_cap, %reserved_smem_offset<2>

  • %total_smem_size

  • %aggr_smem_size

  • %dynamic_smem_size

  • %current_graph_exec

10.1. 特殊寄存器:%tid

%tid

CTA内的线程标识符。

语法(预定义)

.sreg .v4 .u32 %tid;                  // thread id vector
.sreg .u32 %tid.x, %tid.y, %tid.z;    // thread id components

描述

一个预定义的、只读的、每个线程特有的特殊寄存器,使用线程在CTA内的标识符进行初始化。%tid特殊寄存器包含一个与CTA形状匹配的1D、2D或3D向量;未使用维度中的%tid值为0。第四个元素未使用且始终返回零。每个维度的线程数由预定义的特殊寄存器%ntid指定。

CTA中的每个线程都有一个唯一的%tid

%tid 组件值在每个CTA维度中的范围是从 0%ntid-1

%tid.y == %tid.z == 0 在一维CTA中。%tid.z == 0 在二维CTA中。

保证:

0  <=  %tid.x <  %ntid.x
0  <=  %tid.y <  %ntid.y
0  <=  %tid.z <  %ntid.z

PTX ISA 说明

在PTX ISA版本1.0中引入,类型为.v4.u16

在PTX ISA 2.0版本中重新定义为.v4.u32类型。为保持与旧版PTX代码的兼容性,可以使用16位 movcvt指令来读取%tid每个分量的低16位数据。

目标ISA注意事项

支持所有目标架构。

示例

mov.u32      %r1,%tid.x;  // move tid.x to %rh

// legacy code accessing 16-bit components of %tid
mov.u16      %rh,%tid.x;
cvt.u32.u16  %r2,%tid.z;  // zero-extend tid.z to %r2

10.2. 特殊寄存器:%ntid

%ntid

每个CTA的线程ID数量。

语法(预定义)

.sreg .v4 .u32 %ntid;                   // CTA shape vector
.sreg .u32 %ntid.x, %ntid.y, %ntid.z;   // CTA dimensions

描述

一个预定义的、只读的特殊寄存器,初始化为每个CTA维度中的线程ID数量。%ntid特殊寄存器包含一个3D CTA形状向量,用于保存CTA维度。CTA维度为非零值;第四个元素未使用且始终返回零。CTA中的线程总数为(%ntid.x * %ntid.y * %ntid.z)

%ntid.y == %ntid.z == 1 in 1D CTAs.
%ntid.z ==1 in 2D CTAs.

%ntid.{x,y,z} 的最大值如下:

目标架构

%ntid.x

%ntid.y

%ntid.z

sm_1x

512

512

64

sm_20, sm_3x, sm_5x, sm_6x, sm_7x, sm_8x, sm_9x

1024

1024

64

PTX ISA 说明

在PTX ISA版本1.0中引入,类型为.v4.u16

在PTX ISA 2.0版本中重新定义为.v4.u32类型。为兼容旧版PTX代码,可使用16位 movcvt指令来读取%ntid每个分量的低16位。

目标ISA注意事项

支持所有目标架构。

示例

// compute unified thread id for 2D CTA
mov.u32  %r0,%tid.x;
mov.u32  %h1,%tid.y;
mov.u32  %h2,%ntid.x;
mad.u32  %r0,%h1,%h2,%r0;

mov.u16  %rh,%ntid.x;      // legacy code

10.3. 特殊寄存器:%laneid

%车道ID

车道标识符。

语法(预定义)

.sreg .u32 %laneid;

描述

一个预定义的只读特殊寄存器,用于返回线程在warp内的lane标识。lane标识符的范围从零到WARP_SZ-1

PTX ISA 说明

在PTX ISA版本1.3中引入。

目标ISA注意事项

支持所有目标架构。

示例

mov.u32  %r, %laneid;

10.4. 特殊寄存器:%warpid

%warpid

线程束标识符。

语法(预定义)

.sreg .u32 %warpid;

描述

一个预定义的只读特殊寄存器,用于返回线程的warp标识符。该warp标识符在CTA内提供唯一的warp编号,但在网格内的不同CTA之间不保证唯一。同一个warp内的所有线程将具有相同的warp标识符。

请注意,%warpid返回的是线程在被读取时的位置,但其值在执行过程中可能会发生变化,例如由于线程被抢占后重新调度。因此,如果内核代码中需要这样的值,应使用%ctaid%tid来计算虚拟warp索引;%warpid主要用于支持性能分析和诊断代码采样和记录诸如工作位置映射和负载分布等信息。

PTX ISA 说明

在PTX ISA版本1.3中引入。

目标ISA注意事项

支持所有目标架构。

示例

mov.u32  %r, %warpid;

10.5. 特殊寄存器:%nwarpid

%nwarpid

warp标识符的数量。

语法(预定义)

.sreg .u32 %nwarpid;

描述

一个预定义的只读特殊寄存器,返回最大warp标识符数量。

PTX ISA 说明

在PTX ISA 2.0版本中引入。

目标ISA注意事项

%nwarpid 需要 sm_20 或更高版本。

示例

mov.u32  %r, %nwarpid;

10.6. 特殊寄存器:%ctaid

%ctaid

网格内的CTA标识符。

语法(预定义)

.sreg .v4 .u32 %ctaid;                      // CTA id vector
.sreg .u32 %ctaid.x, %ctaid.y, %ctaid.z;    // CTA id components

描述

一个预定义的、只读的特殊寄存器,用于在CTA网格内初始化CTA标识符。%ctaid特殊寄存器包含一个1D、2D或3D向量,具体取决于CTA网格的形状和秩。第四个元素未使用且始终返回零。

保证:

0  <=  %ctaid.x <  %nctaid.x
0  <=  %ctaid.y <  %nctaid.y
0  <=  %ctaid.z <  %nctaid.z

PTX ISA 说明

在PTX ISA版本1.0中引入,类型为.v4.u16

在PTX ISA 2.0版本中重新定义为.v4.u32类型。为保持与旧版PTX代码的兼容性,可以使用16位 movcvt指令来读取%ctaid每个分量的低16位数据。

目标ISA注意事项

支持所有目标架构。

示例

mov.u32  %r0,%ctaid.x;
mov.u16  %rh,%ctaid.y;   // legacy code

10.7. 特殊寄存器:%nctaid

%nctaid

每个网格的CTA ID数量。

语法(预定义)

.sreg .v4 .u32 %nctaid                      // Grid shape vector
.sreg .u32 %nctaid.x,%nctaid.y,%nctaid.z;   // Grid dimensions

描述

一个预定义的只读特殊寄存器,初始化为每个网格维度中的CTA数量。%nctaid特殊寄存器包含一个3D网格形状向量,其中每个元素的值至少为1。第四个元素未使用且始终返回零。

%nctaid.{x,y,z} 的最大值如下:

目标架构

%nctaid.x

%nctaid.y

%nctaid.z

sm_1x, sm_20

65535

65535

65535

sm_3x, sm_5x, sm_6x, sm_7x, sm_8x, sm_9x

231 -1

65535

65535

PTX ISA 说明

在PTX ISA版本1.0中引入,类型为.v4.u16

在PTX ISA 2.0版本中重新定义为类型.v4.u32。为了兼容旧版PTX代码,可以使用16位movcvt指令来读取%nctaid每个分量的低16位。

目标ISA注意事项

支持所有目标架构。

示例

mov.u32  %r0,%nctaid.x;
mov.u16  %rh,%nctaid.x;     // legacy code

10.8. 特殊寄存器:%smid

%smid

SM标识符。

语法(预定义)

.sreg .u32 %smid;

描述

一个预定义的只读特殊寄存器,用于返回执行特定线程的处理器(SM)标识符。SM标识符的范围从0%nsmid-1。SM标识符的编号不保证是连续的。

注意事项

请注意,%smid返回的是线程在读取时的位置,但其值在执行过程中可能会发生变化,例如由于抢占后线程的重新调度。%smid主要用于支持性能分析和诊断代码采样和记录诸如工作场所映射和负载分布等信息。

PTX ISA 说明

在PTX ISA版本1.3中引入。

目标ISA注意事项

支持所有目标架构。

示例

mov.u32  %r, %smid;

10.9. 特殊寄存器:%nsmid

%nsmid

SM标识符的数量。

语法(预定义)

.sreg .u32 %nsmid;

描述

一个预定义的只读特殊寄存器,用于返回SM标识符的最大数量。SM标识符的编号不保证是连续的,因此%nsmid可能大于设备中实际的SM物理数量。

PTX ISA 说明

在PTX ISA 2.0版本中引入。

目标ISA注意事项

%nsmid 需要 sm_20 或更高版本。

示例

mov.u32  %r, %nsmid;

10.10. 特殊寄存器:%gridid

%网格ID

网格标识符。

语法(预定义)

.sreg .u64 %gridid;

描述

一个预定义的只读特殊寄存器,使用每个网格的时间网格标识符进行初始化。%gridid被调试器用来区分并发(小型)网格中的CTAs和集群。

在执行过程中,可能会重复启动程序,每次启动都会生成一个CTA网格。此变量提供了当前上下文的时序网格启动编号。

对于sm_1x目标,%gridid的范围限制在[0..216-1]。对于sm_20%gridid的范围限制在[0..232-1]。而sm_30则支持完整的64位范围。

PTX ISA 说明

在PTX ISA版本1.0中引入,类型为.u16

在PTX ISA版本1.3中重新定义为.u32类型。

在PTX ISA 3.0版本中重新定义为.u64类型。

为了与旧版PTX代码兼容,可以使用16位和32位的movcvt指令来读取%gridid每个分量的低16位或32位。

目标ISA注意事项

支持所有目标架构。

示例

mov.u64  %s, %gridid;  // 64-bit read of %gridid
mov.u32  %r, %gridid;  // legacy code with 32-bit %gridid

10.11. 特殊寄存器:%is_explicit_cluster

%is_explicit_cluster

检查用户是否已明确指定集群启动。

语法(预定义)

.sreg .pred %is_explicit_cluster;

描述

一个预定义的、只读的特殊寄存器,其初始值为用户是否明确指定了集群启动的谓词值。

PTX ISA 说明

在PTX ISA版本7.8中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

.reg .pred p;

mov.pred  p, %is_explicit_cluster;

10.12. 特殊寄存器:%clusterid

%clusterid

网格中的集群标识符。

语法(预定义)

.sreg .v4 .u32 %clusterid;
.sreg .u32 %clusterid.x, %clusterid.y, %clusterid.z;

描述

一个预定义的、只读的特殊寄存器,在每个维度上使用网格中的集群标识符进行初始化。网格中的每个集群都有一个唯一的标识符。

%clusterid 特殊寄存器包含一个一维、二维或三维向量,具体取决于集群的形状和秩。第四个元素未使用,始终返回零。

保证:

0  <=  %clusterid.x <  %nclusterid.x
0  <=  %clusterid.y <  %nclusterid.y
0  <=  %clusterid.z <  %nclusterid.z

PTX ISA 说明

在PTX ISA版本7.8中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

.reg .b32 %r<2>;
.reg .v4 .b32 %rx;

mov.u32     %r0, %clusterid.x;
mov.u32     %r1, %clusterid.z;
mov.v4.u32  %rx, %clusterid;

10.13. 特殊寄存器:%nclusterid

%nclusterid

每个网格的集群标识符数量。

语法(预定义)

.sreg .v4 .u32 %nclusterid;
.sreg .u32 %nclusterid.x, %nclusterid.y, %nclusterid.z;

描述

一个预定义的、只读的特殊寄存器,初始化时包含网格每个维度中的集群数量。

%nclusterid 特殊寄存器包含一个3D网格形状向量,该向量按集群维度存储网格尺寸。第四个元素未使用且始终返回零。

有关%nclusterid.{x,y,z}最大值的详细信息,请参阅Cuda编程指南

PTX ISA 说明

在PTX ISA版本7.8中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

.reg .b32 %r<2>;
.reg .v4 .b32 %rx;

mov.u32     %r0, %nclusterid.x;
mov.u32     %r1, %nclusterid.z;
mov.v4.u32  %rx, %nclusterid;

10.14. 特殊寄存器:%cluster_ctaid

%cluster_ctaid

集群中的CTA标识符。

语法(预定义)

.sreg .v4 .u32 %cluster_ctaid;
.sreg .u32 %cluster_ctaid.x, %cluster_ctaid.y, %cluster_ctaid.z;

描述

一个预定义的、只读的特殊寄存器,在每个维度的集群中使用CTA标识符初始化。集群中的每个CTA都有一个唯一的CTA标识符。

%cluster_ctaid 特殊寄存器包含一个一维、二维或三维向量,具体取决于集群的形状。第四个元素未使用且始终返回零。

保证:

0  <=  %cluster_ctaid.x <  %cluster_nctaid.x
0  <=  %cluster_ctaid.y <  %cluster_nctaid.y
0  <=  %cluster_ctaid.z <  %cluster_nctaid.z

PTX ISA 说明

在PTX ISA版本7.8中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

.reg .b32 %r<2>;
.reg .v4 .b32 %rx;

mov.u32     %r0, %cluster_ctaid.x;
mov.u32     %r1, %cluster_ctaid.z;
mov.v4.u32  %rx, %cluster_ctaid;

10.15. 特殊寄存器:%cluster_nctaid

%cluster_nctaid

每个集群的CTA标识符数量。

语法(预定义)

.sreg .v4 .u32 %cluster_nctaid;
.sreg .u32 %cluster_nctaid.x, %cluster_nctaid.y, %cluster_nctaid.z;

描述

一个预定义的、只读的特殊寄存器,初始化为集群中每个维度上的CTA数量。

%cluster_nctaid 特殊寄存器包含一个3D网格形状向量,用于存储以CTA为单位的集群维度。第四个元素未使用,始终返回零。

有关%cluster_nctaid.{x,y,z}最大值的详细信息,请参阅Cuda编程指南

PTX ISA 说明

在PTX ISA版本7.8中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

.reg .b32 %r<2>;
.reg .v4 .b32 %rx;

mov.u32     %r0, %cluster_nctaid.x;
mov.u32     %r1, %cluster_nctaid.z;
mov.v4.u32  %rx, %cluster_nctaid;

10.16. 特殊寄存器:%cluster_ctarank

%cluster_ctarank

集群中跨所有维度的CTA标识符。

语法(预定义)

.sreg .u32 %cluster_ctarank;

描述

一个预定义的、只读的特殊寄存器,初始化为集群中跨所有维度的CTA等级。

保证:

0  <=  %cluster_ctarank <  %cluster_nctarank

PTX ISA 说明

在PTX ISA版本7.8中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

.reg .b32 %r;

mov.u32  %r, %cluster_ctarank;

10.17. 特殊寄存器:%cluster_nctarank

%cluster_nctarank

集群中所有维度的CTA标识符数量。

语法(预定义)

.sreg .u32 %cluster_nctarank;

描述

一个预定义的、只读的特殊寄存器,初始化为跨所有维度的集群内CTA数量。

PTX ISA 说明

在PTX ISA版本7.8中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

.reg .b32 %r;

mov.u32  %r, %cluster_nctarank;

10.18. 特殊寄存器:%lanemask_eq

%lanemask_eq

32位掩码,其中位设置的位置等于线程在warp中的通道编号。

语法(预定义)

.sreg .u32 %lanemask_eq;

描述

一个预定义的、只读的特殊寄存器,初始化为一个32位掩码,其中与线程在warp中的车道号对应的位被置位。

PTX ISA 说明

在PTX ISA 2.0版本中引入。

目标ISA注意事项

%lanemask_eq 需要 sm_20 或更高版本。

示例

mov.u32     %r, %lanemask_eq;

10.19. 特殊寄存器:%lanemask_le

%lanemask_le

32位掩码,其位设置在小于或等于线程在warp中的lane编号的位置。

语法(预定义)

.sreg .u32 %lanemask_le;

描述

一个预定义的、只读的特殊寄存器,初始化为一个32位掩码,其中位设置的位置小于或等于线程在warp中的车道编号。

PTX ISA 说明

在PTX ISA 2.0版本中引入。

目标ISA注意事项

%lanemask_le 需要 sm_20 或更高版本。

示例

mov.u32     %r, %lanemask_le

10.20. 特殊寄存器:%lanemask_lt

%lanemask_lt

32位掩码,其位设置在小于线程在warp中的通道编号的位置。

语法(预定义)

.sreg .u32 %lanemask_lt;

描述

一个预定义的、只读的特殊寄存器,初始化为一个32位掩码,其中位设置的位置小于线程在warp中的车道编号。

PTX ISA 说明

在PTX ISA 2.0版本中引入。

目标ISA注意事项

%lanemask_lt 需要 sm_20 或更高版本。

示例

mov.u32     %r, %lanemask_lt;

10.21. 特殊寄存器:%lanemask_ge

%lanemask_ge

32位掩码,其位设置在大于或等于线程在warp中的车道号的位置。

语法(预定义)

.sreg .u32 %lanemask_ge;

描述

一个预定义的、只读的特殊寄存器,初始化为32位掩码,其中位设置的位置大于或等于线程在warp中的车道编号。

PTX ISA 说明

在PTX ISA 2.0版本中引入。

目标ISA注意事项

%lanemask_ge 需要 sm_20 或更高版本。

示例

mov.u32     %r, %lanemask_ge;

10.22. 特殊寄存器:%lanemask_gt

%lanemask_gt

32位掩码,其位设置在大于线程在warp中的通道编号的位置。

语法(预定义)

.sreg .u32 %lanemask_gt;

描述

一个预定义的、只读的特殊寄存器,初始化为一个32位掩码,其中在warp中线程通道号以上的位置设置了位。

PTX ISA 说明

在PTX ISA 2.0版本中引入。

目标ISA注意事项

%lanemask_gt 需要 sm_20 或更高版本。

示例

mov.u32     %r, %lanemask_gt;

10.23. 特殊寄存器:%clock, %clock_hi

%clock, %clock_hi

%clock

一个预定义的、只读的32位无符号循环计数器。

%clock_hi

%clock64 特殊寄存器的高32位。

语法(预定义)

.sreg .u32 %clock;
.sreg .u32 %clock_hi;

描述

特殊寄存器 %clock%clock_hi 是无符号32位只读循环计数器,会静默回绕。

PTX ISA 说明

%clock 在PTX ISA 1.0版本中引入。

%clock_hi 在PTX ISA版本5.0中引入。

目标ISA注意事项

%clock 在所有目标架构上都受支持。

%clock_hi 需要 sm_20 或更高版本。

示例

mov.u32 r1,%clock;
mov.u32 r2, %clock_hi;

10.24. 特殊寄存器:%clock64

%clock64

一个预定义的、只读的64位无符号循环计数器。

语法(预定义)

.sreg .u64 %clock64;

描述

特殊寄存器 %clock64 是一个无符号64位只读循环计数器,会静默回绕。

注意事项

%clock64的低32位与%clock相同。

%clock64的高32位与%clock_hi相同。

PTX ISA 说明

在PTX ISA 2.0版本中引入。

目标ISA注意事项

%clock64 需要 sm_20 或更高版本。

示例

mov.u64  r1,%clock64;

10.25. 特殊寄存器:%pm0..%pm7

%pm0..%pm7

性能监控计数器。

语法(预定义)

.sreg .u32 %pm<8>;

描述

特殊寄存器 %pm0..%pm7 是32位无符号只读性能监控计数器。它们当前的行为尚未定义。

PTX ISA 说明

%pm0..%pm3 在PTX ISA版本1.3中引入。

%pm4..%pm7 在PTX ISA 3.0版本中引入。

目标ISA注意事项

%pm0..%pm3 在所有目标架构上都受支持。

%pm4..%pm7 需要 sm_20 或更高版本。

示例

mov.u32  r1,%pm0;
mov.u32  r1,%pm7;

10.26. 特殊寄存器:%pm0_64..%pm7_64

%pm0_64..%pm7_64

64位性能监控计数器。

语法(预定义)

.sreg .u64 %pm0_64;
.sreg .u64 %pm1_64;
.sreg .u64 %pm2_64;
.sreg .u64 %pm3_64;
.sreg .u64 %pm4_64;
.sreg .u64 %pm5_64;
.sreg .u64 %pm6_64;
.sreg .u64 %pm7_64;

描述

特殊寄存器 %pm0_64..%pm7_64 是64位无符号只读性能监控计数器。它们的行为目前尚未定义。

注意事项

%pm0_64..%pm7_64的低32位与%pm0..%pm7相同。

PTX ISA 说明

%pm0_64..%pm7_64 在PTX ISA 4.0版本中引入。

目标ISA注意事项

%pm0_64..%pm7_64 需要 sm_50 或更高版本。

示例

mov.u32  r1,%pm0_64;
mov.u32  r1,%pm7_64;

10.27. 特殊寄存器:%envreg<32>

%环境寄存器<32>

驱动程序定义的只读寄存器。

语法(预定义)

.sreg .b32 %envreg<32>;

描述

一组32个预定义的只读寄存器,用于捕获PTX程序在PTX虚拟机之外的执行环境。这些寄存器在内核启动前由驱动程序初始化,可以包含CTA范围或网格范围的值。

这些寄存器的精确语义在驱动程序文档中有定义。

PTX ISA 说明

在PTX ISA 2.1版本中引入。

目标ISA注意事项

支持所有目标架构。

示例

mov.b32      %r1,%envreg0;  // move envreg0 to %r1

10.28. 特殊寄存器:%globaltimer, %globaltimer_lo, %globaltimer_hi

%globaltimer, %globaltimer_lo, %globaltimer_hi

%globaltimer

一个预定义的64位全局纳秒计时器。

%globaltimer_lo

%globaltimer的低32位。

%globaltimer_hi

%globaltimer的高32位。

语法(预定义)

.sreg .u64 %globaltimer;
.sreg .u32 %globaltimer_lo, %globaltimer_hi;

描述

专为NVIDIA工具设计的特殊寄存器。其行为与具体目标相关,并可能在未来的GPU中发生变化或被移除。当JIT编译到其他目标时,这些寄存器的值未作定义。

PTX ISA 说明

在PTX ISA版本3.1中引入。

目标ISA注意事项

需要目标 sm_30 或更高版本。

示例

mov.u64  r1,%globaltimer;

10.29. 特殊寄存器:%reserved_smem_offset_begin, %reserved_smem_offset_end, %reserved_smem_offset_cap, %reserved_smem_offset_<2>

%reserved_smem_offset_begin, %reserved_smem_offset_end, %reserved_smem_offset_cap, %reserved_smem_offset_<2>

%reserved_smem_offset_begin

保留共享内存区域的起始位置。

%reserved_smem_offset_end

保留共享内存区域的结束。

%reserved_smem_offset_cap

保留共享内存区域的总大小。

%reserved_smem_offset_<2>

保留共享内存区域中的偏移量。

语法(预定义)

.sreg .b32 %reserved_smem_offset_begin;
.sreg .b32 %reserved_smem_offset_end;
.sreg .b32 %reserved_smem_offset_cap;
.sreg .b32 %reserved_smem_offset_<2>;

描述

这些是预定义的只读特殊寄存器,包含为NVIDIA系统软件使用而保留的共享内存区域的信息。该共享内存区域对用户不可用,从用户代码访问此区域会导致未定义行为。详情请参阅CUDA编程指南

PTX ISA 说明

在PTX ISA版本7.6中引入。

目标ISA注意事项

需要 sm_80 或更高版本。

示例

.reg .b32 %reg_begin, %reg_end, %reg_cap, %reg_offset0, %reg_offset1;

mov.b32 %reg_begin,   %reserved_smem_offset_begin;
mov.b32 %reg_end,     %reserved_smem_offset_end;
mov.b32 %reg_cap,     %reserved_smem_offset_cap;
mov.b32 %reg_offset0, %reserved_smem_offset_0;
mov.b32 %reg_offset1, %reserved_smem_offset_1;

10.30. 特殊寄存器:%total_smem_size

%总共享内存大小

内核CTA使用的共享内存总大小。

语法(预定义)

.sreg .u32 %total_smem_size;

描述

一个预定义的、只读的特殊寄存器,在核函数启动时初始化为为该CTA分配的共享内存总大小(包括静态和动态分配,但不包括为NVIDIA系统软件使用保留的共享内存)。

返回的大小是目标架构支持的共享内存分配单元大小的整数倍。

分配单元的值如下:

目标架构

共享内存分配单元大小

sm_2x

128 字节

sm_3x, sm_5x, sm_6x, sm_7x

256 字节

sm_8x, sm_9x

128 字节

PTX ISA 说明

在PTX ISA版本4.1中引入。

目标ISA注意事项

需要 sm_20 或更高版本。

示例

mov.u32  %r, %total_smem_size;

10.31. 特殊寄存器:%aggr_smem_size

%aggr_smem_size

内核CTA使用的共享内存总大小。

语法(预定义)

.sreg .u32 %aggr_smem_size;

描述

一个预定义的、只读的特殊寄存器,初始化为共享内存的总聚合大小,包括在启动时分配的用户共享内存(静态和动态)的大小以及为NVIDIA系统软件使用保留的共享内存区域的大小。

PTX ISA 说明

在PTX ISA版本8.1中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

mov.u32  %r, %aggr_smem_size;

10.32. 特殊寄存器:%dynamic_smem_size

%dynamic_smem_size

内核启动时动态分配的共享内存大小。

语法(预定义)

.sreg .u32 %dynamic_smem_size;

描述

内核启动时动态分配的共享内存大小。

一个预定义的、只读的特殊寄存器,在启动时为内核的CTA动态分配的共享内存大小初始化。

PTX ISA 说明

在PTX ISA版本4.1中引入。

目标ISA注意事项

需要 sm_20 或更高版本。

示例

mov.u32  %r, %dynamic_smem_size;

10.33. 特殊寄存器:%current_graph_exec

%当前图形执行

当前正在执行的CUDA设备图的标识符。

语法(预定义)

.sreg .u64 %current_graph_exec;

描述

一个预定义的、只读的特殊寄存器,初始化为当前正在执行的CUDA设备图的标识符。如果执行的内核不是CUDA设备图的一部分,则该寄存器值为0。

有关CUDA设备图的更多详情,请参阅CUDA编程指南

PTX ISA 说明

在PTX ISA版本8.0中引入。

目标ISA注意事项

需要 sm_50 或更高版本。

示例

mov.u64  r1, %current_graph_exec;

11. 指令

11.1. PTX模块指令

以下指令声明了模块中代码的PTX ISA版本、生成代码的目标架构以及PTX模块内的地址大小。

  • .version

  • .target

  • .address_size

11.1.1. PTX模块指令:.version

.version

PTX ISA版本号。

语法

.version  major.minor    // major, minor are integers

描述

指定PTX语言版本号。

主版本号会在PTX语言发生不兼容变更时递增,例如语法或语义的修改。PTX编译器会利用主版本号来确保旧版PTX代码的正确执行。

次要版本号会在PTX添加新功能时递增。

语义

表示此模块必须使用支持相同或更高版本号的工具进行编译。

每个PTX模块必须以.version指令开头,且模块内其他位置不允许出现其他.version指令。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

.version 3.1
.version 3.0
.version 2.3

11.1.2. PTX模块指令:.target

.target

架构与平台目标。

语法

.target stringlist         // comma separated list of target specifiers
string = { sm_120a, sm_120                   // sm_12x target architectures
           sm_100a, sm_100, sm_101a, sm_101  // sm_10x target architectures
           sm_90a, sm_90,                    // sm_9x target architectures
           sm_80, sm_86, sm_87, sm_89,       // sm_8x target architectures
           sm_70, sm_72, sm_75,              // sm_7x target architectures
           sm_60, sm_61, sm_62,              // sm_6x target architectures
           sm_50, sm_52, sm_53,              // sm_5x target architectures
           sm_30, sm_32, sm_35, sm_37,       // sm_3x target architectures
           sm_20,                            // sm_2x target architectures
           sm_10, sm_11, sm_12, sm_13,       // sm_1x target architectures
           texmode_unified, texmode_independent,   // texturing mode
           debug,                                  // platform option
           map_f64_to_f32 };                       // platform option

描述

指定当前PTX代码生成所针对的目标架构功能集。通常,SM架构的世代遵循洋葱层模型,每一代都会新增功能并保留前代的所有特性。这种洋葱层模型使得为特定目标生成的PTX代码能够在后续世代的设备上运行。

带有后缀“a”的目标架构(例如sm_90a)包含仅在该指定架构上支持的架构加速特性,因此这类目标不遵循洋葱层模型。因此,为此类目标生成的PTX代码无法在后续代设备上运行。架构加速特性只能在与支持这些特性的目标架构一起使用。

语义

每个PTX模块必须以.version指令开头,紧接着是一个包含目标架构和可选平台选项的.target指令。.target指令指定单一目标架构,但后续可以使用.target指令来改变解析期间允许的目标功能集。包含多个.target指令的程序将只能在支持程序中列出的最高编号架构所有功能的设备上编译和运行。

PTX功能会根据指定的目标架构进行检查,如果使用了不支持的功能,则会生成错误。下表总结了PTX中随目标架构变化的功能。

目标

描述

sm_120

sm_120架构的基准功能集。

sm_120a

添加对sm_120a加速功能的支持。

目标

描述

sm_100

sm_100 架构的基准功能集。

sm_100a

添加对sm_100a加速功能的支持。

sm_101

sm_101架构的基准功能集。

sm_101a

增加对sm_101a加速功能的支持。

目标

描述

sm_90

sm_90 架构的基准功能集。

sm_90a

添加对sm_90a加速功能的支持。

目标

描述

sm_80

sm_80 架构的基准功能集。

sm_86

minmax指令添加对.xorsign修饰符的支持。

sm_87

sm_86 架构的基准功能集。

sm_89

sm_86 架构的基准功能集。

目标

描述

sm_70

sm_70架构的基准功能集。

sm_72

wmma指令中增加了对整数被乘数和累加器矩阵的支持。

增加对cvt.pack指令的支持。

sm_75

wmma指令中增加对子字节整数和单比特乘数矩阵的支持。

增加对ldmatrix指令的支持。

增加对movmatrix指令的支持。

增加对tanh指令的支持。

目标

描述

sm_60

sm_60 架构的基准功能集。

sm_61

增加对dp2adp4a指令的支持。

sm_62

sm_61架构的基准功能集。

目标

描述

sm_50

sm_50 架构的基准功能集。

sm_52

sm_50 架构的基准功能集。

sm_53

.f16.f16x2类型添加对算术运算、比较和纹理指令的支持。

目标

描述

sm_30

sm_30架构的基准功能集。

sm_32

添加64位{atom,red}.{and,or,xor,min,max}指令。

添加shf指令。

添加ld.global.nc指令。

sm_35

增加对CUDA动态并行性的支持。

sm_37

sm_35架构的基准功能集。

目标

描述

sm_20

sm_20架构的基准功能集。

目标

描述

sm_10

sm_10架构的基准功能集。

如果使用了任何.f64指令,则需要map_f64_to_f32

sm_11

新增64位{atom,red}.{and,or,xor,min,max}指令。

如果使用了任何.f64指令,则需要map_f64_to_f32

sm_12

添加{atom,red}.shared、64位{atom,red}.globalvote指令。

如果使用了任何.f64指令,则需要map_f64_to_f32

sm_13

增加双精度支持,包括扩展的舍入修饰符。

禁止使用map_f64_to_f32

纹理模式是为整个模块指定的,不能在模块内更改。

.target调试选项声明PTX文件包含DWARF调试信息,后续PTX编译将保留源代码级调试所需的信息。如果声明了调试选项但在文件中未找到DWARF信息,则会生成错误消息。该调试选项要求PTX ISA版本3.0或更高。

map_f64_to_f32 表示无论目标架构如何,所有双精度指令都会映射到单精度。这使得高级语言编译器能够将包含double类型的程序编译到不支持双精度运算的目标设备上。请注意,.f64 存储仍保持为64位,只有一半被从.f64转换为.f32的指令所使用。

注意事项

形式为compute_xx的目标也可以作为sm_xx目标的同义词接受。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标字符串 sm_10sm_11 在PTX ISA 1.0版本中引入。

目标字符串 sm_12sm_13 在PTX ISA版本1.2中引入。

PTX ISA 1.5版本中引入的纹理映射模式。

目标字符串 sm_20 在PTX ISA版本2.0中引入。

目标字符串 sm_30 在PTX ISA 3.0版本中引入。

平台选项 debug 在PTX ISA 3.0版本中引入。

目标字符串 sm_35 在PTX ISA 3.1版本中引入。

目标字符串 sm_32sm_50 在PTX ISA 4.0版本中引入。

目标字符串 sm_37sm_52 在PTX ISA 4.1版本中引入。

目标字符串 sm_53 在PTX ISA版本4.2中引入。

目标字符串 sm_60, sm_61, sm_62 在PTX ISA 5.0版本中引入。

目标字符串 sm_70 在PTX ISA 6.0版本中引入。

目标字符串 sm_72 在PTX ISA版本6.1中引入。

目标字符串 sm_75 在PTX ISA版本6.3中引入。

目标字符串 sm_80 在PTX ISA版本7.0中引入。

目标字符串 sm_86 在PTX ISA 7.1版本中引入。

目标字符串 sm_87 在PTX ISA 7.4版本中引入。

目标字符串 sm_89 在PTX ISA 7.8版本中引入。

目标字符串 sm_90 在PTX ISA 7.8版本中引入。

目标字符串 sm_90a 在PTX ISA 8.0版本中引入。

目标字符串 sm_100 在PTX ISA版本8.6中引入。

目标字符串 sm_100a 在PTX ISA版本8.6中引入。

目标字符串 sm_101 在PTX ISA 8.6版本中引入。

目标字符串 sm_101a 在PTX ISA版本8.6中引入。

目标字符串 sm_120 在PTX ISA 8.7版本中引入。

目标字符串 sm_120a 在PTX ISA 8.7版本中引入。

目标ISA注意事项

.target 指令在所有目标架构上都受支持。

示例

.target sm_10       // baseline target architecture
.target sm_13       // supports double-precision
.target sm_20, texmode_independent
.target sm_90       // baseline target architecture
.target sm_90a      // PTX using arch accelerated features

11.1.3. PTX模块指令:.address_size

.address_size

PTX模块中使用的地址大小。

语法

.address_size  address-size
address-size = { 32, 64 };

描述

指定PTX代码和PTX中的二进制DWARF信息在整个模块中假设的地址大小。

不允许在模块内重新定义此指令。在存在单独编译的情况下,所有模块必须指定(或默认)相同的地址大小。

.address_size 指令是可选的,但如果模块中存在该指令,则必须紧跟在 .target 指令之后。

语义

如果省略.address_size指令,地址大小默认为32。

PTX ISA 说明

在PTX ISA版本2.3中引入。

目标ISA注意事项

支持所有目标架构。

示例

// example directives
   .address_size 32       // addresses are 32 bit
   .address_size 64       // addresses are 64 bit

// example of directive placement within a module
   .version 2.3
   .target sm_20
   .address_size 64
...
.entry foo () {
...
}

11.2. 指定内核入口点与函数

以下指令指定内核入口点和函数。

  • .entry

  • .func

11.2.1. 内核与函数指令:.entry

.entry

内核入口点和主体,带有可选参数。

语法

.entry kernel-name ( param-list )  kernel-body
.entry kernel-name  kernel-body

描述

定义内核函数的入口点名称、参数和函数体。

参数通过.param空间内存传递,并列在可选的括号参数列表中。参数可以在内核体中按名称引用,并使用ld.param{::entry}指令加载到寄存器中。

除了常规参数外,还可以传递不透明的.texref.samplerref.surfref变量作为参数。这些参数只能在纹理和表面加载、存储及查询指令中通过名称引用,无法通过ld.param指令访问。

执行内核的CTA的形状和大小可在特殊寄存器中获取。

语义

指定内核程序的入口点。

在核函数启动时,会确定核函数的维度和属性,并通过特殊寄存器(如%ntid%nctaid等)使其可用。

PTX ISA 说明

对于PTX ISA 1.4及更高版本,参数变量在内核参数列表中声明。对于PTX ISA 1.0至1.3版本,参数变量在内核主体中声明。

PTX 对普通(非不透明类型)参数支持的最大内存大小为32764字节。根据PTX ISA版本的不同,参数大小限制也有所变化。下表展示了不同PTX ISA版本允许的参数大小:

PTX ISA 版本

最大参数大小(字节)

PTX ISA 版本 8.1 及以上

32764

PTX ISA 版本 1.5 及以上

4352

PTX ISA 版本 1.4 及以上

256

CUDA和OpenCL驱动程序支持以下参数内存限制:

驱动程序

参数内存大小

CUDA

sm_1x 为256字节,sm_2x 及以上 为4096字节, sm_70 及以上为32764字节

OpenCL

对于sm_70及更高版本为32764字节,在sm_6x及更低版本上为4352字节

目标ISA注意事项

支持所有目标架构。

示例

.entry cta_fft
.entry filter ( .param .b32 x, .param .b32 y, .param .b32 z )
{
    .reg .b32 %r<99>;
    ld.param.b32  %r1, [x];
    ld.param.b32  %r2, [y];
    ld.param.b32  %r3, [z];
    ...
}

.entry prefix_sum ( .param .align 4 .s32 pitch[8000] )
{
    .reg .s32 %t;
    ld.param::entry.s32  %t, [pitch];
    ...
}

11.2.2. 内核与函数指令:.func

.func

函数定义。

语法

.func {.attribute(attr-list)} fname {.noreturn} function-body
.func {.attribute(attr-list)} fname (param-list) {.noreturn} function-body
.func {.attribute(attr-list)} (ret-param) fname (param-list) function-body

描述

定义一个函数,包括输入和返回参数以及可选的函数体。

可选的.noreturn指令表示该函数不会返回到调用者函数。对于具有返回参数的函数,不能指定.noreturn指令。有关.noreturn指令的详细说明,请参阅性能调优指令:.noreturn

可选的.attribute指令用于指定与函数相关的附加信息。有关允许的属性,请参阅变量和函数属性指令:.attribute的描述。

一个没有函数体的 .func 定义表示函数原型。

参数列表定义了函数体内的局部作用域变量。参数必须是寄存器或参数状态空间中的基本类型。寄存器状态空间中的参数可以直接在函数体内的指令中引用。.param空间中的参数需要通过函数体内的ld.param{::func}st.param{::func}指令来访问。参数传递采用值传递方式。

参数列表中的最后一个参数可以是一个未指定大小的.b8类型的.param数组。它用于将任意数量的参数打包到单个数组对象中传递给函数。

当调用一个带有未指定大小的最后一个参数的函数时,如果未通过该参数传递任何值,则可以从call指令中省略最后一个参数。对此数组参数的访问必须在数组边界范围内。如果未传递数组或访问超出了实际传递数组的边界,则访问结果是未定义的。

语义

PTX语法隐藏了底层调用约定和ABI的所有细节。

参数传递的实现交由优化编译器处理,它可能使用寄存器和堆栈位置的组合来传递参数。

版本发布说明

对于PTX ISA 1.x版本的代码,参数必须位于寄存器状态空间,不存在堆栈,且递归是非法的。

PTX ISA 2.0及更高版本,目标为sm_20或更高时,允许在.param状态空间中使用参数,实现了带有堆栈的ABI,并支持递归。

PTX ISA 2.0及更高版本,目标架构为sm_20或更高时,最多支持一个返回值。

PTX ISA 说明

在PTX ISA版本1.0中引入。

支持PTX ISA 6.0版本中引入的未指定大小数组参数。

支持PTX ISA版本6.4中引入的.noreturn指令。

支持PTX ISA版本8.0中引入的.attribute指令。

目标ISA注意事项

支持所有目标架构的函数,不含未指定大小的数组参数。

未指定大小的数组参数需要 sm_30 或更高版本。

.noreturn 指令需要 sm_30 或更高版本。

.attribute 指令需要 sm_90 或更高版本。

示例

.func (.reg .b32 rval) foo (.reg .b32 N, .reg .f64 dbl)
{
.reg .b32 localVar;

... use N, dbl;
other code;

mov.b32 rval,result;
ret;
}

...
call (fooval), foo, (val0, val1);  // return value in fooval
...

.func foo (.reg .b32 N, .reg .f64 dbl) .noreturn
{
.reg .b32 localVar;
... use N, dbl;
other code;
mov.b32 rval, result;
ret;
}
...
call foo, (val0, val1);
...

.func (.param .u32 rval) bar(.param .u32 N, .param .align 4 .b8 numbers[])
{
    .reg .b32 input0, input1;
    ld.param.b32   input0, [numbers + 0];
    ld.param.b32   input1, [numbers + 4];
    ...
    other code;
    ret;
}
...

.param .u32 N;
.param .align 4 .b8 numbers[8];
st.param.u32    [N], 2;
st.param.b32    [numbers + 0], 5;
st.param.b32    [numbers + 4], 10;
call (rval), bar, (N, numbers);
...

11.2.3. 内核与函数指令:.alias

.alias

为现有函数符号定义别名。

语法

.alias fAlias, fAliasee;

描述

.alias 是一个模块作用域指令,用于定义标识符 fAlias 作为由 fAliasee 指定函数的别名。

fAliasfAliasee 都是非入口函数符号。

标识符 fAlias 是一个没有函数体的函数声明。

标识符 fAliasee 是一个函数符号,必须在与 .alias 声明相同的模块中定义。函数 fAliasee 不能具有 .weak 链接属性。

fAliasfAliasee 的原型必须匹配。

程序可以使用fAliasfAlisee标识符来引用用fAliasee定义的函数。

PTX ISA 说明

.alias 指令在PTX ISA 6.3中引入。

目标ISA注意事项

.alias 指令需要 sm_30 或更高版本。

示例

.visible .func foo(.param .u32 p) {
   ...
}
.visible .func bar(.param .u32 p);
.alias bar, foo;
.entry test()
{
      .param .u32 p;
      ...
      call foo, (p);       // call foo directly
       ...
       .param .u32 p;
       call bar, (p);        // call foo through alias
}
.entry filter ( .param .b32 x, .param .b32 y, .param .b32 z )
{
    .reg .b32 %r1, %r2, %r3;
    ld.param.b32  %r1, [x];
    ld.param.b32  %r2, [y];
    ld.param.b32  %r3, [z];
    ...
}

11.3. 控制流指令

PTX提供了用于指定brx.idxcall指令潜在目标的指令。有关更多信息,请参阅brx.idxcall的描述。

  • .branchtargets

  • .calltargets

  • .callprototype

11.3.1. 控制流指令:.branchtargets

.branchtargets

声明一个潜在分支目标的列表。

语法

Label:   .branchtargets  list-of-labels ;

描述

声明一个潜在分支目标的列表,用于后续的brx.idx操作,并将该列表与行首的标签相关联。

列表中的所有控制流标签必须与声明位于同一函数内。

标签列表可以使用紧凑的简写语法来枚举具有共同前缀的一系列标签,类似于参数化变量名中描述的语法。

PTX ISA 说明

在PTX ISA 2.1版本中引入。

目标ISA注意事项

需要 sm_20 或更高版本。

示例

  .function foo () {
      .reg .u32 %r0;
      ...
      L1:
      ...
      L2:
      ...
      L3:
      ...
      ts: .branchtargets L1, L2, L3;
      @p brx.idx %r0, ts;
      ...

.function bar() {
      .reg .u32 %r0;
      ...
      N0:
      ...
      N1:
      ...
      N2:
      ...
      N3:
      ...
      N4:
      ...
      ts: .branchtargets N<5>;
      @p brx.idx %r0, ts;
      ...

11.3.2. 控制流指令:.calltargets

.calltargets

声明一个潜在的调用目标列表。

语法

Label:   .calltargets  list-of-functions ;

描述

声明后续间接调用的潜在调用目标列表,并将该列表与行首的标签相关联。

列表中命名的所有函数必须在.calltargets指令之前声明,且所有函数必须具有相同的类型签名。

PTX ISA 说明

在PTX ISA 2.1版本中引入。

目标ISA注意事项

需要 sm_20 或更高版本。

示例

calltgt:  .calltargets  fastsin, fastcos;
...
@p   call  (%f1), %r0, (%x), calltgt;
...

11.3.3. 控制流指令:.callprototype

.callprototype

声明一个原型用于间接调用。

语法

 // no input or return parameters
label: .callprototype _ .noreturn;
// input params, no return params
label: .callprototype _ (param-list) .noreturn;
// no input params, // return params
label: .callprototype (ret-param) _ ;
// input, return parameters
label: .callprototype (ret-param) _ (param-list);

描述

定义一个没有特定函数名称的原型,并将该原型与一个标签关联。之后,该原型可用于间接调用指令中,其中对可能的调用目标了解不完整。

参数可以具有寄存器或参数状态空间中的基本类型,也可以是参数状态空间中的数组类型。可以使用汇符号'_'来避免使用虚拟参数名称。

可选的.noreturn指令表示该函数不会返回到调用者函数。对于具有返回参数的函数,不能指定.noreturn指令。详情请参阅性能调优指令:.noreturn中对.noreturn指令的说明。

PTX ISA 说明

在PTX ISA 2.1版本中引入。

支持PTX ISA版本6.4中引入的.noreturn指令。

目标ISA注意事项

需要 sm_20 或更高版本。

.noreturn 指令需要 sm_30 或更高版本。

示例

Fproto1: .callprototype  _ ;
Fproto2: .callprototype  _ (.param .f32 _);
Fproto3: .callprototype  (.param .u32 _) _ ;
Fproto4: .callprototype  (.param .u32 _) _ (.param .f32 _);
...
@p   call  (%val), %r0, (%f1), Fproto4;
...

// example of array parameter
Fproto5: .callprototype _ (.param .b8 _[12]);

Fproto6: .callprototype  _ (.param .f32 _) .noreturn;
...
@p   call  %r0, (%f1), Fproto6;
...

11.4. 性能调优指令

为了提供底层性能调优的机制,PTX支持以下指令,这些指令会将信息传递给后端优化编译器。

  • .maxnreg

  • .maxntid

  • .reqntid

  • .minnctapersm

  • .maxnctapersm (已弃用)

  • .pragma

.maxnreg指令用于指定单个线程可分配的最大寄存器数量;.maxntid指令用于指定线程块(CTA)中的最大线程数;.reqntid指令用于指定线程块(CTA)中所需的线程数;而.minnctapersm指令则用于指定单个多处理器(SM)上需调度的最小线程块数量。这些指令可用于控制资源需求(例如寄存器数量),从而增加总线程数以提供更多隐藏内存延迟的机会。.minnctapersm指令可与.maxntid.reqntid指令配合使用,在无需直接指定最大寄存器数量的情况下,平衡每个线程的寄存器使用量与多处理器利用率。在为具有不同每SM寄存器数量的多设备编译PTX时,这种方法可能获得更好的性能。

目前,.maxnreg.maxntid.reqntid.minnctapersm指令可以针对每个入口点单独设置,且必须出现在.entry指令与其主体之间。这些指令的优先级高于传递给优化后端的所有模块级约束。如果指令约束条件不一致或无法满足指定目标设备的要求,系统将生成警告信息。

支持使用通用的.pragma指令向PTX后端传递信息。该指令会将一系列字符串传递给后端,这些字符串在PTX虚拟机模型中没有语义含义。.pragma值的解释由后端实现决定,不在PTX ISA规范范围内。请注意,.pragma指令可以出现在模块(文件)作用域、入口作用域,或作为内核或设备函数体内的语句。

11.4.1. 性能调优指令:.maxnreg

.maxnreg

每个线程可分配的最大寄存器数量。

语法

.maxnreg n

描述

声明每个线程在CTA中可使用的最大寄存器数量。

语义

编译器保证不会超过此限制。实际使用的寄存器数量可能更少;例如,后端可能能够编译为使用更少的寄存器,或者最大寄存器数量可能进一步受到.maxntid.maxctapersm的约束。

PTX ISA 说明

在PTX ISA版本1.3中引入。

目标ISA注意事项

支持所有目标架构。

示例

.entry foo .maxnreg 16 { ... }  // max regs per thread = 16

11.4.2. 性能调优指令:.maxntid

.maxntid

线程块(CTA)中的最大线程数。

语法

.maxntid nx
.maxntid nx, ny
.maxntid nx, ny, nz

描述

声明线程块(CTA)中的最大线程数。这个最大值通过指定1D、2D或3D CTA每个维度的最大范围来确定。最大线程数是各维度最大范围的乘积。

语义

线程块中的最大线程数,计算为每个维度指定的最大范围的乘积,保证在出现此指令的内核调用中不会被超过。超过最大线程数将导致运行时错误或内核启动失败。

请注意,该指令确保线程的总数不超过最大值,但不保证任何特定维度的限制不会被超出。

PTX ISA 说明

在PTX ISA版本1.3中引入。

目标ISA注意事项

支持所有目标架构。

示例

.entry foo .maxntid 256       { ... }  // max threads = 256
.entry bar .maxntid 16,16,4   { ... }  // max threads = 1024

11.4.3. 性能调优指令:.reqntid

.reqntid

线程块(CTA)中的线程数量。

语法

.reqntid nx
.reqntid nx, ny
.reqntid nx, ny, nz

描述

通过指定一维、二维或三维CTA每个维度的范围来声明线程块(CTA)中的线程数量。线程总数是每个维度线程数的乘积。

语义

内核任何调用中指定的每个CTA维度大小必须与此指令中指定的值相等。如果在启动时指定了不同的CTA维度,将导致运行时错误或内核启动失败。

注意事项

.reqntid指令不能与.maxntid指令同时使用。

PTX ISA 说明

在PTX ISA 2.1版本中引入。

目标ISA注意事项

支持所有目标架构。

示例

.entry foo .reqntid 256       { ... }  // num threads = 256
.entry bar .reqntid 16,16,4   { ... }  // num threads = 1024

11.4.4. 性能调优指令:.minnctapersm

.minnctapersm

每个SM的最小CTA数量。

语法

.minnctapersm ncta

描述

声明从内核网格映射到单个多处理器(SM)的最小CTA数量。

注意事项

基于.minnctapersm的优化需要同时指定.maxntid.reqntid

如果由.minnctapersm.maxntid / .reqntid在单个SM上产生的总线程数超过SM支持的最大线程数,则指令.minnctapersm 将被忽略。

在PTX ISA 2.1或更高版本中,如果指定了.minnctapersm但未同时指定.maxntid.reqntid,则会生成警告。

PTX ISA 说明

在PTX ISA 2.0版本中引入,作为.maxnctapersm的替代。

目标ISA注意事项

支持所有目标架构。

示例

.entry foo .maxntid 256 .minnctapersm 4 { ... }

11.4.5. 性能调优指令:.maxnctapersm (已弃用)

.maxnctapersm

每个SM的最大CTA数量。

语法

.maxnctapersm ncta

描述

声明从内核网格中可以映射到单个多处理器(SM)的最大CTA数量。

注意事项

基于.maxnctapersm的优化通常也需要指定.maxntid。优化后端编译器会使用.maxntid.maxnctapersm来计算每个线程寄存器使用量的上限,以便将指定数量的CTA映射到单个多处理器上。然而,如果后端使用的寄存器数量远低于这个上限,则可能会将额外的CTA映射到单个多处理器上。因此,在PTX ISA 2.0版本中,.maxnctapersm已被重命名为.minnctapersm。

PTX ISA 说明

在PTX ISA版本1.3中引入。在PTX ISA版本2.0中已弃用。

目标ISA注意事项

支持所有目标架构。

示例

.entry foo .maxntid 256 .maxnctapersm 4 { ... }

11.4.6. 性能调优指令:.noreturn

.noreturn

表示该函数不会返回到其调用者函数。

语法

.noreturn

描述

表示该函数不会返回到其调用者函数。

语义

可选的.noreturn指令表示该函数不会返回到调用者函数。.noreturn指令只能在设备函数上指定,并且必须出现在.func指令与其函数体之间。

不能在具有返回参数的函数上指定该指令。

如果带有.noreturn指令的函数在运行时返回到调用函数,则行为是未定义的。

PTX ISA 说明

在PTX ISA版本6.4中引入。

目标ISA注意事项

需要 sm_30 或更高版本。

示例

.func foo .noreturn { ... }

11.4.7. 性能调优指令:.pragma

.pragma

向PTX后端编译器传递指令。

语法

.pragma list-of-strings ;

描述

向PTX后端编译器传递模块作用域、入口作用域或语句级别的指令。

.pragma指令可以出现在模块作用域、入口作用域或语句级别。

语义

.pragma指令字符串的解释是具体实现相关的,不会影响PTX语义。有关ptxas中定义的pragma字符串描述,请参阅Descriptions of .pragma Strings

PTX ISA 说明

在PTX ISA 2.0版本中引入。

目标ISA注意事项

支持所有目标架构。

示例

.pragma "nounroll";    // disable unrolling in backend

// disable unrolling for current kernel
.entry foo .pragma "nounroll"; { ... }

11.5. 调试指令

DWARF格式的调试信息通过以下指令在PTX模块中传递:

  • @@DWARF

  • .section

  • .file

  • .loc

.section指令在PTX ISA 2.0版本中引入,取代了@@DWARF语法。@@DWARF语法在PTX ISA 2.0版本中已被弃用,但仍支持旧版PTX ISA 1.x代码。

从PTX ISA版本3.0开始,包含DWARF调试信息的PTX文件应当包含.target debug平台选项。这个前置声明指示PTX编译保留用于源代码级调试的映射关系。

11.5.1. 调试指令: @@dwarf

@@dwarf

DWARF格式信息。

语法

@@DWARF dwarf-string

dwarf-string may have one of the
.byte   byte-list   // comma-separated hexadecimal byte values
.4byte  int32-list  // comma-separated hexadecimal integers in range [0..2^32-1]
.quad   int64-list  // comma-separated hexadecimal integers in range [0..2^64-1]
.4byte  label
.quad   label

PTX ISA 说明

在PTX ISA版本1.2中引入。自PTX ISA版本2.0起已弃用,由.section指令替代。

目标ISA注意事项

支持所有目标架构。

示例

@@DWARF .section .debug_pubnames, "", @progbits
@@DWARF .byte   0x2b, 0x00, 0x00, 0x00, 0x02, 0x00
@@DWARF .4byte  .debug_info
@@DWARF .4byte  0x000006b5, 0x00000364, 0x61395a5f, 0x5f736f63
@@DWARF .4byte  0x6e69616d, 0x63613031, 0x6150736f, 0x736d6172
@@DWARF .byte   0x00, 0x00, 0x00, 0x00, 0x00

11.5.2. 调试指令:.section

.section

PTX 段定义。

语法

.section section_name { dwarf-lines }

dwarf-lines have the following formats:
  .b8    byte-list       // comma-separated list of integers
                         // in range [-128..255]
  .b16   int16-list      // comma-separated list of integers
                         // in range [-2^15..2^16-1]
  .b32   int32-list      // comma-separated list of integers
                         // in range [-2^31..2^32-1]
  label:                 // Define label inside the debug section
  .b64   int64-list      // comma-separated list of integers
                         // in range [-2^63..2^64-1]
  .b32   label
  .b64   label
  .b32   label+imm       // a sum of label address plus a constant integer byte
                         // offset(signed, 32bit)
  .b64   label+imm       // a sum of label address plus a constant integer byte
                         // offset(signed, 64bit)
  .b32   label1-label2   // a difference in label addresses between labels in
                         // the same dwarf section (32bit)
  .b64   label3-label4   // a difference in label addresses between labels in
                         // the same dwarf section (64bit)

PTX ISA 说明

在PTX ISA 2.0版本中引入,取代了@@DWARF语法。

PTX ISA 3.2版本中引入的label+imm表达式。

在PTX ISA 6.0版本中引入的对dwarf-lines中.b16整数的支持。

在PTX ISA 7.2版本中引入了对在DWARF段内定义label的支持。

label1-label2 表达式在PTX ISA 7.5版本中引入。

PTX ISA 7.5版本中引入了负数形式的dwarf行号。

目标ISA注意事项

支持所有目标架构。

示例

.section .debug_pubnames
{
    .b32    LpubNames_end0-LpubNames_begin0
  LpubNames_begin0:
    .b8     0x2b, 0x00, 0x00, 0x00, 0x02, 0x00
    .b32    .debug_info
  info_label1:
    .b32    0x000006b5, 0x00000364, 0x61395a5f, 0x5f736f63
    .b32    0x6e69616d, 0x63613031, 0x6150736f, 0x736d6172
    .b8     0x00, 0x00, 0x00, 0x00, 0x00
  LpubNames_end0:
}

.section .debug_info
{
    .b32 11430
    .b8 2, 0
    .b32 .debug_abbrev
    .b8 8, 1, 108, 103, 101, 110, 102, 101, 58, 32, 69, 68, 71, 32, 52, 46, 49
    .b8 0
    .b32 3, 37, 176, -99
    .b32 info_label1
    .b32 .debug_loc+0x4
    .b8 -11, 11, 112, 97
    .b32 info_label1+12
    .b64 -1
    .b16 -5, -65535
}

11.5.3. 调试指令:.file

.file

源文件名。

语法

.file file_index "filename" {, timestamp, file_size}

描述

将源文件名与整数索引关联。.loc指令通过索引引用源文件。

.file 指令允许可选地指定一个无符号数字表示最后修改时间,以及一个无符号整数表示源文件的字节大小。timestampfile_size 的值可以为0,表示该信息不可用。

timestamp 值的格式为C和C++数据类型 time_t

file_size 是一个无符号64位整数。

.file指令仅允许在最外层作用域中使用,即与内核和设备函数声明处于同一层级。

语义

如果未指定时间戳和文件大小,则默认为0。

PTX ISA 说明

在PTX ISA版本1.0中引入。

时间戳和文件大小在PTX ISA 3.2版本中引入。

目标ISA注意事项

支持所有目标架构。

示例

.file 1 "example.cu"
.file 2 "kernel.cu"
.file 1 "kernel.cu", 1339013327, 64118

11.5.4. 调试指令:.loc

.loc

源文件位置。

语法

.loc file_index line_number column_position
.loc file_index line_number column_position,function_name label {+ immediate }, inlined_at file_index2 line_number2 column_position2

描述

声明与后续词法PTX指令相关联的源文件位置(源文件、行号和列位置)。.loc引用由.file指令定义的file_index

为了标识从内联函数生成的PTX指令,可以在.loc指令中添加额外的属性.inlined_at.inlined_at属性用于指定函数被内联时的源代码位置。file_index2line_number2column_position2共同指定了函数被内联的具体位置。作为.inlined_at指令一部分指定的源代码位置,必须在词法上先于.loc指令中的源代码位置。

function_name属性指定了名为.debug_str的DWARF节中的偏移量。偏移量可以表示为label表达式或label + immediate表达式,其中label.debug_str节中定义。DWARF节.debug_str包含以ASCII空字符结尾的字符串,这些字符串指定了内联函数的名称。

请注意,PTX指令可能有一个关联的源位置,由最近词法上位于前面的.loc指令确定;如果没有前面的.loc指令,则没有关联的源位置。PTX中的标签会继承词法上紧随其后的指令的位置。没有后续PTX指令的标签则没有关联的源位置。

PTX ISA 说明

在PTX ISA版本1.0中引入。

function_nameinlined_at 属性是在 PTX ISA 7.2 版本中引入的。

目标ISA注意事项

支持所有目标架构。

示例

    .loc 2 4237 0
L1:                        // line 4237, col 0 of file #2,
                           // inherited from mov
    mov.u32  %r1,%r2;      // line 4237, col 0 of file #2
    add.u32  %r2,%r1,%r3;  // line 4237, col 0 of file #2
...
L2:                        // line 4239, col 5 of file #2,
                           // inherited from sub
    .loc 2 4239 5
    sub.u32  %r2,%r1,%r3;  // line 4239, col 5 of file #2
    .loc 1 21 3
    .loc 1 9 3, function_name info_string0, inlined_at 1 21 3
    ld.global.u32   %r1, [gg]; // Function at line 9
    setp.lt.s32 %p1, %r1, 8;   // inlined at line 21
    .loc 1 27 3
    .loc 1 10 5, function_name info_string1, inlined_at 1 27 3
    .loc 1 15 3, function_name .debug_str+16, inlined_at 1 10 5
    setp.ne.s32 %p2, %r1, 18;
    @%p2 bra    BB2_3;

    .section .debug_str {
    info_string0:
     .b8 95  // _
     .b8 90  // z
     .b8 51  // 3
     .b8 102 // f
     .b8 111 // o
     .b8 111 // o
     .b8 118 // v
     .b8 0

    info_string1:
     .b8 95  // _
     .b8 90  // z
     .b8 51  // 3
     .b8 98  // b
     .b8 97  // a
     .b8 114 // r
     .b8 118 // v
     .b8 0
     .b8 95  // _
     .b8 90  // z
     .b8 51  // 3
     .b8 99  // c
     .b8 97  // a
     .b8 114 // r
     .b8 118 // v
     .b8 0
    }

11.6. Linking Directives

  • .extern

  • .visible

  • .weak

11.6.1. 链接指令:.extern

.extern

外部符号声明。

语法

.extern identifier

描述

声明标识符在当前模块外部定义。定义该标识符的模块必须在一个单独的目标文件中仅将其定义为.weak.visible一次。符号的外部声明可以出现多次,对该符号的引用将针对该符号的单一定义进行解析。

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

.extern .global .b32 foo;  // foo is defined in another module

11.6.2. 链接指令:.visible

.visible

可见(外部)符号声明。

语法

.visible identifier

描述

声明标识符为全局可见。与C语言不同,在C中除非声明为static否则标识符默认全局可见,而PTX标识符仅在当前模块内可见,除非在当前模块外声明.visible

PTX ISA 说明

在PTX ISA版本1.0中引入。

目标ISA注意事项

支持所有目标架构。

示例

.visible .global .b32 foo;  // foo will be externally visible

11.6.3. 链接指令:.weak

.weak

可见(外部)符号声明。

语法

.weak identifier

描述

声明标识符为全局可见但的。弱符号与全局可见符号类似,但在链接过程中,符号解析时弱符号仅在全局可见符号之后被选用。与全局可见符号不同,多个目标文件可以声明相同的弱符号,并且只有当没有同名的全局符号时,对符号的引用才会解析为弱符号。

PTX ISA 说明

在PTX ISA版本3.1中引入。

目标ISA注意事项

支持所有目标架构。

示例

.weak .func (.reg .b32 val) foo;  // foo will be externally visible

11.6.4. 链接指令:.common

.common

可见(外部)符号声明。

语法

.common identifier

描述

声明标识符为全局可见但"共用"。

公共符号与全局可见符号类似。然而,多个目标文件可以声明相同的公共符号,它们可能具有不同的类型和大小,对符号的引用将根据具有最大大小的公共符号进行解析。

只有一个目标文件可以初始化公共符号,且该文件必须包含该公共符号在所有其他目标文件定义中的最大尺寸。

.common 链接指令只能用于具有 .global 存储的变量。它不能用于函数符号或不透明类型的符号。

PTX ISA 说明

在PTX ISA版本5.0中引入。

目标ISA注意事项

.common 指令需要 sm_20 或更高版本。

示例

.common .global .u32 gbl;

11.7. 集群维度指令

以下指令用于指定有关集群的信息:

  • .reqnctapercluster

  • .explicitcluster

  • .maxclusterrank

.reqnctapercluster指令指定了集群中的CTA数量。.explicitcluster指令表示该内核应使用显式集群详细信息启动。.maxclusterrank指令指定了集群中的最大CTA数量。

集群维度指令只能应用于内核函数。

11.7.1. 集群维度指令:.reqnctapercluster

.reqnctapercluster

声明集群中的CTA数量。

语法

.reqnctapercluster nx
.reqnctapercluster nx, ny
.reqnctapercluster nx, ny, nz

描述

通过指定一维、二维或三维集群每个维度的范围来设置集群中的线程块(CTA)数量。CTA的总数是各维度CTA数量的乘积。对于带有.reqnctapercluster指令的内核,如果在启动时未指定相同值,运行时将使用指定值进行启动配置。

语义

如果在启动时明确指定了集群维度,它应与本指令中指定的值保持一致。在启动时指定不同的集群维度将导致运行时错误或内核启动失败。

PTX ISA 说明

在PTX ISA版本7.8中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

.entry foo .reqnctapercluster 2         { . . . }
.entry bar .reqnctapercluster 2, 2, 1   { . . . }
.entry ker .reqnctapercluster 3, 2      { . . . }

11.7.2. 集群维度指令:.explicitcluster

.explicitcluster

声明Kernel必须明确指定集群维度才能启动。

语法

.explicitcluster

描述

声明该Kernel应通过显式指定集群维度来启动。

语义

带有.explicitcluster指令的内核必须显式指定集群维度(可以在启动时或通过.reqnctapercluster指定),否则程序将在运行时出错或内核启动失败。

PTX ISA 说明

在PTX ISA版本7.8中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

.entry foo .explicitcluster         { . . . }

11.7.3. 集群维度指令:.maxclusterrank

.maxclusterrank

声明集群中可以包含的最大CTA数量。

语法

.maxclusterrank n

描述

声明允许作为集群一部分的最大线程块(CTAs)数量。

语义

内核任何调用中指定的每个集群维度中的CTA数量乘积必须小于或等于该指令中指定的值。否则调用将导致运行时错误或内核启动失败。

.maxclusterrank指令不能与.reqnctapercluster指令同时使用。

PTX ISA 说明

在PTX ISA版本7.8中引入。

目标ISA注意事项

需要 sm_90 或更高版本。

示例

.entry foo ..maxclusterrank 8         { . . . }

12. Release Notes

本节描述PTX ISA及其实现的变更历史。第一部分介绍当前发布的PTX ISA 8.7版本中的ISA及实现变更,其余部分则记录了从PTX ISA 2.0版本以来各历史版本中的变更内容。

表54展示了PTX的版本历史。

表54 PTX 版本历史

PTX ISA版本

CUDA发布版本

支持的目标平台

PTX ISA 1.0

CUDA 1.0

sm_{10,11}

PTX ISA 1.1

CUDA 1.1

sm_{10,11}

PTX ISA 1.2

CUDA 2.0

sm_{10,11,12,13}

PTX ISA 1.3

CUDA 2.1

sm_{10,11,12,13}

PTX ISA 1.4

CUDA 2.2

sm_{10,11,12,13}

PTX ISA 1.5

驱动版本 r190

sm_{10,11,12,13}

PTX ISA 2.0

CUDA 3.0, 驱动版本 r195

sm_{10,11,12,13}, sm_20

PTX ISA 2.1

CUDA 3.1, 驱动版本 r256

sm_{10,11,12,13}, sm_20

PTX ISA 2.2

CUDA 3.2, 驱动版本 r260

sm_{10,11,12,13}, sm_20

PTX ISA 2.3

CUDA 4.0, 驱动版本 r270

sm_{10,11,12,13}, sm_20

PTX ISA 3.0

CUDA 4.1, 驱动版本 r285

sm_{10,11,12,13}, sm_20

CUDA 4.2, 驱动版本 r295

sm_{10,11,12,13}, sm_20, sm_30

PTX ISA 3.1

CUDA 5.0, 驱动版本 r302

sm_{10,11,12,13}, sm_20, sm_{30,35}

PTX ISA 3.2

CUDA 5.5,驱动程序 r319

sm_{10,11,12,13}, sm_20, sm_{30,35}

PTX ISA 4.0

CUDA 6.0, 驱动版本 r331

sm_{10,11,12,13}, sm_20, sm_{30,32,35}, sm_50

PTX ISA 4.1

CUDA 6.5, 驱动版本 r340

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52}

PTX ISA 4.2

CUDA 7.0, 驱动版本 r346

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}

PTX ISA 4.3

CUDA 7.5, 驱动版本 r352

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}

PTX ISA 5.0

CUDA 8.0, 驱动版本 r361

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}

PTX ISA 6.0

CUDA 9.0, 驱动版本 r384

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_70

PTX ISA 6.1

CUDA 9.1, 驱动版本 r387

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_70, sm_72

PTX ISA 6.2

CUDA 9.2, 驱动版本 r396

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_70, sm_72

PTX ISA 6.3

CUDA 10.0, 驱动版本 r400

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_70, sm_72, sm_75

PTX ISA 6.4

CUDA 10.1, 驱动版本 r418

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_70, sm_72, sm_75

PTX ISA 6.5

CUDA 10.2, 驱动版本 r440

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_70, sm_72, sm_75

PTX ISA 7.0

CUDA 11.0, 驱动版本 r445

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_{70,72,75}, sm_80

PTX ISA 7.1

CUDA 11.1, 驱动版本 r455

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_{70,72,75}, sm_{80,86}

PTX ISA 7.2

CUDA 11.2,驱动程序 r460

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_{70,72,75}, sm_{80,86}

PTX ISA 7.3

CUDA 11.3, 驱动版本 r465

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_{70,72,75}, sm_{80,86}

PTX ISA 7.4

CUDA 11.4,驱动程序 r470

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_{70,72,75}, sm_{80,86,87}

PTX ISA 7.5

CUDA 11.5, 驱动版本 r495

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_{70,72,75}, sm_{80,86,87}

PTX ISA 7.6

CUDA 11.6, 驱动版本 r510

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_{70,72,75}, sm_{80,86,87}

PTX ISA 7.7

CUDA 11.7,驱动程序 r515

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_{70,72,75}, sm_{80,86,87}

PTX ISA 7.8

CUDA 11.8, 驱动版本 r520

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_{70,72,75}, sm_{80,86,87,89}, sm_90

PTX ISA 8.0

CUDA 12.0, 驱动版本 r525

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_{70,72,75}, sm_{80,86,87,89}, sm_{90,90a}

PTX ISA 8.1

CUDA 12.1, 驱动版本 r530

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_{70,72,75}, sm_{80,86,87,89}, sm_{90,90a}

PTX ISA 8.2

CUDA 12.2, 驱动版本 r535

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_{70,72,75}, sm_{80,86,87,89}, sm_{90,90a}

PTX ISA 8.3

CUDA 12.3, 驱动版本 r545

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_{70,72,75}, sm_{80,86,87,89}, sm_{90,90a}

PTX ISA 8.4

CUDA 12.4, 驱动版本 r550

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_{70,72,75}, sm_{80,86,87,89}, sm_{90,90a}

PTX ISA 8.5

CUDA 12.5, 驱动版本 r555

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_{70,72,75}, sm_{80,86,87,89}, sm_{90,90a}

CUDA 12.6, 驱动版本 r560

PTX ISA 8.6

CUDA 12.7, 驱动版本 r565

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_{70,72,75}, sm_{80,86,87,89}, sm_{90,90a}, sm_{100,100a}, sm_{101,101a}

PTX ISA 8.7

CUDA 12.8,驱动程序 r570

sm_{10,11,12,13}, sm_20, sm_{30,32,35,37}, sm_{50,52,53}, sm_{60,61,62}, sm_{70,72,75}, sm_{80,86,87,89}, sm_{90,90a}, sm_{100,100a}, sm_{101,101a}, sm_{120,120a}

12.1. PTX ISA 8.7版本的变更内容

新功能

PTX ISA 8.7版本引入了以下新特性:

  • 新增对sm_120目标架构的支持。

  • 新增对支持专用加速特性的目标sm_120a的支持。

  • 扩展tcgen05.mma指令以添加对.kind::mxf4nvf4.scale_vec::4X限定符的支持。

  • 扩展mma指令以支持.f16类型的累加器和.m16n8k16形状,同时支持FP8类型.e4m3.e5m2

  • 扩展cvt指令以添加对.rs舍入模式及目标类型 .e2m1x4.e4m3x4.e5m2x4.e3m2x4.e2m3x4的支持。

  • 扩展了对st.asyncred.async指令的支持,新增了对.mmio.release.global.scope限定符的支持。

  • 扩展tensormap.replace指令,为.elemtype限定符添加对值1315的支持。

  • 扩展了mmamma.sp::ordered_metadata指令,新增支持类型.e3m2/.e2m3/.e2m1以及限定符.kind.block_scale.scale_vec_size

语义变更与说明

  • 明确了在.tile::gather4.tile::scatter4模式下,张量坐标需要指定为{col_idx, row_idx0, row_idx1, row_idx2, row_idx3}即{x, y0, y1, y2, y3},而非{x0, x1, x2, x3, y}。

  • 更新了tcgen05.mma指令的Instruction Descriptor,以明确保留供未来使用的位。

12.2. PTX ISA 8.6版本的变更

新功能

PTX ISA 8.6版本引入了以下新特性:

  • 新增对sm_100目标架构的支持。

  • 新增对支持专用加速特性的目标sm_100a的支持。

  • 新增支持sm_101目标架构。

  • 新增对支持专用加速特性的目标sm_101a的支持。

  • 扩展了cp.async.bulkcp.async.bulk.tensor指令,新增 .shared::cta作为目标状态空间。

  • 扩展fence指令以添加对.acquire.release限定符的支持。

  • 扩展了fencefence.proxy指令,以添加对.sync_restrict限定符的支持。

  • 扩展ldmatrix指令以支持.m16n16.m8n16形状和.b8类型。

  • 扩展ldmatrix指令以支持.src_fmt.dst_fmt限定符。

  • 扩展stmatrix指令以支持.m16n8形状和.b8类型。

  • 新增对clusterlaunchcontrol指令的支持。

  • 扩展addsubfma指令,以支持混合精度浮点运算,其中.f32作为目标操作数类型,.f16/.bf16作为源操作数类型。

  • 扩展addsubmulfma指令以支持.f32x2类型。

  • 扩展cvt指令,增加.tf32类型以支持.rn/.rz舍入模式的.satfinite限定符。

  • 扩展cp.async.bulk指令以支持.cp_mask限定符和byteMask操作数。

  • 扩展了multimem.ld_reducemultimem.st指令以支持.e5m2.e5m2x2.e5m2x4.e4m3.e4m3x2.e4m3x4类型。

  • 扩展cvt指令以支持与.e2m1x2.e3m2x2.e2m3x2.ue8m0x2类型之间的相互转换。

  • 扩展了cp.async.bulk.tensorcp.async.bulk.prefetch.tensor指令, 以支持新的加载模式限定符.tile::scatter4.tile::gather4

  • 扩展tensormap.replace指令以新增对修饰符.swizzle_atomicity的支持,用于实现新的swizzle模式。

  • 扩展了mbarrier.arrivembarrier.arrive_drop.mbarrier.test_wait.mbarrier.try_wait指令以支持.relaxed限定符。

  • 扩展了cp.async.bulk.tensorcp.async.bulk.prefetch.tensor指令, 以支持新的load_mode限定符.im2col::w.im2col::w::128

  • 扩展cp.async.bulk.tensor指令以支持新的限定符.cta_group

  • 添加对st.bulk指令的支持。

  • 新增对tcgen05特性及相关指令的支持:tcgen05.alloctcgen05.dealloctcgen05.relinquish_alloc_permittcgen05.ldtcgen05.sttcgen05.waittcgen05.cptcgen05.shifttcgen05.mmatcgen05.mma.sptcgen05.mma.wstcgen05.mma.ws.sptcgen05.fencetcgen05.commit

  • 扩展redux.sync指令,增加对.f32类型的支持,包含限定符.abs.NaN

语义变更与说明

无。

12.3. PTX ISA 8.5版本的变更

新功能

PTX ISA 8.5版本引入了以下新特性:

  • 新增对mma.sp::ordered_metadata指令的支持。

语义变更与说明

  • 对于指令mma.sp的稀疏元数据(操作数e)而言,值0b00000b01010b10100b1111是无效的,使用这些值会导致未定义行为。

12.4. PTX ISA 8.4版本的变更

新功能

PTX ISA 8.4版本引入了以下新特性:

  • 扩展了ldstatom指令,增加了.b128类型以支持.sys作用域。

  • 扩展整数wgmma.mma_async指令以支持将.u8.s8.s8.u8分别作为.atype.btype

  • 扩展 mmamma.sp 指令以支持 FP8 类型 .e4m3.e5m2

语义变更与说明

无。

12.5. PTX ISA 版本8.3的变更

新功能

PTX ISA 8.3版本引入了以下新特性:

  • 添加对used_bytes_mask编译指示的支持,该指示用于为加载操作指定已使用字节的掩码。

  • 扩展了isspacepcvta.toldst指令,使其能够接受带有.param状态空间限定符的::entry::func子限定符。

  • 为指令 ldld.global.ncldustmovatom 添加了对 .b128 类型的支持。

  • 新增对指令tensormap.replacetensormap.cp_fenceproxy的支持,并为指令fence.proxy添加限定符.to_proxykind::from_proxykind支持,以实现修改tensor-map的功能。

语义变更与说明

无。

12.6. PTX ISA 8.2版本变更

新功能

PTX ISA 8.2版本引入了以下新特性:

  • ldst指令添加了对.mmio限定符的支持。

  • 扩展 lop3 指令以支持谓词目标。

  • 扩展multimem.ld_reduce指令以支持.acc::f32限定符,允许中间累加保持.f32精度。

  • 扩展了异步warpgroup级别的矩阵乘加操作wgmma.mma_async,以支持.sp修饰符,该修饰符允许在输入矩阵A为稀疏时执行矩阵乘加操作。

语义变更与说明

.multicast::cluster限定符在cp.async.bulkcp.async.bulk.tensor指令上针对目标架构sm_90a进行了优化,在其他目标架构上性能可能会大幅降低,因此建议.multicast::clustersm_90a配合使用。

12.7. PTX ISA 8.1版本的变更

新功能

PTX ISA 8.1版本引入了以下新特性:

  • 增加了对st.asyncred.async指令的支持,分别用于共享内存上的异步存储和异步归约操作。

  • 为半精度fma指令添加了对.oob修饰符的支持。

  • .f16.bf16.tf32格式的cvt指令添加了对.satfinite饱和修饰符的支持。

  • cvt.e4m3/.e5m2格式的支持扩展至sm_89架构。

  • 扩展atomred指令以支持向量类型。

  • 添加对特殊寄存器%aggr_smem_size的支持。

  • 扩展sured指令以支持64位min/max操作。

  • 增加了对内核参数大小增加到32764字节的支持。

  • 在内存一致性模型中增加对多内存地址的支持。

  • 新增对multimem.ld_reducemultimem.stmultimem.red指令的支持,用于在多存储器地址上执行内存操作。

语义变更与说明

无。

12.8. PTX ISA 8.0版本的变更

新功能

PTX ISA 8.0版本引入了以下新特性:

  • 新增对支持专用加速特性的目标sm_90a的支持。

  • 增加了对异步warpgroup级矩阵乘加操作wgmma的支持。

  • 扩展了异步复制操作,增加了针对大规模数据(包括张量数据)的批量操作功能。

  • 引入了打包整数类型 .u16x2.s16x2

  • 扩展整数算术指令add以支持打包整数类型.u16x2.s16x2

  • 扩展整数算术指令 minmax 以支持打包整数类型 .u16x2.s16x2,以及在 .s16x2.s32 类型上支持饱和修饰符 .relu

  • 添加对特殊寄存器%current_graph_exec的支持,该寄存器用于标识当前正在执行的CUDA设备图。

  • 新增对elect.sync指令的支持。

  • 为函数和变量添加对.unified属性的支持。

  • 新增对setmaxnreg指令的支持。

  • barrier.cluster指令添加了对.sem限定符的支持。

  • 扩展fence指令,允许使用op_restrict限定符实现特定操作码的同步。

  • mbarrier.arrivembarrier.arrive_dropmbarrier.test_waitmbarrier.try_wait操作添加了对.cluster范围的支持。

  • 增加了对mbarrier对象上事务计数操作的支持,通过.expect_tx.complete_tx限定符指定。

语义变更与说明

无。

12.9. PTX ISA 7.8版本的变更内容

新功能

PTX ISA 7.8版本引入了以下新特性:

  • 新增支持sm_89目标架构。

  • 新增对sm_90目标架构的支持。

  • 扩展了barbarrier指令,使其能够接受可选的限定符.cta

  • 扩展.shared状态空间限定符,可选添加子限定符::cta

  • 新增对movmatrix指令的支持,该指令可在寄存器中跨线程束转置矩阵。

  • 新增对stmatrix指令的支持,该指令用于将一个或多个矩阵存储到共享内存中。

  • 扩展.f64浮点类型的mma操作,支持.m16n8k4.m16n8k8.m16n8k16形状。

  • 扩展了addsubmulsetsetpcvttanhex2atomred指令,支持bf16替代浮点数据格式。

  • 新增支持新的替代浮点数据格式 .e4m3.e5m2

  • 扩展cvt指令以转换.e4m3.e5m2替代浮点数据格式。

  • 新增支持griddepcontrol指令作为通信机制,用于控制依赖网格的执行。

  • 扩展mbarrier指令,新增支持阶段完成检查操作try_wait

  • 新增对新的线程作用域.cluster的支持,这是一组协作线程阵列(CTAs)。

  • 扩展 fence/membarldstatomred 指令以支持 .cluster 作用域。

  • 新增支持将共享状态空间的可见性扩展到集群内的所有线程。

  • 扩展.shared状态空间限定符,新增::cluster子限定符,用于实现共享内存的集群级可见性。

  • 扩展了isspacepcvtaldstatomred指令,使其能够接受带有.shared状态空间限定符的::cluster子限定符。

  • 新增对mapa指令的支持,用于将共享内存地址映射到集群内不同CTA中的对应地址。

  • 新增支持getctarank指令,用于查询包含指定地址的CTA的排名。

  • 新增对新的屏障同步指令barrier.cluster的支持。

  • 将内存一致性模型扩展至包含新的集群范围。

  • 新增对集群信息相关特殊寄存器的支持:%is_explicit_cluster%clusterid%nclusterid%cluster_ctaid%cluster_nctaid%cluster_ctarank%cluster_nctarank

  • 新增对集群维度指令.reqnctapercluster.explicitcluster.maxclusterrank的支持。

语义变更与说明

无。

12.10. PTX ISA 7.7版本变更

新功能

PTX ISA 7.7版本引入了以下新特性:

  • 扩展了isspacepcvta指令,使其包含内核函数参数的.param状态空间。

语义变更与说明

无。

12.11. PTX ISA 7.6版本的变更

新功能

PTX ISA 7.6版本引入了以下新特性:

  • 支持szext指令,该指令可对指定值执行符号扩展或零扩展。

  • 支持bmsk指令,该指令可从指定位位置开始创建指定宽度的位掩码。

  • 支持特殊寄存器 %reserved_smem_offset_begin, %reserved_smem_offset_end, %reserved_smem_offset_cap, %reserved_smem_offset<2>.

语义变更与说明

无。

12.12. PTX ISA 7.5版本的变更

新功能

PTX ISA 7.5版本引入了以下新特性:

  • 调试信息增强,支持在.section调试指令中使用标签差异和负值。

  • 支持在cp.async指令上使用ignore-src操作数。

  • 对内存一致性模型的扩展引入了以下新概念:

    • 一个内存代理作为不同内存访问方法的抽象标签。

    • 虚拟别名作为访问同一物理内存位置的不同内存地址。

  • 支持新的fence.proxymembar.proxy指令,允许通过虚拟别名执行的内存访问同步。

语义变更与说明

无。

12.13. PTX ISA 7.4版本变更

新功能

PTX ISA 7.4版本引入了以下新特性:

  • 支持 sm_87 目标架构。

  • 支持.level::eviction_priority限定符,允许在ldld.global.ncstprefetch指令上指定缓存驱逐优先级提示。

  • 支持.level::prefetch_size限定符,允许在ldcp.async指令上指定数据预取提示。

  • 支持createpolicy指令,该指令允许构建不同类型的缓存淘汰策略。

  • 支持.level::cache_hint限定符,允许在ldld.global.ncstatomredcp.async指令中使用缓存驱逐策略。

  • 支持对缓存数据执行applyprioritydiscard操作。

语义变更与说明

无。

12.14. PTX ISA 7.3版本的变更

新功能

PTX ISA 7.3版本引入了以下新特性:

  • 扩展了初始化器中使用的mask()运算符,使其也支持整数常量表达式。

  • 增加了对堆栈操作指令的支持,允许使用stacksavestackrestore指令来操作堆栈,以及使用alloca指令为每个线程分配堆栈。

语义变更与说明

旧版PTX ISA规范中未实现的alloca功能已在PTX ISA 7.3版本中被新的堆栈操作指令所取代。

12.15. PTX ISA 7.2版本变更

新功能

PTX ISA 7.2版本引入了以下新特性:

  • 增强.loc指令以表示内联函数信息。

  • 新增支持在调试部分内定义标签。

  • 扩展了minmax指令以支持.xorsign.abs修饰符。

语义变更与说明

无。

12.16. PTX ISA 7.1版本的变更

新功能

PTX ISA 7.1版本引入了以下新特性:

  • 支持 sm_86 目标架构。

  • 新增了一个运算符mask(),用于从初始化器中使用的变量地址提取特定字节。

  • 扩展了textld4指令,使其能够返回一个可选谓词,用于指示指定坐标处的数据是否驻留在内存中。

  • 扩展单比特wmmamma指令以支持.and操作。

  • 扩展mma指令以支持.sp修饰符,该修饰符允许在输入矩阵A为稀疏时执行矩阵乘加运算。

  • 扩展mbarrier.test_wait指令以测试特定阶段奇偶校验的完成情况。

语义变更与说明

无。

12.17. PTX ISA 7.0版本变更

新功能

PTX ISA 7.0版本引入了以下新特性:

  • 支持 sm_80 目标架构。

  • 新增对异步拷贝指令的支持,允许将数据从一个状态空间异步拷贝到另一个状态空间。

  • 新增对mbarrier指令的支持,该指令允许在内存中创建mbarrier对象,并使用这些对象来同步线程以及由线程发起的异步复制操作。

  • 新增对redux.sync指令的支持,该指令允许在warp内的线程间执行归约操作。

  • 新增支持新的替代浮点数据格式 .bf16.tf32

  • 扩展wmma指令以支持.f64类型,形状为.m8n8k4

  • 扩展wmma指令以支持.bf16数据格式。

  • 扩展wmma指令以支持形状为.m16n16k8.tf32数据格式。

  • 扩展mma指令以支持形状为.m8n8k4.f64类型。

  • 扩展mma指令以支持.bf16.tf32数据格式,形状为.m16n8k8

  • 扩展mma指令以支持新形状.m8n8k128.m16n8k4.m16n8k16.m16n8k32.m16n8k64.m16n8k128.m16n8k256

  • 扩展absneg指令以支持.bf16.bf16x2数据格式。

  • 扩展minmax指令以支持.NaN修饰符及.f16.f16x2.bf16.bf16x2数据格式。

  • 扩展fma指令以支持.relu饱和模式以及.bf16.bf16x2数据格式。

  • 扩展cvt指令以支持.relu饱和模式以及.f16.f16x2.bf16.bf16x2.tf32目标格式。

  • 新增支持计算双曲正切的tanh指令。

  • 扩展ex2指令以支持.f16.f16x2类型。

语义变更与说明

无。

12.18. PTX ISA 6.5版本的变更

新功能

PTX ISA 6.5版本引入了以下新特性:

  • 为半精度比较指令set添加了对整数目标类型的支持。

  • 扩展abs指令以支持.f16.f16x2类型。

  • 新增对cvt.pack指令的支持,该指令允许转换两个整数值并将结果打包在一起。

  • mma指令上新增了.m16n8k8.m8n8k16.m8n8k32三种形状。

  • 新增对ldmatrix指令的支持,该指令可从共享内存加载一个或多个矩阵供mma指令使用。

已移除的功能

PTX ISA 6.5版本移除了以下功能:

  • 移除了对浮点wmma.mma指令上.satfinite限定符的支持。该支持自PTX ISA 6.4版本起已被弃用。

语义变更与说明

无。

12.19. PTX ISA 6.4版本的变更

新功能

PTX ISA 6.4版本引入了以下新特性:

  • 新增对.noreturn指令的支持,该指令可用于表明函数不会返回到其调用函数。

  • 新增对mma指令的支持,该指令可执行矩阵乘加运算。

已弃用的功能

PTX ISA 版本 6.4 弃用了以下功能:

  • 支持在浮点wmma.mma指令上使用.satfinite限定符。

已移除的功能

PTX ISA 版本 6.4 移除了以下功能:

  • 对于.targetsm_70及更高版本,已移除对不带.sync限定符的shflvote指令的支持。如PTX ISA 6.2版本文档所述,该支持自PTX ISA 6.0版本起已被弃用。

语义变更与说明

  • 明确了.weak符号的引用解析仅考虑同名.weak.visible符号,不考虑同名的局部符号。

  • 明确了在cvt指令中,修饰符.ftz仅当.atype.dtype.f32时才能被指定。

12.20. PTX ISA 6.3版本的变更

新功能

PTX ISA 6.3版本引入了以下新特性:

  • 支持 sm_75 目标架构。

  • 新增支持一个名为nanosleep的新指令,该指令可将线程暂停指定时长。

  • 新增支持.alias指令,该指令允许为函数符号定义别名。

  • 扩展atom指令以执行.f16加法运算和.cas.b16操作。

  • 扩展red指令以执行.f16加法运算。

  • wmma指令已扩展支持乘数矩阵类型.s8.u8.s4.u4.b1以及累加器矩阵类型.s32

语义变更与说明

  • 为所有wmma指令引入了强制性的.aligned限定符。

  • 指定了传递给wmma.loadwmma.store的基地址和步长参数所需的对齐方式。

  • 明确了由wmma操作返回的片段布局与架构相关,在不同链接兼容的SM架构下编译的函数间传递wmma片段可能无法按预期工作。

  • 已明确说明{atom/red}.f16x2}操作的原子性保证是针对两个.f16元素分别进行的,但不能保证作为单个32位访问时的原子性。

12.21. PTX ISA 版本 6.2 的变更

新功能

PTX ISA 6.2版本引入了以下新特性:

  • 新增指令 activemask 用于查询warp中的活动线程。

  • 扩展原子和归约指令,以执行带有强制.noftz限定符的.f16x2加法运算。

已弃用的功能

PTX ISA 版本 6.2 弃用了以下功能:

  • 从PTX ISA版本6.0开始(该版本引入了实现独立线程调度sm_70架构),不推荐使用不带.syncshflvote指令。

语义变更与说明

  • 明确了wmma指令只能在条件执行代码中使用,前提是已知warp中的所有线程对条件的评估结果相同,否则行为将是未定义的。

  • 在内存一致性模型中,对道德强操作的定义进行了更新,将栅栏操作排除在完全重叠的要求之外,因为栅栏操作并不访问内存。

12.22. PTX ISA 版本6.1的变更

新功能

PTX ISA 6.1版本引入了以下新特性:

  • 支持 sm_72 目标架构。

  • wmma指令中新增支持32x8x168x32x16矩阵形状。

语义变更与说明

无。

12.23. PTX ISA 6.0版本的变更

新功能

PTX ISA 6.0版本引入了以下新特性:

  • 支持 sm_70 目标架构。

  • 指定在sm_70及更高架构上运行程序的内存一致性模型。

  • 对内存指令的各种扩展,用于指定内存同步语义以及可以观察到此类同步的范围。

  • 新增用于矩阵运算的指令wmma,支持从内存加载矩阵、执行乘加运算并将结果存储回内存。

  • 支持新的 barrier 指令。

  • 扩展neg指令以支持.f16.f16x2类型。

  • 新增指令 fns 用于查找整数中的第n个置位比特。

  • 新增指令 bar.warp.sync 用于实现线程束内线程同步。

  • 扩展了voteshfl指令,新增.sync修饰符,该修饰符会先等待指定线程完成,再分别执行voteshfl操作。

  • 新增指令 match.sync,支持在线程束(warp)内跨线程广播和比较数值。

  • 新增指令 brx.idx,允许根据潜在目标列表中的索引跳转到相应标签。

  • 支持.func的无大小数组参数,可用于实现可变参数函数。

  • 支持在dwarf-lines中使用.b16整数类型。

  • 支持使用mov指令获取设备函数返回参数的地址。

语义变更与说明

  • bar指令的语义已更新,表明执行线程会等待其warp中其他未退出的线程。

  • PTX 2.1中引入但未实现的间接分支支持已从规范中移除。

  • 支持获取标签地址以及在初始化器中使用标签的功能,这些之前未实现的部分已从规范中移除。

  • 规范中未实现的变参函数支持已被移除。

12.24. PTX ISA 5.0版本的变更

新功能

PTX ISA 5.0版本引入了以下新特性:

  • 支持 sm_60, sm_61, sm_62 目标架构。

  • 扩展原子和归约指令以执行双精度加法运算。

  • 扩展原子和归约指令以指定scope修饰符。

  • 新增一个.common指令,允许链接多个包含相同符号但大小不同的声明目标文件。

  • 新增一条dp4a指令,支持4路点积累加操作。

  • 新增一条dp2a指令,支持2路点积累加操作。

  • 支持特殊寄存器 %clock_hi

语义变更与说明

关于ldst指令中缓存修饰符的语义已明确说明,这些缓存操作仅被视为性能提示,不会改变程序的内存一致性行为。

关于ldst指令中volatile操作的语义已得到澄清,以反映优化编译器如何处理volatile操作。

12.25. PTX ISA 4.3版本的变更

新功能

PTX ISA 4.3版本引入了以下新特性:

  • 新增了一个lop3指令,支持对3个输入执行任意逻辑运算。

  • 在扩展精度算术指令中增加了对64位计算的支持。

  • 扩展tex.grad指令以支持cubeacube几何体。

  • 扩展tld4指令以支持a2dcubeacube几何结构。

  • 扩展textld4指令以支持偏移向量和深度比较的可选操作数。

  • 扩展txq指令以支持从特定LOD查询纹理字段。

语义变更与说明

无。

12.26. PTX ISA 4.2版本的变更

新功能

PTX ISA 4.2版本引入了以下新特性:

  • 支持 sm_53 目标架构。

  • 支持针对.f16.f16x2类型的算术、比较和纹理指令。

  • 支持用于表面的memory_layout字段,以及用于查询该字段的suq指令支持。

语义变更与说明

ABI下的参数传递语义已更新,表明用于参数传递的ld.paramst.param指令不能被谓词化。

{atom/red}.add.f32的语义已更新,表明在全局内存上的原子操作中,次正规输入和结果会被刷新为保留符号的零;而共享内存上的原子操作则会保留次正规输入和结果,不会将它们刷新为零。

12.27. PTX ISA 4.1版本变更

新功能

PTX ISA 4.1版本引入了以下新特性:

  • 支持 sm_37sm_52 目标架构。

  • 新增对纹理字段array_sizenum_mipmap_levelsnum_samples的支持,以及用于查询这些字段的txq指令支持。

  • 支持为Surfaces新增array_size字段,以及suq指令用于查询该字段。

  • 支持特殊寄存器 %total_smem_size%dynamic_smem_size

语义变更与说明

无。

12.28. PTX ISA 4.0版本的变更

新功能

PTX ISA 4.0版本引入了以下新特性:

  • 支持 sm_32sm_50 目标架构。

  • 支持64位性能计数器专用寄存器 %pm0_64,..,%pm7_64

  • 新增一条istypep指令。

  • 新增了一条指令 rsqrt.approx.ftz.f64,用于快速计算数值的平方根倒数近似值。

  • 支持新的指令.attribute,用于指定变量的特殊属性。

  • 支持 .managed 变量属性。

语义变更与说明

vote指令的语义已更新,明确指出当warp中的非活动线程参与vote.ballot.b32时,其对应条目将贡献0值。

12.29. PTX ISA 3.2版本变更

新功能

PTX ISA 3.2版本引入了以下新特性:

  • 纹理指令支持从多重采样和多采样数组纹理中读取数据。

  • 扩展了.section调试指令,使其包含标签和立即表达式。

  • 扩展 .file 指令以包含时间戳和文件大小信息。

语义变更与说明

vavrg2vavrg4 指令的语义已更新,表明该指令仅在 Va[i] + Vb[i] 为非负数时加 1,并且加法结果会右移 1 位(而不是除以 2)。

12.30. PTX ISA 3.1版本的变更

新功能

PTX ISA 3.1版本引入了以下新特性:

  • 支持 sm_35 目标架构。

  • 支持CUDA动态并行功能,允许内核创建和同步新的工作。

  • ld.global.nc 用于通过非一致性纹理缓存加载只读全局数据。

  • 新增了一个漏斗移位指令 shf

  • 扩展原子和归约指令以执行64位{and, or, xor}操作,以及64位整数{min, max}操作。

  • 添加对mipmaps的支持。

  • 增加对纹理和表面间接访问的支持。

  • 将对通用寻址的支持扩展到包含.const状态空间,并新增一个运算符generic(),用于为初始化器中使用的.global.const变量生成通用地址。

  • 新增一个.weak指令,允许链接多个包含相同符号声明的目标文件。

语义变更与说明

PTX 3.1重新定义了初始化器中全局变量的默认寻址方式,从通用地址改为全局状态空间中的偏移量。传统PTX代码在初始化器中使用每个全局变量时会被视为具有隐式的generic()运算符。PTX 3.1代码应在初始化器中包含显式的generic()运算符,使用cvta.global在运行时形成通用地址,或通过ld.global从非通用地址加载。

指令 mad.f32sm_20 及更高版本的目标架构中需要指定舍入修饰符。但对于 PTX ISA 3.0 及更早版本,ptxas 不会强制此要求,mad.f32 会默认静默转为 mad.rn.f32。对于 PTX ISA 3.1 版本,ptxas 会生成警告并默认转为 mad.rn.f32,而在后续版本中,ptxas 将对 PTX ISA 3.2 及更高版本强制执行此要求。

12.31. PTX ISA 3.0版本的变更

新功能

PTX ISA 3.0版本引入了以下新特性:

  • 支持 sm_30 目标架构。

  • SIMD视频指令。

  • 新增了一条warp shuffle指令。

  • 指令 mad.ccmadc 用于高效的高精度整数乘法运算。

  • 展示3D和阵列几何形状的指令。

  • 纹理指令支持从立方体贴图和立方体贴图数组纹理中读取。

  • 平台选项 .target debug 用于声明 PTX 模块包含 DWARF 调试信息。

  • pmevent.mask,用于触发多个性能监控事件。

  • 性能监控计数器专用寄存器 %pm4..%pm7

语义变更与说明

特殊寄存器 %gridid 已从32位扩展至64位。

PTX ISA 3.0版本在编译为应用二进制接口(ABI)时弃用了模块作用域的.reg.local变量。在不使用ABI进行编译时,模块作用域的.reg.local变量仍像之前一样被支持。当编译包含模块作用域.reg.local变量的旧版PTX代码(3.0之前的ISA版本)时,编译器会静默禁用ABI的使用。

shfl指令的语义已更新,明确指出对于warp内非活跃和被谓词关闭的线程,源操作数a的值是不可预测的。

PTX模块不再允许重复的.version指令。该功能从未实现,因此语义上没有变化。

未实现的指令 suld.psust.p.{u32,s32,f32} 已被移除。

12.32. PTX ISA 版本2.3的变更

新功能

PTX 2.3 新增了对纹理数组的支持。纹理数组功能允许访问一维或二维纹理数组,其中通过整数值索引纹理数组,然后使用一个或两个单精度浮点坐标在所选定的一维或二维纹理中进行寻址。

PTX 2.3 新增了一个指令 .address_size,用于指定地址大小。

.const.global 状态空间中的变量默认初始化为零。

语义变更与说明

.maxntid指令的语义已更新以匹配当前实现。具体来说,.maxntid仅保证线程块中的线程总数不超过最大值。此前,语义表明每个维度会单独强制执行最大值限制,但实际情况并非如此。

位字段提取和插入指令BFE和BFI现在表明,lenpos操作数被限制在0..255值范围内。

未实现的指令 {atom,red}.{min,max}.f32 已被移除。

12.33. PTX ISA 2.2版本的变更内容

新功能

PTX 2.2 新增了一个用于指定内核参数属性的指令;具体来说,新增了以下指令功能:用于指定内核参数为指针、用于指定该参数指向的状态空间,并可选择性地指定参数所指向内存的对齐方式。

PTX 2.2版本在.samplerref不透明类型中新增了一个名为force_unnormalized_coords的字段。该字段在独立纹理模式下用于覆盖纹理头中的normalized_coords字段。添加此字段是为了支持像OpenCL这样的语言,这些语言在采样器头而非纹理头中表示归一化/非归一化坐标的属性。

PTX 2.2 弃用了显式常量存储区,并为 .const 状态空间提供了大型扁平地址空间支持。仍兼容使用显式常量存储区的旧版PTX代码。

PTX 2.2 新增了一条 tld4 指令,用于从构成给定纹理位置双线性插值区域的四个纹素中加载分量(rgba)。该指令可用于在软件中计算更高精度的双线性插值结果,或执行更高带宽的纹理加载。

语义变更与说明

无。

12.34. PTX ISA 2.1版本的变更

新功能

底层基于堆栈的ABI在PTX ISA 2.1版本中支持sm_2x目标架构。

已实现对sm_2x目标的间接调用支持。

新增了.branchtargets.calltargets指令,用于指定间接分支和间接函数调用的潜在目标。同时添加了.callprototype指令,用于声明间接函数调用的类型签名。

现在可以在变量初始化器中指定.global.const变量的名称来表示它们的地址。

新增了一组32个专用于驱动的执行环境特殊寄存器。这些寄存器被命名为%envreg0..%envreg31

纹理和表面新增了通道数据类型和通道顺序的字段,txqsuq指令支持查询这些字段。

指令 .minnctapersm 已取代 .maxnctapersm 指令。

已添加指令.reqntid,用于指定确切的CTA维度。

新增了一条指令rcp.approx.ftz.f64,用于计算快速粗略的近似倒数。

语义变更与说明

如果指定了.minnctapersm但未同时指定.maxntid,则会发出警告。

12.35. PTX ISA 2.0版本的变更

新功能

浮点扩展

本节描述针对sm_20目标的PTX ISA 2.0版本中的浮点运算变更。其目标是在尽可能实现IEEE 754标准合规性的同时,最大限度保持与旧版PTX ISA 1.x代码和sm_1x目标的向后兼容性。

PTX ISA 1.x版本的变更如下:

  • 对于sm_20目标架构,单精度指令默认支持次正规数。可以使用.ftz修饰符来保持与sm_1x的向后兼容性。

  • 单精度运算 addsubmul 现在支持针对 sm_20 目标的 .rm.rp 舍入修饰符。

  • 新增了单精度融合乘加(fma)指令,支持符合IEEE 754标准的舍入修饰符和对次正规数的处理。fma.f32指令还支持.ftz.sat修饰符。fma.f32需要sm_20架构支持。mad.f32指令已扩展支持舍入修饰符,使其在sm_20目标架构下与fma.f32功能相同。对于sm_20目标架构,fma.f32mad.f32都需要指定舍入修饰符。

  • 保留mad.f32指令不带舍入的功能,以便编译器能为sm_1x目标生成代码。当为sm_1x编译的代码在sm_20设备上执行时,mad.f32会映射到fma.rn.f32

  • 已添加符合IEEE 754舍入规范的单精度和双精度divrcpsqrt运算。这些运算通过使用舍入修饰符来标识,并要求sm_20架构支持。

  • 已添加指令 testpcopysign

新指令

新增了一条均匀加载指令ldu

Surface指令支持额外的.clamp修饰符,.clamp.zero

指令 sust 现在支持格式化表面存储。

新增了一条前导零计数指令 clz

新增了一个查找前导非符号位指令bfind

新增了一条位反转指令brev

已添加位字段提取和插入指令bfebfi

新增了一个人口计数指令popc

新增了一个投票表决指令vote.ballot.b32

指令 {atom,red}.add.f32 已实现。

指令 {atom,red}.shared 已扩展支持针对 sm_20 目标的64位数据类型处理。

新增了一个系统级内存屏障指令membar.sys

bar 指令已扩展如下:

  • 已添加bar.arrive指令。

  • 已添加指令 bar.red.popc.u32bar.red.{and,or}.pred

  • bar 现在支持可选的线程计数和寄存器操作数。

已添加标量视频指令(包括prmt)。

新增了指令 isspacep 用于查询通用地址是否落在指定的状态空间窗口内。

新增了指令 cvta,用于将全局地址、局部地址和共享地址转换为通用地址,反之亦然。

其他新功能

指令 ld, ldu, st, prefetch, prefetchu, isspacep, cvta, atom, 和 red 现在支持通用寻址。

新增了特殊寄存器 %nwarpid%nsmid%clock64%lanemask_{eq,le,lt,ge,gt}

缓存操作已添加到指令ldstsuldsust中,例如用于将数据预取到内存层次结构的指定层级。同时还新增了prefetchprefetchu指令。

.maxnctapersm 指令已被弃用,并替换为 .minnctapersm 以更好地匹配其行为和用法。

新增了一个名为.section的新指令,用于替代通过PTX传递DWARF格式调试信息时使用的@@DWARF语法。

新增了一个指令.pragma nounroll,允许用户禁用循环展开。

语义变更与说明

在PTX ISA 1.4及更早版本中,cvt.ftz指令存在的错误(当源或目标类型大小为64位时,单精度次正规输入和结果不会被刷新为零)已被修复。在PTX ISA 1.5及更高版本中,cvt.ftz(以及针对.target sm_1xcvt指令,其中隐含.ftz)会对所有浮点指令类型组合将单精度次正规输入和结果刷新为保留符号的零值。为保持与旧版PTX代码的兼容性,当.version为1.4或更早时,仅当源和目标类型大小都不是64位时,才会将单精度次正规输入和结果刷新为保留符号的零值。

特殊寄存器组件 %tid, %ntid, %ctaid%nctaid 已从16位扩展至32位。这些寄存器现在的类型为 .v4.u32

在PTX ISA版本1.5中,独立纹理模式下可用的采样器数量被错误地列为32个;正确的数量应为16个。

14. .pragma字符串描述

本节介绍由ptxas定义的.pragma字符串。

14.1. Pragma字符串:"nounroll"

“nounroll”

在优化后端编译器时禁用循环展开。

语法

.pragma "nounroll";

描述

"nounroll" pragma是一个指令,用于在优化后端编译器中禁用循环展开。

"nounroll" pragma 可以在模块、入口函数和语句级别使用,具有以下含义:

module scope

禁用模块中所有循环的展开,包括位于.pragma之前的循环。

entry-function scope

禁用入口函数体中所有循环的展开。

statement-level pragma

禁用当前块作为循环头的循环展开。

请注意,为了在语句级别达到预期效果,"nounroll"指令必须出现在目标循环头部基本块中的任何指令语句之前。循环头部块被定义为支配循环体内所有块的块,并且是循环回边的目标。出现在循环头部块之外的语句级"nounroll"指令会被静默忽略。

PTX ISA 说明

在PTX ISA 2.0版本中引入。

目标ISA注意事项

需要sm_20或更高版本。对于sm_1x目标将被忽略。

示例

.entry foo (...)
.pragma "nounroll";  // do not unroll any loop in this function
{
...
}

.func bar (...)
{
...
L1_head:
     .pragma "nounroll";  // do not unroll this loop
     ...
@p   bra L1_end;
L1_body:
     ...
L1_continue:
     bra L1_head;
L1_end:
     ...
}

14.2. Pragma字符串:"used_bytes_mask"

“used_bytes_mask”

用于指示ld操作数据中已使用字节的掩码。

语法

.pragma "used_bytes_mask mask";

描述

"used_bytes_mask" pragma 是一个指令,用于根据提供的掩码指定加载操作中使用的字节。

"used_bytes_mask" pragma 需要在需要获取加载操作中使用的字节信息的加载指令之前指定。 如果紧随其后的指令不是加载指令,则该编译指示将被忽略。

对于没有此编译指令的加载指令,假定加载操作中的所有字节都会被使用。

操作数 mask 是一个32位整数,其置位位表示加载操作数据中使用的字节。

语义

Each bit in mask operand corresponds to a byte data where each set bit represents the used byte.
Most-significant bit corresponds to most-significant byte of data.

// For 4 bytes load with only lower 3 bytes used
.pragma "used_bytes_mask 0x7";
ld.global.u32 %r0, [gbl];     // Higher 1 byte from %r0 is unused

// For vector load of 16 bytes with lower 12 bytes used
.pragma "used_bytes_mask 0xfff";
ld.global.v4.u32 {%r0, %r1, %r2, %r3}, [gbl];  // %r3 unused

PTX ISA 说明

在PTX ISA版本8.3中引入。

目标ISA注意事项

需要 sm_50 或更高版本。

示例

.pragma "used_bytes_mask 0xfff";
ld.global.v4.u32 {%r0, %r1, %r2, %r3}, [gbl]; // Only lower 12 bytes used

15. 通知

15.1. 注意事项

本文档仅供信息参考之用,不应视为对产品功能、状态或质量的保证。NVIDIA公司(“NVIDIA”)对本文件所含信息的准确性或完整性不作任何明示或暗示的陈述或保证,并对其中可能存在的错误不承担任何责任。NVIDIA对于因使用此类信息而产生的后果、或因使用该信息导致的第三方专利或其他权利侵权概不负责。本文件不构成对开发、发布或交付任何材料(定义见下文)、代码或功能的承诺。

NVIDIA保留随时对本文件进行更正、修改、增强、改进以及任何其他变更的权利,恕不另行通知。

客户在下单前应获取最新的相关信息,并确认这些信息是最新且完整的。

除非NVIDIA与客户授权代表签署的单独销售协议中另有约定,否则NVIDIA产品的销售均以订单确认时提供的NVIDIA标准销售条款和条件为准(以下简称"销售条款")。NVIDIA特此明确反对将任何客户通用条款适用于本文件所述NVIDIA产品的采购。本文件不直接或间接构成任何合同义务。

NVIDIA产品并非设计、授权或保证适用于医疗、军事、航空、航天或生命支持设备,也不适用于那些可以合理预期NVIDIA产品故障或失灵会导致人身伤害、死亡、财产或环境损害的应用场景。NVIDIA对于在此类设备或应用中使用和/或包含NVIDIA产品不承担任何责任,因此客户需自行承担相关风险。

NVIDIA不声明或保证基于本文档的产品适用于任何特定用途。NVIDIA未必会对每个产品的所有参数进行测试。客户应全权负责评估和确定本文档所含信息的适用性,确保产品适合并满足客户计划的应用需求,并执行必要的应用测试以避免应用或产品出现故障。客户产品设计中的缺陷可能会影响NVIDIA产品的质量和可靠性,并可能导致超出本文档范围的其他或不同的条件和/或要求。对于任何因以下原因导致的故障、损坏、成本或问题,NVIDIA不承担任何责任:(i) 以违反本文档的任何方式使用NVIDIA产品或(ii) 客户产品设计。

本文档不授予任何NVIDIA专利权、版权或其他NVIDIA知识产权的明示或暗示许可。NVIDIA发布的关于第三方产品或服务的信息,不构成NVIDIA对这些产品或服务的使用许可或担保认可。使用此类信息可能需要获得第三方基于其专利或其他知识产权的许可,或需要获得NVIDIA基于其专利或其他知识产权的许可。

本文件中的信息仅可在获得NVIDIA事先书面批准、未经改动完整复制且完全符合所有适用的出口法律法规,并附带所有相关条件、限制和声明的情况下进行复制。

本文件及所有NVIDIA设计规格、参考板、文件、图纸、诊断工具、清单和其他文档(统称及单独称为"材料")均以"现状"提供。NVIDIA不对材料作出任何明示或默示的保证,包括但不限于对不侵权、适销性和特定用途适用性的默示保证免责。在法律允许的最大范围内,NVIDIA不就因使用本文件导致的任何损害承担责任,包括但不限于任何直接、间接、特殊、附带、惩罚性或后果性损害,无论损害成因如何,也无论责任理论为何,即使NVIDIA已被告知发生此类损害的可能性。不论客户因任何原因可能遭受的任何损害,NVIDIA对客户就本文所述产品的全部及累计责任应受产品销售条款的限制。

15.2. OpenCL

OpenCL是苹果公司的商标,经Khronos Group Inc.授权使用。

15.3. 商标

NVIDIA和NVIDIA标识是美国及其他国家NVIDIA公司的商标或注册商标。其他公司及产品名称可能是其各自关联公司的商标。