使用 cuSolver 进行奇异值分解(SVD)比 MATLAB 慢很多

3
我尝试使用cuSOLVER中的gesvd函数,发现其在使用double数组或gpuArray时都比MATLAB中的svd函数慢得多。请见以下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();
}

输出:

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 . . .

**使用svd函数的Matlab代码:

%% 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

4
[64x64] 不是你会注意到性能提升的数组大小。 - pSoLT
3
当你说“我需要一个好的实现”时,你指的是什么?这只是性能问题吗?如果是这样,你正在使用什么GPU,其双精度性能是多少?对于SVD来说,双精度实际上是否必要? - talonmies
@talonmies 我也尝试使用单精度格式,但效果不大。单精度需要 2970 毫秒,而双精度需要 3655 毫秒。然而,在维基百科上,单精度和双精度的 gflops 分别为 1317 和 41.16。 - bytestorm
@talonmies 是的,我确实明白像svd这样的例程在CPU上比移动GPU更快。但我的意图是想知道Matlab如何在CPU和GPU上实现如此出色的性能;请注意,我已经添加了Matlab在GPU上运行的时间。我想坚持使用GPU,因为svd只是我在CUDA上编写的程序的一小部分。 - bytestorm
1
@talonmies http://in.mathworks.com/help/distcomp/run-built-in-functions-on-a-gpu.html;但是这里说如果任何参数是gpuArray,svd将在GPU上运行。 - bytestorm
显示剩余9条评论
1个回答

3
众所周知,SVD算法并不适合并行化。您会发现,需要非常大的数组才能在双精度下看到好处。对于GPU而言,单精度可能会获得更好的结果。如果只请求一个输出,您还将获得更好的结果,因为仅计算奇异值使用了更快的算法。
此外,这也高度依赖于您的GPU质量。如果您使用像GeForce GTX这样的显卡,则对于像SVD这样的双精度算法,您实际上不会看到太多GPU方面的好处。
从根本上讲,GPU核心的性能比现代CPU核心要低得多,它们通过非常广泛的并行性来弥补这一点。SVD算法过于依赖串行分解迭代。也许您可以通过重新考虑代数运算,以便不需要每次都计算完整的分解来解决问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接