DAY79:阅读 Compute Capabilities

H. Compute Capabilities

The general specifications and features of a compute device depend on its compute capability (see Compute Capability).

Table 13 gives the features and technical specifications associated to each compute capability.

Floating-Point Standard reviews the compliance with the IEEE floating-point standard.

Sections Compute Capability 3.x, Compute Capability 5.x, Compute Capability 6.x, and Compute Capability 7.x give more details on the architecture of devices of compute capability 3.x, 5.x, 6.x, and 7.x respectively.

H.1. Features and Technical Specifications

.2. Floating-Point Standard

All compute devices follow the IEEE 754-2008 standard for binary floating-point arithmetic with the following deviations:

  • There is no dynamically configurable rounding mode; however, most of the operations support multiple IEEE rounding modes, exposed via device intrinsics;
  • There is no mechanism for detecting that a floating-point exception has occurred and all operations behave as if the IEEE-754 exceptions are always masked, and deliver the masked response as defined by IEEE-754 if there is an exceptional event; for the same reason, while SNaN encodings are supported, they are not signaling and are handled as quiet;
  • The result of a single-precision floating-point operation involving one or more input NaNs is the quiet NaN of bit pattern 0x7fffffff;
  • Double-precision floating-point absolute value and negation are not compliant with IEEE-754 with respect to NaNs; these are passed through unchanged;

Code must be compiled with -ftz=false, -prec-div=true, and -prec-sqrt=true to ensure IEEE compliance (this is the default setting; see the nvcc user manual for description of these compilation flags).

Regardless of the setting of the compiler flag -ftz,

  • Atomic single-precision floating-point adds on global memory always operate in flush-to-zero mode, i.e., behave equivalent to FADD.F32.FTZ.RN,
  • Atomic single-precision floating-point adds on shared memory always operate with denormal support, i.e., behave equivalent to FADD.F32.RN.

In accordance to the IEEE-754R standard, if one of the input parameters to fminf(), fmin(), fmaxf(), or fmax() is NaN, but not the other, the result is the non-NaN parameter.

The conversion of a floating-point value to an integer value in the case where the floating-point value falls outside the range of the integer format is left undefined by IEEE-754. For compute devices, the behavior is to clamp to the end of the supported range. This is unlike the x86 architecture behavior.

The behavior of integer division by zero and integer overflow is left undefined by IEEE-754. For compute devices, there is no mechanism for detecting that such integer operation exceptions have occurred. Integer division by zero yields an unspecified, machine-specific value.

http://developer.nvidia.com/content/precision-performance-floating-point-and-ieee-754-compliance-nvidia-gpus includes more information on the floating point accuracy and compliance of NVIDIA GPUs.

文备注/经验分享:

本章表格主要说明了各种计算能力了下的卡的"特性"的异同。请注意该表格列出了目前所有支持的计算能力的卡的对比情况.而计算能力也往往是一般用户口中的"代", 某代某代的卡如何如何。 它决定了用户在编写代码的时候, 所能用到的特性; 有些特性是如此重要, 也往往决定了用户购买的选择.例如说, 用户的代码需要动态的细分处理, 需要使用动态并行(DP)的能力, 那么根据此表格, 显然就不能上3.0的一代Kepler.例如说, 前几天我们论坛有个帖子, 有一个很奇特的需求, 要对一张图片的所有像素的值进行求和. 我们当时在论坛提醒用户, 需要注意整数类型的累加后的溢出情况(例如使用16-bit, 32-bit等等的整数类型), 则该用户基于精度考虑, 可能会选择使用浮点类型,特别的, 他可能会选择double来进行简单的原子累加(block内部进行无原子操作的规约累加, 然后就地在global memory可选的进行double累加原子操作, 规避二次kernel启动), 则这个特性需要他至少购买计算能力6.0, 也就是Pascal这一代或者更高的卡,才能完成他的设计目标. 但是需要说明的是, 往往越新的一代卡, 折算到单位计算性能下的单价越贵.例如很多人现在已经拿到了2080(例如我们的七月, 括号里的请删除),它主打的是新的Tensor Core的特色(不用考虑RT Core, 用CUDA的人暂时用不到它), 而单精度的单位GFlops下的性能却没有显著提升, 价格却高了不少,则用户实际上可以考虑购买上一代的1080之类的卡, 甚至同样的预算能上1080Ti, 从而取得更好的性价比. 这样根据实际项目对计算能力的主要特性的取舍不同, 用户可以作出一定预算下的正确采购要求.

本表格需要特别注意的是两个地方, 一个是半精度的支持, 一个是Tensor Core, 本章节的表格只作出了支持, 或者不支持的说明, 但实际上这两点在支持的情况下, 外加计算能力5.3的特例(TX1)。 但这只是支持情况, 在支持的卡上, 至少保证了200%速率(相比该卡上的单精度速率)的半精度通用性能,但是在支持Tensor Core的卡上(主要是7.0和7.5, 后者也就目前我们说的图灵, 包括你用的2080), 具有非常显著的专用半精度性能, 也就是在矩阵乘法的情况下, 具有800%的半精度性能提升(同样和该卡的单精度性能相比), 我们都知道, 特别的, 在深度学习的情况下, 主要就用两个操作,一个是矩阵乘法, 一个是卷积, 而前者又可能占用了大头, 所以哪怕在通用的支持FP16半精度的情况下, 用户也应该尽量考虑使用7.0+, 虽然本表格将5.3/6.x/7.x都简单的划成了支持半精度, 但用户从实用的角度, 应当考虑购买新卡(或者新的嵌入式计算设备,例如还没有出来的Xavier? 而不是5.X/6.X的TX1/TX2)。 类似的, 该表格还对7.X的Tensor Core支持统一划成了支持, 但实际上这两代的Tensor Core差别非常大,后一代的Tensor Core还支持INT8和INT4计算, 具有非常显著的性能提升(例如INT8是1600%), 用户在特定的需求的情况下, 例如密集的INT8计算, 应当只能考虑7.5的图灵卡,(也包括一些挖矿用户, 但好在现在挖矿不景气, BTM之类的币种正在被人忽视中, 否则Turing又会暴涨一段时间, 万幸)。 此外, 考虑到现在的需要用的卡只有6.X+了, 该表格还没有给出6.X+的主要特性: (1)支持深度学习指令(INT8), 至少具有400%的性能(例如__dp4a()函数, 手册没说), 该函数在Pascal(6.1)+上被支持, 而且比较通用(相比Tensor Core 2代的1600%极度专用化的INT8加速), 很多图像处理的用户应当考虑这点. (2)增强的Unified Memory, Pascal+的Unified Memory得到了极大增强, 但是这里也只是简单的和3.X/5.X等等一起, 简单的标注了支持. 实际上Unified memory也可以划成至少两代, 3.X/5.X上的是初级的第一代, 6.X起具有非常显著的特性提升. 这个等我们到了后面的Unified Memory再详细说. 所以用户对这个计算能力导致的特性异同的表格, 应当注意这种粗略的说法, 从而在选购和应用的时候作出更恰当的判断.(实际上, 6.1的家用Pascal(不包含GP100), 这两个特性是最主要的特性了. 去掉这两个特性的Pascal等于只是Maxwell---但制程作出了优化而已(从28nm)) 第二个表格, 则说明了在CUDA使用的情况下, 一些硬件本身的限制情况.主要的大部分都是相同的(以前的计算能力还有复杂的启动block形状的限制不同, grid内部的blocks数量的限制不同, 这些现在如今都被取消了) 你可以看到, 标准的1024个线程的最大block大小, 最多(2^31 - 1)的X方向的Blocks数量,这些都减轻了用户的记忆负担, 和提高了程序的设计时候的思考量. 我说一下这个表格对普通的CUDA用户所主要造成的影响: (1)Shared Memory / L1大小, 根据我们之前的章节, Shared Memory等于一种随机访问性能好(不同Banks的不同深度可以同时对用户提供数据服务), 用户可控的L1 Cache,而L1是全自动的. 所以不同的计算能力的情况下的不同的Shared Memory大小往往影响性能(一般情况下, 越大越好),但是这里需要指出几个例外,一个是计算能力3.7, 它具有超常的非常巨大的Shared Memory(112KB/SM), 而目前已知的3.7的卡只有K80,K80是NV对失败的Kepler一代所作出的最后努力, 它将2个SM的资源累加到了一个SM上,所以你会看到在K80上具有惊人的Shared Memory大小.同理你会看到所有的寄存器大小都是256KB(也就是本表格的64K个),但只有K80是512KB的惊人大小, 这同样是因为它将2个SM的资源合成了1个SM. 所以说, 如果Kepler一代到现在还有价值的卡, 只有K80了. 然后同样的你会看到7.X的Shared Memory似乎比较小.但是这里需要指出的是, 7.X的SM里面(Volta/Turing), SP数量只有减半的, 它们的大小实际上等同于乘以2,也就是相当于192KB和128KB的等价效果(而不是96KB和64KB), 这点需要特别注意. 类似的, 专业卡的6.0(GP100), 看上去Shared Memory还不如6.1的家用卡大,但实际上它的SM里面的SP也是减半的, 同样的SP规模折算下, 它具有双倍的资源,实际上6.0和6.1对比, 等价于128KB vs 96KB, 而不是64KB vs 96KB. 这点用户也需要知道. 此外, 部分嵌入式的设备, 具有较少的寄存器数量, (例如5.3和6.2的TX1/TX2,只有标准的每个block能用的寄存器数量的一半),这导致你至少需要在这些设备上, 每个SM能上2个block, 才能有效的利用资源. 这点也需要注意一下. 注意最后该表格给出了一个最大512M条指令, 每个kernel的限制.这个实际上是很搞笑的, 哪怕在每条指令8B大小的情况下(例如6.X, 实际上稍微大一点), 512M条指令的Kernel, 编译出来也有4GB大了,如果是7.X, 16B每条指令, 则有8GB那么大, 我从来没有见过如此巨大的kernel.但是考虑到总是有人喜欢问:"我的kernel能写的多大" "我的kernel写了这么长没事吗"这样的问题,该表格给出这种指标, 也很搞笑和无奈的.所以也挺好的. 此外, 关于本表格的能绑定的surface数量是16这个问题, 实际上是一个历史问题, 具体可以参考我们之前章节的bindless, 无需绑定的那种surface, texture之类的, 后者没有这种限制.也没有每个绑定的纹理在启动每个kernel所带来的固定额外开销.

关于第三部分, N卡所遵循的浮点标准(IEEE-754) 这里主要说明了几点: 没有全局可以配置的动态的圆整(取整)模式,很多处理器上(例如X87?), 具有用户可调的浮点取整模式, 但是N卡作为GPU,没有这种全局的模式, 而是每条指令都可以自行设定.回到具体的CUDA C用户, 用户可以注意到有些intrinsic functions具有_rz()之类的结尾, 就是起到这种作用的(欢迎参考我们之前的章节中说的4大圆整模式) 此外, 本章节说的NaN的问题, 部分处理器带有多种NaN处理模式, 很多人以前在我们的群里, 三天两头的发, 我计算出错了,出现了一些奇特的值(数值printf的时候带有一些字母) 这里需要注意Quiet NaN和Signaling NaN的区别, 后者还是NaN(异常值), 只是带有额外的描述信息.(例如你0/0时候的结果,就属于NaN) 本章节说明了, 如果单精度的运算过程中, 输入运算数据有异常值NaN的, 结果都将只是Quiet NaN,具体的细节可以自行搜索一下.实际上wikipedia上有很详细的描述, 如果我没记错的话. 然后还说了双精度运算的时候, NaN数据会被Passthrough, 直接传递异常值的负载信息.这点也需要注意.但无论如何, 出现了NaN, 并导致NaN在程序的多次迭代计算中传播开来,最后导致整体结果面目全非, 一般是用户的锅, 需要详细检查中间过程的结果数据, 和/或输入值的数据, 做好它们的异常值处理.此外, 关于C++ Exception, 我们的GPU目前并不支持计算过程中的exception报告, 而是会产生Inf/Nan/0这种数据,这也是很正常的选择, 因为同时在GPU上执行的数据执行是海量的, 如果像常规的CPU那样, 中途暂停一个或者多个kernel的计算, 报告CPU, 然后CPU单独处理, 将会是灾难性的, GPU啥都不用干了.你会得到海量的并发异常的. 本段落还说了, 可以全局的用某些参数来控制精度信息(你可以选择更高性能的, 但精度较低的; 和较慢的, 但精度较高的代码生成,这点在之前说过, 请参考我们之前的相关章节. 注意该章节提到的SFU部分) 然后这里强调的是, 原子操作有时具有较低的精度: 在global memory上的原子累加, 总是直接将denormals(或者叫subnormal number), 当成0来处理.而在Shared memory上的同样的累加, 支持这种接近0的subnormal number, 并不将它们当成0来处理.(将很接近0的subnormal值当成0来处理会损失精度),

忘记了subnormal numbers是什么了? 之前我们的章节提到过:当浮点数小到非常接近0的时候, 已经无法用原始的浮点格式(指数浮动)表示了, 此时为了尽量保存有效数字, 将转换成使用固定指数的定点数表示, 此时float之类的都将不再能保持23-bit这种有效数字位数, 但同时不将这些非常小的数值当成0处理, 而是尽量保存为定点位数, 有一位算一位, 从而尽量能够为用户保存精度。 当使用或者出现subnormal number的时候, 往往代表你需要转换到下一级更高精度的数值方式了, 例如half可以考虑改用float, float可以考虑改用double 而double可以考虑改用float128(目前N卡并不能直接支持它), 但是可以用整数模拟,Turing的并发INT/FP整数单元, 可以在你同时使用常规FP单元硬件直接计算Float之类的同时,还能上INT单元模拟double和float128计算, 显著提升性能, 并有效保护精度。 关于本段落里面的FADD.F32.FTZ.RN和.RN,实际上是PTX的内容, 有兴趣的用户可以看一下PTX手册(这本是CUDA C编程指南, PTX有非常详细的, 类似本书的另外一本)

有不明白的地方,请在本文后留言

或者在我们的技术论坛bbs.gpuworld.cn上发帖

原文发布于微信公众号 - 吉浦迅科技(gpusolution)

原文发表时间:2018-10-10

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大数据挖掘DT机器学习

利用word2vec对关键词进行聚类

按照一般的思路,可以用新闻ID向量来表示某个关键词,这就像广告推荐系统里面用用户访问类别向量来表示用户一样,然后就可以用kmeans的方法进行聚类了。不过对于新...

56110
来自专栏CDA数据分析师

如何高效地学好 R?

本文由知乎著名答主黄宝臣原创,CDA数据分析师已获得授权 学R主要在于5点三阶段: 第一阶段有一点:基础的文件操作(read.*,write.*)、数据结构知...

1965
来自专栏编程心路

人人都可以用C语言写推箱子小游戏

C语言,作为大多数人的第一门编程语言,重要性不言而喻,很多编程习惯,逻辑方式在此时就已经形成了。这个是我学习 C语言 后写的推箱子小游戏,自己的逻辑能力得到了提...

6064
来自专栏机器之心

教程 | 如何通过PyTorch上手Tensor Comprehensions?

选自pytorch 作者:Priya Goyal等 机器之心编译 参与:乾树、黄小天 Tensor Comprehensions 是一个降低高性能代码编写门槛的...

2987
来自专栏Linyb极客之路

浅谈黑盒测试和白盒测试

  从图中可以直接看出来,黑盒测试就当整个程序是个黑盒子,我们看不到它里面做了些什么事情,只能通过输入输出看是否能得到我们所需的来测试。而白盒测试可以当盒子是透...

1941
来自专栏华章科技

教程 | 如何优雅而高效地使用Matplotlib实现数据可视化

Matplotlib 能创建非常多的可视化图表,它也有一个丰富的 Python 工具生态环境,很多更高级的可视化工具使用 Matplotlib 作为基础库。因此...

642
来自专栏AI科技评论

用于规划的分层有限状态控制器| IJCAI2016杰出论文详解

导读:2016国际人工智能联合会议(IJCAI2016)于7月9日至7月15日举行,今年会议聚焦于人类意识的人工智能,本文是IJCAI2016杰出论文(Dist...

3234
来自专栏Leetcode名企之路

服务端开发95th 99th 是什么意思?

作者:滔滔不绝 链接:https://www.zhihu.com/question/20575291/answer/22814728 来源:知乎

921
来自专栏吉浦迅科技

DAY59:阅读 #pragma unroll

By default, the compiler unrolls small loops with a known trip count. The #pragm...

612
来自专栏ATYUN订阅号

使用50行Python教AI玩运杆游戏

嗨,大家好!今天我想展示如何使用50行Python代码教一台机器来平衡杆!我们将使用标准的OpenAI Gym作为我们的测试环境,并只使用numpy创建我们的智...

1563

扫码关注云+社区