自cuda7.5开始我们可以直接用half(fp16)编程,理论上速度会比float快一倍左右。理想虽好,现实却比较骨感,在实际中会遇到各种坑,最终的结果却是不一定有收益,下面把自己在用half编程中踩过的坑记录一下。
1. half编程和计算能力密切相关

half编程要求GPU的计算能力要大于等于5.3,这就意味着大家很多GPU不支持此功能。例如,GTX 1050之前的GPU全不支持half计算,此外Tesla K和M系列也不支持half计算。可以通过该网页查看自己GPU的计算能力。即使我们的GPU支持half计算,性能也不一定高,当计算能力为6.1的时候,GPU的half计算能力几乎为0。对比不同计算能力的吞吐量我们可以发现当计算能力为6.1时,其16bit的算术运算能力只有2,显著小于其他计算能力。

注意,此处并不是写错了,而确实就是如此,英伟达官方也给出了解释:

It’s not an error.
    The design of the cc6.1 SM is different from the design of the cc6.0/cc6.2/cc7.x/cc5.3 SM in this respect.
    The throughput of FP16 on cc6.1 is relatively low. The reason for the existence of such a low throughput capability is for application compatibility. It is not a performance path on cc6.1
    Note that for parameter storage (as opposed to compute throughput) FP16 could still possibly be a “win” on cc6.1, in some cases, where memory traffic drives application performance. The 2:1 ratio of parameter storage density over FP32 means that in some cases it may be beneficial to store data in (packed) FP16 format, but convert on the fly to FP32 (for calculations) and back to FP16 (for storage). This assumes your app/algorithm can tolerate the numerical implications of parameter storage in FP16.

简单解释就是为了程序兼容性阉割了half计算能力。比较不幸的是,估计大家目前还在使用的GPU大部分还是计算能力6.1的,比如Tesla P4/P40,GTX 1080,TITAN X/Xp等。如果你还在用这些GPU就放弃通过使用half加速代码的想法吧。
2. float转half的位置和cuda版本相关

我们在代码中应用half的场景基本如下:在host端将float模型或者特征转换为half,然后将half模型或者特征传输到device端进行计算,计算完成后将half结果再传递到host端,最后在host端将half转换成float。但是在cuda9.2之前居然不支持这么做:因为在host端没有float2half函数,该函数只能在device端执行!这个设计真是有点反人类啊。如果你想在host端完成类型转换,请自行搜寻开源代码,给大家提供一个host端的float2half:

#define F16_EXPONENT_BITS 0x1F
#define F16_EXPONENT_SHIFT 10
#define F16_EXPONENT_BIAS 15
#define F16_MANTISSA_BITS 0x3ff
#define F16_MANTISSA_SHIFT (23 - F16_EXPONENT_SHIFT)
#define F16_MAX_EXPONENT (F16_EXPONENT_BITS << F16_EXPONENT_SHIFT)inline half F32toF16(float val){uint32_t f32 = (*(uint32_t *) &val);uint16_t f16 = 0;/* Decode IEEE 754 little-endian 32-bit floating-point value */int sign = (f32 >> 16) & 0x8000;/* Map exponent to the range [-127,128] */int exponent = ((f32 >> 23) & 0xff) - 127;int mantissa = f32 & 0x007fffff;if (exponent == 128){ /* Infinity or NaN */f16 = sign | F16_MAX_EXPONENT;if (mantissa) f16 |= (mantissa & F16_MANTISSA_BITS);}else if (exponent > 15){ /* Overflow - flush to Infinity */f16 = sign | F16_MAX_EXPONENT;}else if (exponent > -15){ /* Representable value */exponent += F16_EXPONENT_BIAS;mantissa >>= F16_MANTISSA_SHIFT;f16 = sign | exponent << F16_EXPONENT_SHIFT | mantissa;}else{f16 = sign;}return *(half*)&f16;}

在device端有__float2half和__half2float两个函数可以完成类型转换。不过这两个函数是对单个变量完成转换,要想device端实现对矩阵的转换还需要自己写kernel。
3. 基础运算需要利用intrinsic指令完成

float类型可以直接用+、-、*、/完成基本的数学运算,但是对half类型,我们必须用half相关的intrinsic指令完成。指令类型包括:基本算术运算、比较运算、类型转换运算和math运算等。而且比较恶心的是,基本算术运算指令前面需要将__,而math运算指令却又没有__,非常不统一。用intrinsic指令的坏处就是代码变长,开发略微复杂。
4. cublas中没有gemv相关的half函数

为了加速half类型的矩阵乘法,cublas中提供了cublasHgemm和cublasGemmEx函数,但是却没有提供level2相关的矩阵向量乘法cublasHgemv函数,gemv只有float和double版。导致我们只能手写kernel实现gemv或者用gemm代替,但是这两种方案都会使half性能大大折扣。尤其是在实现LSTM层的时候,其内部循环中包含两次gemv操作,结果导致half版本比float版本慢很多。所以当你想用half来加速LSTM模型,也请放弃幻想。
5. cublasHgemm不一定快

在计算能力大于6.1的GPU上,实现half矩阵乘法首先想到用cublasHgemm,但是在实测中发现该函数不一定比cublasSgemm快。如果出现这种情况,请试一下cublasGemmEX函数。本人在T4上验证的结论是:cublasHgemm运行速度比cublasSgemm还要慢,但是换用cublasGemmEX就会比cublasSgemm快。
6. half计算容易溢出

由于half位数较短,表示的数范围很小,大约在-65535~65536之间,所以在运算过程中比较容易溢出。比如在行归一化操作中,我们要求一行数据的平方和,如果行中出现较大值就极易出现溢出。此时就要求我们不能直接照搬float代码,而需要做适当变换,避免溢出。在行归一化中,我们可以通过在求平方和的过程中除以行数来避免溢出。

cuda half编程的各种坑相关推荐

  1. CUDA C编程接口技术分析

    CUDA C编程接口技术分析 编程接口 CUDA C为熟悉C编程语言的用户提供了一个简单的路径,可以方便地编写程序供设备执行. 它由C语言的最小扩展集和运行库组成. 核心语言扩展已经引入:cuda c ...

  2. CUDA C++编程接口:编译

    CUDA C++编程接口:编译 一.概述 CUDA C++为熟悉C++编程语言的用户提供了一个简单的路径,以方便地编写程序以执行该设备. 它由一组最小的扩展到C++语言和运行库. 在编程模型中引入了核 ...

  3. CUDA C++编程手册(总论)

    CUDA C++编程手册(总论) CUDA C++ Programming Guide The programming guide to the CUDA model and interface. C ...

  4. 《CUDA C编程权威指南》——1.5节总结

    本节书摘来自华章社区<CUDA C编程权威指南>一书中的第1章,第1.5节总结,作者[美] 马克斯·格罗斯曼(Max Grossman) ,更多章节内容可以访问云栖社区"华章社区 ...

  5. 《CUDA C编程权威指南》——3.4 避免分支分化

    本节书摘来自华章计算机<CUDA C编程权威指南>一书中的第3章,第3.4节,作者 [美] 马克斯·格罗斯曼(Max Grossman),译 颜成钢 殷建 李亮,更多章节内容可以访问云栖社 ...

  6. C与CUDA混合编程的配置问题

    C与CUDA混合编程的配置问题 2015-10-05 19:58 526人阅读 评论(2) 收藏 举报 分类: CUDA(6) 目录(?)[+] 原文: http://blog.csdn.net/u0 ...

  7. c cuda 指定gpu_《CUDA C编程权威指南》——1.3 用GPU输出Hello World-阿里云开发者社区...

    本节书摘来自华章计算机<CUDA C编程权威指南>一书中的第1章,第1.3节,作者 [美] 马克斯·格罗斯曼(Max Grossman),译 颜成钢 殷建 李亮,更多章节内容可以访问云栖社 ...

  8. anaconda+cuda+cudnn+pytorch安装踩坑大全

    windows环境下anaconda+pycharm+cuda+cudnn+pytorch安装踩坑大全 anaconda安装 pycharm安装 cuda cudnn pytorch gym环境安装 ...

  9. Gtk与Cuda混合编程

    大家都知道,不同的编译器有不同的-L和-I,而在编译过程中要实现两种编译器的混合使用,利用Makefile是一个很好地解决方案,比如之前在mpi与cuda混合编程中讨论的那样,基于图形界面的GTK编程 ...

最新文章

  1. 常玩手机会导致手指残疾? 专家称没依据
  2. 项目上线与LOG记录
  3. 怎么安装python3-如何装python3
  4. mysql-ubuntu卸载安装mysql
  5. vuejs怎么在服务器上发布部署
  6. [深度学习]为什么梯度反方向是函数值下降最快的方向?
  7. 开源评论:QQ真的会“开放”和“共享”吗?
  8. penpyxl basic function demo code
  9. jquery项目中一些比较常用的简单方法
  10. JavaWeb的环境配置
  11. showdoc修改json转表格格式
  12. 高频谐振功率放大器仿真
  13. t分布em参数估计matlab,参数估计的MATLAB实现.ppt
  14. 计算机主板供电,台式机计算机主板供电电路.doc
  15. 数据分析学习记录(四)--在origin中实现单因素方差分析和非参数检验
  16. 软件工程复习题-快来划重点
  17. 3蛋白wb_99% 的实验小白都会收藏,WB、ELISA、IHC 进阶攻略
  18. 【C语言】强符号和弱符号
  19. 多尺度动态图卷积神经网络----Multi-scale Dynamic Graph Convolutional Network for Hyperspectral Image Classificati
  20. mysql复制--主从复制配置

热门文章

  1. 如何正确使用OPcache优化系统性能
  2. 我们的测试只有盲从与跟随?
  3. 简述以下html代码中各对标记的作用,全国2003年4月高等教育自学考试 网页设计与制作试题 课程代码00900...
  4. 2023年下半年,智能家居渠道有什么新趋势?
  5. 传统制造企业利用工业互联网平台转型升级的路径与策略
  6. 用Python爬取电影数据并可视化分析
  7. 视觉笔记manifold(五)manifold使用小技巧
  8. 三大电信运营商TD-LTE 4G频段划分确定
  9. Java生鲜电商平台-优惠券设计与架构
  10. rabbitMq一收一发