[英]Why is this code ten times slower on the GPU than CPU?
我有一個問題,可以歸結為對一組矩陣的每個元素執行一些算術運算。 我認為這聽起來像一種計算,可以從轉移到GPU上受益匪淺。 但是,我僅成功地將計算速度降低了10倍!
這是我的測試系統的細節:
下面的代碼在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.