В последнее время я использую 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 кажется слишком сложным, есть ли другой удобный метод для решения методом наименьших квадратов?
я думаю что 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()
два раза, чтобы решить две возникающие линейные системы. Возможно, вы захотите посмотреть, приведет ли это решение к более быстрому алгоритму.Других решений пока нет …