Parallel processing 用Cuda并行实现阵列中连续子序列和的计算 让我们考虑以下数组: 表=[80,12,14,5,70,9,26,30,8,12,16,15] 我想使用cuda计算大小为4的所有可能序列的总和: 例如: S1=80+12+14+5=111 S2=12+14+5+70 =101 S3=14+5+70+9 =98 ....
您有一个使用Cuda将此任务并行化的有效想法。上一个表只是我的例子,我将使用大表。我们可以通过使用推力的单个操作(Parallel processing 用Cuda并行实现阵列中连续子序列和的计算 让我们考虑以下数组: 表=[80,12,14,5,70,9,26,30,8,12,16,15] 我想使用cuda计算大小为4的所有可能序列的总和: 例如: S1=80+12+14+5=111 S2=12+14+5+70 =101 S3=14+5+70+9 =98 ....,parallel-processing,cuda,gpu,Parallel Processing,Cuda,Gpu,您有一个使用Cuda将此任务并行化的有效想法。上一个表只是我的例子,我将使用大表。我们可以通过使用推力的单个操作(推力::转换)来实现这一点。在CUDA中,这可以被认为是一个相当简单的1-D模具操作 关于一维模板操作的详细说明,请参见幻灯片49-58 这实际上是一个简化的情况,因为模具宽度为4,并且它仅位于中心点的一侧 下面是一个比较两种方法的示例: $ cat t88.cu #include <thrust/device_vector.h> #include <thrust/
推力::转换
)来实现这一点。在CUDA中,这可以被认为是一个相当简单的1-D模具操作
关于一维模板操作的详细说明,请参见幻灯片49-58
这实际上是一个简化的情况,因为模具宽度为4,并且它仅位于中心点的一侧
下面是一个比较两种方法的示例:
$ cat t88.cu
#include <thrust/device_vector.h>
#include <thrust/transform.h>
#include <thrust/iterator/zip_iterator.h>
#include <thrust/copy.h>
#include <iostream>
const int nTPB=256;
typedef float mytype;
const int ds = 1048576*32;
struct sum4
{
template <typename T>
__host__ __device__
mytype operator()(const T t){
return thrust::get<0>(t) + thrust::get<1>(t) + thrust::get<2>(t) + thrust::get<3>(t);
}
};
template <typename T>
__global__ void sum4kernel(const T * __restrict__ in, T * __restrict__ out, const unsigned dsize)
{
__shared__ T sdata[nTPB+3];
unsigned idx = threadIdx.x+blockDim.x*blockIdx.x;
if (idx < dsize) sdata[threadIdx.x] = in[idx];
if ((threadIdx.x < 3) && ((idx+blockDim.x) < dsize)) sdata[threadIdx.x + blockDim.x] = in[idx + blockDim.x];
__syncthreads();
T temp = sdata[threadIdx.x];
temp += sdata[threadIdx.x+1];
temp += sdata[threadIdx.x+2];
temp += sdata[threadIdx.x+3];
if (idx < dsize - 4) out[idx] = temp;
}
int main(){
mytype hdata1[] = {80,12,14,5,70,9,26,30,8,12,16,15};
unsigned ds1 = sizeof(hdata1)/sizeof(hdata1[0]);
mytype hres1[ds1-4];
thrust::device_vector<mytype> ddata1(hdata1, hdata1+ds1);
thrust::device_vector<mytype> dres1(ds1-4);
thrust::transform(thrust::make_zip_iterator(thrust::make_tuple(ddata1.begin(), ddata1.begin()+1, ddata1.begin()+2, ddata1.begin()+3)), thrust::make_zip_iterator(thrust::make_tuple(ddata1.end()-3, ddata1.end()-2, ddata1.end()-1, ddata1.end())), dres1.begin(), sum4());
thrust::copy(dres1.begin(), dres1.end(), std::ostream_iterator<mytype>(std::cout, ","));
std::cout << std::endl;
sum4kernel<<<(ds1+nTPB-1)/nTPB, nTPB>>>(thrust::raw_pointer_cast(ddata1.data()), thrust::raw_pointer_cast(dres1.data()), ds1);
cudaMemcpy(hres1, thrust::raw_pointer_cast(dres1.data()), (ds1-4)*sizeof(mytype), cudaMemcpyDeviceToHost);
for (int i = 0; i < ds1-4; i++)
std::cout << hres1[i] << ",";
std::cout << std::endl;
thrust::device_vector<mytype> ddata2(ds, 1);
thrust::device_vector<mytype> dres2(ds-4);
cudaEvent_t start, stop;
cudaEventCreate(&start); cudaEventCreate(&stop);
cudaEventRecord(start);
thrust::transform(thrust::make_zip_iterator(thrust::make_tuple(ddata2.begin(), ddata2.begin()+1, ddata2.begin()+2, ddata2.begin()+3)), thrust::make_zip_iterator(thrust::make_tuple(ddata2.end()-3, ddata2.end()-2, ddata2.end()-1, ddata2.end())), dres2.begin(), sum4());
cudaEventRecord(stop);
thrust::host_vector<mytype> hres2 = dres2;
float et;
cudaEventElapsedTime(&et, start, stop);
std::cout << "thrust time: " << et << "ms" << std::endl;
// validate
for (int i = 0; i < ds-4; i++) if (hres2[i] != 4) {std::cout << "thrust validation failure: " << i << "," << hres2[i] << std::endl; return 1;}
cudaEventRecord(start);
sum4kernel<<<(ds+nTPB-1)/nTPB, nTPB>>>(thrust::raw_pointer_cast(ddata2.data()), thrust::raw_pointer_cast(dres2.data()), ds);
cudaEventRecord(stop);
cudaMemcpy(&(hres2[0]), thrust::raw_pointer_cast(dres2.data()), (ds-4)*sizeof(mytype), cudaMemcpyDeviceToHost);
cudaEventElapsedTime(&et, start, stop);
std::cout << "cuda time: " << et << "ms" << std::endl;
for (int i = 0; i < ds-4; i++) if (hres2[i] != 4) {std::cout << "cuda validation failure: " << i << "," << hres2[i] << std::endl; return 1;}
}
$ nvcc -arch=sm_61 -o t88 t88.cu
$ ./t88
111,101,98,110,135,73,76,66,
111,101,98,110,135,73,76,66,
thrust time: 0.902464ms
cuda time: 0.76288ms
$
因此,CUDA实现似乎实现了大约最大的可用带宽。float4向量,其元素左移(elementwise)1,然后将最新元素分配给下一个数组元素,然后将其dots乘积写入S元素。或者,将最新的元素添加到变量中,从该变量中减去最旧的元素,然后将其写入S元素中?但这是针对单线程的。对于多线程,它可能需要本地阵列而不是全局阵列。您是否介意进一步解释cuda实现背后的想法?@Robert CrovellaI添加了一个链接,其中提供了使用cuda中共享内存的一维模具示例的入门培训幻灯片。谢谢您的清晰说明answer@Robert克罗维拉,当我读到NVIDIA关于1-D模具操作的文章时,我注意到每个迭代中的总和没有被重用为以下元素,对吗?。事实上,我将在下一步使用半径为100的海量数据,这将是非常昂贵的。你认为呢?我在nvidia的文档中讨论了这个循环:for(int offset=-RADIUS;offset这对我来说似乎是一个不同的问题。我认为我们无法在几句评论的空间内对其进行分类。您的问题显然要求子序列长度为4。如果子序列长度足够大,您可能会耗尽共享内存,并且/或者它可能指示或建议使用不同的算法。是的,在这些示例中,线程之间不重用求和是正确的。尝试天真地重用线程之间的求和将导致序列化。
(32*1048576 elements * 2 ops/element * 4 bytes/op) / 0.00076288 s = ~350GB/s