GPU编程和流式多处理器(四)
GPU编程和流式多处理器(四)
3.2. 单精度(32位)
单精度浮点支持是GPU计算的主力军。GPU已经过优化,可以在此数据类型上原生提供高性能,不仅适用于核心标准IEEE操作(例如加法和乘法),还适用于非标准操作(例如对先验的近似(例如sin()和log()))。32位值与整数保存在同一寄存器文件中,因此单精度浮点值和32位整数(使用__float_as_int()和__int_as_float())之间的强制转换是免费的。
加法,乘法和乘加
编译器自动将浮点值的+,–和*运算符转换为加,乘和乘加指令。所述__fadd_rn()和__fmul_rn()内部函数可以被用于加入抑制融合和乘法操作进入乘加指令。
对等与除法
对于具有2.x或更高版本的计算能力的设备,使用--prec-div = true编译代码时,除法运算符符合IEEE规定。对于具有计算能力1.x的设备或对于具有计算能力2.x的设备,当使用--prec-div = false编译代码时,除法运算符和__fdividef(x,y)具有相同的精度,但精度为2 126 <y <2 128,__fdividef(x,y)的结果为零,而除法运算符的结果为正确。另外,对于2 126 <y <2 128,如果x为无穷大,则__fdividef(x,y)返回NaN,而除法运算符返回无穷大。
先验者(SFU)
SM中的特殊功能单元(SFU)实现了六个常见超越功能的快速版本。
- 正弦和余弦
- 对数和指数
- 倒数和倒数平方根
(摘自有关Tesla架构论文)摘录了受支持的操作和相应的精度。SFU并不能完全实现精度,但是它们可以很好地近似于这些功能,并且速度很快。对于比优化的CPU等效端口(例如25倍或更多)快得多的CUDA端口,代码很可能依赖SFU。
SFU精度
给出的内在函数访问SFU。指定--fast-math编译器选项将使编译器将常规C运行时调用替换为上面列出的相应SFU内部函数。
__saturate(x)如果x <0返回0 ,如果x> 1返回1,否则返回x。
3.3.双精度(64位)
带有SM 1.3的CUDA中添加了双精度浮点支持(最早在GeForce GTX 280中实现),而SM 2.0则提供了大大改进的双精度支持(功能和性能)。CUDA对双精度的硬件支持具有全速异常功能,并且从SM 2.x开始,符合IEEE 754 c的本机融合乘法加法指令(FMAD)。2008年,仅执行一个舍入步骤。FMAD除了是本质上有用的操作之外,还可以使某些功能(与Newton-Raphson迭代收敛)完全准确。
与单精度运算一样,编译器会自动将标准C运算符转换为乘法,加法和乘加指令。所述__dadd_rn()和__dmul_rn()内部函数可以被用于加入抑制融合和乘法操作进入乘加指令。
3.4.半精度(16位)
对于5位指数和10位有效数,半值具有足够的精度以用于HDR(高动态范围)图像,并可用于保存其他不需要浮点精度的值,例如角度。半精度值仅用于存储,而不用于计算,因此硬件仅提供指令以转换为32位或从32位转换。13这些指令以__halftofloat()和__floattohalf()内部函数公开。
float __halftofloat(unsigned short);
unsigned short __floattohalf(float);
这些内部函数使用unsigned short,因为C语言尚未标准化半浮点类型。
3.5. 案例研究:浮动→转换一半
研究float → half转换操作是了解浮点编码和舍入细节的有用方法。因为这是一个简单的一元运算,所以我们可以专注于编码和舍入,而不会被浮点运算的细节和中间表示的精度所分散。
当从float转换为half时,对于任何太大而无法表示的float的正确输出是half infinity。任何太小而不能代表一半(甚至是反常的一半)的浮点数都必须固定为0.0。舍入为0.0的一半的最大浮点数为0x32FFFFFF或2.98 -8,而舍入为无穷大的一半的最小浮点数为65520.0。此范围内的float值可以转换为一半通过传播符号位,重新偏置指数(因为float的8位指数偏差为127,一半的5位指数偏差为15),然后将float的尾数四舍五入到最接近的一半尾数。在所有情况下,舍入都是简单的,除非输入值恰好落在两个可能的输出值之间。在这种情况下,IEEE标准指定四舍五入到“最近的偶数”值。在十进制算术中,这意味着四舍五入为1.5到2.0,但也四舍五入为2.5到2.0,以及(例如)四舍五入到0.5到0.0。
清单3显示了一个C例程,该例程完全复制了CUDA硬件实现的浮点到一半转换操作。变量exp和mag包含输入指数和“幅值”,尾数和指数以及被屏蔽的符号位。可以对幅度执行许多操作,例如比较和舍入操作,而无需分离指数和尾数。
清单3中使用的宏LG_MAKE_MASK创建具有给定位数的掩码:#define LG_MAKE_MASK(bits)((1 << bits)-1)。甲挥发性联合用于治疗相同的32位值作为浮子和无符号整型; 诸如*((float *)(&u))之类的习惯用法不是可移植的。该例程首先传播输入符号位并将其屏蔽掉。
提取幅度和指数后,该函数处理输入浮点为INF或NaN的特殊情况,并尽早退出。请注意,INF是带符号的,但NaN具有规范的无符号值。第50–80行将输入浮点值钳位到与可表示的半值相对应的最小值或最大值,然后重新计算钳位值的大小。不要被构造f32MinRInfin和f32MaxRf16_zero的复杂代码所迷惑;它们是分别具有值0x477ff000和0x32ffffff的常量。
例程的其余部分处理输出正常和异常的情况(输入异常在前面的代码中被钳位,因此mag对应于正常float)。与钳位代码一样,f32Minf16Normal是一个常量,其值为0x38ffffff。
要构建法线,必须计算新的指数(第92和93行),正确舍入的10位尾数将移入输出。要构造非正规数,必须将隐式1与输出尾数进行“或”运算,然后将所得尾数偏移与输入指数对应的量。对于法线和非法线,输出尾数的舍入分两步完成。舍入是通过添加一个1的掩码来实现的,该掩码的末尾刚好等于输出的LSB,如图3所示。

图3圆角(一半)。
如果输入的位12置1,则此操作将增加输出尾数;否则,将增加输出尾数。如果输入尾数全为1,则溢出会导致输出指数正确递增。如果我们向此调整的最高有效位再加1,我们将获得小学风格的四舍五入,其中平局决胜数更大。取而代之的是,即使设置了舍入到最接近值,如果设置了10位输出的LSB,则我们有条件地增加输出尾数(图4)。请注意,这些步骤可以按任何顺序执行,也可以以许多不同的方式重新制定。

图4舍入到最接近的偶数(一半)。
实际上,开发人员应使用__floattohalf()内在函数将float转换为一半,编译器会将其转换为单个F2F机器指令。提供此示例例程的目的纯粹是为了帮助理解浮点布局和舍入。同样,检查所有INF / NAN和非标准值的特殊情况代码有助于说明IEEE规范的这些功能自诞生以来就一直引起争议:它们使硬件变慢,成本更高,或者由于增加的硅片面积和工程设计而使硬件变慢验证工作。
在本书随附的代码中,清单3中的ConvertFloatToHalf()例程被合并到名为float_to_float16.cu的程序中,该程序针对每个32位浮点值测试其输出。
3.6. 数学库
CUDA包括一个以C运行时库为模型的内置数学库,但有一些小区别:CUDA硬件不包括舍入模式寄存器(取而代之的是,舍入模式是按指令编码的),因此14作为引用当前舍入模式的rint(),始终舍入为最接近值。此外,硬件不会引发浮点异常。异常运算的结果(例如,取负数的平方根)将编码为NaN。
表13列出了数学库函数以及每个函数的最大误差(单位为ulps)。在float上操作的大多数函数的函数名称后均带有“ f”,例如,计算正弦函数的函数如下。双*对于Bessel函数jnf(n,x)和jn(n,x),对于n = 128,最大绝对误差分别为2.2x10 -6
和5x10
-12。
**对于Bessel函数ynf(n,x),| x |的误差为⌈22.5n⌉;否则,对于n = 128,最大绝对误差为2.2x10 -6。对于yn(n,x),最大绝对误差为5X10 -12。
- 在SM 1.x类硬件上,由于中间尾数的截断,合并到FMAD指令中的加法和乘法运算的精度将受到影响。
- 在SM 2.x和更高版本的硬件上,开发人员可以通过指定--prec-div = true将错误率降低到0 ulps。
- 对于float,| x | <8的错误是9 ulps;否则,最大绝对误差为2.2X10 -6。对于double,| x | <8的误差为7 ulps;对于x,误差为7 ulps。否则,最大绝对误差为5×10-12。
- 在间隔–10.001,–2.264内,lgammaf()的错误大于6。在间隔–11.001,–2.2637内,lgamma()的错误大于4。
- 在SM 2.x和更高版本的硬件上,开发人员可以通过指定--prec-sqrt = true将此错误率降低至0 ulps。
这些在表13中表示为例如sin [f]。
转换为整数
根据C运行时库的定义,nearint()和rint()函数使用“当前舍入方向”将浮点值舍入到最接近的整数,在CUDA中,该值始终舍入到最接近偶数。在C运行时中,nearingint()和rint()的区别仅在于对INEXACT异常的处理。但是,由于CUDA不会引发浮点异常,因此函数的行为相同。
round()实现小学风格的舍入:对于整数之间中间的浮点值,输入始终从零舍入。NVIDIA建议不要使用此功能,因为它会扩展到八(8)条指令,而不是rint()及其变体的一条指令。trunc()截断或“砍除”浮点值,四舍五入为零。它编译为一条指令。
分数和指数
flost frexpf(float x,int * eptr);
frexpf()将输入分解为范围为[0.5,1.0)的浮点有效数和2的整数指数,使得
x =有效数·2指数
float logbf(float x);
logbf()从x提取指数,并将其作为浮点值返回。它等效于floorf(log2f(x)),但速度更快。如果x为非正规数,则logbf()返回x标准化后的指数。
float ldexpf(float x,int exp);
float scalbnf(float x,int n);
float scanblnf(float x,long n);
ldexpf(),scalbnf()和scalblnf()都通过直接操纵浮点指数来计算x2 n。
浮点余数
modff()将输入分为小数和整数部分。
float modff(float x,float * intpart);
返回值是x的小数部分,具有相同的符号。
restderf(x,y)计算x除以y的浮点余数。返回值为xn * y,其中n为x / y,四舍五入到最接近的整数。如果| x –ny | = 0.5,则选择n为偶数。
float remquof(float x, float y, int *quo);
计算余数并传回积分商x / y的低位,其符号与x / y相同。
贝塞尔函数
n阶贝塞尔函数与微分方程有关

n可以是一个实数,但是对于C运行时而言,它是一个非负整数。
该二阶常微分方程的解结合了第一类和第二类的贝塞尔函数。

数学运行时函数jn [f]()和yn [f]()分别计算J n(x)和Y n(x)。对于n = 0和n = 1的特殊情况,j0f(),j1f(),y0f()和y1f()计算这些函数。
伽玛功能
伽马函数Γ是阶乘函数的扩展,其自变量向下移动1,变为实数。它具有多种定义,其中之一如下。

该函数的增长如此之快,返回值损失精度相对较小的输入值,所以该库提供了lgamma函数()函数,该函数返回伽玛函数的自然对数,除了tgamma()(“真伽马”)功能。
GPU编程和流式多处理器(四)的更多相关文章
- GPU编程和流式多处理器(三)
GPU编程和流式多处理器(三) 3. Floating-Point Support 快速的本机浮点硬件是GPU的存在理由,并且在许多方面,它们在浮点实现方面都等于或优于CPU.全速支持异常可以根据每条 ...
- GPU编程和流式多处理器(二)
GPU编程和流式多处理器(二) 2. 整数支持 SM具有32位整数运算的完整补充. 加法运算符的可选否定加法 乘法与乘法加法 整数除法 逻辑运算 条件码操作 to/from浮点转换 其它操作(例如,S ...
- GPU编程和流式多处理器(六)
GPU编程和流式多处理器(六) 5. 纹理和表面 读取和写入纹理和表面的指令,所引用的隐式状态,比其他指令要多得多.header中包含诸如基地址,尺寸,格式和纹理内容的解释之类的参数,该header是 ...
- GPU编程和流式多处理器(五)
GPU编程和流式多处理器(五) 4. 条件代码 硬件实现了"条件代码"或CC寄存器,其中包含用于整数比较的常用4位状态向量(符号,进位,零,溢出).可以使用比较指令(例如ISET) ...
- GPU编程和流式多处理器
GPU编程和流式多处理器 流式多处理器(SM)是运行CUDA内核的GPU的一部分.本章重点介绍SM的指令集功能. 流式多处理器(SM)是运行我们的CUDA内核的GPU的一部分.每个SM包含以下内容. ...
- GPU编程和流式多处理器(七)
6. 杂项说明 6.1. warp级原语 warp作为执行的原始单元(自然位于线程和块之间),重要性对CUDA程序员显而易见.从SM 1.x开始,NVIDIA开始添加专门针对thread的指令. Vo ...
- GPU 编程入门到精通(四)之 GPU 程序优化
博主因为工作其中的须要,開始学习 GPU 上面的编程,主要涉及到的是基于 GPU 的深度学习方面的知识,鉴于之前没有接触过 GPU 编程.因此在这里特地学习一下 GPU 上面的编程.有志同道合的小伙伴 ...
- GPU 编程入门到精通(五)之 GPU 程序优化进阶
博主因为工作其中的须要,開始学习 GPU 上面的编程,主要涉及到的是基于 GPU 的深度学习方面的知识.鉴于之前没有接触过 GPU 编程.因此在这里特地学习一下 GPU 上面的编程. 有志同道合的小伙 ...
- GPU 编程入门到精通(三)之 第一个 GPU 程序
博主因为工作其中的须要.開始学习 GPU 上面的编程,主要涉及到的是基于 GPU 的深度学习方面的知识,鉴于之前没有接触过 GPU 编程,因此在这里特地学习一下 GPU 上面的编程.有志同道合的小伙伴 ...
随机推荐
- 一个DDOS病毒的分析(二)
一.基本信息 样本名称:hra33.dll或者lpk.dll 样本大小: 66560 字节 文件类型:Win32的dll文件 病毒名称:Dropped:Generic.ServStart.A3D47B ...
- hdu4370 比较抽象的最短路
题意: 给你一个n*n的矩阵,然后让咱们构造另一个n*n的矩阵,构造的矩阵有如下要求, 1.X12+X13+...X1n=1. 2.X1n+X2n+...Xn-1n=1. 3.for ea ...
- LAMP环境搭建一个Discuz论坛
LAMP是Linux+Apache+Mysql/MariaDB+Perl/PHP/Python的简称.一组常用来搭建动态网站或者服务器的开源软件,本身都是各自独立的程序,但是因为常被放在一起使用,拥有 ...
- hdu2276 矩阵构造
题意: 给了n个灯泡的状态,他们绕成一个环,0是灭,1是亮,每一秒灯泡的状态都会改变,规则是如果当前这个灯泡的左边的灯泡当前是状态1,那么下一秒当前的这个灯泡状态就改变0变1,1变0,最后问 ...
- React-列表 & Key
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title&g ...
- ExtJS4中Ext.onReady、Ext.define、Ext.create
1.Ext.onReady 说明:onReady内的语句块会在页面上下文加载后再执行. 2.Ext.define 说明:创建类,可以继承其他类,也可以被继承. 例子1: 1 <script ty ...
- LinkedList作为栈和队列的使用
最近在LeekCode用java写一些算法时,经常遇到要使用栈和队列结构,使用栈的话,Stack已经不被推荐使用了,所以栈和队列我们通常都是用LinkedList这种双链表结构实现.Linkedlis ...
- Nmap浅析(1)——主机发现
主机发现 当网络不通时,Ping一下网关来检查网关是否正常.当测试的目标是一个网络时,其中在线的主机才是目标,那么就需要技术来找出这些目标. 技术的方法大都与TCP/IP协议族中的协议相对应. ...
- gparted 当分区空间大于1T 用gparted分区
lsblkfdisk -lparted -s /dev/sdb mklabel msdos parted -s /dev/sdb mkpart primary 0 100%lsblk dfparted ...
- Ubuntu 15.04下安装Docker
最近听说Docker很火,不知道什么东西,只知道是一个容器,可以跨平台.闲来无事,我也来倒弄倒弄.本文主要介绍:Ubuntu下的安装,以及基本的入门命令介绍:我的机器是Ubuntu 15.04 64位 ...