7

我即将着手将我编写的程序转换为 CUDA,以希望提高处理速度。

现在显然我的旧程序一个接一个地执行了许多函数,我在主程序中将这些函数分开并按顺序调用每个函数。

void main ()
{
  *initialization of variables*
  function1()
  function2()
  function3()
  print result;
}

这些函数本质上是串行的,因为函数 2 取决于函数 1 的结果。

好的,所以现在我想将这些函数转换为内核,并并行运行函数中的任务。

是否像以并行方式重写每个函数一样简单,然后在我的主程序中,一个接一个地调用每个内核?这比它需要的慢吗?例如,我可以让我的 GPU 直接执行下一个并行操作,而无需返回 CPU 来初始化下一个内核吗?

显然,我会将所有运行时变量保留在 GPU 内存上以限制正在进行的数据传输量,所以我是否应该担心内核调用之间的时间?

我希望这个问题很清楚,如果没有,请让我详细说明。谢谢。

这是一个额外的问题,以便我可以检查我的理智。最终这个程序的输入是一个视频文件,通过不同的函数,每一帧都会产生一个结果。我的计划是一次抓取多个帧(比如 8 个唯一帧),然后将我拥有的块总数除以这 8 个帧,然后块中的多个线程将对图像数据执行更多并行操作,例如向量加法,傅里叶变换等。
这是解决问题的正确方法吗?

4

3 回答 3

6

在某些情况下,您可以让程序在 GPU 上以全部潜在速度运行,而从普通 CPU 版本进行的移植工作很少,这可能就是其中之一。

如果您有可能拥有这样的功能:

void process_single_video_frame(void* part_of_frame)
{
  // initialize variables
  ...
  intermediate_result_1 = function1(part_of_frame);
  intermediate_result_2 = function2(intermediate_result_1);
  intermediate_result_3 = function3(intermediate_result_2);
  store_results(intermediate_result_3);
}

并且您可以同时处理许多 part_of_frames。说,几千,

function1(),function2()function3()通过几乎相同的代码路径(也就是说,程序流并不严重依赖于帧的内容),

然后,本地内存可能会为您完成所有工作。本地内存是一种存储在全局内存中的内存。它以一种微妙而深刻的方式不同于全局内存......内存只是以这样一种方式交错,相邻线程将访问相邻的 32 位字,如果线程全部从它们的本地内存阵列的相同位置。

您的程序流程将是您从复制part_of_frame到本地数组开始并为中间结果准备其他本地数组。然后,您可以在代码中的各个函数之间传递指向本地数组的指针。

一些伪代码:

const int size_of_one_frame_part = 1000;

__global__ void my_kernel(int* all_parts_of_frames) {
    int i = blockIdx.x * blockDim.x + threadIdx.x;
    int my_local_array[size_of_one_frame_part];
    memcpy(my_local_array, all_parts_of_frames + i * size_of_one_frame_part);
    int local_intermediate_1[100];
    function1(local_intermediate_1, my_local_array);
    ...
}

__device__ void function1(int* dst, int* src) {
   ...
}

总而言之,这种方法可以让您使用几乎不变的 CPU 函数,因为并行性不是来自创建函数的并行版本,而是通过并行运行整个函数链。这又是通过硬件支持在本地数组中交错内存来实现的。

笔记:

  • 从全局内存到本地内存的初始副本part_of_frame没有合并,但希望您有足够的计算来隐藏它。

  • part_of_frame在计算能力 <= 1.3 的设备上,每个线程只有 16KiB 可用的本地内存,这对于您和其他中间数据可能不够用。但是在计算能力 >= 2.0 时,这已经扩展到 512KiB,这应该足够了。

于 2012-07-18T21:07:50.093 回答
5

回答你的一些问题:

调用内核并没有那么昂贵,所以不要害怕程序流从 GPU 返回到 CPU。只要将结果保存在 GPU 内存中,就不会产生太多开销。如果您愿意,您可以制作一个简单地按顺序调用其他设备函数的内核。AFAIK 这将更难调试和分析,我不确定是否可以分析内核调用的函数。

关于并行化:

我认为任何允许您在多个数据流上运行计算的想法都是好的。您的代码越像着色器越好(这意味着它将具有在 gpu 上快速运行所需的特性)。多帧的想法很好。关于它的一些提示:尽可能减少同步,尽可能少访问内存,尝试增加计算时间与 IO 请求时间的比率,利用 gpu 寄存器/共享内存,更喜欢 many-read-from-one一对多的设计。

于 2012-07-18T20:16:45.577 回答
1

如果 GPU 资源足以在单个内核中处理 3 个函数,那么您可以将您的函数放入一个大内核中,或者您可以串行启动 3 个内核以分别运行这些函数。在性能方面几乎没有什么不同,因为内核启动的硬件开销可以忽略不计,软件开销也很低。

但是,如果 GPU 资源不够,将 3 个函数放在一个内核中可能会牺牲性能。在这种情况下,最好将每个函数放入单独的内核中。

于 2012-07-18T20:41:11.990 回答