For loop Cuda内核中的大型for循环不';不适用于大型阵列

For loop Cuda内核中的大型for循环不';不适用于大型阵列,for-loop,cuda,For Loop,Cuda,我使用Cuda实现了各种算法,例如矩阵乘法、Cholesky分解和下三角矩阵的求逆(通过正向替换) 对于其中一些算法,我在内核中有一个For循环,它多次重复部分内核代码。对于(由1D数组表示的)矩阵(浮点数)来说,它可以很好地工作到大约200x200,for循环调用内核代码的部分200次。将矩阵大小增加到1000x1000(内核代码的for循环调用部分增加1000倍)会使GPU占用与使用较小矩阵大小的试验所预期的相同的计算时间。但是似乎没有运行内核代码(包括for循环之外的部分)(自初始化以来,

我使用Cuda实现了各种算法,例如矩阵乘法、Cholesky分解和下三角矩阵的求逆(通过正向替换)

对于其中一些算法,我在内核中有一个For循环,它多次重复部分内核代码。对于(由1D数组表示的)矩阵(浮点数)来说,它可以很好地工作到大约200x200,for循环调用内核代码的部分200次。将矩阵大小增加到1000x1000(内核代码的for循环调用部分增加1000倍)会使GPU占用与使用较小矩阵大小的试验所预期的相同的计算时间。但是似乎没有运行内核代码(包括for循环之外的部分)(自初始化以来,输出矩阵的元素没有任何更改)。如果我将矩阵大小增加到500左右,有时如果我将for循环中的限制器设置为某个较低的值(例如3),我就能够让内核运行

我在这里是否达到了一些硬件限制,或者是否有一个技巧可以让这些for循环在大型矩阵中工作

这是一个可以复制到.cu文件中的完整代码示例。内核试图将矩阵A(W*H)的内容复制到矩阵B(W*H)。输出显示了两个矩阵的第一个元素,对于W*H<200x200,这很好,对于W*H=1000x1000,似乎不会发生复制,因为B的元素保持为零,就像初始化后没有发生任何事情一样。我正在基于linux的服务器上编译和运行此代码。对于大型矩阵,错误检查在第67行给出:“GPUassert:unspecified launch failure”,该行是将矩阵B从设备复制到主机的CUDAMemcy行

  #include <cuda.h>
  #include <cuda_runtime.h>
  #include <cuda_runtime_api.h>
  #include <stdio.h> 
  #include <stdlib.h>
  #include <math.h>
  #include <iostream>
  #include <time.h>

#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
inline void gpuAssert(cudaError_t code, char *file, int line, bool abort=true)
{
   if (code != cudaSuccess) 
   {
      fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
      if (abort) exit(code);
   }
}  

__global__ void MatrixCopy(float *A, float *B, int W)
{

int i = blockIdx.x*blockDim.x + threadIdx.x;
int j = blockIdx.y*blockDim.y + threadIdx.y;

B[j*W + i]=A[j*W + i];

}

int main(void)
{

clock_t start1=clock();

int W=1000;
int H=1000;
float *A, *B;
float *devA, *devB;

A=(float*)malloc(W*H*sizeof(float));
B=(float*)malloc(W*H*sizeof(float));

for(int i=0; i<=W*H; i++)
{
    A[i]=rand() % 3;
    A[i]=A[i]+1;
    B[i]=0;
}

gpuErrchk( cudaMalloc( (void**)&devA, W*H*sizeof(float) ) ); 
gpuErrchk( cudaMalloc( (void**)&devB, W*H*sizeof(float) ) ); 

gpuErrchk( cudaMemcpy( devA, A, W*H*sizeof(float), cudaMemcpyHostToDevice ) );
gpuErrchk( cudaMemcpy( devB, B, W*H*sizeof(float), cudaMemcpyHostToDevice ) );

dim3 threads(32,32);
int bloW=(int)ceil((double)W/32);
int bloH=(int)ceil((double)H/32);
dim3 blocks(bloW, bloH);

clock_t finish1=clock();
clock_t start2=clock();

MatrixCopy<<<blocks,threads>>>(devA, devB, W);
gpuErrchk( cudaPeekAtLastError() );

gpuErrchk( cudaMemcpy( B, devB, W*H*sizeof(float), cudaMemcpyDeviceToHost ) );

clock_t finish2=clock();

printf("\nGPU calculation time (ms): %d\nInitialization time (ms): %d\n\n", (int)ceil(double(((finish2-start2)*1000/(CLOCKS_PER_SEC)))), (int)ceil(double(((finish1-start1)*1000/(CLOCKS_PER_SEC)))));
printf("\n%f\n", A[0]);
printf("\n%f\n\n", B[0]);

gpuErrchk( cudaFree(devA) );
gpuErrchk( cudaFree(devB) );

free(A);
free(B);

#ifdef _WIN32 
    system ("PAUSE"); 
#endif 

return 0;

}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#定义gpuerchk(ans){gpuAssert((ans),_文件_,_行__)}
内联void gpuAssert(cudaError\u t代码,char*文件,int行,bool abort=true)
{
如果(代码!=cudaSuccess)
{
fprintf(标准,“GPUassert:%s%s%d\n”,cudaGetErrorString(代码)、文件、行);
如果(中止)退出(代码);
}
}  
__全局无效矩阵复制(浮点*A、浮点*B、整数W)
{
int i=blockIdx.x*blockDim.x+threadIdx.x;
int j=blockIdx.y*blockDim.y+threadIdx.y;
B[j*W+i]=A[j*W+i];
}
内部主(空)
{
clock_t start1=clock();
int W=1000;
int H=1000;
浮动*A、*B;
浮动*devA,*devB;
A=(浮动*)malloc(W*H*sizeof(浮动));
B=(浮动*)malloc(W*H*sizeof(浮动));

对于(inti=0;i,内核没有线程检查

您可以这样决定网格大小(以块为单位):

int bloW=(int)ceil((double)W/32);
int bloH=(int)ceil((double)H/32);
__global__ void MatrixCopy(float *A, float *B, int W, int H)
{

  int i = blockIdx.x*blockDim.x + threadIdx.x;
  int j = blockIdx.y*blockDim.y + threadIdx.y;

  if ((i <  W) && (j < H))
    B[j*W + i]=A[j*W + i];

}
对于
H
W
的值,即使不是每个块大小的线程数的倍数(32),这会在您关心的实际矩阵(1000x1000)之外创建额外的线程和块。这没有错;这是常见的做法

但是,我们必须确保这些额外的线程实际上不做任何事情(即不生成对内存的无效访问)

如果将内核修改为如下所示:

int bloW=(int)ceil((double)W/32);
int bloH=(int)ceil((double)H/32);
__global__ void MatrixCopy(float *A, float *B, int W, int H)
{

  int i = blockIdx.x*blockDim.x + threadIdx.x;
  int j = blockIdx.y*blockDim.y + threadIdx.y;

  if ((i <  W) && (j < H))
    B[j*W + i]=A[j*W + i];

}
全局无效矩阵复制(浮点*A,浮点*B,整数W,整数H)
{
int i=blockIdx.x*blockDim.x+threadIdx.x;
int j=blockIdx.y*blockDim.y+threadIdx.y;
如果((i
我认为你会有更好的结果。如果没有这一点,内核中的
A
B
引用会生成越界访问,你可以查看你是否使用
cuda memcheck
运行代码。你还必须修改内核调用行以添加
H
参数。我没有我已经整理好了你的
i
变量是对应于
H
还是
W
;我假设你可以这样做,并在需要时进行更改。在这种情况下,因为矩阵是正方形的,所以这并不重要


当您在CUDA代码方面遇到问题时,您应该随时这样做。我建议您在发布请求帮助之前这样做。

您需要提供有关您的问题以及源代码的更详细信息。以上信息不足。您是说您正在递归内核吗?不,没有递归。可能您有sa我喜欢这样的问题。你的内核只是需要很长的时间来计算,并被系统终止。最好是提供一个最小大小的代码,用完整的、你正在使用的硬件(你关心硬件限制)和编译字符串再现你的问题。我在这方面真的是一个初学者(我第一次接触Cuda是在一两个月前,我没有每天使用它,这就是为什么我在让错误检查正常工作时遇到一些麻烦的原因)。在内核中使用if语句对索引进行限制似乎可以解决此脚本和我编写的另一个执行矩阵乘法的脚本的问题。我认为在Cuda内核中应该避免使用if语句,但我没有注意到对索引进行限制的单if语句的性能下降。非常感谢!将-arch=sm_20添加到编译字符串并不是必需的,所需的只是if语句。我知道它必须非常简单。是的,在这种情况下,
-arch=sm_20
确实不是必需的。您依赖于运行时发生的JIT编译操作,以便在体系结构上进行迁移sm_10(默认值,每个块仅支持512个线程)和sm_20(支持1024个线程,对应于实际运行的GPU)之间的差异是的,但是一般来说,为正确的体系结构编译是明智的吗?例如,如果OP有
double
s而不是
float
s,它们会降级还是JIT会保持精度?对于原子操作也是同样的问题?是的,为正确的体系结构编译更好