【问题标题】:CUDA/CUBLAS Matrix-Vector MultiplicationCUDA/CUBLAS 矩阵向量乘法
【发布时间】:2013-09-02 04:58:30
【问题描述】:

我之前发布了一个关于 CUDA 中的矩阵向​​量乘法和编写我自己的内核的问题。完成此操作后,我决定按照一些用户的建议(感谢@Robert Crovella)在 SO 上使用 CUBLAS 来实现我的问题,以期实现更高的性能(我的项目是性能驱动的)。

澄清一下:我想将一个 NxN 矩阵与一个 1xN 向量相乘。

我已经查看下面粘贴的代码几天了,但我无法弄清楚为什么乘法会给我一个不正确的结果。我担心我使用 数组会引起问题(这是使用这些数据类型的更大系统的一部分)。我并不是要将这个线程用作调试工具,但我认为这也将有助于其他试图实现这一目标的用户,因为我没有在互联网上找到一个特别全面的资源来解决我的特定问题(以及 cublas v2 API)。提前致谢!

#include <cuda.h>
#include <vector>
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <stdlib.h>
#include <cmath>
#include <cublas_v2.h>
#include <time.h>

//#include "timenow.cu"

// error check macros
#define cudaCheckErrors(msg) \
    do { \
        cudaError_t __err = cudaGetLastError(); \
        if (__err != cudaSuccess) { \
            fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \
                msg, cudaGetErrorString(__err), \
                __FILE__, __LINE__); \
            fprintf(stderr, "*** FAILED - ABORTING\n"); \
            exit(1); \
        } \
    } while (0)

// for CUBLAS V2 API
#define cublasCheckErrors(fn) \
    do { \
        cublasStatus_t __err = fn; \
        if (__err != CUBLAS_STATUS_SUCCESS) { \
            fprintf(stderr, "Fatal cublas error: %d (at %s:%d)\n", \
                (int)(__err), \
                __FILE__, __LINE__); \
            fprintf(stderr, "*** FAILED - ABORTING\n"); \
            exit(1); \
        } \
    } while (0)

// random data filler
void fillvector(float *data, int N){
    for(int i=0; i<N; i++){
        data[i] = float(rand() % 10);
    }
}

//printer
void printer(bool printOut, float *data, int N){
    if(printOut == true){
    for(int i=0; i<N; i++){
        printf("%2.1f ", data[i]);
    }
    printf("\n");
    }
}

/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////

int main(){

bool printOut = true;

int N;
std::cout << "Enter N: " ;
std::cin >> N;

std::vector<float> x0;
x0.resize(N);

std::vector<float> p;
p.resize(N*N);

// matrix A
std::vector<float> A[N];
for(int i=0;i<N;i++){
        A[i].resize(N);
    fillvector(A[i].data(), N);
    printer(printOut, A[i].data(), N);
}
printf("\n");
fillvector(x0.data(), N);
printer(printOut, x0.data(), N);

printf("\nStarting CUDA computation...");
///double startTime = timenow();

// device pointers
float *d_A, *d_p, *d_b, *d_x0, *d_v, *d_temp;

cudaMalloc((void**)&d_A, N*N*sizeof(float));
cudaMalloc((void**)&d_temp, N*sizeof(float));
cudaMalloc((void**)&d_x0, N*sizeof(float));
cudaCheckErrors("cuda malloc fail");

// might need to flatten A...
cublasSetVector(N, sizeof(float), &x0, 1, d_x0, 1);
//daMemcpy(d_x0, &x0, N*sizeof(float), cudaMemcpyHostToDevice);
cublasSetMatrix(N, N, sizeof(float), &A, N, d_A, N);
cudaCheckErrors("cuda memcpy of A or x0 fail");

float *temp;
temp = (float *)malloc(N*sizeof(temp));

cublasHandle_t handle;
cublasCheckErrors(cublasCreate(&handle));

float alpha = 1.0f;
float beta = 0.0f;
cublasCheckErrors(cublasSgemv(handle, CUBLAS_OP_N, N, N, &alpha, d_A, N, d_x0, 1, &beta, d_temp, 1));

cublasGetVector(N, sizeof(float), &temp, 1, d_temp, 1);
//cudaMemcpy(temp, d_temp, N*sizeof(float), cudaMemcpyDeviceToHost);
cudaCheckErrors("returning to host failed");

printf("\n");
printer(printOut, temp, N);

/*alpha = -1.0;
cublasSaxpy(handle, N, &alpha, d_temp, 1, d_v, 1);
cublasGetVector(N, sizeof(float) * N, d_v, 1, &v, 1);
printf("\n");
for(int i=0; i<N; i++){
    printf("%2.1f ",v[i]);
}*/

printf("\nFinished CUDA computations...");
//double endTime = timenow();

//double timeDiff = endTime - startTime;
//printf("\nRuntime: %2.3f seconds \n", timeDiff);

cudaFree(d_temp);
cudaFree(d_A);
cudaFree(d_p);
cudaFree(d_x0);

return 0;
} 

【问题讨论】:

  • 您的问题是什么?如果是您的乘法给出了不正确的结果,请告诉我们您得到的结果和您期望的结果。此外,您没有对 get/set matrix/vector 调用进行 cublas 错误检查。
  • 您的程序似乎允许任何输入。当我输入 N=2 时,我在您的“返回主机失败”消息中收到无效参数的 cuda 错误消息。你的问题到底是什么?
  • 嗨罗伯特,我想将 NxN 的方阵乘以 1xN 的向量。 N 应该是任意大小。
  • 如果我设置 N=5 说,我得到以下针对 temp 返回的输出: 之前:0.0 2.0 3.0 7.0 5.0 之后:0.0 0.0 0.0 0.0 -158953476882379259956604960768.0
  • 即使我将 N=5 传递给您的代码,它也会返回带有“返回主机失败”消息的错误。这是您正在运行的代码吗?请在运行时将实际程序输出粘贴到问题中(您可以编辑问题,不要尝试将其放入 cmets。)

标签: c++ vector matrix cuda cublas


【解决方案1】:
  • 我们不会以这种方式引用向量的第一个元素:

    cublasSetVector(N, sizeof(float), &x0, 1, d_x0,   
    

您应该这样做:

cublasSetVector(N, sizeof(float), &(x0[0]), 1, d_x0, 1);

同样对于您的SetMatrix 调用引用A

cublasSetMatrix(N, N, sizeof(float), &(A[0]), N, d_A, N);
  • 您的 GetVector 调用有 2 个错误:

    cublasGetVector(N, sizeof(float), &temp, 1, d_temp, 1);
    

你有你的tempd_temp参数reversed(你正在从设备复制到主机),你不应该使用temp的地址:它已经是一个指针。这样做:

cublasGetVector(N, sizeof(float), d_temp, 1, temp, 1);
  • 您没有对所有 cublas 调用进行正确的错误检查,例如您的 get/set matrix/vector 调用。对这些也使用与其他 cublas 调用相同的方法。

  • 您正在创建 A 作为向量数组。这不适用于cublasSetMatrix。相反,我们需要将A 创建为一个平面向量,其大小 (N*N) 足以存储整个矩阵。

  • 最后,cublas 期望它使用的矩阵以列优先顺序存储。如果您以行优先顺序传递 C 样式数组,则应在 cublasSgemv 中使用该矩阵的转置:

    cublasCheckErrors(cublasSgemv(handle, CUBLAS_OP_T, N, N, &alpha, d_A, N, d_x0, 1, &beta, d_temp, 1));
    

以下代码修复了这些不同的问题:

$ cat t235.cu
#include <cuda.h>
#include <vector>
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <stdlib.h>
#include <cmath>
#include <cublas_v2.h>
#include <time.h>

//#include "timenow.cu"

// error check macros
#define cudaCheckErrors(msg) \
    do { \
        cudaError_t __err = cudaGetLastError(); \
        if (__err != cudaSuccess) { \
            fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \
                msg, cudaGetErrorString(__err), \
                __FILE__, __LINE__); \
            fprintf(stderr, "*** FAILED - ABORTING\n"); \
            exit(1); \
        } \
    } while (0)

// for CUBLAS V2 API
#define cublasCheckErrors(fn) \
    do { \
        cublasStatus_t __err = fn; \
        if (__err != CUBLAS_STATUS_SUCCESS) { \
            fprintf(stderr, "Fatal cublas error: %d (at %s:%d)\n", \
                (int)(__err), \
                __FILE__, __LINE__); \
            fprintf(stderr, "*** FAILED - ABORTING\n"); \
            exit(1); \
        } \
    } while (0)

// random data filler
void fillvector(float *data, int N){
    for(int i=0; i<N; i++){
        data[i] = float(rand() % 10);
    }
}

//printer
void printer(bool printOut, float *data, int N){
    if(printOut == true){
    for(int i=0; i<N; i++){
        printf("%2.1f ", data[i]);
    }
    printf("\n");
    }
}

/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////

int main(){

bool printOut = true;

int N;
std::cout << "Enter N: " ;
std::cin >> N;

std::vector<float> x0;
x0.resize(N);

std::vector<float> p;
p.resize(N*N);

// matrix A
std::vector<float> A(N*N);
fillvector(A.data(), N*N);
for (int i=0; i< N; i++){
  printer(printOut, &(A[(i*N)]), N);
  printf("\n");}
fillvector(x0.data(), N);
printer(printOut, x0.data(), N);

printf("\nStarting CUDA computation...");
///double startTime = timenow();

// device pointers
float *d_A, *d_x0, *d_temp;

cudaMalloc((void**)&d_A, N*N*sizeof(float));
cudaMalloc((void**)&d_temp, N*sizeof(float));
cudaMalloc((void**)&d_x0, N*sizeof(float));
cudaCheckErrors("cuda malloc fail");

// might need to flatten A...
cublasCheckErrors(cublasSetVector(N, sizeof(float), &(x0[0]), 1, d_x0, 1));
//daMemcpy(d_x0, &x0, N*sizeof(float), cudaMemcpyHostToDevice);
cublasCheckErrors(cublasSetMatrix(N, N, sizeof(float), &(A[0]), N, d_A, N));
//cudaCheckErrors("cuda memcpy of A or x0 fail");

float *temp;
temp = (float *)malloc(N*sizeof(temp));

cublasHandle_t handle;
cublasCheckErrors(cublasCreate(&handle));

float alpha = 1.0f;
float beta = 0.0f;
cublasCheckErrors(cublasSgemv(handle, CUBLAS_OP_T, N, N, &alpha, d_A, N, d_x0, 1, &beta, d_temp, 1));

cublasCheckErrors(cublasGetVector(N, sizeof(float), d_temp, 1, temp, 1));
//cudaMemcpy(temp, d_temp, N*sizeof(float), cudaMemcpyDeviceToHost);
//cudaCheckErrors("returning to host failed");

printf("\n");
printer(printOut, temp, N);

/*alpha = -1.0;
cublasSaxpy(handle, N, &alpha, d_temp, 1, d_v, 1);
cublasGetVector(N, sizeof(float) * N, d_v, 1, &v, 1);
printf("\n");
for(int i=0; i<N; i++){
    printf("%2.1f ",v[i]);
}*/

printf("\nFinished CUDA computations...\n");
//double endTime = timenow();

//double timeDiff = endTime - startTime;
//printf("\nRuntime: %2.3f seconds \n", timeDiff);

cudaFree(d_temp);
cudaFree(d_A);
//cudaFree(d_p);
cudaFree(d_x0);

return 0;
}
$ nvcc -arch=sm_20 -O3 -o t235 t235.cu -lcublas
$ ./t235
Enter N: 5
3.0 6.0 7.0 5.0 3.0

5.0 6.0 2.0 9.0 1.0

2.0 7.0 0.0 9.0 3.0

6.0 0.0 6.0 2.0 6.0

1.0 8.0 7.0 9.0 2.0

0.0 2.0 3.0 7.0 5.0

Starting CUDA computation...
83.0 86.0 92.0 62.0 110.0

Finished CUDA computations...
$

【讨论】:

  • 非常感谢罗伯特。关于如何指定/使用数组及其指针的要点特别有用!
  • 对于使用 6 个内核的并行 cpu 计算,您如何评价它?我问是因为我不完全相信我的计时模块可以正常工作。我在看 N 大约 1000。
  • 我还担心矩阵 A 的扁平化会影响性能多少?之后的矩阵操作是否有权衡?
  • 您的代码可能需要重新编写,但没有理由降低性能。
  • 嗨罗伯特,是否可以将 cublasSgemv() 与设备上的 2 个平面矩阵 d_A 和 d_Q 一起使用,希望我想将 A[N x N] 与 Q[N 中的一列相乘x (0.5N+1)] ?
猜你喜欢
  • 2011-07-29
  • 2011-08-23
  • 2012-05-06
  • 2020-03-09
  • 2020-10-29
  • 1970-01-01
  • 1970-01-01
  • 2017-09-24
  • 2011-09-07
相关资源
最近更新 更多