当今的GPU在可编程处理器核心中执行大多数算术操作时,主要采用IEEE 754兼容的单精度32位浮点运算(参见第3章)。早期GPU中的固定点算术后来被16位、24位和32位浮点运算所取代,最终演变为符合IEEE 754标准的32位浮点运算。然而,GPU中某些固定的函数逻辑单元,例如纹理过滤硬件,仍在使用专有的数值格式。最近的GPU也开始提供IEEE 754兼容的双精度64位浮点指令。
支持的格式
IEEE 754浮点运算标准规定了基本的计算格式和存储格式。GPU在计算中使用两种基本格式,即32位和64位二进制浮点数,通常分别称为单精度和双精度。该标准还指定了16位二进制存储浮点格式,即半精度。GPU和Cg着色语言采用了窄的16位半精度数据格式,以实现高效的数据存储和传输,同时保持高动态范围。GPU在纹理过滤单元和光栅操作单元内部,对许多纹理过滤和像素混合计算采用了半精度进行处理。Industrial Light and Magic开发的OpenEXR高动态范围图像文件格式,在计算机成像和电影制作应用中,也采用相同的半精度格式来表示颜色分量值[2003]。
半精度:一个16bit的二进制浮点数格式。其组成格式是:1位符号位、5位指数位、10位尾数位,还有一个推定的整数位。
Industrial Light and Magic [2003]. OpenEXR , www.openexr.com .
基础算术
现代GPU可编程内核中常见的单精度浮点运算包括加法、乘法、乘累加、求最小值、最大值、比较、设置谓词以及整数与浮点数之间的转换等操作。浮点指令通常提供了用于取反和绝对值的操作数修饰符。
乘累加:单条指令进行的一个融合运算操作:先进行一次乘法,再进行一次加法。
现今大多数GPU的浮点加法和乘法运算与IEEE 754单精度浮点数标准兼容,其中包括非数(NaN)和无穷大值。浮点加法和乘法运算通常使用IEEE规定的四舍五入至最接近的偶数作为默认舍入模式。
为了提高浮点指令吞吐量,GPU通常采用复合乘加指令(MAD)。乘加操作首先执行带有截断的浮点乘法,然后执行四舍五入至最近的偶数的浮点加法。在一个指令发布周期内完成两个浮点操作,无需指令调度器分派两个独立指令,但需要注意的是,这种计算并非融合乘加,而是在加法前先截断乘积结果。
GPU通常会将小于正常规格的源操作数截断至符号保留的零值,并且在四舍五入后,如果结果溢出目标输出指数范围,也会将其截断为符号保留的零值。本节稍后将进一步讨论与这一乘加操作不同的融合乘加指令。
特殊算术
GPU通过专用硬件加速特殊函数计算、属性插值及纹理过滤等任务。其中,特殊函数指令涵盖了诸如余弦、正弦、二进制指数、二进制对数、倒数和倒数平方根等运算。属性插值指令则高效地生成从平面方程评估得出的像素属性。在B.4节中介绍的SFU(特殊功能单元)能够计算特殊函数并插值平面属性[Oberman和Siu, 2005]。
SFU(special function unit):一个用于计算特殊函数和插值平面属性的硬件单元。
Oberman, S. F. and M. Y. Siu [2005]. “A High-Performance Area- Effi cient Multifunction Interpolator,” Proc.
Seventeenth IEEE Symp. Computer Arithmetic , 272–79.
有多种方法可用于硬件中实现特殊函数的计算。研究表明,基于增强型极小极大近似法的二次插值是一种非常高效的硬件函数逼近方法,适用于倒数、倒数平方根、 l o g 2 x log_2x log2x、 2 x 2^x 2x、 s i n ( x ) sin(x) sin(x)和 c o s ( x ) cos(x) cos(x)等多种函数的近似。
我们可以简述SFU二次插值的方法:对于一个具有n位有效数字的二进制输入操作数
X
X
X,其有效数字被划分为两部分,
X
u
X_u
Xu是包含m位的高位部分,
X
l
X_l
Xl则是包含n-m位的低位部分。高位部分的m位
X
u
X_u
Xu用于查询一组三个查找表,从而返回三个有限字长系数
C
0
C0
C0、
C
1
C1
C1和
C
2
C2
C2。每个要近似的函数都需要一套独特的查找表。这些系数用于在区间
X
u
≤
X
<
X
u
+
2
−
m
X_u ≤ X < X_u + 2^{−m}
Xu≤X<Xu+2−m内通过求解以下表达式来近似给定函数f(X):
f
(
x
)
=
C
0
+
C
1
∗
(
X
−
X
u
)
+
C
2
∗
(
X
−
X
U
)
2
f(x) = C_0 + C_1 * (X - X_u) + C_2*(X - X_U)^2
f(x)=C0+C1∗(X−Xu)+C2∗(X−XU)2
这种方法利用高位部分的信息从查找表获取系数,并结合低位部分的具体数值,通过二次多项式插值的方式快速估算出特定函数的值。
每个函数估计的精度通常可以达到22到24位有效数字的水平。图B.6.1展示了这些函数近似的统计实例。按照IEEE 754标准,除法和平方根等运算需要满足精确舍入的要求;然而,在许多GPU应用中,并不要求严格符合这一标准。相反,对于这类应用而言,更高的计算吞吐量比最后一比特的精度更为重要。因此,针对SFU特殊函数,CUDA数学库提供了两种版本的函数:一种是保证全精度计算的版本,另一种则是采用SFU指令精度的快速计算版本,以适应不同的性能和精度需求。
函数 | 输入区间 | 精度 | ULP误差 | 四舍五入 | 单调性 |
---|---|---|---|---|---|
1 / x 1/x 1/x | [1,2) | 24.02 | 0.98 | 87 | yes |
1 / ( x ) 1/\sqrt(x) 1/(x) | [1,4) | 23.40 | 1.52 | 78 | yes |
2 x 2^x 2x | [0,1) | 22.51 | 1.41 | 74 | yes |
l o g 2 x log_2x log2x | [1,2) | 22.57 | N/A | N/A | yes |
s i n / c o s sin/cos sin/cos | [0, π / 2 \pi/2 π/2) | 22.47 | N/A | N/A | no |
N/A :不适用
ULP:最后一步的单位。
GPU中的另一个专门算术操作是属性插值。关键属性通常在构成待渲染场景的几何体顶点上指定,例如颜色、深度以及纹理坐标。为了确定每个像素位置处的属性值,需要在(x, y)屏幕空间中对这些属性进行插值。在(x, y)平面上,给定属性 U U U的值可以通过如下形式的平面方程来表达:
U ( x , y ) = A ⋅ x + B ⋅ y + C U(x, y) = A·x + B·y + C U(x,y)=A⋅x+B⋅y+C
其中,A、B 和 C 是与属性 U 相关联的插值参数。这些插值参数 A、B 和 C 都是以单精度浮点数表示的。
考虑到像素着色器处理器中既需要函数评估器(用于计算诸如由 SFU 提供的特殊函数)又需要属性插值器(负责在屏幕上的 (x, y) 坐标对各个关键属性值进行插值),为了提升效率,可以设计一种单一的 SFU(流式多指令流单元(Streaming Multi-Processor Unit) 或者 Shader Function Unit,根据上下文可能有不同的具体指代),使其能够同时执行这两个功能。值得注意的是,无论是函数评估还是属性插值,两者都采用了乘积和运算来实现插值,并且在两个功能中需要求和的项的数量是非常相近的。通过这样的插值过程,GPU能够在像素级别生成连续和平滑的视觉效果。
纹理操作
纹理映射和滤波是GPU中另一组重要的专门浮点运算操作。在纹理映射中涉及的操作主要包括以下步骤:
接收当前屏幕像素(x, y)对应的纹理坐标(s, t),其中s和t均为单精度浮点数。
- 计算细节层次(level of detail),以确定正确的纹理MIP贴图层级。
- 计算三线性插值的比例因子。
- 根据选定的MIP贴图层级调整纹理坐标(s, t)。
- 访问内存并检索所需的纹理元素(texels)。
- 对检索到的纹理元素执行滤波操作。
纹理映射在全速运行时需要大量的浮点计算,其中大部分计算可以在16位半精度下完成。例如,GeForce 8800 Ultra除了常规的IEEE单精度浮点指令外,还能提供大约500 GFLOPS的专有格式浮点计算能力,用于执行纹理映射指令。关于纹理映射和滤波更详细的信息,可参考Foley和van Dam [1995]的著作。
MIP贴图(MIP-map)源自拉丁短语“multum in parvo”,意为“小空间中的大量信息”。MIP贴图包含了一系列预先计算的不同分辨率的图像,用于提高渲染速度并减少失真现象。
Foley , J. , A. van Dam , S. Feiner , and J. Hughes [ 1995 ]. Computer Graphics: Principles and Practice, second
edition in C , Addison-Wesley , Reading, MA .
性能
浮点加法和乘法运算的硬件实现了完全流水线化设计,通过优化延迟时间以平衡延迟与硬件面积。尽管如此,在流水线结构中,特殊函数(如倒数、倒数平方根等)的吞吐率相比浮点加法和乘法运算较低。在现代GPU中,一般一个SFU由四个SP核心共享,此时特殊函数的吞吐率为浮点运算速度的四分之一左右,这是典型的表现。
相比之下,CPU对于类似的特殊函数(如除法和平方根运算)虽然能提供更准确的结果,但其吞吐率通常显著低于GPU。此外,属性插值硬件也通常是完全流水线化的,以确保像素着色器能够全速运行。
双精度
较新的GPU,例如Tesla T10P,也支持在硬件层面执行IEEE 754 64位双精度浮点运算。双精度浮点数的标准算术运算包括加法、乘法以及不同浮点数和整数格式之间的转换。2008年版的IEEE 754浮点数标准中包含了对融合乘加(Fused Multiply-Add, FMA)操作的规定,正如第三章所述。FMA操作一次性完成浮点数的乘法和加法,仅进行一次舍入。这种融合乘法和加法操作在中间计算过程中保持了完全的精度,这使得涉及产品累积的浮点数计算(包括点积、矩阵乘法和多项式求值)能够更加精确。FMA指令还能够实现精确舍入除法和平方根的有效软件实现,从而无需硬件级别的除法或平方根单元。
双精度硬件FMA单元实现了64位加法、乘法、转换以及FMA自身操作。双精度FMA单元的架构允许在输入和输出上均支持全速的非规范化数运算。图B.6.2显示了一个FMA单元的框图。
如图B.6.1所示,A和B的尾数部分相乘形成一个106位的乘积,结果暂存于带进位保存形式。与此同时,53位加数C条件反转并与其对齐至106位乘积。接着,通过一个161位宽的进位保存加法器(Carry-Save Adder, CSA),将106位乘积的和与携带结果与对齐后的加数相加。随后,进位保存输出通过一个进位传播加法器(Carry-Propagate Adder)相加以产生一个未舍入的、无冗余的二补数形式的结果。结果根据条件进行重新补充,以便以符号-模形式返回结果。补充后的结果被归一化,然后再按照目标格式进行舍入,以适应目标格式大小。