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

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

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