深度学习Meetup速记(二)——使用 CUDA 加速R的应用

浏览: 4025

使用 CUDA 加速R的应用


编者按

8月26日,星环科技与深度学习国际交流群举行首次深度学习Meetup,共有四位嘉宾进行了精彩的分享,本文系 NVIDIA高级架构工程师赵鹏的精彩分享内容。活动详情可关注星环科技官方微信,更可免费获取演讲PPT资料。以下为分享内容正文:



今天主要介绍一些关于性能方面的东西。大家对于应用可能比较感兴趣,在做KNN和语义分析的时候,不管是用什么框架,很快就会遇到很多性能问题。

我接下来要介绍的是R语言在GPU上的加速。R语言和python在实际中应用较广,比如R语言能用在统计分析与制图。所以用R语言和Python这类的语言,进行数据分析时速度非常快,你可能不需要很多的code,就可以达到一些非常好的结果。

其实,R语言和Python很像,我接下来要介绍的技巧对于两者很多是通用的。我主要介绍一下数据分析和R语言的背景;接着是怎么用CUDA的一些libraries,用到的上层语言;然后是我们怎么用编译指导性语言把底层code变成GPU可以加速的;最后有一个case study,将介绍一个DNN的case。


1. BACKGROUND


首先,我们来看一下背景。对于R语言,为什么要关心它呢?

第一,R语言有很多包,是一些领域专家所写,包括社会科学,经济学,还有生物学,他们在各个领域上有非常强的理论背景,他们根据理论分析性写的包,可靠性非常强。学计算机也可以写一个包,在很多处理上就没有他们处理地完善。专业研究者写包,专业性、可靠性都非常高。​

第二,R语言与Python语言的设计就以数据处理为目标的。各种数据的变化,数据的抽取合并,使用C/C++的一些转制合并也能实现,但是使用这些上层语言就比较简单。介绍了一些公司和软件,很多软件包括OFFICE都能访问R。但是R语言和python的速度是一个缺点,对于R语言更明显。因为首先R是单线程,哪怕是在64位的CPU上,也运行不快;其次,是memory的设计,数据必须加载进内存里才可以使用,这种设计的好处是数据分析非常快,可以非常快地进行数据查询等操作,但是处理大数据的时候往往数据是超过内存大小的。


2.DEPLOY CUDA LIBRARIES TO R


这次我们解决的主要是第一个问题,怎么用GPU提高R语言的速度。首先看下R语言的开发环境,上层的生产级,用户通过R处理一些生产的问题,用各种包。如果要用GPU,则使用功能对应的GPU包。已有的GPU相对较少,很多情况下如果没有现成的GPU包,那么可以用以下三种方式来获得GPU包:第一,已拥有的CUDA加速的库;第二,对数据运算任务,自己做一些简单的并行化;第三,完全写一个GPU的函数去执行。那么,通过这些方式把GPU硬件和多核的CPU连接起来。

其实,真正GPU加速的库网上有很多,也有很多小道的算法,例如现在流行的深度学习的CuDNN。很多情况下,只需要使用上层语言去加载对应的GPU库,就能极大提升程序性能。接下来,我想用2个例子示范库的使用方法,其中会牵扯到一些细节的问题。

2.1 Case 1


第一个例子是线性代数库,线性代数库是应用很多的库。现在做的DNN、CNN都是在底层把计算转换为矩阵乘法。加速矩阵乘法就是用的BLAS库。很多情况下,我的application叫做R standard interface,就是单线程实现的矩阵乘的库,我们可以很容易地把这个库替换如图下面的部分,既可以替换成GPU加速的cuBLAS库,也可以是多核或多线程的intel的MKL库和OpenBLAS。通过这种方式,可以很快地提高矩阵运算速度。

下面来看看怎么用。Linux下有个NVBLAS库,它其实是cuBLAS的wrapper,从它里面我们就可以调整各种精度的矩阵乘。它不仅支持单GPU,还支持多GPU。它的主要好处是对code不需要做任何改变,也就是zero programming effort,只需要把NVBLAS库load在前面,这个库就被替换成cuBLAS,跑到GPU上。所以在做应用程序开发,大家通常有一种思路,如果想加速一个计算,就尝试把这个计算转换成矩阵计算,并加载各种多线程库或并行库,那么程序可以得到很快地提高,这其中不需要太多code的重写。

然后看下benchmark,我们有两种benchmark。蓝色的线是用原来的的R跑的程序,可以看到运行时间多很多,加载了NVBLAS库以后,运行时间少了很多。其他是一些在很多程序里运用的基础算法。当程序比较依赖于这种基础算法,就可以考虑加载很多并行库,来看程序的运行效果。


2.1 Case 2


第二个例子其实只包括矩阵计算,矩阵计算其实相当有限,如果要用其他库,比如FFT库,做中间库的工程师就需要写一些中间脚本来调用GPU库。图中这是原始的R里面的FFT,我们需要校这块GPU的code,所以需要通过C语言写一个wrapper来调用CUDA的核。

有一部分在R语言里面调用CUDA,包括内存拷贝、真正的计算等。当wrapper写好后,就可以在R语言里面直接调用刚才写的 wrapper ,从上层的语言传导给下层的C语言,再传给CPU或多线程的库。做好这些后,对于上层的应用来说,CPU和GPU的FFT是没有区别的。程序就是在调用一些wrapper。

接下来可以看到GPU做FFT的一些性能。随着数据量的增大,CPU的时间非常大,GPU的时间是相对比较小的,最大有将近8倍的加速比。



3. APPLY DIRECTIVES


有时候库里没有适合我的GPU的算法,问题比较特殊,这时候就需要用些directives,一些比较方便的方法。比如在for循环前面加上 ‘#pragma’的声明,整个循环就可以被并行执行。这种方式适合于code base里面已经有非常大部分的code了,如果要重写这部分code不太可能,工作量太大,风险也太大。如果选用directives,可以继承大部分code,只把计算方式做一些改变。

下面我们看code in dist(),经常使用用来计算距离或者DNN里面都会用到。在R语言里面也是用C来写的,在C里面要加速,用directive就比较简单。只需要在前面加一句语句声明这个for循环是可以并行执行的就可以了,编译器就会把这个for语句里面的DATA拆散到各个的核里面。

那么接下来可以看一下性能,在原始语句里面相当于只加了一句话,就有了两倍的加速比,这个effort还是比较值的。但是在实际工程中还比较难,因为语句较长的话,数据结构就会比较复杂,映射多个核里面就会有数据冲突或数据竞争之类的问题。在真正工程中使用就要更小心一点,for语句比较长,就要看看这个语句包含的函数是否安全。

下面方法和刚才所说的比较相似。当有一些计算非常密集的时候,就不得不用C等一些底层的语言去写。那么我可以用C写,然后用CUDA去实现,完全写成一个GPU的code。

以上我讲的就是通过上层语言,三种运用GPU的方法。第一种就是各种底层加速库,第二种在C code里面加编译指导语言,第三种是直接写一个CUDA程序。



4. Case Study:UNLOCK PERFORMANCE LIMIT OF DNN BY CUDA® IN R


下面用DNN的case来看。大家可以用各种各样的框架,但是有个缺点就是难以debug。比如想把某个隐藏层的数据可视化或单独拿出来做一些调整,有时候就会比较困难,因为这些框架写的东西都比较底层。而如果我们用R语言来写,这些数据都是可以自己管理的,相对较容易分析,灵活性较高。用C、C++或CUDA来写,性能很高,但是很难改一些东西,需要改很多相关的东西,容易引发bug。所以用R语言实现功能,用GPU加速,虽然性能较原先比较差,但是保留了很好的灵活性。


最后介绍一个简单的例子。全连接的DNN,只用一句R语言写的,code非常简单。主要的运算在矩阵乘,用三种方法加速应用程序。这里我们运用的Benchmark是MNIST,大家应该比较熟悉,是识别手写字的。我们用刚才的R.DNN的code和H2O的code对比一下,如图中所示,当隐藏层计算量比较大的时候,H2O还是明显比较快的。

接下来做了一个简单的Profiling。大家可能会写各种各样的应用程序,当我们发现程序性能有问题的时候,就要去寻找时间花费在了什么上面。像图中这个程序,90%的时间用在了矩阵乘上面。 那对它运用刚才介绍的第一种方法,直接用CUBLAS库去跑。对于程序而言,任何改变都没有,使用GPU后速度可以比H2O快两倍。矩阵乘之后可以看到矩阵转置,对于上层语言而言做一次转置时间是比较多的,我们略做调整,使用R语言本身的C下面的BLAS。做了简单转化之后,我们发现,如图中,最后可以达到1.8倍的加速。

再往后发现Pmax用的时间最多,Pmax是R语言里面的一个Function,但是用R语言做相对较慢,就可以用CUDA版本来写。如图,其实这个Pmax的CUDA只有一行code。当Pmax使用CUDA来实现的时候,它的执行时间能从31s降到6s,加速了5倍。总的来说,使用这两种方法之后一共有12倍的加速。

从下面这个Performance on Linux的图可以看出,当我使用GPU加速之后,运行时间从24min降到了2min。H2O的1个thread需要9min,20个thread还需要5min。所以哪怕自己实现一个code,用GPU调整的很好,其实比现成产品还要更快。有时候很多时间都浪费在别处了,真正内核的计算时间不是特别多,调整程序就会快很多。


刚才说到的是用一个GPU的,对于这种程序我们也很容易扩展到多个GPU。R语言本身就有个包叫multicores,它可以把我们的一个程序映射到多个进程里去,再把每个进程的工作扩展到每个GPU上去。

最后的结果我们可以参考这张图,CPU从1个到20个的过程,其实到6个CPU时,它的时间就不再下降了。那么对GPU来说,1个GPU就可以完全超过20个CPU的时间,6个GPU就可以快很,最终差不多有160多倍。


(演讲内容到此结束)



演讲嘉宾:赵鹏    NVIDIA高级架构工程师

个人简介:曾担任Cadence的高级软件工程师,在包括多核,分布式以及GPU通用计算方面具有丰富的研究和实践经验,对于并行算法的设计,实现,调试以及测试具有独特的见解和丰富的工程经验,善于帮助客户和公司解决性能瓶颈问题。现主要致力于机器学习,深度学习和数据分析领域的计算加速和解决方案的设计。

推荐 0
本文由 星环科技 创作,采用 知识共享署名-相同方式共享 3.0 中国大陆许可协议 进行许可。
转载、引用前需联系作者,并署名作者且注明文章出处。
本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责。本站是一个个人学习交流的平台,并不用于任何商业目的,如果有任何问题,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

0 个评论

要回复文章请先登录注册