0%

CUDA高性能并行计算(1)--CUDA初步探究

CUDA初步探究

什么是CUDA

CUDA,即Compute Unified Device Architecture的简称,是由NVIDIA公司创立的基于他们公司生产的图形处理器GPUs的一个并行计算平台和编程模型。

通过CUDA,GPUs可以很方便地被用来进行通用计算(有点像在CPU中进行的数值计算等等)。在没有CUDA之前,GPUs一般只用来进行图形渲染(如通过OpenGL,DirectX)。
开发人员可以通过调用CUDA的API,来进行并行编程,达到高性能计算目的。NVIDIA公司为了吸引更多的开发人员,对CUDA进行了编程语言扩展,如CUDA C/C++,CUDA Fortran语言。注意CUDA C/C++可以看作一个新的编程语言,因为NVIDIA配置了相应的编译器nvcc,CUDA Fortran一样。更多信息可以参考文献。

本系列的博客跳过了CUDA环境的安装,如果有需要,请自行查阅相关教程,这方面资料还是很多的。

另外,本博客的环境是基于CUDA10.2,VS2017,windows10下的。

运行CUDA的样例

首先先找到CUDA的安装目录(本机是C:\Program Files\NVIDIA GPU Computing Toolkit),然后进入extras/demo_suite目录,就能看到已经编译好的样例程序。

我们运行nbody.exe,这是一个模拟上千个受到万有引力的粒子运动并进行可视化的程序。可以在控制台中进行调整参数,这个程序还有很炫酷的。

除了nbody.exe,这个目录下还有其他的一些例程可以运行,同样的,还有一些未被编译的例程,可以手动去编译。

编写一个串行程序

我们首先使用传统方式来写一个计算一个点到一条直线上若干点的距离的程序。

这个简单的cpp程序使用了一个for循环,先对循环变量进行归一化处理,然后计算并存储与参考位置的距离。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

#include <stdio.h>
#include <math.h>
#define N 64


float scale(int i, int n)
{
return ((float)i / (n - 1));
}

float distance(float x1, float x2)
{
return sqrt((x2 - x1)*(x2 - x1));
}

int main()
{
float out[N] = { 0.0f };
float ref = 0.5f;

for (int i = 0; i < N; i++)
{
float x = scale(i, N);
out[i] = distance(x, ref);
}
for (auto & i : out)
{
printf("%f ", i);
}
return 0;
}


接下来我们使用另外一种方式来编写这个程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#define N 64

float scale(int i, int n)
{
return ((float)i / (n - 1));
}

float distance(float x1, float x2)
{
return sqrt((x2 - x1)*(x2 - x1));
}

void distanceArray(float * out, float * in, float ref, int n)
{
for (int i = 0; i < n; i++)
{
out[i] = distance(i, n);
}
}

int main()
{
float * in = (float *)calloc(N, sizeof(float));
float * out = (float *)calloc(N, sizeof(float));
const float ref = 0.5f;

for (int i = 0; i < N; i++)
{
in[i] = scale(i, N);
}

distanceArray(out, in, ref, N);


for (int i =0;i<N;i++)
{
printf("%f ", out[i]);
}

free(in);
free(out);

return 0;
}

我们来重新审视一下这两个程序,这两个程序都是使用串行的方式处理的,即每次处理一个数值,只是两个的写法不一样。

  • 第一个程序是在mian函数中手动处理每个点

  • 而第二个程序则是提供了一个统一的抽象接口,mian函数只需要一次调用这个函数就能计算出所有的值

表面上看这两个程序没有本质的区别,但是其标志性的指出了并行程序的编写思路:提供数据并调用接口,一次性获得所有的结果