CUDA:在求和缩减过程中计算所有部分和的方法

CUDA:在求和缩减过程中计算所有部分和的方法,cuda,thrust,Cuda,Thrust,我在CUDA一次又一次地遇到这个问题。对于一组元素,我做了一些GPU计算。这会产生一些具有线性意义的值(例如,在内存方面): 现在,对于GPU计算的下一阶段,我需要以下值: memory_size = sum(element_sizes) memory_offsets = [ 0, 10, 110, 133 ] 我可以使用NVIDIA提供的缩减代码,在我的GPU上以80 gbps的速度计算内存大小。但是,我不能使用这段代码,因为它使用了一种不构成内存偏移量数组的分支技术。我尝试了很多方法,但我

我在CUDA一次又一次地遇到这个问题。对于一组元素,我做了一些GPU计算。这会产生一些具有线性意义的值(例如,在内存方面):

现在,对于GPU计算的下一阶段,我需要以下值:

memory_size = sum(element_sizes)
memory_offsets = [ 0, 10, 110, 133 ]
我可以使用NVIDIA提供的缩减代码,在我的GPU上以80 gbps的速度计算
内存大小。但是,我不能使用这段代码,因为它使用了一种不构成内存偏移量数组的分支技术。我尝试了很多方法,但我发现简单地将
元素大小
复制到主机,并使用
simd
for循环计算偏移量是最简单、最快的方法:

// in pseudo code
host_element_sizes = copy_to_host(element_sizes);
host_offsets = (... *) malloc(...);

int total_size = 0;
for(int i = 0; i < ...; ...){
    host_offsets[i] = total_size;
    total_size += host_element_sizes[i];
}

device_offsets = (... *) device_malloc(...);
device_offsets = copy_to_device(host_offsets,...);
//在伪代码中
主机\元素\大小=将\复制到\主机(元素\大小);
主机偏移量=(…*)malloc(…);
int total_size=0;
对于(int i=0;i<…;…){
主机偏移量[i]=总大小;
总大小+=主机元素大小[i];
}
设备偏移量=(…*)设备偏移量(…);
设备偏移量=复制到设备(主机偏移量,…);

然而,我已经做了很多次了,它开始成为一个瓶颈。这似乎是一个典型的问题,但我没有找到解决办法


对于CUDA程序员来说,解决这个问题的期望方法是什么?

我认为您正在寻找的算法是一种简单的算法。向量上的前缀和生成另一个向量,该向量包含输入向量的累积和值。前缀和至少存在于两种变体中-独占扫描或包含扫描。从概念上讲,这些是相似的

如果您的
元素\u大小
向量已存储在GPU全局内存中(这似乎是基于您的伪代码的情况),则存在在GPU上运行的库函数,您可以在该点调用,以生成
内存\u偏移量
数据(向量),而
memory\u size
值可以从向量中的最后一个值轻松获得,根据您是进行包含扫描还是独占扫描略有变化

下面是一个简单的工作示例,使用:

$cat t319.cu
#包括
#包括
#包括
#包括
#包括
int main(){
常量int元素_大小[]={10100,23,45};
const int ds=sizeof(元素大小)/sizeof(元素大小[0]);
推力:设备矢量dv(元件尺寸,元件尺寸+ds);
推力:装置矢量dv\u mo(ds);
推力:独占扫描(dv_es.begin(),dv_es.end(),dv_mo.begin());

std::cout@RobertCrovella Ok将签出并删除或标记为副本。正在传输中atm@RobertCrovella谢谢!这是一个令人头痛的问题。@RobertCrovella在我的例子中,有必要保留顺序。使用此方法生成解决方案似乎需要索引列表,因为我需要知道哪个元素偏移量在哪个元素偏移量中位置。如果您想保持顺序,我将使用
元素大小
数组上的前缀和计算所有内容。前缀和本身将为您提供
内存偏移量
数组,前缀和的最后一个值将是和减少量,即
内存大小
。您还没有真正显示
元素大小s
数组是生成的,所以我不确定能否给你一个精确的例子。但是如果你的
元素大小
数组在GPU全局内存中,那么在它上生成前缀和是一个很简单的操作——例如,来自推力的库调用。这是一个很好的问题,所以:不是每个人都知道前缀和是什么。我编辑了这个问题标题添加一些关键字(“所有部分和”)以帮助其他人找到问题。因此我在内核中工作。我可以调用内核中的推力函数吗?不管怎样,在这里找到我的答案:(看起来有一个主机函数重载)是的,推力函数。但是,库可能会给你更多的灵活性。它还具有扫描操作,可以在设备范围内、块范围内或更小的粒度上完成,所有操作都可以从设备代码调用。cub还可以让你更直接地控制算法所需的任何临时位置。
// in pseudo code
host_element_sizes = copy_to_host(element_sizes);
host_offsets = (... *) malloc(...);

int total_size = 0;
for(int i = 0; i < ...; ...){
    host_offsets[i] = total_size;
    total_size += host_element_sizes[i];
}

device_offsets = (... *) device_malloc(...);
device_offsets = copy_to_device(host_offsets,...);
$ cat t319.cu
#include <thrust/scan.h>
#include <thrust/device_vector.h>
#include <thrust/host_vector.h>
#include <thrust/copy.h>
#include <iostream>



int main(){

  const int element_sizes[] = { 10, 100, 23, 45 };
  const int ds = sizeof(element_sizes)/sizeof(element_sizes[0]);
  thrust::device_vector<int> dv_es(element_sizes, element_sizes+ds);
  thrust::device_vector<int> dv_mo(ds);
  thrust::exclusive_scan(dv_es.begin(), dv_es.end(), dv_mo.begin());
  std::cout << "element_sizes:" << std::endl;
  thrust::copy_n(dv_es.begin(), ds, std::ostream_iterator<int>(std::cout, ","));
  std::cout << std::endl << "memory_offsets:" << std::endl;
  thrust::copy_n(dv_mo.begin(), ds, std::ostream_iterator<int>(std::cout, ","));
  std::cout << std::endl << "memory_size:" << std::endl << dv_es[ds-1] + dv_mo[ds-1] << std::endl;
}
$ nvcc -o t319 t319.cu
$ ./t319
element_sizes:
10,100,23,45,
memory_offsets:
0,10,110,133,
memory_size:
178
$