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’

в то время как (Itera<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-интерфейсами, которые вы используете, поэтому с ними мало что можно сделать.

Чтобы улучшить ваш код, я бы предложил сделать следующее.

  1. Вы можете избавиться от A_temp_cpu сохраняя копию устройства на матрице A, Затем вы можете скопировать строки A в ряды A_temp_gpu а также A_temp2_gpu по назначению ядра. Это позволит избежать выполнения первых двух cudaMemcpys.
  2. Вы можете предварительно выделить A_temp_gpu а также A_temp2_gpu вне while цикл с использованием максимально возможного значения itera вместо itera, Это позволит избежать первых двух cudaMallocс внутри петли. То же самое относится и к x_temp,
  3. Насколько я знаю, culaDeviceSgels решает линейную систему уравнений. Я думаю, что вы можете сделать то же самое, используя только API-интерфейсы cuBLAS. Например, вы можете сначала выполнить факторизацию LU, cublasDgetrfBatched() а затем использовать cublasStrsv() два раза, чтобы решить две возникающие линейные системы. Возможно, вы захотите посмотреть, приведет ли это решение к более быстрому алгоритму.
1

Другие решения

Других решений пока нет …