找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 159|回复: 2

DAY79:阅读标准数学函数

[复制链接]
发表于 2018-8-27 13:40:48 | 显示全部楼层 |阅读模式
E. Mathematical Functions
The reference manual lists, along with their description, all the functions of the C/C++ standard library mathematical functions that are supported in device code, as well as all intrinsic functions (that are only supported in device code).
This appendix provides accuracy information for some of these functions when applicable.

E.1. Standard Functions
The functions from this section can be used in both host and device code.
This section specifies the error bounds of each function when executed on the device and also when executed on the host in the case where the host does not supply the function.
The error bounds are generated from extensive but not exhaustive tests, so they are not guaranteed bounds.
Single-Precision Floating-Point Functions
Addition and multiplication are IEEE-compliant, so have a maximum error of 0.5 ulp.
The recommended way to round a single-precision floating-point operand to an integer, with the result being a single-precision floating-point number is rintf(), not roundf(). The reason is that roundf() maps to an 8-instruction sequence on the device, whereas rintf() maps to a single instruction. truncf(), ceilf(), and floorf() each map to a single instruction as well.

Double-Precision Floating-Point Functions
The recommended way to round a double-precision floating-point operand to an integer, with the result being a double-precision floating-point number is rint(), not round(). The reason is that round() maps to an 8-instruction sequence on the device, whereas rint() maps to a single instruction. trunc(), ceil(), and floor() each map to a single instruction as well.



回复

使用道具 举报

 楼主| 发表于 2018-8-27 15:11:22 | 显示全部楼层
到了数学函数说明章节了.这里主要有几点需要说明的:
(1)CDUA C提供了一些除了运算符号(+, -, *, /这种等等)之外的, 用函数方式提供的数学运算操作.
其中有些和运算符号是相同功能的, 但可能带有不同的精度结果(有些比直接的运算符更快), 或者带有不同的圆整模式(例如_rz结尾的).而另外一些则是运算符号没有的(例如sinf).后者应当考虑直接使用它们, 而不是自行用基本运算计算出来, 自行的计算往往无法被编译器识别被特定的操作(例如sinf会利用SFU进行正弦函数的部分运算, 手工无法利用它).
(2)除了能在GPU中使用这些提供的数学函数, 还能在CPU端使用它们.也就是说, CUDA提供了设备端的数学运算支持外, 还扩充了你的CPU上的运算能力.
这里基本上可以分成3点:
-是原本CPU上有的操作, 例如刚才的sinf, 则这些函数提供了对应的GPU上的支持.
-原本CPU上没有的操作, 例如rsqrtf()函数(平方根的倒数),CUDA C不仅仅对它提供了GPU上的支持,还增加了CPU上的能力, 你也可以直接在CPU上使用它.
-一些函数在部分host平台上能用, 例如log2f(), exp2f(), 以及, hypotf(),它们要么是只能在Linux下用(GCC),要么只能使用最新的VS版本(例如VS 2017里面的VC),CUDA C则为它们提供了统一的支持:GPU上能用, CPU端无论何种平台(例如Windows, 例如低版本的VC), 都被CUDA增加补全了支持.这样提供了统一的跨平台的, 同时GPU和CPU端的广泛支持,节省了用户大量使用#if判断和手写代码的工作量. 还是很不错的.


注意这第二点说了, 一些运算精度/误差方面的信息, 对GPU上的函数是有效的. 而只对CPU上的, 由CUDA补充的那些同名函数有效.也就是说, 如果一个平台下如果host端的编译环境已经提供了某函数,而不是被CUDA补充上去的, 则不适用本章节(以及后续)说明的误差信息.
(3)谈论到一些运算的精度或者说误差, 则必须要注意它们是和谁比较的.我们目前适用的浮点运算规范,是IEEE 754, IEEE 754定义了一个"正确的圆整"概念. 也就是说,如果一个运算用手工计算结果是:1.3456789(我这里用10进制举例, 实际应当是2进制的), 如果只有6-bit小数. 则正确的圆整结果应当是1.345679,而不是1.345678之类的.也就是说, 正确的圆整应当会是能用这6位小数所表达的最接近的值.这种正确的圆整, 结果在0.5个ULP之类, 什么是ULP?来自EE的同学(例如lady我), 应当直接看成是类似LSB的概念(最低有效bit).小于等于0.5个ULP的就是当前有效数字位所能表达的最精确结果了(注意是和手工计算比较的.),很多资料也将这种情况定义成0个ULP(这种往往是和当前所能表示的结果进行比较的, 而不是手工计算结果),所以一般情况下, 看到0.5或者0 ULP, 说明已经是最精确的结果了.很多人对GPU运算曾经有过疑惑, 则可以参考一些运算的这个信息.
(前些年很多人怀疑GPU算这么快, 结果不精确吧. 本章节和后续内容很大程度上可以打消你的这个顾虑)
然后还需要说明的是,CUDA定义了4种圆整方式, 其中默认的就是这种尽量取最接近的值.但是还有另外3种硬件直接支持的圆整模式,向上取整(靠近正无穷+inf的方向),这种方式往往会在CUDA里看到_rp()这种后缀.例如1.234, 如果只能最后保留2位小数, 则是1.24.
这是第一种.
第二种则是考虑负无穷方向, -inf的取整, 也叫向下取整, 例如1.23456, 如果只能4-bit小数, 则结果是1.2345,一般在CUDA里看到以_rm()结尾的函数就是这种.注意这两种均和最接近的(10进值时候是4舍5入, 2进值是0舍1入)取整方式不同.然后还剩下则是靠近0方向取证.这种一般被写成_rz(), 很多CDUA函数都能发现这种结尾.也就是说, CUDA里一共有: _rp, _rm, _rz, 以及没有任何结尾.没有任何结果是向最接近的取整. 这是默认的.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-8-27 15:16:53 | 显示全部楼层
sisiy 发表于 2018-8-27 15:11
到了数学函数说明章节了.这里主要有几点需要说明的:
(1)CDUA C提供了一些除了运算符号(+, -, *, /这种等等 ...

记住这4种基本取整/圆整方式后, 本章节还提出了一个注意事项.一些函数, 它的运算要求取整要求不是这4种, 则会带来额外的, 甚至多很多的运算代价.例如本章节最后的段落给出了float和double的rintf和roundf()函数比较.前者rintf()是要求最精确的取整结果的, 而roundf()则要求一种"away from zero", 也就是远离0的取整方式, 大致相当于正数对+inf, 复数对-inf取整.因为roundf()这种取整函数不是CUDA硬件, 也就是不是你的GPU卡能直接支持的方式,它需要通过软件模拟, 例如一种可能的判断方式是判断范围, 然后改成其他运算方式, 或者增加一些修正量, 还要处理特殊值(NaN之类--这个以后再说),所以需要想当大的代价, 实际的代价可能roundf()需要8-10条指令, 而rintf()可能只需要1条指令.所以用户在使用这种的时候需要额外注意选取代价小, 精度高的.(但roundf()这种可能也有它的用途, 在一些数学运算上, 能尽量规避0可能会导致一些好处, 例如原本会导致n/0这种产生inf或者nan(0/0), 会现在更大概率产生更常规的数值. 以及, 也可能有其他更多用途. 毕竟lady不是从事数值运算方面的)。
以及需要补充的是, 注意rintf()在host上有两个版本:
rintf和nearbyintf,前者会产生浮点异常可以让用户自行捕获,后者则不会,然而GPU上并不支持这点, 所以rintf和nearbyintf实际上这里并无这个区别了(GPU上也没法支持, 这么大的同时进行中的浮点运算, 例如5120个SP的某GPU, 如果只有1/10000的概率会运算出来需要触发异常的数值结果,那么也将近2个GPU周期就要触发异常一次,这样GPU将寸步难行了. 时间都耗费在处理这上面了. 所以目前GPU并不支持的)。

4种基本圆整模式很重要.很多运算都需要特定的圆整模式, 而不是最接近值的.而且这4种基本模式大部分指令(例如编译出来的FADD.RZ指令)都能直接用后缀支持, 并不需要额外转换一次, 等于0代价。所以用户需要注意并利用这点. 写出满足自己用途和精度要求的代码.


回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

快速回复 返回顶部 返回列表