提问

体验GPU计算带来的乐趣

摄影与媒体技术  / 媒体技术  / 倒序浏览   © 著作权归作者本人所有

#楼主# 2018-3-18

跳转到指定楼层
首先,字没打错,是G P U,没打错,我要打的字就是 G P U
下面开始——GPU计算的简单实现我们知道,CPU一般用于处理数据。计算方面,它叫做ALU,也就是算术逻辑单元。
而GPU因为常年去做图像处理,它自己也变得越来越适合浮点数计算了——成百上千个内核在同一时间一起计算。
就这样看来,GPU计算的速度一定很棒:大量数据一次性便计算完成了。
换句话说,比如我要执行下面的代码:
buff[0] *=
0.5
;
buff[1] *= 0.5;
...
buff[10000000000000] *= 0.5;
想到怎么写了吗?或许你是这样做的:
int i = 10000000000001;
while(i--)
    buff[i - 1] *= 0.5;
那么假如没有 任何优化的话,CPU是不是就像这样执行的?
[td=1,1,50.00%]           步骤[td=1,1,50.00%]           执行内容[td=1,1,50.00%]           1[td=1,1,50.00%]           buff[0] *= 0.5;[td=1,1,50.00%]           2[td=1,1,50.00%]           buff[1] *= 0.5;[td=1,1,50.00%]           ......[td=1,1,50.00%]           buff[..] *= 0.5;[td=1,1,50.00%]          n[td=1,1,50.00%]           buff[n] *= 0.5;需要很久,对吧?因为它是进行一次次的去乘,所以需要从第一个乘到最后。
那么仔细的观察可以看到,每一次它都是取乘以0.5。要是能一次性计算一大堆,比如一次性计算100个,那就好了。如此一来,一次性计算100的步骤就变成了
[td=1,1,50.00%]           步骤[td=1,1,50.00%]           执行内容[td=1,1,50.00%]           1[td=1,1,50.00%] buff[0 * 100 + 0]   *= 0.5;
buff[0 * 100 + 1]   *= 0.5;
.......
buff[0 * 100 + 99] *= 0.5;[td=1,1,50.00%]           2[td=1,1,50.00%] buff[1 * 100 + 0]   *= 0.5;
buff[0 * 100 + 1]   *= 0.5;
......
buff[0 * 100 + 99] *= 0.5;[td=1,1,50.00%]           ......[td=1,1,50.00%] buff[.. * 100 + 0]   *= 0.5;
buff[.. * 100 + 1]   *= 0.5;
......
buff[.. * 100 + 99] *= 0.5;[td=1,1,50.00%]           k = n / 100[td=1,1,50.00%] buff[k * 100 + 0]   *= 0.5;
buff[k * 100 + 1]   *= 0.5;
.......
buff[k * 100 + 99] *= 0.5;
这样一来,我最起码可以把原来计算一百次的时间压缩到执行一次的时间——很明显,运算的次数没变,但是花费的时间大大的减少了。这个就是并行计算。
早在CPU的Intel Pentium III出现时,就已经出现了一个很小的并行计算指令集——SSE,在更早的时候也有MMX指令集。这些指令集在现在的CPU上依然存在。在M$的VC++编译器中,很多运算可能就会被优化成SSE或者SSE2指令集来操作——更快、占用更低。
我们知道,GPU本身是用于图像处理的。举一个最简单的例子,我们在Windows上使用的Direct 3D这玩意,它就会把数据丢给GPU去运算。那么这个运算量有多大呢?我们不妨来想一下:一个像素点有4个数据:R,G,B,透明度(RGBA),那么一个屏幕是1920*1080,它计算的时候就需要1920*1080*4=8,294,400次运算,大约是2的23次方次。如此大的运算量,CPU做看起来会把桌面什么的fps刷到1帧。那么GPU又是怎么处理的呢?这个时候其实就和并行计算类似了——一次性处理一大堆,那么就可以降低运算花费的时间了。
说了这么些,估计你也想知道GPU这样有什么鸟用对吧?只能绘图看起来并无它用。但是随着时代的发展,GPU开始用于计算领域——矩阵乘法、二维离散傅里叶变换等。因为这些算法有一个共同的特点:计算量非常大,因而,它们被弄到GPU上尝试实现,结果也是显而易见的——它们的执行速度无一都不变得更快了。
那么,我们就来实现一下GPU计算

在这之前,先简单的介绍一下GPU编程可能会用到的语言。最为大家所知晓的应该是Nvidia的CUDA,非常强大,包含了各种各样的库,又因为是开发商给自己的硬件开发的玩意,所以效率也是出奇的高。但是缺点很明显,只能用在Nvidia的显卡上。第二个叫做OpenC,是业界第一个跨平台的异构编程框架,也是最通用的一个框架。作为一个开放的标准,OpenCL并不局限于某个特定的GPU厂商。最后,就是Visual Studio自己带的C++ AMP,它降低了程序的编写难度(其实看着还是头大的......),不过只有MS做了实现,基于DirectX。因为吧,我的电脑太不争气了,安装个CUDA告诉我C盘空间不足......所以只好去找C++ AMP的事情了。下面,我们就用C++ AMP来实现GPU计算。
MS很赞的送了介绍、参考等等。这都不是最重要的,最重要的是,它是中文的:[msdn]基于代码的 C++ AMP 简介
按照里面的内容,我们需要做三件事:
    [li]把数据提交到GPU,也就是array_view这个模板类[/li][li]进行GPU计算,也就是用parallel_for_each[/li][li]把数据弄回来[/li]
然后,开始实战:

#include <stdio.h>
#include <amp.h>
using namespace Concurrency;
double xin[1000] = { 0.0 };
int main()
{
    array_view<double, 1> doubleMul(1000, xin);
    parallel_for_each(doubleMul.extent, [=](index<1> idx) __GPU_ONLY
    {
        doubleMul[idx] += 5;
        doubleMul[idx] *= 10;
    });
    for (int i = 0; i < 1000; i++)
    {
        xin = static_cast<double>(doubleMul);
        if (xin != 50.0)
            printf("%lf\r\n", xin);
    }
    system("pause");
    return 0;
}
好了,GPU计算的实现就告一段落了。要是,有写的不足,的,地方,麻烦,指,出。
转播转播 分享淘帖
回复

使用道具

2

主题

20

帖子

366

积分

中士

积分
366
沙发
Harriet_Li 发表于 2018-3-29 16:49:59
如果只是单次的科学计算,可以尝试使用Matlab,可以使用GPU加速矩阵运算。
回复

使用道具 举报

科学狂人20 该用户已被删除
板凳
科学狂人20 发表于 2018-3-31 00:43:19
提示: 作者被禁止或删除 内容自动屏蔽
回复

使用道具 举报

B Color Link Quote Code Smilies
Archiver|手机版|小黑屋|MakerTime 创客时代  
Powered by Discuz! X3.3  © 2001-2017 Comsenz Inc.