簡體   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