1

我对 CUDA 相当陌生,想了解更多关于复数算术及其对速度的影响。

我需要为“j[]”数组中的所有元素求解以下复数方程,并将答案存储在“Ans[]”中:

Ans [0] = (2.0/((20.5*(j[0]*j[0]))+(5.55*j[0])+20));
Ans [1] = (2.0/((20.5*(j[1]*j[1]))+(5.55*j[1])+20));
...
...
...
Ans [n] = (2.0/((20.5*(j[n]*j[n]))+(5.55*j[n])+20));

由于我需要对“j”的所有元素执行相同的计算,因此我可以并行化此代码并让每个线程/块处理每个计算(blockIdx.x = 0 -> Ans [0] 等)据我所知,如果我对很多元素并行执行此操作,我应该能够看到速度的提高。然而,可以用一行 c++ 代码编写的内容在 GPU 中需要几行代码才能完成。

我的问题是,所有额外的代码行是否意味着更长的处理时间,因为它涉及在大量临时文件中保存中间值。如果是这样,当元素数量少于 1000 个时,在 GPU 中进行这种计算是否仍然有意义?(任意数)

方程:

C++ -> Ans [0] = (2.0/((20.5*(j[0]*j[0]))+(5.55*j[0])+20));

我的GPU版本:

int tid = blockIdx.x;

    temp1[tid] = cuCmul(j[tid], j[tid]);
    temp2[tid] = cuCmul(temp1[tid], make_cuDoubleComplex(20.5, 0));
    temp3[tid] = cuCmul(j[tid], make_cuDoubleComplex(5.55, 0));
    temp4[tid] = cuCadd(temp2[tid], temp3[tid]);
    temp5[tid] = cuCadd(temp4[tid], make_cuDoubleComplex(20, 0));
    Ans[tid] = cuCdiv(make_cuDoubleComplex(2.0, 0), temp5[tid]);

另外,请让我知道是否有更有效的方法来为 GPU 编写这个

4

2 回答 2

2

可以用一行 c++ 代码编写的内容需要在 GPU 中完成几行。

这可能不是真的,至少对于您展示的示例而言。您似乎担心临时存储,但编译器(主机和 GPU)非常擅长确定临时存储是否有意义,并对其进行优化或优化。许多程序员陷入这样的陷阱,认为他们编写的 C 代码可以很好地代表机器将执行的操作,就存储使用和操作顺序而言,但对于现代编译器,通常情况并非如此。

例如,您说这是您的 CPU 代码:

Ans [0] = (2.0/((20.5*(j[0]*j[0]))+(5.55*j[0])+20));

GPU版本可以写成:

Ans [0] = cuCdiv(make_cuDoubleComplex(2.0, 0), cuCadd(cuCadd(cuCmul(cuCmul(j[tid], j[tid]), make_cuDoubleComplex(20.5, 0)), cuCmul(j[tid], make_cuDoubleComplex(5.55, 0))), make_cuDoubleComplex(20, 0)));

不使用显式临时存储。(但是,代码肯定很难阅读。)但是在主机 (C) 或设备 (GPU) 案例中“幕后”发生的事情可能看起来不同。在计算如何优化这样的一行或几行代码方面,编译器通常比程序员要好。

首先让您的代码正常工作。然后对它进行基准测试(时间)。然后决定是否要仔细研究优化。像视觉分析器这样的工具可以帮助发现优化机会。

尽管您的宿主 C 代码看起来很简单,但请记住,一个复数仍然有 2 个与之关联的数量。尽管这在(抽象的)C 代码中并不明显,但“在幕后”编译器仍在执行必要的操作,以根据 +、-、*、/ 的各种操作分别处理数字

我的问题是,所有额外的代码行是否意味着更长的处理时间,因为它涉及在大量临时文件中保存中间值。

不一定,因为我上面描述的原因。您对任何一种实现都在做几乎相同的工作,编译器会观察到这一点并可能生成类似的机器代码。

如果是这样,当元素数量少于 1000 个时,在 GPU 中进行这种计算是否仍然有意义?(任意数)

如果您像这样计算的答案总数约为 1000,那么对于现代 GPU,您的问题“非常小”。现代 GPU 可能有 8 个(或更多)SM,每个 SM 能够同时运行 1 到 3 个 warp(32 个线程),并且机器还需要相当稳定的“准备运行”的 warp,以保持所有管道(内存,计算等)忙。1000 个线程可能是实现 GPU 充分利用的最低要求。显然,这在很大程度上取决于您将在哪个或哪些 GPU 上运行。例如,笔记本电脑中的小型低端 GPU 可能能够以更小的问题实现高利用率。但是,如果您的计算范围是您在此处显示的类型的 1000,那么我也无法想象在 CPU(主机代码)上花费太多时间。

于 2013-07-08T17:47:00.487 回答
2

CUDA 与 C++ 的一个子集一起工作。支持的功能之一是重载运算符。

__device__ __host__ cuDoubleComplex  operator*(cuDoubleComplex a, cuDoubleComplex b) { return cuCmul(a,b); }
__device__ __host__ cuDoubleComplex  operator+(cuDoubleComplex a, cuDoubleComplex b) { return cuCadd(a,b); }
__device__ __host__ cuDoubleComplex  operator/(cuDoubleComplex a, cuDoubleComplex b) { return cuCdiv(a,b); }

当输入之一是双精度而不是 cuDoubleComplex 时,您可以类似地重载运算符。

如果您没有在其他内核中使用相同的操作,最好继续执行您正在执行的操作。但是,如果您正在处理需要在其他内核中继续使用类似操作的大型项目,最好有一个包含所有这些重载运算符的头文件。

我的问题是,所有额外的代码行是否意味着更长的处理时间,因为它涉及在大量临时文件中保存中间值。如果是这样,当元素数量少于 1000 个时,在 GPU 中进行这种计算是否仍然有意义?(任意数)

编译器通常应该为相同的操作生成相同数量的临时变量,而与代码行无关。加速来自 GPU 执行的并行操作的数量。在大约 1000 个元素的情况下,单线程主机端实现应该能够击败仅执行这些操作的 CUDA 内核。将数据从主机复制到设备、启动内核、读取和写入全局内存等都会产生开销。

启用 CUDA 的 GPU 通常能够一次运行数千个线程。并且每个线程都应该具有相对较高的计算带宽比,以最佳地使用 GPU。

于 2013-07-08T19:31:57.527 回答