与 MATLAB 相比,使用 cuSolver 时 SVD 非常慢

2023-12-24

我正在尝试使用gesvd函数来自cuSOLVER我发现它比svdMATLAB 中的函数,对于这两种情况都使用double数组或gpuArray.

C++ 代码 [使用cuSolver]:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <cuda_runtime.h>
#include <cusolverDn.h>
// Macro for timing kernel runs
#define START_METER {\
    cudaEvent_t start, stop;\
    float elapsedTime;\
    cudaEventCreate(&start);\
    cudaEventRecord(start, 0);
#define STOP_METER cudaEventCreate(&stop);\
    cudaEventRecord(stop, 0);\
    cudaEventSynchronize(stop);\
    cudaEventElapsedTime(&elapsedTime, start, stop);\
    printf("Elapsed time : %f ms\n", elapsedTime);\
                }

void cusolverSVD_Test()
{
    const int m = 64;
    const int rows = m;
    const int cols = m;
    /*       | 3.5 0.5 0 |
    *   A = | 0.5 3.5 0 |
    *       | 0   0   2 |
    *
    */
    double A[rows*m];
    for (int i = 0; i < cols; i++)
    {
        for (int j = 0; j < rows; j++)
        {
            A[i*rows + j] = (double)rand() / RAND_MAX;
            if (i == j){
                A[i*rows + j] += 1;
            }
        }
    }

    cusolverDnHandle_t handle;
    cusolverDnCreate(&handle);
    int lwork;

    cusolverDnDgesvd_bufferSize(
        handle,
        rows,
        cols,
        &lwork);

    double *d_A;
    cudaMalloc(&d_A, sizeof(double)*rows*cols);
    cudaMemcpy(d_A, A, sizeof(double)*rows*cols, cudaMemcpyHostToDevice);

    double *d_S;
    cudaMalloc(&d_S, sizeof(double)*rows);

    double *d_U;
    cudaMalloc(&d_U, sizeof(double)*rows*rows);

    double *d_VT;
    cudaMalloc(&d_VT, sizeof(double)*rows*rows);

    double *d_work;
    cudaMalloc(&d_work, sizeof(double)*lwork);

    double *d_rwork;
    cudaMalloc(&d_rwork, sizeof(double)*(rows - 1));

    int *devInfo;
    cudaMalloc(&devInfo, sizeof(int));

    for (int t = 0; t < 10; t++)
    {
        signed char jobu = 'A';
        signed char jobvt = 'A';
        START_METER
            cusolverDnDgesvd(
            handle,
            jobu,
            jobvt,
            rows,
            cols,
            d_A,
            rows,
            d_S,
            d_U,
            rows,
            d_VT,
            rows,
            d_work,
            lwork,
            d_rwork,
            devInfo);
        STOP_METER
    }

    cudaFree(d_A);
    cudaFree(d_rwork);
    cudaFree(d_S);
    cudaFree(d_U);
    cudaFree(d_VT);
    cudaFree(d_work);

}

int main()
{
    cusolverSVD_Test();
}

Output:

Elapsed time : 63.318016 ms
Elapsed time : 66.745316 ms
Elapsed time : 65.966530 ms
Elapsed time : 65.999939 ms
Elapsed time : 64.821053 ms
Elapsed time : 65.184547 ms
Elapsed time : 65.722916 ms
Elapsed time : 60.618786 ms
Elapsed time : 54.937569 ms
Elapsed time : 53.751263 ms
Press any key to continue . . .

**Matlab 代码使用svd功能*:

%% SVD on gpu
A = rand(64, 64) + eye(64);
tic
[~, ~, ~] = svd(A);
t = toc;
fprintf('CPU time: %f ms\n', t*1000);


d_A = gpuArray(A);
tic
[~, ~, ~] = svd(d_A);
t = toc;
fprintf('GPU time: %f ms\n', t*1000);

%% Output
% >> CPU time: 0.947754 ms
% >> GPU time: 2.168100 ms

Matlab 是否使用一些更快的算法?或者我只是犯了一些错误?我真的需要一个好的 SVD 实现/算法,我可以在其中使用CUDA.

更新:使用 1000 x 1000 矩阵时的执行时间

C++:

3655 ms (Double Precision)
2970 ms (Single Precision)

Matlab:

CPU time: 280.641123 ms
GPU time: 646.033498 ms

SVD 算法并行性不佳是一个已知问题。您会发现需要非常大的数组才能看到双精度的好处。您的 GPU 的单精度可能会获得更好的结果。如果您只请求一个输出,您也会获得更好的结果,因为单独计算奇异值使用更快的算法。

这也很大程度上取决于 GPU 的质量。如果您使用的是 GeForce GTX 等显卡,对于 SVD 等算法,双精度 GPU 确实不会带来太多好处。

从根本上来说,GPU 核心的性能比现代 CPU 核心要低得多,它们通过非常广泛的并行性来弥补这一点。 SVD 算法过于依赖串行分解迭代。也许您可以通过重新思考代数来解决您的问题,这样您就不需要每次都计算完整的因式分解。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

与 MATLAB 相比,使用 cuSolver 时 SVD 非常慢 的相关文章

随机推荐