C++ CUDA的性能取决于声明变量

C++ CUDA的性能取决于声明变量,c++,performance,cuda,C++,Performance,Cuda,在这种情况下,是否有提高CUDA性能的技巧,例如声明全局/局部变量、参数传递、内存复制 在下面的例子中,我试图找出为什么sum_gpu_FAST和sum_gpu_SLOW两种性能相差太大的原因 在这里您可以看到整个示例代码 #include <iostream> #include <chrono> #define N 10000000 __global__ void sum_gpu_FAST(int (&data)[N][2], int& sum, int

在这种情况下,是否有提高CUDA性能的技巧,例如声明全局/局部变量、参数传递、内存复制

在下面的例子中,我试图找出为什么sum_gpu_FAST和sum_gpu_SLOW两种性能相差太大的原因

在这里您可以看到整个示例代码

#include <iostream>
#include <chrono>
#define N 10000000
__global__
void sum_gpu_FAST(int (&data)[N][2], int& sum, int n) {  // runtime : 2.42342s
    int s = 0;
    for (int i = 0; i < n; i++) 
        s += data[i][0] * 10 + data[i][1];
    sum = s;
}
__global__
void sum_gpu_SLOW(int (&data)[N][2], int& sum, int n) {  // runtime : 436.64ms
    sum = 0;
    for (int i = 0; i < n; i++) {
        sum += data[i][0] * 10 + data[i][1];
    }
}
void sum_cpu(int (*data)[2], int& sum, int n) {
    for (int i = 0; i < n; i++) {
        sum +=  data[i][0] * 10 + data[i][1];
    }
}
int main()
{
    int (*v)[2] = new int[N][2];
    for (int i = 0; i < N; i++)
        v[i][0] = 1, v[i][1] = 3;
    printf ("-CPU------------------------------------------------\n");
    {
        int sum = 0;
        auto start = std::chrono::system_clock::now();
        sum_cpu(v, sum, N);
        auto end   = std::chrono::system_clock::now();
        // print output
        std::cout << sum << " / " << (end-start).count() / 1000000 << "ms" << std::endl;
    }
    printf ("-GPU-Ready------------------------------------------\n");
    int *dev_sum       = nullptr;
    int (*dev_v)[N][2] = nullptr;
    cudaMalloc((void **)&dev_v,   sizeof(int[N][2]));
    cudaMalloc((void **)&dev_sum, sizeof(int));
    cudaMemcpy(dev_v, v, sizeof(int[N][2]), cudaMemcpyHostToDevice);
    printf("-GPU-FAST-------------------------------------------\n");
    {
        int sum = 0;
        auto start = std::chrono::system_clock::now();
        sum_gpu_FAST<<<1, 1>>> (*dev_v, *dev_sum, N);
        cudaDeviceSynchronize(); // wait until end of kernel
        auto end   = std::chrono::system_clock::now();
        // print output
        cudaMemcpy( &sum, dev_sum, sizeof(int), cudaMemcpyDeviceToHost );
        std::cout << sum << " / " << (end-start).count() / 1000000 << "ms" << std::endl;
    }
    printf("-GPU-SLOW-------------------------------------------\n");
    {
        int sum = 0;
        auto start = std::chrono::system_clock::now();
        sum_gpu_SLOW<<<1, 1>>> (*dev_v, *dev_sum, N);
        cudaDeviceSynchronize(); // wait until end of kernel
        auto end   = std::chrono::system_clock::now();
        // print output
        cudaMemcpy( &sum, dev_sum, sizeof(int), cudaMemcpyDeviceToHost );
        std::cout << sum << " / " << (end-start).count() / 1000000 << "ms" << std::endl;
    }
    printf("----------------------------------------------------\n");
    return 0;
}



#包括
#包括
#定义N 10000000
__全球的__
void sum_gpu_FAST(int(&data)[N][2],int&sum,intn){//运行时:2.42342s
int s=0;
对于(int i=0;i
在fast情况下,您正在创建一个局部变量,该变量(可能)包含在寄存器中:

int s = 0;
for (int i = 0; i < n; i++) 
    s += data[i][0] * 10 + data[i][1];
在循环迭代期间,从全局内存进行读取,但唯一的写入操作是对寄存器:

int s = 0;
for (int i = 0; i < n; i++) 
    s += data[i][0] * 10 + data[i][1];
因此,在每次循环迭代时,更新的值都会写入全局内存:

sum = 0;
for (int i = 0; i < n; i++) {
    sum += data[i][0] * 10 + data[i][1];
在上面的fast示例中,我们看到在内核的末尾有一条全局存储(
STG
)指令,就在return语句(
EXIT
)之前,并且在内核中的任何循环之外。虽然我还没有全部展示,但是在快速内核中确实没有其他的
STG
指令,除了末尾的指令。我们在慢速内核的末尾看到了一个不同的故事:

        code for sm_70
                Function : _Z12sum_gpu_SLOWRA10000000_A2_iRii
        .headerflags    @"EF_CUDA_SM70 EF_CUDA_PTX_SM(EF_CUDA_SM70)"
        /*0000*/                   IMAD.MOV.U32 R1, RZ, RZ, c[0x0][0x28] ;       /* 0x00000a00ff017624 */
                                                                                 /* 0x000fd000078e00ff */
...
        /*0460*/                   STG.E.SYS [R2], R7 ;                          /* 0x0000000702007386 */
                                                                                 /* 0x0005e2000010e900 */
        /*0470*/              @!P0 BRA 0x2f0 ;                                   /* 0xfffffe7000008947 */
                                                                                 /* 0x000fea000383ffff */
        /*0480*/                   EXIT ;                                        /* 0x000000000000794d */
                                                                                 /* 0x000fea0003800000 */

慢速内核以循环中的
STG
指令结束循环。慢速内核在整个内核中也有许多
STG
指令的实例,可能是因为编译器展开的缘故。

谢谢您的解释。我将详细检查。要扩展Robert的答案,一般来说,C/C+编译器假设其他线程不读取/修改内容。CUDA编译器通常会假设相反的情况(因为它在设计上是并行的)。因此,在CUDA代码中,通常最好将本地内容显式本地化。