繁体   English   中英

为什么此代码在GPU上比CPU慢十倍?

[英]Why is this code ten times slower on the GPU than CPU?

我有一个问题,可以归结为对一组矩阵的每个元素执行一些算术运算。 我认为这听起来像一种计算,可以从转移到GPU上受益匪浅。 但是,我仅成功地将计算速度降低了10倍!

这是我的测试系统的细节:

  • 作业系统:Windows 10
  • CPU:酷睿i7-4700MQ @ 2.40 GHz
  • GPU:GeForce GT 750M(计算能力3.0)
  • CUDA SDK:v7.5

下面的代码在CPU和GPU上执行与我的生产代码等效的计算。 后者始终比我的机器慢十倍(CPU约650ms; GPU约7s)。

我尝试过更改网格和块的大小; 我增加和减少了传递给GPU的数组的大小; 我已经通过可视分析器运行它; 我尝试使用整数数据而不是使用双精度数据,但是无论我做什么,GPU版本始终比同等CPU慢得多。

那么为什么GPU版本这么慢,而我上面没有提到,有什么变化,我可以尝试改善其性能吗?

这是我的命令行: nvcc source.cu -o CPUSpeedTest.exe -arch=sm_30

这是source.cu的内容:

#include <iostream>
#include <windows.h>
#include <cuda_runtime_api.h>

void AdjustArrayOnCPU(double factor1, double factor2, double factor3, double denominator, double* array, int arrayLength, double* curve, int curveLength)
{
    for (size_t i = 0; i < arrayLength; i++)
    {
        double adjustmentFactor = factor1 * factor2 * factor3 * (curve[i] / denominator);
        array[i] = array[i] * adjustmentFactor;
    }
}

__global__ void CudaKernel(double factor1, double factor2, double factor3, double denominator, double* array, int arrayLength, double* curve, int curveLength)
{
    int idx = threadIdx.x + blockIdx.x * blockDim.x;

    if (idx < arrayLength)
    {
        double adjustmentFactor = factor1 * factor2 * factor3 * (curve[idx] / denominator);
        array[idx] = array[idx] * adjustmentFactor;
    }
}

void AdjustArrayOnGPU(double array[], int arrayLength, double factor1, double factor2, double factor3, double denominator, double curve[], int curveLength)
{
    double *dev_row, *dev_curve;

    cudaMalloc((void**)&dev_row, sizeof(double) * arrayLength);
    cudaMalloc((void**)&dev_curve, sizeof(double) * curveLength);

    cudaMemcpy(dev_row, array, sizeof(double) * arrayLength, cudaMemcpyHostToDevice);
    cudaMemcpy(dev_curve, curve, sizeof(double) * curveLength, cudaMemcpyHostToDevice);

    CudaKernel<<<100, 1000>>>(factor1, factor2, factor3, denominator, dev_row, arrayLength, dev_curve, curveLength);

    cudaMemcpy(array, dev_row, sizeof(double) * arrayLength, cudaMemcpyDeviceToHost);

    cudaFree(dev_curve);
    cudaFree(dev_row);
}

void FillArray(int length, double row[])
{
    for (size_t i = 0; i < length; i++) row[i] = 0.1 + i;
}

int main(void)
{
    const int arrayLength = 10000;

    double arrayForCPU[arrayLength], curve1[arrayLength], arrayForGPU[arrayLength], curve2[arrayLength];;

    FillArray(arrayLength, curve1);
    FillArray(arrayLength, curve2);

    ///////////////////////////////////// CPU Version ////////////////////////////////////////

    LARGE_INTEGER StartingTime, EndingTime, ElapsedMilliseconds, Frequency;

    QueryPerformanceFrequency(&Frequency);
    QueryPerformanceCounter(&StartingTime);

    for (size_t iterations = 0; iterations < 10000; iterations++)
    {
        FillArray(arrayLength, arrayForCPU);
        AdjustArrayOnCPU(1.0, 1.0, 1.0, 1.0, arrayForCPU, 10000, curve1, 10000);
    }

    QueryPerformanceCounter(&EndingTime);

    ElapsedMilliseconds.QuadPart = EndingTime.QuadPart - StartingTime.QuadPart;
    ElapsedMilliseconds.QuadPart *= 1000;
    ElapsedMilliseconds.QuadPart /= Frequency.QuadPart;
    std::cout << "Elapsed Milliseconds: " << ElapsedMilliseconds.QuadPart << std::endl;

    ///////////////////////////////////// GPU Version ////////////////////////////////////////

    cudaEvent_t start, stop;
    cudaEventCreate(&start);
    cudaEventCreate(&stop);

    cudaEventRecord(start);

    for (size_t iterations = 0; iterations < 10000; iterations++)
    {
        FillArray(arrayLength, arrayForGPU);
        AdjustArrayOnGPU(arrayForGPU, 10000, 1.0, 1.0, 1.0, 1.0, curve2, 10000);
    }

    cudaEventRecord(stop);
    cudaEventSynchronize(stop);

    float elapsedTime;
    cudaEventElapsedTime(&elapsedTime, start, stop);

    std::cout << "CUDA Elapsed Milliseconds: " << elapsedTime << std::endl;

    cudaEventDestroy(start);
    cudaEventDestroy(stop);

    return 0;
}

这是CUDASpeedTest.exe输出的示例

Elapsed Milliseconds: 565
CUDA Elapsed Milliseconds: 7156.76

对于大多数使用CUDA的开发人员来说,接下来发生的事情可能会令人尴尬地显而易见,但对于其他人(如我本人)却是有价值的,因为他们对这项技术是新的。

GPU代码比CPU慢十倍,因为GPU代码表现出了完美的性能破坏特性。

GPU代码花费大部分时间在GPU上分配内存,将数据复制到设备,执行非常非常简单的计算(无论运行的处理器类型如何,这都非常快),然后将数据从设备复制回给主机。

如评论中所述,如果要处理的数据结构的大小存在上限,则GPU上的缓冲区可以精确分配一次并重新使用。 在上面的代码中,这将GPU至CPU的运行时间从10:1降低到4:1。

剩余的性能差异归因于以下事实:由于其简单性,CPU能够在很短的时间内连续执行数百万次所需的计算。 在上面的代码中,计算涉及从数组中读取值,进行一些乘法运算,最后是对数组元素的赋值。 这种简单的操作必须执行数百万次,才能并行执行,其好处超过了将数据传输到GPU并返回的必要时间。 在我的测试系统上,盈亏平衡点是一百万个数组元素,GPU和CPU在(大约)相同的时间内执行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM