Cuda: решение наименьших квадратов, плохая скорость
В последнее время я использую Cuda для написания алгоритма под названием "Погоня за ортогональным соответствием". В моем уродливом коде Cuda вся итерация занимает 60 секунд, а Eigen lib занимает всего 3 секунды...
В моем коде Матрица A - это [640,1024], а y - [640,1], на каждом шаге я выбираю несколько векторов из A, чтобы создать новую Матрицу с именем A_temp [640,itera], iter=1:500 . Я новый массив MaxDex_Host[] в CPU, чтобы сказать, какой столбец выбрать.
Я хочу получить x_temp[itera,1] из A_temp*x_temp=y, используя метод наименьших квадратов, я использую cula API 'culaDeviceSgels' и API умножения матрицы на векторные кубы.
Таким образом, culaDeviceSgels будет вызывать 500 раз, и я думаю, что это будет быстрее, чем QR.Sovler от Eigen lib.
Я проверил анализ производительности Nisight, и обнаружил, что история занимает много времени. Я инициализирую кублы до итерации и уничтожаю их после получения результата. Итак, я хочу знать, что такое custreamdestory, отличается от cublasdestory?
Основная проблема - это memcpy и функция 'gemm_kernel1x1val' . Я думаю, что эта функция от 'culaDeviceSgels'
while (итера<500): я использую cublasSgemv и cublasIsamax, чтобы получить MaxDex_Host [итера], затем
MaxDex_Host[itera]=pos;
itera++;
float* A_temp_cpu=new float[M*itera]; // matrix all in col-major
for (int j=0;j<itera;j++) // to get A_temp [M,itera] , the MaxDex_Host[] shows the positon of which column of A to chose ,
{
for (int i=0;i<M;i++) //M=640 , and A is 640*1024 ,itera is add 1 each step
{
A_temp_cpu[j*M+i]=A[MaxDex_Host[j]*M+i];
}
}
// I must allocate one more array because culaDeviceSgels will decompose the one input Array , and I want to use A_temp after least-square solving.
float* A_temp_gpu;
float* A_temp2_gpu;
cudaMalloc((void**)&A_temp_gpu,Size_float*M*itera);
cudaMalloc((void**)&A_temp2_gpu,Size_float*M*itera);
cudaMemcpy(A_temp_gpu,A_temp_cpu,Size_float*M*itera,cudaMemcpyHostToDevice);
cudaMemcpy(A_temp2_gpu,A_temp_gpu,Size_float*M*itera,cudaMemcpyDeviceToDevice);
culaDeviceSgels('N',M,itera,1,A_temp_gpu,M,y_Gpu_temp,M);// the x_temp I want is in y_Gpu_temp's return value , stored in the y_Gpu_temp[0]——y_Gpu_temp[itera-1]
float* x_temp;
cudaMalloc((void**)&x_temp,Size_float*itera);
cudaMemcpy(x_temp,y_Gpu_temp,Size_float*itera,cudaMemcpyDeviceToDevice);
Управление памятью у Cuda кажется слишком сложным, есть ли другой удобный метод для решения методом наименьших квадратов?
1 ответ
Я думаю что custreamdestory
а также gemm_kernel1x1val
внутренне вызываются API-интерфейсами, которые вы используете, поэтому с ними мало что можно сделать.
Чтобы улучшить ваш код, я бы предложил сделать следующее.
- Вы можете избавиться от
A_temp_cpu
сохраняя копию устройства на матрицеA
, Затем вы можете скопировать строкиA
в рядыA_temp_gpu
а такжеA_temp2_gpu
по назначению ядра. Это позволит избежать выполнения первых двухcudaMemcpy
s. - Вы можете предварительно выделить
A_temp_gpu
а такжеA_temp2_gpu
внеwhile
цикл с использованием максимально возможного значенияitera
вместоitera
, Это позволит избежать первых двухcudaMalloc
с внутри петли. То же самое относится и кx_temp
, - Насколько я знаю,
culaDeviceSgels
решает линейную систему уравнений. Я думаю, что вы можете сделать то же самое, используя только API cuBLAS. Например, вы можете сначала выполнить факторизацию LU,cublasDgetrfBatched()
а затем использоватьcublasStrsv()
два раза, чтобы решить две возникающие линейные системы. Возможно, вы захотите посмотреть, приведет ли это решение к более быстрому алгоритму.