CPU超越GPU

时间:2015-11-17 17:16:45

标签: cuda gpu

我对理解SM中的CUDA线程处理有一些疑问。以下命题是从我读过的内容中推断出来的: 我的GPU是:GTX650Ti。

  1. 块中的线程数必须始终是Warp大小的倍数。因此,每个SM可以处理32个线程的块(warpSize)。
  2. 我的SM可以同时计算的最大线程数是2048(maxThreadsPerMultiProcessor)。
  3. 由于每个SM中可以同时计算2048个线程,并且warpSize为32,因此可以同时计算64个块。
  4. 由于我的GPU有4个SM,因此可以同时执行64X4 = 256个线程块。
  5. 因此,内核启动可能具有以下启动参数:<<<<<<<<>>>>>每个内核启动都会调用8192个线程。
  6. 是吗?

    因此,如果我在我的内核中有一个10M元素的向量,这意味着我必须在每个8192个元素的1221个作业(内核启动)中对其进行分割?

    这个任务的产生是因为我正在比较顺序程序和我的CUDA程序之间的时间性能。但我只能看到CPU超越了GPU。我还尝试使用最大启动参数,例如<<<<<<<<<<<<<<<<<结果非常相似。

    那么,我在做什么或配置错误?

    这是我使用的代码:

    #include "cuda_runtime.h"
    #include "device_launch_parameters.h"
    #include <math.h>
    #include <time.h>
    #include "C:\cdev.h"
    #include <thrust/device_vector.h>
    
    using namespace thrust;
    using namespace std;
    
    #define N (1024 * 16384)
    
    cdev devices;
    
    __global__ void eucliDist(double *c, double *a, double *b)
    {
        int i = blockDim.x * blockIdx.x + threadIdx.x;
        if (i < N)
            c[i] = sqrt(pow(a[i], 2) + pow(b[i], 2));
    }
    
    int main()
    {
        clock_t start, end;
        double elapsed;
        static double A[N];
        static double B[N];
        for (int i = 0; i < N; i++)
        {
            A[i] = double(i);
            B[i] = double(i);
        }   
        static double C[N];
    
        // Sequential execution of F(x,y) = sqrt((x^2 + y^2))
        start = clock();
        for (int i = 0; i < N; i++)
            C[i] = sqrt(pow(A[i], 2) + pow(B[i], 2));
        end = clock();
        elapsed = double(end - start) / CLOCKS_PER_SEC;
        cout << "Elapsed time for sequential processing is: " << elapsed << " seconds." << endl;
    
        // CUDA Initialization
        unsigned int threadNum;
        unsigned int blockNum;
        cudaError_t cudaStatus;
        threadNum = devices.ID[0].maxThreadsPerBlock;
        blockNum = ceil(double(N) / double(threadNum));
        // Parallel execution with Thrust of F(x,y) = sqrt((x^2 + y^2))
        vector<double> vectorA(N);
        vector<double> vectorB(N);
        for (int i = 0; i < N; i++)
        {
            vectorA[i] = double(i);
            vectorB[i] = double(i);
        }
        vector<double> vectorC(N);
        start = clock();
        device_vector<double> thrustA(N);
        cudaStatus = cudaGetLastError();
        if (cudaStatus != cudaSuccess)
        {
            cerr << "Device vector allocation failed: " << cudaGetErrorString(cudaStatus) << " (thrustA)" << endl;
            cin.get();
            return 1;
        }
        device_vector<double> thrustB(N);
        cudaStatus = cudaGetLastError();
        if (cudaStatus != cudaSuccess)
        {
            cerr << "Device vector allocation failed: " << cudaGetErrorString(cudaStatus) << " (thrustB)" << endl;
            cin.get();
            return 1;
        }
        device_vector<double> thrustC(N);
        cudaStatus = cudaGetLastError();
        if (cudaStatus != cudaSuccess)
        {
            cerr << "Device vector allocation failed: " << cudaGetErrorString(cudaStatus) << " (thrustC)" << endl;
            cin.get();
            return 1;
        }
        thrustA = vectorA;
        cudaStatus = cudaGetLastError();
        if (cudaStatus != cudaSuccess)
        {
            cerr << "Host to device copy failed (Thrust): " << cudaGetErrorString(cudaStatus) << " (vectorA -> thrustA)" << endl;
            cin.get();
            return 1;
        }
        thrustB = vectorB;
        cudaStatus = cudaGetLastError();
        if (cudaStatus != cudaSuccess)
        {
            cerr << "Host to device copy failed (Thrust): " << cudaGetErrorString(cudaStatus) << " (vectorB -> thrustB)" << endl;
            cin.get();
            return 1;
        }
        eucliDist <<<blockNum, threadNum>>>(raw_pointer_cast(thrustC.data()), raw_pointer_cast(thrustA.data()), raw_pointer_cast(thrustB.data()));
        cudaStatus = cudaGetLastError();
        if (cudaStatus != cudaSuccess)
        {
            cerr << "Kernel launch failed (Thrust): " << cudaGetErrorString(cudaStatus) << " (euclidDist)" << endl;
            cin.get();
            return 1;
        }
        thrust::copy(thrustC.begin(), thrustC.end(), vectorC.begin());
        cudaStatus = cudaGetLastError();
        if (cudaStatus != cudaSuccess)
        {
            cerr << "Device to host copy failed: " << cudaGetErrorString(cudaStatus) << " (thrustC -> vectorC)" << endl;
            cin.get();
            return 1;
        }
        end = clock();
        elapsed = double(end - start) / CLOCKS_PER_SEC;
        cout << "Elapsed time parallel processing is (Thrust): " << elapsed << " seconds." << endl;
    
        cin.get();
        return 0;
    }
    

    建议将不胜感激。

3 个答案:

答案 0 :(得分:3)

让我们首先纠正你在问题中发布的很多内容:

  1. 块中的线程数应始终为Warp大小的倍数。 每个SM可以处理32个线程的多个块(warpSize),每个块最多1024个线程(cudaDevAttrMaxThreadsPerBlock)。
  2. 我的SM可以同时计算的最大线程数是2048 (cudaDevAttrMaxThreadsPerMultiProcessor)。
  3. 同时最多可以在SM上驻留16个区块。
  4. 由于我的GPU有4个SM,最多可以有16 x 4 = 64个线程块 同时执行。
  5. 根据资源限制汇总architecture maximum,内核启动参数可以是here以外的任何值。设备上最大驻留线程数为4 x 2048 = 8192个线程。
  6.   

    因此,如果我在我的内核中有一个10M元素的向量,这意味着我必须在每个8192个元素的1221个作业(内核启动)中对其进行分割?

    不,您将在单个内核启动中启动9766个1024个线程的块。或者启动足够的块以完全占用GPU(最多64个,具体取决于资源),并让每个线程处理输入向量的多个元素。

答案 1 :(得分:0)

你应该打破每次操作的时间;你可能每个元素的工作量很少,以至于你花费大部分时间在主机和设备之间来回复制内存。

如果计算确实是问题,那么可能是您尝试进行的操作。 pow(x,2)并不是一种特别有效的方法。虽然这在CPU上很糟糕,但在GPU上却特别糟糕,因为它可能意味着你必须使用特殊的功能单元,并且其中很多都没有,所以它会产生瓶颈,因为它和#39;是您计算的主要部分。

(除此之外:单精度(倒数)平方根在不同的功能单元中处理,具有更多可用的吞吐量)

更糟糕的是,你使用的是双精度浮点数; GPU设计用于处理单精度浮点数。虽然它可以做到双倍精度,但它的吞吐量却要低得多。

因此,你应该

  • 使用x*x而非pow(x,2)
  • 计算正方形
  • 使用float代替double(如果适用于您的应用)

答案 2 :(得分:0)

我的应用程序使用thrust :: device_vector在设备中分配内存。这就是试图在我的计划中进行推力工作的原因。 我终于找到了提高CPU性能的问题和解决方案。这对于决定使用device_vectors而不是数组的其他用户非常有用。

正如@Hurkyl对我说的那样:我正在测量主机和设备之间副本的延迟,反之亦然。所有这些长时间延迟都是由于使用了以下说明:

  1. thrustA = vectorA用于从主机复制到设备。这种复制操作可能会清晰而优雅,但要小心。
  2. thrust :: copy用于从设备复制到主机。此函数非常类似于使用带有std :: vector复制函数的复制。
  3. 这两项操作是我代码中的瓶颈。

    考虑变量:

    vector<double> A;
    device_vector<double> thrustA;
    

    解决方案非常简单。我只是用众所周知的cudaMemcpy()函数替换了这两个指令,即

    从主机复制到设备:

    cudaMemcpy(raw_pointer_cast(thrustA.data()), raw_pointer_cast(A.data()), A.size(), cudaMemcpyHostToDevice);
    

    从设备复制到主机:

    cudaMemcpy(raw_pointer_cast(A.data()), raw_pointer_cast(thrustA.data()), A.size(), cudaMemcpyDeviceToHost);
    

    感谢所有花时间解决我问题的人。你的意见非常丰富,让我更了解CUDA。