У меня проблема, которая сводится к выполнению некоторой арифметики для каждого элемента набора матриц. Я думал, что это похоже на то, какое вычисление может сильно повлиять на перенос на GPU. Тем не менее, мне удалось только замедлить вычисление в 10 раз!Почему этот код в десять раз медленнее, чем у процессора?
Вот специфика моей тестовой системы:
- ОС: Windows 10
- CPU: Ядро i7-4700MQ @ 2,40 ГГц
- GPU: GeForce GT 750M (вычислительные возможности 3,0)
- CUDA SDK: v7.5
Нижеприведенный код выполняет эквивалентные вычисления в моем производственном коде, на процессоре и на графическом процессоре. Последний последовательно в десять раз медленнее на моей машине (процессор примерно 650 мс, GPU около 7 с).
Я попытался изменить размеры сетки и блока; Я увеличил и уменьшил размер массива, переданного на GPU; Я запустил его через визуальный профайлер; Я пробовал целочисленные данные, а не удваивал, но что бы я ни делал, версия графического процессора всегда значительно медленнее, чем эквивалент процессора.
Так почему же версия 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
Вы заявляете, что запускали код через визуальный профилировщик nvidia, что, по его словам, ограничивало производительность вашего ядра CUDA? –
Вы не измеряете одни и те же операции на устройстве и принимаете стартеры. Распределение и освобождение памяти происходит медленно. Вы также можете стать жертвой пакетной обработки WDDM. Фаза вычисления вашего кода графического процессора, вероятно, составляет около 1% от общего времени выполнения. – talonmies
Когда mallocing на каждой итерации какой-либо товар? –