我目前正在使用下面的Reduction函数来使用CUDA对数组中的所有元素求和:
__global__ void reduceSum(int *input, int *input2, int *input3, int *outdata, int size){
extern __shared__ int sdata[];
unsigned int tID = threadIdx.x;
unsigned int i = tID + blockIdx.x * (blockDim.x * 2);
sdata[tID] = input[i] + input[i + blockDim.x];
__syncthreads();
for (unsigned int stride = blockDim.x / 2; stride > 32; stride >>= 1)
{
if (tID < stride)
{
sdata[tID] += sdata[tID + stride];
}
__syncthreads();
}
if (tID < 32){ warpReduce(sdata, tID); }
if (tID == 0)
{
outdata[blockIdx.x] = sdata[0];
}
}
然而,正如你从函数参数中看到的,我希望能够在一个约简函数中对三个单独的数组求和。现在显然有一个简单的方法可以做到这一点,那就是启动内核三次,每次传递一个不同的数组,当然这会很好地工作。我只是作为一个测试内核来写的,但是,真实的的内核最终会接受一个结构体数组,我需要对每个结构体的所有X
,Y
和Z
值执行加法,这就是为什么我需要在一个内核中对它们求和。
我已经初始化并分配了所有三个数组的内存
int test[1000];
std::fill_n(test, 1000, 1);
int *d_test;
int test2[1000];
std::fill_n(test2, 1000, 2);
int *d_test2;
int test3[1000];
std::fill_n(test3, 1000, 3);
int *d_test3;
cudaMalloc((void**)&d_test, 1000 * sizeof(int));
cudaMalloc((void**)&d_test2, 1000 * sizeof(int));
cudaMalloc((void**)&d_test3, 1000 * sizeof(int));
我不确定我应该为这种内核使用什么样的网格和块尺寸,我也不完全确定如何修改归约循环来放置我想要的数据,即输出数组:
Block 1 Result|Block 2 Result|Block 3 Result|Block 4 Result|Block 5 Result|Block 6 Result|
Test Array 1 Sums Test Array 2 Sums Test Array 3 Sums
我希望这是有意义的。或者有没有更好的方法,只有一个归约函数,但能够返回Struct.X
,Struct.Y
或struct.Z
的总和?
下面是struct:
template <typename T>
struct planet {
T x, y, z;
T vx, vy, vz;
T mass;
};
我需要把所有的vx
加起来并存储它,把所有的vy
加起来并存储它,把所有的vz
加起来并存储它。
2条答案
按热度按时间lnlaulya1#
或者有没有更好的方法,只使用一个reduction函数,但能够返回Struct.X、Struct.Y或struct.Z的总和?
通常加速计算的主要焦点是速度。GPU代码的速度(性能)通常在很大程度上取决于数据存储和访问模式。因此,尽管正如您在问题中指出的那样,我们可以通过多种方式实现解决方案,但让我们专注于应该相对较快的事情。
像这样的缩减没有太多的算术/操作强度,所以我们对性能的关注主要围绕着数据存储来进行有效的访问。当访问全局内存时,GPU通常会以大块的方式进行访问-- 32字节或128字节的块。为了有效地利用内存子系统,我们希望在每个请求中使用所有被请求的32或128个字节。
但你的结构隐含的数据存储模式:
基本排除了这个可能性对于这个问题,您关心的是
vx
、vy
和vz
。这3个项目在给定的结构(元素)中应该是连续的,但是在这些结构的数组中,它们将被其他结构项目所需的存储空间分隔开,至少:(for例如,假设
T
是float
)这指出了GPU中结构阵列(AoS)存储格式的一个关键缺点。由于GPU的访问粒度(32字节),从连续结构访问相同元素是低效的。在这种情况下,通常的性能建议是将AoS存储转换为SoA(阵列结构):
上面只是一个可能的例子,可能不是你实际使用的,因为结构没有什么用处,因为我们只有一个
N
行星的结构。关键是,现在当我访问连续行星的vx
时,各个vx
元素在内存中都是相邻的,因此32字节的读取给了我32字节的vx
数据,没有浪费或未使用的元素。通过这样的转换,从代码组织的Angular 来看,缩减问题再次变得相对简单。您可以使用与单个数组归约代码基本相同的代码,要么连续调用3次,要么直接扩展内核代码,基本上独立处理所有3个数组。一个“3合1”内核可能看起来像这样:
(在浏览器中编码-未测试-仅仅是您所显示的“参考内核”的扩展)
上面的AoS -> SoA数据转换可能也会在代码的其他地方带来性能上的好处。由于建议的内核将一次处理3个数组,因此网格和块的尺寸应该与您在单数组情况下使用的参考内核完全相同。每个块的共享内存存储将需要增加(三倍)。
zdwk9cvp2#
Robert Crovella给出了一个很好的答案,强调了AoS -> SoA布局转换的重要性,这通常会提高GPU的性能,我只是想提出一个可能更方便的中间立场。CUDA语言提供了一些向量类型,用于您所描述的目的(请参阅CUDA编程指南的本节)。
例如,CUDA定义了int 3,一种存储3个整数的数据类型。
类似的类型也存在于float,chars,double等。这些数据类型的优点在于,它们可以用一条指令加载,这可能会给您带来一些性能提升。参见this NVIDIA blog post以获得对此的讨论。在这种情况下,它也是一种更“自然”的数据类型,它可能会使代码的其他部分更容易使用。例如,您可以定义:
使用这种数据类型的约简内核可能看起来像这样(改编自Robert的)。