从全局内存加载

时间:2014-11-22 09:13:25

标签: cuda

假设这样的简单内核:

__global__ void fg(struct s_tp tp, struct s_param p)

{

  const uint bid = blockIdx.y * gridDim.x + blockIdx.x;
  const uint tid = threadIdx.x;
  const uint idx = bid * blockDim.x + tid;

  if(idx >= p.ntp) return;

  double3 r = tp.rh[idx];

  double d = sqrt(r.x*r.x + r.y*r.y + r.z*r.z);

  tp.d[idx] = d;

}

这是真的吗?:

  double3 r = tp.rh[idx];
  • 数据从全局内存加载到r变量中。

  • r存储在寄存器中,或者如果有很多变量,则存储在本地存储器中。

  • r未存储在共享内存中。

  • 计算
  • d,然后将其写回全局存储器。

  • 寄存器比其他存储器更快。

  • 如果寄存器空间已满(某些大内核),则使用本地内存,访问速度较慢

  • 当我需要双打时,有什么方法可以加快速度吗?例如,首先将数据加载到共享内存中然后再运行它们?

感谢所有人。

2 个答案:

答案 0 :(得分:2)

是的,这几乎都是真的。

  

•当我需要双打时,有什么方法可以加快速度吗?例如,首先将数据加载到共享内存中然后再操作它们?

当数据重用(多次加载相同的数据项,通常由线程块中的多个线程加载)时,或者当您专门使用共享内存来帮助时,使用共享内存非常有用全局合并,例如在optimized matrix transpose期间。

数据重用意味着您多次使用(加载)数据,并且对于共享内存有用,这意味着您要通过多个线程多次加载它。如果您在一个线程中多次使用它,那么单个加载以及将其存储在寄存器中的编译器(自动)“优化”就是您所需要的。

修改 @Jez给出的答案对于最佳加载有一些好的想法。我建议另一个想法是将您的AoS数据存储方案转换为SoA方案。数据存储转换是提高CUDA代码速度的常用方法。

您的s_tp结构(未显示)似乎每个项目/结构有多个double数量的存储空间。如果您为这些数量中的每一个创建单独的数组,您将有机会获得最佳的加载/存储。像这样:

__global__ void fg(struct s_tp tp, double* s_tp_rx, double* s_tp_ry, double* s_tp_rz, double* s_tp_d, struct s_param p)

{

  const uint bid = blockIdx.y * gridDim.x + blockIdx.x;
  const uint tid = threadIdx.x;
  const uint idx = bid * blockDim.x + tid;

  if(idx >= p.ntp) return;

  double rx = s_tp_rx[idx];
  double ry = s_tp_ry[idx];
  double rz = s_tp_rz[idx];

  double d = sqrt(rx*rx + ry*ry + rz*rz);

  s_tp_d[idx] = d;

}

对于类似类型的使用模式,此方法也可能在您的设备代码的其他地方带来好处。

答案 1 :(得分:1)

一切都是真的。

  

当我需要双打时,有什么方法可以加快速度吗?例如加载   数据首先进入共享内存然后运行它们?

对于您提供的示例,您的实现可能不是最佳的。您应该做的第一件事是将实现的带宽与参考内核的带宽进行比较,例如cudaMemcpy。如果差距很大,并且通过缩小这一差距而获得的加速很有意义,那么可能会进行优化。

看看你的内核有两件事让我感到不太理想:

  1. 每个帖子的工作量不大。如果可能,每个线程处理多个元素可以提高性能。这部分是因为它避免了线程初始化/删除开销。
  2. 从double3加载并不像从其他类型加载那样高效。加载数据的最佳方法通常是每个线程使用128位加载。加载三个连续的64位值将会更慢,可能不会很多,但速度会慢一些。
  3. 编辑:Robert Crovella在下面的回答为第二点提供了一个很好的解决方案,需要改变您的数据类型。出于某种原因,我原本以为这不是一个选项,所以如果你只是改变你的数据类型,下面的解决方案可能是过分的!

    虽然为每个线程添加更多工作是一件相当简单的事情,但是为解决方案优化内存访问模式(不更改数据类型)则不那么容易。幸运的是,有些库可以提供帮助。我认为使用CUB,特别是BlockLoad集合,应该允许您更有效地加载。通过使用转置运算符为每个线程加载6 double个项目,您可以为每个线程处理两个元素,将它们打包到double2中,并正常存储它们。