CUDA 矩阵乘法优化

2023-11-17

矩阵乘法

  为了单纯起见,我们这里以方形的矩阵为例子。基本上,假设有两个矩阵 A 和 B,则计算 AB = C 的方法如下:

       for(j = 0; j< n; j++) {
            C[i][j]
=0;
            
for(k= 0; k< n; k++) {
                C[i][j]
+= A[i][k]* B[k][j];
            }
        }
    }

一开始,我们先准备好产生数据、设定 CUDA 等等的工作:
  int main()
    {
        
float*a, *b, *c,*d;
        
int n= 1000;

        
if(!InitCUDA())return 0;

        a
= (float*) malloc(sizeof(float)* n * n);
        b
= (float*) malloc(sizeof(float)* n * n);
        c
= (float*) malloc(sizeof(float)* n * n);
        d
= (float*) malloc(sizeof(float)* n * n);

        srand(
0);

        matgen(a, n, n);
        matgen(b, n, n);

        clock_t time
= matmultCUDA(a, n, b, n, c, n, n);

        matmult(a, n, b, n, d, n, n);
        compare_mat(c, n, d, n, n);

        
double sec= (double) time/ CLOCKS_PER_SEC;
        printf(
"Time used: %.2f (%.2lf GFLOPS)\n", sec,
            
2.0* n * n * n/ (sec * 1E9));

        
return0;
    }

InitCUDA 函式和第一个 CUDA 程序一样,可以直接参考前面的文章。以下是上面用到的一些其它的函式:

  产生矩阵:

void matgen(float* a,int lda, int n)
    {
        
int i, j;

        
for(i= 0; i< n; i++) {
            
for(j= 0; j< n; j++) {
                a[i
* lda+ j] = (float) rand()/ RAND_MAX +
                    (
float) rand()/ (RAND_MAX* RAND_MAX);
            }
        }
    }

这个函式只是利用随机数生成器把矩阵填满 0 ~ 1 之间的数字。特别注意到因为 C 语言中无法声明变动大小的二维矩阵,所以我们使用 i * lda + j 的方式。

  进行矩阵乘法:

void matmult(constfloat* a,int lda, const float* b,int ldb,
        
float* c,int ldc, int n)
    {
        
int i, j, k;

        
for(i= 0; i< n; i++) {
            
for(j= 0; j< n; j++) {
                
double t= 0;
                
for(k= 0; k< n; k++) {
                    t
+= a[i* lda + k] * b[k* ldb + j];
                }
                c[i
* ldc+ j] = t;
            }
        }
    }

这是以 CPU 进行矩阵乘法、用来进行验证答案正确与否的程序。特别注意到它用 double 来储存暂时的计算结果,以提高精确度。

  验证结果:

void compare_mat(constfloat* a,int lda,
        
constfloat* b,int ldb, int n)
    {
        
float max_err= 0;
    
float average_err= 0;
        
int i, j;

        
for(i= 0; i< n; i++) {
            
for(j= 0; j< n; j++) {
                
if(b[i* ldb + j] !=0) {
                    
float err= fabs((a[i* lda + j] -
                        b[i
* ldb+ j]) / b[i * ldb+ j]);
                    
if(max_err< err) max_err= err;
                    average_err
+= err;
                }
            }
        }

        printf(
"Max error: %g Average error: %g\n",
            max_err, average_err
/ (n* n));
    }
 

这个函式计算两个矩阵的最大相对误差和平均相对误差,并把结果印出来。

  最后是 CUDA 的矩阵乘法的部份:

#define NUM_THREADS 256

    clock_t matmultCUDA(
constfloat* a,int lda,
        
constfloat* b,int ldb, float* c,int ldc, int n)
    {
        
float*ac, *bc, *cc;
        clock_t start, end;

        start
= clock();
        cudaMalloc((
void**)&ac, sizeof(float)* n * n);
        cudaMalloc((
void**)&bc, sizeof(float)* n * n);
        cudaMalloc((
void**)&cc, sizeof(float)* n * n);

        cudaMemcpy2D(ac,
sizeof(float)* n, a, sizeof(float)* lda,
            
sizeof(float)* n, n, cudaMemcpyHostToDevice);
        cudaMemcpy2D(bc,
sizeof(float)* n, b, sizeof(float)* ldb,
            
sizeof(float)* n, n, cudaMemcpyHostToDevice);

        
int blocks= (n + NUM_THREADS - 1)/ NUM_THREADS;
        matMultCUDA
<<<blocks* n, NUM_THREADS>>>
            (ac, n, bc, n, cc, n, n);

        cudaMemcpy2D(c,
sizeof(float)* ldc, cc, sizeof(float)* n,
        
sizeof(float)* n, n, cudaMemcpyDeviceToHost);

        cudaFree(ac);
        cudaFree(bc);
        cudaFree(cc);

        end
= clock();

        
return end- start;
    }

这个函式相当单纯,就是在显卡内存中配置存放矩阵的内存,然后把主内存中的矩阵数据复制到显卡内存上。不过,因为我们的矩阵乘法函式可以指定 pitch(即 lda、ldb、和 ldc),所以如果用一般的 cudaMemcpy 函式来复制内存的话,会需要每个 row 都分开复制,那会需要呼叫很多次 cudaMemcpy 函式,会使效率变得很差。因此,在这里我们用了一个新的 cudaMemcpy2D 函式,它是用来复制二维数组,可以指定数组的 pitch。这样就可以透过一次函数调用就可以了。

  进行计算的 kernel 如下:

 __global__ staticvoid matMultCUDA(constfloat* a, size_t lda,
        
constfloat* b, size_t ldb,float* c, size_t ldc,int n)
    {
        
constint tid = threadIdx.x;
        
constint bid = blockIdx.x;
        
constint idx = bid * blockDim.x+ tid;
        
constint row = idx / n;
        
constint column = idx % n;
        
int i;

        
if(row< n && column < n) {
            
float t= 0;
            
for(i= 0; i< n; i++) {
                t
+= a[row* lda + i] * b[i* ldb + column];
            }
            c[row
* ldc+ column] = t;
        }
    }

这个函式一开始先从 bid 和 tid 计算出这个 thread 应该计算的 row 和 column,在判断 row 和 column 在范围内之后,就直接进行计算,并把结果写到 c 矩阵中,是非常单纯的函式。

  在 GeForce 8800GT 上实际执行的结果如下:

  Max error: 2.01484e-006 Average error: 3.36637e-007

  Time used: 1.1560 (1.73 GFLOPS)

  可以看到两个问题:

  很明显的,执行效率相当低落。

  最大相对误差偏高。理想上应该要低于 1e-6。

  计算结果的误差偏高的原因是,在 CPU 上进行计算时,我们使用 double(即 64 bits 浮点数)来累进计算过程,而在 GPU 上则只能用 float(32 bits 浮点数)。在累加大量数字的时候,由于累加结果很快会变大,因此后面的数字很容易被舍去过多的位数。

  由于 CUDA 的浮点数运算,在进行加、减、乘法时是符合 IEEE 754 规定的精确度的,因此,我们可以利用 Kahan's Summation Formula 来提高精确度。把程序改成:

if(row < n&& column < n) {
        
float t= 0;
        
float y= 0;
        
for(i= 0; i< n; i++) {
            
float r;
            y
-= a[row* lda + i] * b[i* ldb + column];
            r
= t- y;
            y
= (r- t) + y;
            t
= r;
        }
    }

修改后的程序的执行结果是:

  Max error: 1.19209e-007 Average error: 4.22751e-008

  Time used: 1.1560 (1.73 GFLOPS)

  可以看到相对误差有很大的改善,效率则没什么变化。

  由于 Kahan's Summation Formula 需要的运算量提高,但是效率却没有什么改变,可以看出这个 kernel 主要的瓶颈应该是在内存的存取动作上。这是因为有大量的内存读取是重复的。例如,矩阵 a 的一个 row 在每次进行计算时都被重复读入,但这是相当浪费的。这样的计算方式,总共需要读取 2*n3 次内存。如果让一个 row 只需要读入一次的话,就可以减到为 n3+n2 次。
 

  第一个改良

  和我们的第一个 CUDA 程序一样,我们可以利用 shared memory 来储存每个 row 的数据。不过,因为只有同一个 block 的 threads 可以共享 shared memory,因此现在一个 row 只能由同一个 block 的 threads 来进行计算。另外我们也需要能存放一整个 row 的 shared memory。因此,把先把呼叫 kernel 的部份改成:

  matMultCUDA<<>>

  (ac, n, bc, n, cc, n, n);

  kernel 的部份则改成:

__global__ staticvoid matMultCUDA(constfloat* a, size_t lda,
        
constfloat* b, size_t ldb,float* c, size_t ldc,int n)
    {
        
extern __shared__float data[];
        
constint tid = threadIdx.x;
        
constint row = blockIdx.x;
        
int i, j;

        
for(i= tid; i < n; i += blockDim.x) {
          data[i]
= a[row* lda + i];
        }

        __syncthreads();

        
for(j= tid; j < n; j += blockDim.x) {
          
float t= 0;
            
float y= 0;
            
for(i= 0; i< n; i++) {
              
float r;
              y
-= data[i]* b[i * ldb + j];
                r
= t- y;
                y
= (r- t) + y;
              t
= r;
            }
            c[row
* ldc+ j] = t;
        }
    }

第一个部份先把整个 row 读到 shared memory 中,而第二个部份则进行计算,并没有太大的变化。主要的差别是现在一个 row 只由一个 block 进行计算。

  在 GeForce 8800GT 上,执行的结果是:

  Max error: 1.19209e-007 Average error: 4.22751e-008

  Time used: 0.4220 (4.74 GFLOPS)

  很明显的,计算的结果并没有改变,不过速度则提高了超过一倍。虽然如此,但是这样的效率仍不尽理想,因为理论上 GeForce 8800GT 有超过 300GFLOPS 的运算性能。即使是把 Kahan's Summation Formula 所需要的额外运算考虑进去,这样的效率仍然连理论最大值的十分之一都不到。

  会有这样的结果,原因其实还是同样的:对内存的存取次数太多了。虽然现在 A 矩阵的 row 的数据已经不再需要重复读取,但是 B 矩阵的 column 的数据仍然一直被重复读取。

  另一个问题比较不是那么明显:对 B 矩阵的读取,虽然看起来不连续,但实际上它是连续的。这是因为不同的 thread 会读取不同的 column,因此同时间每个 thread 读取的各个 column 加起来,就是一个连续的内存区块。那么,为什么效率还是不佳呢?这是因为,GPU 上的内存控制器,从某个固定的倍数地址开始读取,才会有最高的效率(例如 16 bytes 的倍数)。由于矩阵大小并不是 16 的倍数(这里使用的是 1000x1000 的矩阵),所以造成效率不佳的情形。

  要解决这个问题,我们可以在 cudaMalloc 的时候稍微修改一下,让宽度变成 适当的倍数就可以了。但是,适当的倍数是多少呢?幸运的是,我们并不需要知道这些细节。CUDA 提供了一个 cudaMallocPitch 的函式,可以自动以最佳的倍数来配置内存。因此,我们可以把 cudaMalloc 的部份改成:

size_t pitch_a, pitch_b, pitch_c;
    cudaMallocPitch((void**)&ac, &pitch_a, sizeof(float)* n, n);
    cudaMallocPitch((
void**)&bc, &pitch_b, sizeof(float)* n, n);
    cudaMallocPitch((
void**)&cc, &pitch_c, sizeof(float)* n, n);

cudaMallocPitch 函式会以适当的倍数配置内存,并把配置的宽度传回。因此,在把矩阵复制到显卡内存上时,要使用它传回的宽度:

cudaMemcpy2D(ac, pitch_a, a, sizeof(float) * lda,
        
sizeof(float) * n, n, cudaMemcpyHostToDevice);
    cudaMemcpy2D(bc, pitch_b, b,
sizeof(float) * ldb,
        
sizeof(float) * n, n, cudaMemcpyHostToDevice);

呼叫 kernel 的部份也需要修改:

  matMultCUDA<<>>

  (ac, pitch_a / sizeof(float), bc, pitch_b / sizeof(float),

  cc, pitch_c / sizeof(float), n);

  同样的,把计算结果复制回到主内存时,也要使用传回的宽度值:

  cudaMemcpy2D(c, sizeof(float) * ldc, cc, pitch_c,

  sizeof(float) * n, n, cudaMemcpyDeviceToHost);

  这样就完成了。Kernel 部份则不需要修改。

  这样的修改有多大的效果呢?在 GeForce 8800GT 上执行,结果如下:

  Max error: 1.19209e-007 Average error: 4.22751e-008

  Time used: 0.1250 (16.00 GFLOPS)

  可以看到,执行速度又再大幅提高了三倍多!而这只是把内存的配置方式稍微修改一下而已。

  虽然执行速度提高了很多,但是,和前面提到的理论值相比,其实还是有相当的差距。这是因为,前面也提到过,这样的做法需要 n3+n2 次的内存读取,和 n2 次的内存写入动作。由于 n = 1000,每个数字的大小是 32 bits,所以总共的内存存取数据量约为 4GB。除以实际执行的时间 0.125 秒,得到的带宽数值是约 32GB/s,这已经接近 GeForce 8800GT 显卡内存的带宽了。由于我们计算时间的时候,把配置内存、以及数据的复制动作也计算进去,因此实际上花费在 kernel 的时间是更短的(约 0.09 秒)。因此,可以很明显的看出,这个程序的效率,是受限于内存带宽的。

  进一步的改良

  上一节的结论显示出,矩阵乘法的程序,效率是受限于内存带宽的。那有没有办法降低内存的存取次数呢?答案当然是有的,不然就不会有这一节了 :)

  要进一步降低内存带宽的使用,可以注意到,在上一节的方法中,虽然 A 矩阵的存取次数被减至最低,但是 B 矩阵的存取次数并没有减少。这是因为我们只将 A 矩阵的 row 加载到 shared memory 中,但是 B 矩阵的 column 也是有被重复使用的。理想上应该也可以避免重复加载才对。不过,由于 B 矩阵的 column 使用的时机,和 A 矩阵的 row 是不同的,所以并不能直接这样做。

  解决方法是 "blocking"。也就是把整个矩阵乘法的动作,切割成很多小矩阵的乘法。例如,要计算 C 矩阵的 (0, 0) ~ (15, 15) 的值,可以把它想成:

  A(0~15, 0~15) * B(0~15, 0~15) + A(0~15,16~31) * B(16~31, 0~15)

  + A(0~15, 32~47) * B(32~47, 0~15) + ...

  这样一来,我们就可以把两个小矩阵加载到 shared memory,则小矩阵本身的乘法就不需要再存取任何外部的内存了!这样一来,假设小矩阵的大小是 k,则实际上需要的内存存取次数就会变成约 2k2(n/k)3 = 2n3/k。

  由于目前 CUDA 每个 block 的 thread 数目最多是 512,因此 k = 16 似乎是一个相当理想的数字(共 256 个 threads)。因此,对于一个 n = 1000 的矩阵来说,我们可以把内存存取的量减少到约 500MB,也就是上一节的存取量的 1/8。理论上,这样应该可以让效率提高八倍(假设没有遇到别的瓶颈)。

  为了方便进行区块的计算,我们让每个 block 有 16x16 个 threads,再建立 (n/16)x(n/16) 个 blocks。把呼叫 kernel 的地方改成:

 int bx = (n + BLOCK_SIZE - 1) / BLOCK_SIZE;
    dim3 blocks(bx, bx);
    dim3 threads(BLOCK_SIZE, BLOCK_SIZE);
    matMultCUDA
<<<blocks, threads>>>(ac, pitch_a / sizeof(float),
        bc, pitch_b
/ sizeof(float), cc, pitch_c / sizeof(float), n);

  BLOCK_SIZE 则是定义成 16。dim3 是 CUDA 的一种数据型态,表示一个 3D 的向量。在这里,我们透过 dim3 来建立 16x16 个 threads 的 block,和 (n/16)x(n/16) 个 blocks。

  Kernel 程序的部份,则改成:

__global__ static void matMultCUDA(const float* a, size_t lda,
        
const float* b, size_t ldb, float* c, size_t ldc, int n)
    {
        __shared__
float matA[BLOCK_SIZE][BLOCK_SIZE];
        __shared__
float matB[BLOCK_SIZE][BLOCK_SIZE];
        
const int tidc = threadIdx.x;
        
const int tidr = threadIdx.y;
        
const int bidc = blockIdx.x * BLOCK_SIZE;
        
const int bidr = blockIdx.y * BLOCK_SIZE;
        
int i, j;

        
float results = 0;
        
float comp = 0;

        
for(j = 0; j < n; j += BLOCK_SIZE) {
          
if(tidr + bidr < n && tidc + j < n) {
              matA[tidr][tidc]
= a[(tidr + bidr) * lda + tidc + j];
            }
            
else {
              matA[tidr][tidc]
= 0;
            }

            
if(tidr + j < n && tidc + bidc < n) {
              matB[tidr][tidc]
= b[(tidr + j) * ldb + tidc + bidc];
            }
            
else {
              matB[tidr][tidc]
= 0;
            }

          __syncthreads();

            
for(i = 0; i < BLOCK_SIZE; i++) {
              
float t;
              comp
-= matA[tidr][i] * matB[i][tidc];
                t
= results - comp;
                comp
= (t - results) + comp;
              results
= t;
            }

          __syncthreads();
        }

        
if(tidr + bidr < n && tidc + bidc < n) {
            c[(tidr
+ bidr) * ldc + tidc + bidc] = results;
        }
    }

注意到因为我们现在使用 16x16 的 threads,因此 threadIdx 变量可以取得 threadIdx.x 和 threadIdx.y,范围分别是 0 ~ 15。blockIdx.x 和 blockIdx.y 变量也是同样的情形,范围分别是 0 ~ n/16。

  在程序中,因为矩阵的大小不一定会是 16 的倍数,因此需要使用 if 判断式检查是否超出矩阵范围。

  这个版本在 GeForce 8800GT 上的执行结果如下:

  Max error: 1.19209e-007 Average error: 4.22751e-008

  Time used: 0.0780 (25.64 GFLOPS)

  速度虽然提高了,但是似乎并没有达到预期中的八倍。当然,前面提到过,我们在计算时间时,把一些复制内存、配置内存的动作也计算在内,这些动作的时间并不会缩短。实际上 kernel 的运行时间,大约是 0.053 秒左右(约略相当于 38GFLOPS),比上一节的版本快了将近一倍。

  如果这一版程序已经不再限于内存带宽,那为什么没有达到预期的效率呢?这是因为这一版程序已经是限于指令周期了。除了使用 Kahan's Summation Formula 会需要更多的运算之外,程序中也有大量计算矩阵地址的乘法等等,这都会需要花费运算资源。另外,那些用来判断超出矩阵范围的 if 判断式,也会有一定的影响。

  要把那些 if 判断式去掉,有一个方法是,在配置内存时,就配置成 16 的倍数,并在复制矩阵到显卡内存之前,先将它清为 0。如下所示:

int newn = ((n + BLOCK_SIZE - 1 ) / BLOCK_SIZE) * BLOCK_SIZE;

    cudaMallocPitch((
void ** ) & ac, & pitch_a,
        
sizeof ( float ) * newn, newn);
   cudaMallocPitch((
void ** ) & bc, & pitch_b,
        
sizeof ( float ) * newn, newn);
   cudaMallocPitch((
void ** ) & cc, & pitch_c,
        
sizeof ( float ) * newn, newn);

   cudaMemset(ac,
0 , pitch_a * newn);
   cudaMemset(bc,
0 , pitch_b * newn);

  这样一来,我们就可以把 kernel 中的 if 判断式都移除了:

__global__ static void matMultCUDA( const float * a, size_t lda,
        
const float * b, size_t ldb, float * c, size_t ldc, int n)
    {
        __shared__
float matA[BLOCK_SIZE][BLOCK_SIZE];
        __shared__
float matB[BLOCK_SIZE][BLOCK_SIZE];
        
const int tidc = threadIdx.x;
        
const int tidr = threadIdx.y;
        
const int bidc = blockIdx.x * BLOCK_SIZE;
        
const int bidr = blockIdx.y * BLOCK_SIZE;
        
int i, j;

        
float results = 0 ;
        
float comp = 0 ;

        
for (j = 0 ; j < n; j += BLOCK_SIZE) {
          matA[tidr][tidc]
= a[(tidr + bidr) * lda + tidc + j];
            matB[tidr][tidc]
= b[(tidr + j) * ldb + tidc + bidc];

            __syncthreads();

          
for (i = 0 ; i < BLOCK_SIZE; i ++ ) {
              
float t;
                comp
-= matA[tidr][i] * matB[i][tidc];
                t
= results - comp;
              comp
= (t - results) + comp;
              results
= t;
            }

          __syncthreads();
        }

        c[(tidr
+ bidr) * ldc + tidc + bidc] = results;
    }

  这个版本的执行结果是:

  Max error: 1.19209e-007 Average error: 4.22751e-008

  Time used: 0.0780 (25.64 GFLOPS)

  似乎没有改善。不过,实际上 kernel 的运行时间已经减少到 0.042 秒(约略相当于 48GFLOPS)。

  结论

  有些读者可能会想,如果把 block 再变得更大(例如 32x32)是否会有帮助呢?当然,由于最后的程序已经不再是受限于内存带宽(在 0.042 秒内存取 500MB 的数据约相当于 12GB/s 的带宽),所以把 block 再加大并不会有帮助了。而且,由于一个 block 内的 thread 数目最多只能到 512 个,将 block 变大也会造成很多额外负担。而且 shared memory 的大小也有限制(GeForce 8800GT 的 shared memory 大小限制是 16384 bytes),所以也不能任意增加 block 的大小。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CUDA 矩阵乘法优化 的相关文章

  • Ubuntu 11.10/12.04 上的 CUDA“无兼容设备”错误

    一段时间以来 我一直在尝试在我的笔记本电脑上设置 Ubuntu 环境来进行 CUDA 编程 我目前双启动 Windows 8 和 Ubuntu 12 04 并想在 Ubuntu 上安装 CUDA 5 该笔记本电脑配有 GeForce GT
  • cudaMemcpyToSymbol 与 cudaMemcpy [关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 help reopen questions 我试图找出
  • 使用 CUDA __device__ 函数时出现链接器错误 2005 和 1169(多重定义的符号)(默认情况下应内联)

    这个问题与以下问题有很大关系 A 如何将CUDA代码分成多个文件 https stackoverflow com questions 2090974 how to separate cuda code into multiple files
  • 在新线程中调用支持 CUDA 的库

    我编写了一些代码并将其放入它自己的库中 该库使用 CUDA 在 GPU 上进行一些处理 我正在使用 Qt 构建 GUI 前端 作为加载 GUI 的一部分 我调用 CUresult res CUdevice dev CUcontext ctx
  • Cuda Bayer/CFA 去马赛克示例

    我编写了一个 CUDA4 Bayer 去马赛克例程 但它比在 16 核 GTS250 上运行的单线程 CPU 代码慢 块大小是 16 16 图像暗淡是 16 的倍数 但更改此值并不会改善它 我做了什么明显愚蠢的事情吗 calling rou
  • cudaMemcpyToSymbol 的问题

    我正在尝试复制到恒定内存 但我不能 因为我对 cudaMemcpyToSymbol 函数的用法有误解 我正在努力追随this http developer download nvidia com compute cuda 4 1 rel t
  • 使用 QuasirandomGenerator (对于傻瓜来说)

    我是 CUDA 的新手 我正在努力在内核中生成随机数 我知道有不同的实现 而且 在 SDK 4 1 中有一个 Niederreiter 拟随机序列生成器的示例 我不知道从哪里开始 我有点悲伤 感觉自己像个傻瓜 有人可以制作一个使用 Nied
  • cuda 共享内存 - 结果不一致

    我正在尝试并行缩减以对 CUDA 中的数组求和 目前我传递一个数组来存储每个块中元素的总和 这是我的代码 include
  • 传递给 CUDA 的结构中的指针

    我已经搞砸了一段时间了 但似乎无法正确处理 我正在尝试将包含数组的对象复制到 CUDA 设备内存中 然后再复制回来 但当我遇到它时我会跨过那座桥 struct MyData float data int dataLen void copyT
  • 如何优化这个 CUDA 内核

    我已经分析了我的模型 似乎该内核约占我总运行时间的 2 3 我一直在寻找优化它的建议 代码如下 global void calcFlux double concs double fluxes double dt int idx blockI
  • 如何在 Visual Studio 2010 中设置 CUDA 编译器标志?

    经过坚持不懈的得到error identifier atomicAdd is undefined 我找到了编译的解决方案 arch sm 20旗帜 但是如何在 VS 2010 中传递这个编译器标志呢 我已经尝试过如下Project gt P
  • 如何在 CUDA 中执行多个矩阵乘法?

    我有一个方阵数组int M 10 以便M i 定位第一个元素i th 矩阵 我想将所有矩阵相乘M i 通过另一个矩阵N 这样我就收到了方阵数组int P 10 作为输出 我看到有不同的可能性 分配不同元素的计算M i 到不同的线程 例如 我
  • 无法在 CUDA 中执行设备内核

    我正在尝试在全局内核中调用设备内核 我的全局内核是矩阵乘法 我的设备内核正在查找乘积矩阵每列中的最大值和索引 以下是代码 device void MaxFunction float Pd float max int x threadIdx
  • CUDA 常量内存是否应该被均匀地访问?

    我的 CUDA 应用程序的恒定内存小于 8KB 既然它都会被缓存 我是否需要担心每个线程访问相同的地址以进行优化 如果是 如何确保所有线程同时访问同一地址 既然它都会被缓存 我是否需要担心每个线程访问相同的地址以进行优化 是的 这缓存本身每
  • 如何在cmake中添加cuda源代码的定义

    我使用的是 Visual Studio 2013 Windows 10 CMake 3 5 1 一切都可以使用标准 C 正确编译 例如 CMakeLists txt project Test add definitions D WINDOW
  • cuda中内核的并行执行

    可以说我有三个全局数组 它们已使用 cudaMemcpy 复制到 GPU 中 但 c 中的这些全局数组尚未使用 cudaHostAlloc 分配 以便分配页面锁定的内存 而不是简单的全局分配 int a 100 b 100 c 100 cu
  • “gld/st_throughput”和“dram_read/write_throughput”指标之间有什么区别?

    在 CUDA 可视化分析器版本 5 中 我知道 gld st requested throughput 是应用程序请求的内存吞吐量 然而 当我试图找到硬件的实际吞吐量时 我很困惑 因为有两对似乎合格的指标 它们是 gld st throug
  • OS X 10.8 上的 PyCuda / 多处理问题

    我正在开发一个项目 将计算任务分配给多个 python 进程 每个进程都与其自己的 CUDA 设备关联 生成子进程时 我使用以下代码 import pycuda driver as cuda class ComputeServer obje
  • cuda-gdb 错误消息

    我尝试使用 cuda gdb 调试我的 CUDA 应用程序 但遇到了一些奇怪的错误 我设置了选项 g G O0构建我的应用程序 我可以在没有 cuda gdb 的情况下运行我的程序 但没有得到正确的结果 因此我决定使用 cuda gdb 但
  • 大型跨平台软件项目的技巧/资源

    我将开始一个大型软件项目 涉及跨平台 GUI 和大量的数字运算 我计划用 C 和 CUDA 编写大部分应用程序后端 并用 Qt4 编写 GUI 我计划使用 Make 作为我的构建系统 这将是一个只有两名开发人员的项目 一旦我相对深入地了解它

随机推荐

  • ctfshow 萌新web10-21

    ctfshow 萌新web10 21 web10 题目提示flag在congfig php中 php中作为执行系统命令的函数 system passthru exec shell exec popen proc open pcntl exe
  • springboot(三) 设置事务管理

    设置事务管理 在Spring Boot中推荐使用 Transactional注解来申明事务 首先需要导入依赖
  • [管理与领导-69]:IT基层管理者 - 辅助技能 - 4- 职业发展规划 - 评估自己、下属、老板的职业性格

    目录 前言 一 心理学上性格 1 1 心理学 1 2 知情意行 1 3 心理学性格 1 4 四大生理人格特征 1 4 1 外向型 1 4 2 内向型 1 5 大五人格特质理论 二 霍兰德社会职业兴趣类型 2 1 霍兰德六型的简要描述 2 2
  • Java 代理模式之静态代理与动态代理

    1 代理模式 代理模式给某一个对象提供一个代理对象 并由代理对象控制对原对象的引用 通俗的来讲代理模式就是我们生活中常见的中介 代理模式的目的 1 通过引入代理对象的方式来间接访问目标对象 防止直接访问目标对象给系统带来的不必要复杂性 2
  • PyQt5 的textedit下的setPlainText()和setText区别

    setPlainText 是纯文本显示 setText 是可以设置为纯文本之外 还可以设置为一些简单的html格式的字符串 当然这些字符串是修饰一个字符串的
  • maven报错You must specify a valid lifecycle phase or a goal in the format

    解决 pom xml文件 标签里面加上
  • golang 解决模块化下载慢、下载失败timeout

    1 在idea编辑器中配置go语言模块化代理 2 配置环境变量 export GOPROXY https goproxy io 然后再执行go get或者直接运行
  • java中的二维数组_Java编程中二维数组的初始化和基本操作实例

    Java二维数组的声明和初始化 1 二维数组可以看成以数组为元素的数组 2 Java中二维数组的声明和初始化应按照从高维到低维的顺序进行 实例 静态初始化 Array2 java 程序代码 public class Array2 publi
  • 浅谈Hadoop体系和MPP体系

    浅谈Hadoop体系和MPP体系 引言 如题 在大数据发展至今 为了应对日益繁多的数据分析处理 和解决客户各种奇思妙 怪 想需求 形形色色的大数据处理的框架和对应的数据存储手段层出不穷 有老当益壮的Hadoop体系 依靠Hadoop巨大的社
  • 基于STM32和EV1527的无线接收解码程序

    一 1527的数据帧结构 无线遥控的编码 从编码类型上来说 分为2类 一类是固定码 也就是编码芯片的地址是不变的 芯片型号以 EV1527 PT2262 为代表 另一种是滚动码 芯片的地址码是变化的 芯片以HS300 HS301为代表 1
  • 数据结构顺序栈C++实现

    栈 先入后出 根据存储方式可以分为顺序栈和链式栈 顺序栈的存储基于数组 链式栈的存储基于链表 关于链表的实现可以参考上一篇博客 https blog csdn net Brillian123 article details 12354964
  • 模糊控制理论基础

    模糊控制理论基础 模糊控制的定义 模糊控制器的输出是通过观察过程的状态和一些如何 控制过程的规则的推理得到的 它包括测量信息的模糊化 推理机制 输出模糊集的精确化 1 模糊集合 普通集合 具有某种特定属性的对象的全体 确定性 模糊集合 隶属
  • 机器视觉运动控制一体机应用例程(十一)产品全局外观检测

    前面讲述的外观检测的课程中 我们都是以矩形ROI区域框选我们需要检测的外观表面范围 但是很多产品外形通常都不是规则的矩形或者圆形 用矩形或者圆形ROI区域很难对产品的外观进行全局检测 可能会遗漏掉某些细节部分 因此 我们引入了将提取的产品轮
  • 全景解密量子信息技术:高层集中学习,国家战略,三大领域一文看懂

    来源 智东西 内参来源 中国信通院 IPRdaily中文网 10月16日下午 高层就量子科技研究相关前景举行了一次会议 强调当今世界正经历百年未有之大变局 科技创新是其中一个关键变量 要充分认识推动量子科技发展的重要性 加强量子科技发展战略
  • numpy之索引和切片

    索引和切片 一维数组 一维数组很简单 基本和列表一致 它们的区别在于数组切片是原始数组视图 这就意味着 如果做任何修改 原始都会跟着更改 这也意味着 如果不想更改原始数组 我们需要进行显式的复制 从而得到它的副本 copy import n
  • json转xml、xml转json

    一 jar包 所需jar包 二 xml2json 方法一 使用json lib 代码 public String xml2json String xml 创建XMLSerializer对象 XMLSerializer xmlSerializ
  • 计算机房面积标准,机房建设标准与规范[共14页].pdf

    电子信息系统机房设计规范 施行日期 2009 年 6 月 1 日 1 总则 1 0 1 为规范电子信息系统机房设计 确保电子信息系统安全 稳定 可靠地运行 做到技术先进 经 济合理 安全适用 节能环保 制定本规范 1 0 2 本规范适用于建
  • GPU工作原理与理解

    本周看GPU看得有点儿乱 GPU英文全称Graphic Processing Unit 中文翻译为 图形处理器 由于GPU具有高并行结构 highly parallel structure 所以GPU在处理图形数据和复杂算法方面拥有比CPU
  • linux并行计算环境搭建与使用,Windows和Linux系统下并行计算环境MPI和OpenMP的搭建...

    windows平台下在Visual Studio2019配置MPI环境 MPI下载安装 项目配置 右击项目 gt 属性 进行配置 右上角 gt 配置管理器 gt 活动解决方案平台 选择 x64 VC 目录 gt 包含目录 添加 C Prog
  • CUDA 矩阵乘法优化

    矩阵乘法 为了单纯起见 我们这里以方形的矩阵为例子 基本上 假设有两个矩阵 A 和 B 则计算 AB C 的方法如下 for j 0 j lt n j C i j 0 for k 0 k lt n k C i j A i k B k j 一