第一章“简介”主要围绕着NVIDIA的PTX(Parallel Thread Execution)指令集架构(ISA)进行介绍,PTX被设计用来展示GPU作为数据并行计算设备的潜能。以下是对第一章内容的概括:
-
可扩展的数据并行计算:
- PTX架构针对GPU强大的并行处理能力进行了优化,通过将同样的程序应用在大量数据元素上并行执行,有效处理高吞吐量的计算密集型任务。这种数据并行模型降低了对复杂控制流的需求,同时通过隐藏内存访问延迟,实现了高效计算。
-
PTX的目标:
- PTX的主要目标包括:
- 为优化编译器和翻译器提供一个统一的源级ISA,方便将PTX代码映射到具体的GPU硬件架构上。
- 促进手写库、高性能内核以及架构测试的发展和优化。
- 提供一个跨越单个GPU到多GPU并行系统的可扩展编程模型。
- PTX的主要目标包括:
-
PTX ISA版本8.4:
- PTX ISA版本8.4引入了多项新特性,如对
.b128
类型的支持、对sparse WGMMA(稀疏权重通用矩阵乘累加)指令的扩展、以及对FP8数据类型(如.e4m3
、.e5m2
、.s8
和.u8
)的支持。
- PTX ISA版本8.4引入了多项新特性,如对
-
文档结构:
- 文档进一步细分为多个章节,分别涵盖编程模型、PTX虚拟机模型、语法、状态空间、类型和变量定义、指令操作数、抽象ABI(应用程序二进制接口)、指令集、特殊寄存器列表、以及编译器和架构层面的指令支持等详细内容。
这一章对PTX ISA及其在支持GPU并行计算中的角色有个初步认识,同时了解到PTX ISA 8.4版本中的新增功能,为后续章节深入了解PTX编程细节打下基础。
第二章“编程模型”详细介绍了使用PTX(Parallel Thread eXecution)指令集架构进行编程的核心概念和结构,主要包括以下几个方面:
-
高度多线程协处理器:
- GPU作为一种高度多线程的协处理器,擅长处理大量的并行数据和计算密集型任务。在GPU上运行的程序充分利用了数千乃至数万个并发执行的线程,这些线程分布在一个或多個并行处理单元(Streaming Multiprocessors, SM)上。
-
线程层次结构:
- 线程在PTX中组织成一个多层级结构,包括:
- 合作线程数组(Cooperative Thread Arrays, CTAs):一组执行相同内核函数的线程,它们共享相同的执行上下文和内存空间(如共享内存)。
- 集群(Cluster):一组CTAs的集合,可以看作是更高级别的线程组织形式,仅在特定架构(sm_90及以上)中引入,允许更大规模的线程调度和通信。
- 网格(Grid):由多个CTA或集群组成的大型并行执行结构,用于在更大的数据集上并行执行内核函数。
- 线程在PTX中组织成一个多层级结构,包括:
-
内存层次结构:
- PTX编程模型中定义了多个内存空间,如全局内存、常量内存、共享内存、局部内存、纹理内存和表面内存等,每个内存空间具有不同的访问速度和特性,程序员需要根据需求和性能优化策略来合理选择和使用。
-
同步与通信:
- 在同一个CTA内的线程可以通过同步指令(如bar.sync)进行同步操作,而在不同CTA或集群间的线程通信则受到限制。程序员需要通过合理的数据布局和内存访问模式来实现跨CTA的有效通信。
-
程序执行流程:
- 主机CPU程序通过API调用启动内核函数(Kernel函数),并将数据分配到适当的内存空间。然后,GPU将内核函数的任务分解成多个线程,按照线程层次结构进行执行。一旦内核执行完毕,结果数据可以被返回到主机内存,供CPU进一步处理。
第二章阐述了在PTX框架下如何组织和调度大量并行线程来高效执行计算任务,以及如何利用不同的内存层次结构来优化数据访问和通信。同时,本章还说明了线程之间的同步机制,以及主机与GPU之间的交互方式。通过理解和掌握这些编程模型特性,开发者可以编写出充分发挥GPU并行计算能力的高效代码。
第三章“PTX机器模型”详细描述了NVIDIA GPU架构中虚拟机模型的基础组件和特性,以及如何在该模型上进行并行计算。以下是本章内容概要:
-
SIMT(Single Instruction Multiple Threads)多处理器阵列:
- PTX虚拟机模型构建在一组高度并行的SIMT多处理器之上,这些多处理器支持大量并发执行的线程。当宿主程序启动内核函数时,线程块会被分配到具备足够执行资源的多处理器上。
-
线程层次结构:
- 线程被组织为合作线程数组(Cooperative Thread Arrays, CTAs),每个CTA内包含若干线程,这些线程以SIMD-like方式执行,即在同一时间内执行同样的指令,但操作的数据可能不同。
- CTAs进一步组织为集群,集群又组合成网格,形成了多层级的线程组织结构,允许灵活的并行计算调度。
-
独立线程调度(Independent Thread Scheduling, ITS):
- 自Volta架构起,NVIDIA引入了ITS,使得GPU可以在单个线程级别进行调度,不再受限于传统的32线程线程束(warp)的统一执行模型。这样一来,即使处于同一个线程束中的线程也可以独立执行,提升了并行执行的灵活性和性能。
-
片上共享内存:
- 每个多处理器都配备了一定大小的片上共享内存,所有该多处理器上的线程都可以访问。共享内存用于加速线程间的通信和数据共享,尤其在执行同一CTA内的并行计算时,它可以提供极高的访问速度。
-
内存层次结构:
- PTX机器模型中还包括全局内存、常量内存、纹理内存等多种内存状态空间。全局内存用于长期存储,常量内存存放只读数据,纹理内存则优化了空间数据的读取性能。
-
寄存器资源:
- 每个多处理器包含多个标量处理器(SP)核心,并为每个线程分配了一定数量的寄存器,这些寄存器用于临时存储中间计算结果,访问速度最快。
本章内容是了解NVIDIA GPU的内部结构和工作原理,以及如何有效地利用这些资源来编写高效并行计算程序。此外,这一章还讨论了如何在不同线程调度模型下处理数据同步、内存访问延迟等问题,为开发者提供必要的背景知识和最佳实践指导。
第四章“语法”详述了PTX指令集架构(ISA)的文本格式和语法规则。这一章内容包括以下几个关键点:
-
源格式:
- PTX代码采用ASCII文本格式,每行以换行符分隔。
- 所有空白字符(如空格、制表符)在语言中都被视为等价的,并且在分离语言元素(tokens)时发挥作用,但除此之外会被忽略。
-
注释:
- PTX使用C风格的注释,即以
//
开始直到行尾的单行注释,以及以/*
开始,以*/
结束的多行注释。
- PTX使用C风格的注释,即以
-
语句结构:
- PTX语句由指令操作码、操作数列表以及终止的分号组成。操作数可以是寄存器变量、常量表达式、地址表达式或标签名,且可以包含一个可选的guard谓词(如
@p
)来控制条件执行。 - 指令关键字均为保留字,列表在表格中罗列,如
add
、div
、mad
、ld
、st
等,涵盖了算术、逻辑、内存访问等各种操作。
- PTX语句由指令操作码、操作数列表以及终止的分号组成。操作数可以是寄存器变量、常量表达式、地址表达式或标签名,且可以包含一个可选的guard谓词(如
-
常量:
- PTX支持整数常量、浮点常量以及常量表达式的声明和使用。整数常量可以是32位或64位,有正负之分,并且支持多种进制表示(十进制、十六进制、八进制和二进制)。
- 浮点常量则表示为双精度(double)值,并且在常量表达式计算中遵循C语言的标准优先级和结合性规则。
-
标识符:
- PTX中的标识符遵循扩展的C++命名规则,可以由字母、数字、下划线和美元符号组成,且首字符不能是数字。标识符没有长度限制,但建议实现至少支持1024个字符。
-
指令语句:
- 指令语句分为指令声明(directive statements)和指令表达式(instruction statements),前者用于管理和控制符号表和地址空间,后者则执行具体计算和内存操作。
第四章详细了解如何书写合法的PTX指令,以及如何构造和评估常量表达式,这对编写和理解PTX代码至关重要。此外,本章还通过举例说明了如何使用各种指令关键字和操作数组合来实现不同的计算和内存访问操作。
State Spaces, Types, and Variables
状态空间、数据类型与变量
本章详细介绍了PTX指令集架构(ISA)中使用的不同状态空间、数据类型以及变量的声明与初始化。
-
状态空间:PTX中定义了多种状态空间,包括但不限于
.global
、.const
、.shared
、.local
、.param
等。.global
状态空间对应于GPU全局内存,.const
状态空间存放只读常量数据,.shared
状态空间为线程块内部共享内存,.local
或.reg
状态空间用于表示线程私有变量,而.param
状态空间则用于存储从主机传入内核的参数。 -
变量初始化:变量在声明时可以指定初始值,类似于C/C++语言的初始化语法。变量的初始化列表可以用花括号包围,对于数组和向量,初始化值需要按照维度嵌套排列。像C语言一样,数组初始化可以是不完全的,即初始化元素的数量可以少于数组维度的实际大小,剩余位置默认初始化为该数组类型的默认值(通常是0)。目前仅支持在
.const
和.global
状态空间中的变量初始化,未明确初始化的变量将自动设置为零。 -
函数参数:内核函数参数在
.param
状态空间中声明,是只读变量,可以从主机传值给内核。内核参数在整个网格内的所有CTA中都是共享的。可以通过ld.param
指令访问这些参数,还可以将参数地址移动到寄存器中,然后通过ld.param
指令间接访问。.param
状态空间的变量可以表示普通数据值,也可以是其他状态空间对象(如.const
、.global
、.local
或.shared
)的指针。 -
张量(Tensors):PTX ISA还支持张量数据的处理,并且详细描述了张量模式的边界框(Bounding Box)、迭代步幅(Traversal Stride)以及越界访问(Out of Boundary Access)的处理方式。张量操作提供了不同的访问模式,如tiled模式和im2col模式,并在不同维度上定义了张量坐标、像素每列(Pixels-per-Column)和通道每像素(Channels-per-Pixel)等参数。
-
数据类型与变量 这部分深入讲解了PTX中支持的各种数据类型及其在变量声明和使用时的注意事项:
-
基本数据类型:包括整数类型(如
.b8
,.u32
,.s64
等)和浮点类型(.f16
,.f32
,.f64
等)。 -
向量类型:允许声明固定长度的向量数据类型,如
.v2.b32
表示一个包含两个32位无符号整数的向量。 -
复合类型:如数组和结构体,支持声明多维数组以及自定义结构体成员。
-
特殊类型:包括纹理和表面数据类型,以及针对现代GPU加速计算引入的张量类型,这些类型在特定的内存访问模式下提供了更高的性能。
-
变量声明:指明如何在PTX代码中声明变量,包括对其所在的内存空间(状态空间)的选择,以及是否提供初始值。
-
向量和数组:描述如何声明和初始化向量和数组变量,以及访问这些变量的元素。
-
初始值:讨论变量初始化的方法,包括简单常数值初始化和复杂的数组或结构体初始化表达式。
-
对齐:强调在声明变量时,对齐要求对于性能和正确性的重要性,特别是在访问向量数据和内存对齐要求严格的GPU硬件上。
- 类似于矩阵乘加运算(WGMMA)这样的高级功能,支持不同规模的矩阵块操作,这些矩阵块可以是子字节(sub-byte)级别或者单比特(single-bit)级别的,但需要注意这些特性在不同版本的PTX和SM架构上有不同的支持程度和预览状态。
- 张量在PTX中的使用,包括张量维度、尺寸和格式的设定,以及张量访问模式(如分块、交错布局和Swizzling模式)的详细说明,这些都与张量在内存中的布局和高效访问紧密相关。
在im2col模式下,边界框是在DHW空间中定义的,张量坐标的访问方式随维度的不同而变化,且张量偏移量也有限制范围。此外,还介绍了不同大小的swizzle模式,用于指定目标数据布局,以便于在内存访问中优化带宽利用率和数据对齐。
整体而言,本章旨在阐述PTX中变量如何在不同状态空间中声明、初始化以及使用,以及如何在GPU编程模型中对复杂数据结构(如张量)进行有效的内存访问和操作。
张量在PTX中是一种多维矩阵结构,存在于内存中,其定义由以下几个关键属性决定:
- 维度数:张量可以是一维、二维、三维、四维或五维。
- 维度大小:每个维度都有一个尺寸,表示沿着该维度的元素数量。
- 元素类型:张量元素可以是位大小类型(如
.b32
、.b64
)、整数类型(.u8
、.u16
、.u32
、.s32
、.u64
、.s64
)、浮点类型(.f16
、.bf16
、.tf32
、.f32
、.f64
,且.f64
操作会进行最近偶数舍入)。 - 张量步长:张量步长描述了在每个维度上从一个元素到下一个元素的偏移量,这有助于处理张量边界外的填充。
PTX支持对张量数据的操作指令,这些指令包括:
- 全局与共享内存间的张量数据复制。
- 使用源张量数据对目标张量进行减少操作。
在访问张量数据时,PTX支持两种模式:
- 分块模式(Tiled mode):在这种模式下,张量的多维布局在目标处保持不变。边界框(Bounding Box)在遍历张量维度时指定要访问的子区域大小,并且可以选择跳过一定数量的元素,这取决于遍历步幅(Traversal-Stride)参数。
- im2col模式:适用于处理3D、4D和5D张量,通常应用于批量图像数据。在这种模式下,张量数据被重新排列成一列列的形式,便于卷积等操作。定义了像素每列(Pixels-per-Column)和通道每像素(Channels-per-Pixel)参数,以及边界框的上下限(Lower-Corner和Upper-Corner)。
在处理张量数据时,PTX指令集还能检测并处理边界框超越张量边界的访问情况,提供两种处理模式:
- 零填充模式(Zero fill mode):超出张量边界的元素被置为0。
- OOB-NaN填充模式(OOB-NaN fill mode):超出张量边界的元素被设置为一个特殊的NaN值,称为OOB-NaN。
此外,还提供了多个实例来说明张量数据在不同访问模式下的具体操作过程,包括边界框的定义、偏移量的计算以及遍历步幅的应用等。
第六章详细介绍了PTX指令操作数的各个方面,包括操作数的类型信息、源操作数、目标操作数、地址、数组和向量的使用以及类型转换等核心内容。
-
操作数类型信息: 所有PTX指令中的操作数都有已知的类型,它们必须与指令模板和指令类型相兼容。不存在自动类型转换,只有当操作数类型与指令类型兼容时,才会发生隐式类型转换。例如,具有相同位宽的类型之间兼容,相同大小的整数类型相互兼容。
-
源操作数: 源操作数通常用a、b、c等名称标识。在PTX的负载-存储模型中,ALU指令的所有操作数必须位于
.reg
寄存器状态空间声明的变量中。大部分操作要求操作数的大小保持一致。cvt
(convert)指令可以接受多种不同类型的源操作数和大小,用于执行数据类型和大小的转换操作。 -
目标操作数: 产生单个结果的PTX指令会将结果存储在标记为d(destination)的操作数字段中,结果操作数是一个寄存器状态空间内的标量或向量变量。
-
地址作为操作数: PTX指令集支持使用地址作为操作数,可以指定内存位置进行读写操作。地址可以是变量名、寄存器加上偏移量或者其他可以直接寻址的状态空间内的地址。例如,
ld.global.v4.f32
和st
等指令就是通过地址操作数访问内存。 -
数组作为操作数: 数组可以在所有类型上声明,并且数组名在声明它的状态空间中成为一个地址常量。数组的大小在程序中是一个常量。内存指令可以访问数组元素,通过提供数组名和索引(或者在某些情况下通过偏移量)来定位数组内的特定元素。
-
向量作为操作数: 向量操作数在PTX中广泛使用,可以是多个相同类型数据的集合,它们在指令中表现为单个操作数,如
ld.global.v4.f32
指令提取向量中的各个元素,并将它们分别放置在寄存器的不同位置。 -
类型转换: 所有算术、逻辑和数据移动指令的操作数必须具有相同的类型和大小,除非该指令本身就定义了改变大小和/或类型的操作。不同类型或大小的操作数在执行操作之前必须进行类型转换。PTX提供了
cvt
指令来进行精确和格式化的类型转换操作,如将较小的整数类型扩展到较大整数类型,或将浮点数转换为整数类型时,会对溢出的情况做出规定,例如超出浮点范围的转换会得到相应的最大值(Inf)或特定值。
通过这些内容,第六章全面阐述了PTX指令操作数的各种特点、使用规则和操作方式,帮助开发者更好地理解和运用PTX指令集进行高效编程。
第七章 "Abstracting the ABI" 主要聚焦于PTX如何提供一种抽象层,隐藏特定调用约定、堆栈布局以及应用程序二进制接口(Application Binary Interface, ABI)的具体实现细节,以增强代码的可移植性和简化编程模型。本章内容包括:
-
函数声明与定义:
- 详细说明了在PTX中如何使用
.func
指令来声明和定义函数,包括返回参数列表、函数名以及输入参数列表的规范,以及如何在函数体内部定义函数行为。
- 详细说明了在PTX中如何使用
-
函数调用与参数传递:
- 描述了如何在PTX中通过函数调用语句和参数传递机制实现不同状态空间变量的传递,尤其是
.param
空间用于存放内核函数参数,并支持设备函数参数从注册状态空间转移到.param
空间的新特性(自PTX ISA版本2.0起)。
- 描述了如何在PTX中通过函数调用语句和参数传递机制实现不同状态空间变量的传递,尤其是
-
变参函数(Variadic Functions)支持:
- 解释了PTX如何支持变长参数列表的函数调用,也就是所谓的varargs函数,确保了函数能够接收不定数量的参数。
-
栈上内存分配(Alloca):
- 讨论了在PTX中如何通过指令或编译器辅助手段在函数内部动态分配栈上内存,以容纳局部变量和临时数据结构。
-
隐藏ABI细节:
- PTX通过对ABI进行抽象化,避免了暴露特定调用约定和堆栈布局的复杂性,从而使开发者能够专注于算法和并行计算逻辑,而不是底层硬件实现的细节。
-
兼容性与移植性:
- 强调了PTX如何通过提供一系列的高级抽象和多种ABI实现的支持,来确保代码能够在不同版本的CUDA架构和未来的GPU平台上平滑迁移和高效执行。
-
函数原型和ABI规范:
- 说明了如何在PTX中声明和遵守与CUDA架构ABI兼容的函数接口,以及如何查阅《PTX Writers Guide to Interoperability》以获取更多关于生成符合ABI标准的PTX代码的具体指导。
通过这一系列的设计和规范,PTX旨在让开发人员在编写GPU程序时,不必过多考虑具体硬件平台的细节,而是能够以一种更为通用和简洁的方式来表达并行计算任务。
第八章“Memory Consistency Model”讨论了在多线程环境下,如何确保PTX指令集架构中的内存操作具有正确的可见性和顺序一致性。本章主要内容包括:
-
模型适用范围:
- 规定了本章中内存一致性模型所施加的约束适用于所有版本的PTX程序,且仅当程序运行在sm_70或更新架构的GPU上时有效。
- 明确指出该模型不适用于纹理访问(包括ld.global.nc)和表面访问。
-
内存操作的基本单位:
- 定义了基本存储单元为字节,并解释了PTX程序可用的状态空间是由内存中连续的字节序列构成的,每个字节都有唯一地址,且地址空间是相对所有有权访问同一状态空间的线程而言的。
-
内存操作与内存位置:
- 每个内存指令都包含一个地址操作数和一个数据类型,地址操作数包含了虚拟地址,将在内存访问时转换为物理地址,物理地址与数据类型大小一起定义了物理内存位置。
-
内存操作的重叠与别名:
- 详细解释了两个内存操作如何因地址重叠和别名关系而互相影响。重叠发生在两个内存操作试图访问重叠的内存区间时,别名则出现在两个不同的虚拟地址映射到同一个物理内存位置。
-
多内存地址:
- 描述了多内存地址的概念,即一个虚拟地址可以指向多个不同设备上的物理内存位置。对此类地址的访问只能通过特定的多内存操作指令。
-
向量数据类型与打包数据类型的内存操作:
- 解释了向量数据类型和打包数据类型在执行内存操作时的模型。向量数据类型的内存操作被模拟为一系列标量操作,执行顺序未定;而打包数据类型的内存操作则涉及对相邻内存位置的成对访问。
-
初始化:
- 讨论了内存中每个字节在程序启动前如何被虚拟写操作W0初始化。如果字节属于有初始值的程序变量,那么W0会写入该字节对应的初始值,否则将写入一个未知但恒定的值。
-
内存操作的顺序一致性:
- 探讨了在多线程执行中,如何通过内存栅栏(例如fence.sc)和其他机制来维护内存操作的顺序一致性,确保线程间操作的可见性和有序性。
-
系统范围内的原子性限制:
- 提及在与主机CPU通信时,某些具有系统范围的强操作可能在某些系统上无法保证原子性执行,建议查阅CUDA原子性要求以了解详细信息。
通过这一章的内容,读者可以了解到PTX是如何通过定义一套严谨的内存一致性模型,来确保在多线程环境中,无论硬件架构如何,都能够正确地处理和协调内存访问,进而确保程序的正确性和效率。
第九章“指令集”深入介绍了PTX指令集的结构、格式和语义。这一章详细说明了PTX指令集中的各种指令类别和操作,并提供了具体指令的使用示例。
-
指令格式与语义:
- 本章首先描述了PTX指令的一般格式,包括操作码(opcode)、操作数、可选的guard谓词(@p),以及指令可能携带的修饰符(如.round、.sat、.ftz等)。
- 详细解释了指令的语义,包括指令执行的结果如何根据操作数和指令类型产生。
-
PTX指令分类:
- 本章列举了众多指令类型,如算术运算指令(包括整数、浮点数运算)、比较指令、分支指令、内存访问指令、原子操作指令、同步指令(如fence、barrier)、转换指令(如cvt)等。
-
指令示例:
- 举例说明了诸如整数和浮点数的加减乘除运算指令(如add、sub、mul、div)、多模加法指令(mad)以及特定类型的算术指令(如mad.ftz、mad.sat)等的使用。
- 对于浮点数mad指令,说明了其在不同架构版本上的行为差异,尤其是在无限精度计算后进行截断和舍入的过程。
-
predicated execution(有条件执行):
- 描述了如何使用谓词寄存器配合指令来控制指令的有条件执行,以及如何进行谓词的比较和操作。
-
指令操作数类型与大小:
- 讨论了指令操作数的类型信息,包括如何声明和处理不同大小的操作数,以及在操作数大小超过指令类型大小时如何处理。
-
内存访问指令:
- 分析了内存操作指令(如ld、st)的工作原理和操作细节,包括如何处理向量数据类型、打包数据类型和初始化内存。
-
状态空间访问:
- 介绍了如何通过指令访问不同状态空间(如全局内存、共享内存、寄存器等)的变量,并解释了状态空间访问成本以及内存一致性模型如何影响这些访问。
-
特定指令详解:
- 对于一些特定功能的指令,如mapa(映射地址)、getctarank(获取CTA秩)、以及其他数据移动和转换指令进行了详细说明,包括如何将地址映射到当前计算线程块(CTA),以及如何获得当前CTA在计算网格中的位置信息。
-
指令集扩展与优化:
- 讨论了针对不同GPU架构版本引入的新的或改进的指令,如WGMMA(Weighted General Matrix Multiply-Accumulate)指令集,用于高效处理矩阵乘加运算。
第九章“指令集”涵盖了PTX指令集从基础到高级的诸多方面,使读者能够深入了解和熟练运用PTX指令进行GPU编程。
第10章主要详细介绍了PTX中特殊寄存器的定义、用途、访问方式以及它们在不同GPU架构版本中的应用和限制。以下是部分内容摘要:
-
线程ID和线程块信息:
%tid
、%ctaid
、%nctaid
:这些寄存器分别表示当前线程的唯一ID、当前线程块的ID以及整个网格中的线程块总数。例如,%tid.x
、%tid.y
和%tid.z
表示线程在三个维度上的索引。%ntid
、%nctaid
:提供了线程块内线程数和网格中线程块数的信息。%cluster*
系列寄存器:这些寄存器与集群级别的粒度相关,如%cluster_ctaid
表示集群内的线程块ID,%cluster_nctaid
表示集群内的线程块总数,%cluster_ctarank
表示当前线程块在集群内的排名。
-
共享内存和总工作负载信息:
%smid
:可能用于区分不同共享内存区域。%nsmid
:可能表示共享内存区域的数量。%total_smem_size
、%aggr_smem_size
和%dynamic_smem_size
:分别表示分配给当前内核的所有共享内存总量、聚合大小以及动态分配的大小。
-
时间戳和计数器:
%clock
、%clock_hi
、%clock64
:用于获取GPU的硬件时钟值,用于测量执行时间和进行同步。%pm0
至%pm7
:可能是性能监控计数器寄存器,用于跟踪硬件事件统计信息。
-
系统保留区域的内存信息:
%reserved_smem_offset_*
:表示GPU内部预留的共享内存区域的起始、结束、总容量和特定偏移量。
-
预设布尔值寄存器:
%is_explicit_cluster
:指示当前内核是否是以集群模式显式启动。
-
线程屏蔽位信息:
%lanemask_eq
、%lanemask_le
、%lanemask_lt
、%lanemask_ge
和%lanemask_gt
:用于反映当前执行线程在Warps中的活动状态,用于实现条件执行和同步操作。
这一章还会涉及上述特殊寄存器的使用示例、如何在代码中读取和操作它们,以及不同架构版本对这些寄存器的限制和新增特性。这些特殊寄存器对于开发者理解GPU并行计算的核心机制至关重要,可以帮助编写高效的CUDA或PTX程序。
第十一章“Directives”主要介绍了PTX指令集中用于声明和管理模块属性、函数属性、内存管理以及链接等元数据的指令。这些指令主要用于控制编译器行为和程序执行时的资源分配、优化策略以及链接阶段的符号解析和重定位。
-
模块指令:
.version
:声明PTX代码使用的PTX指令集版本,确保编译器能够识别并正确处理代码中的所有指令。.target
:指定目标架构和平台选项,决定了PTX代码最终编译为目标设备的最低兼容架构版本。.address_size
:声明模块中地址的位宽,确保编译器能够正确处理地址计算。
-
函数和链接相关指令:
.extern
:声明函数在其他模块中定义,告诉编译器在链接阶段需要找到外部定义。.visible
:使函数对外部模块可见,可以被其他模块调用。.weak
:声明一个全局符号为弱定义,允许在链接时被其他强定义覆盖。.common
:声明全局符号为公共符号,允许多个模块定义并合并为最大的定义,且只有一个模块可以初始化公共符号。.linkonce
或.linkonce_odr
:用于一次性链接的代码段,如果有重复定义则只会保留一份。
-
内存管理指令:
.maxntid
:声明线程块的最大线程数。.reqntid
:要求线程块必须具有指定数量的线程。.minnctapersm
:声明最小的线程块数,确保在单个多处理器上达到一定的利用率。.maxnreg
:声明线程可以使用的最大寄存器数量,影响编译器对寄存器分配的决策。
-
性能调整指令:
.pragma
:传递编译器特定的优化提示或配置信息。
-
集群维度指令:
.reqnctapercluster
:声明在集群级别上的线程块需求。.maxclusterrank
(已弃用):曾经用于指定集群内线程块的最大编号。
-
其他链接相关指令:
.visible.global
、.weak.func
:声明全局可见的函数或变量,用于外部模块访问。.common
:声明一个全局变量为公共变量,多个模块可以定义,但在链接时会合并成单一定义。
通过这些指令,开发者可以对PTX代码进行细致的控制,确保在不同架构和设备上编译和执行时能保持良好的兼容性和性能表现。
第十二章“发行说明”汇总了PTX指令集架构(ISA)各个版本之间的新特性、改动、废弃的功能以及对现有功能的澄清说明。这一章内容主要关注每个PTX ISA版本发布以来引入的变化,例如:
-
新增指令:描述了在特定版本中加入的新指令及其功能,如扩展了对不同数据类型的支持,增加了新的内存操作指令,或者增强了矩阵运算(如wgmma指令的异步版本)等。
-
改进功能:对原有功能进行了扩展或优化,如添加了对原子操作的更多支持,增强了指令的向量化能力,或者是提高了对特定数据格式(如半精度浮点数)的处理能力。
-
废弃功能:标明了哪些功能在新版PTX中已被废弃,提醒开发者不要在新代码中继续使用,同时也指出了替代方案或推荐的做法,如在特定版本中废弃了对某些指令的旧版使用方式,转而推荐使用新的同步修饰符。
-
语义变更与澄清:对先前版本中可能存在歧义或不够清晰的指令行为进行了明确说明,比如明确了wmma指令在条件执行中的正确使用方式,或是修正了对
.noreturn
指令的使用场景。 -
目标架构支持:每个PTX ISA版本都与特定的CUDA版本和驱动程序版本关联,列出了该版本支持的GPU架构(如sm_10到sm_90a),以及对新架构带来的特定功能支持。
通过这一章的内容,开发人员可以了解到最新的PTX ISA版本中有哪些重要更新和变动,以便于他们编写适应最新硬件特性的高效CUDA程序。
第十三章“Pragma字符串描述”主要详细阐述了PTX中使用的各种.pragma
指令及其作用。.pragma
是一种编译指示,允许程序员向编译器传达特殊的要求或指令,以影响编译过程或最终生成的机器码。以下是对部分.pragma
指令的简要描述:
-
nounroll:
- 用于控制编译器的循环展开行为,禁止对特定循环进行优化时的自动循环展开。
-
used_bytes_mask:
- 该指令用于告知编译器在内存加载操作中实际使用的字节数量。通过设置一个32位掩码,编译器可以据此优化内存访问,减少不必要的数据传输,提高性能。例如,当加载一个向量时,若只使用了12字节,可以使用
.pragma "used_bytes_mask 0xfff"
来指示编译器忽略未使用的字节。
- 该指令用于告知编译器在内存加载操作中实际使用的字节数量。通过设置一个32位掩码,编译器可以据此优化内存访问,减少不必要的数据传输,提高性能。例如,当加载一个向量时,若只使用了12字节,可以使用
-
其他可能的
.pragma
指令:- 可能包括但不限于控制代码优化等级、内存布局、数据对齐、指令调度等方面的指令。
每一项.pragma
指令都有特定的语法格式和应用场景,使用时需注意其适用的PTX版本和目标GPU架构。通过正确使用.pragma
指令,程序员能够对编译器进行精细化控制,优化代码执行效率,降低功耗,并解决潜在的硬件限制问题。
第十四章“注意事项”主要包含了使用PTX指令集架构(ISA)及相关文档时的法律条款、版权信息、免责申明以及产品支持政策等内容。以下是该章关键要点的摘要:
-
知识产权声明:
- NVIDIA公司明确表示,该文档不构成对NVIDIA专利权、版权或其他知识产权任何形式的许可授权。
- 文档中提及的第三方产品和服务信息并不代表NVIDIA授予使用这些产品和服务的许可,使用可能需要获得第三方的授权,并遵守相关的专利权和版权规定。
-
免责声明:
- NVIDIA公司不对文档中提供的信息准确性或完整性做出任何保证,不对使用文档所导致的后果承担任何责任,无论是直接还是间接的损失,包括但不限于经济损失、特殊损害、偶然损害等。
- 用户应当在使用PTX ISA和相关信息之前自行获取最新和完整的产品信息,并确保所依赖的信息是准确和完整的。
-
销售和保修条款:
- NVIDIA产品的销售受制于订单确认时提供的NVIDIA标准销售条件,而非文档中的任何内容。这意味着购买和使用NVIDIA产品的具体权益应参照实际签署的销售合同和保修条款。
-
技术支持和版本说明:
- 文档中介绍了不同版本的PTX ISA特性以及它们所支持的GPU架构,强调了特定指令在不同架构上的兼容性和要求。例如,有些新特性或指令需要特定版本的GPU(如sm_53及以上)才能支持。
-
未来兼容性声明:
- 对于未来可能的IEEE 754标准合规性,文档提到某些指令(如涉及NaN处理的指令)在当前版本的实现中,对于NaN输入的行为并未严格遵照标准,但未来版本可能会遵循标准。
通过阅读这一章,用户应当充分理解在使用PTX ISA时的法律风险和责任划分,并始终关注官方文档和产品支持策略的最新进展。