【问题标题】:Matrix-vector multiplication (cublasDgemv) returns zero矩阵向量乘法 (cublasDgemv) 返回零
【发布时间】:2019-04-05 12:14:17
【问题描述】:

对于我第一次尝试 CUDA/cuBLAS,我正在尝试编写一个简单的函数,它将 MxN 矩阵(用向量的向量,std::vector 表示)与 Nx1“ones”向量相乘,以便得到矩阵的行(?)总和。这将利用 cublas_gemv() 以及其他基本的 CUDA 操作,我认为这是一个很好的起点。

在处理了设置问题和阅读/复制示例代码之后,我得到了以下内容:

std::vector<double> test(std::vector<std::vector<double>> in)
{
    std::vector<double> out;
    long in_m = in.size();
    long in_n = in[0].size();
    cudaError_t cudaStat;
    cublasStatus_t stat;
    cublasHandle_t handle;
    // This just converts a vector-of-vectors into a col-first array
    double* p_in = vec2d_to_colfirst_array(in);
    double* p_ones = new double[in_n];
    double* p_out = new double[in_m];
    std::fill(p_ones, p_ones + in_n, 1.0);
    double* dev_in;
    double* dev_ones;
    double* dev_out;
    cudaStat = cudaMalloc((void**)&dev_in, in_m * in_n * sizeof(double));
    cudaStat = cudaMalloc((void**)&dev_ones, in_n * sizeof(double));
    cudaStat = cudaMalloc((void**)&dev_out, in_m * sizeof(double));
    stat = cublasCreate(&handle);
    cudaStat = cudaMemcpy(dev_in, p_in, in_m*in_n * sizeof(double), cudaMemcpyHostToDevice);
    cudaStat = cudaMemcpy(dev_ones, p_ones, in_n * sizeof(double), cudaMemcpyHostToDevice);
    double alpha = 1.0;
    double beta = 0.0;
    stat = cublasDgemv(handle, CUBLAS_OP_N, in_m, in_n, &alpha, dev_in, in_m, dev_ones, 1, &beta, dev_ones, 1);
    cudaStat = cudaMemcpy(p_out, dev_out, in_m * sizeof(double), cudaMemcpyDeviceToHost);
    out.assign(p_out, p_out + in_m);
    cudaFree(dev_in);
    cudaFree(dev_ones);
    cudaFree(dev_out);
    cublasDestroy(handle);
    free(p_in);
    free(p_ones);
    free(p_out);
    return out;
}

它看起来与我阅读的示例没有太大不同,所以我希望它“正常工作”。但是,当我检查p_out 时,它全为零。当然我没有输入一个零 in 矩阵。

我验证了vec2d_to_colfirst_array() 的工作正常,并且dev_in/dev_ones 通过将数据从设备复制回主机然后读取来正确填充。也许问题出在对cublasDgemv() 的调用中,但由于我是新来的(而且与例如 Eigen 相比,BLAS 语法不那么直观),在经历了很多挫折之后,我看不出有什么问题。

任何帮助表示赞赏!

【问题讨论】:

  • stackoverflow.com/a/47755279/1231073。我猜你必须将指针模式设置为CUBLAS_POINTER_MODE_HOST
  • @sgarizvi: 这是 CUBLAS AFAIK 的默认设置
  • @talonmies.. 确实如此.....安迪,你能提供一个MCVE

标签: c++ cuda cublas


【解决方案1】:

错误似乎相当简单。您希望从dev_out 复制结果:

cudaStat = cudaMemcpy(p_out, dev_out, in_m * sizeof(double), cudaMemcpyDeviceToHost);

但您从不在 cublas 通话中使用 dev_out

stat = cublasDgemv(handle, CUBLAS_OP_N, in_m, in_n, &alpha, dev_in, in_m, dev_ones, 1, &beta, dev_ones, 1);

这似乎只是一个复制粘贴错误。如果您将 cublas 调用中的最后一个 dev_ones 实例替换为 dev_out,则您的代码适用于我:

stat = cublasDgemv(handle, CUBLAS_OP_N, in_m, in_n, &alpha, dev_in, in_m, dev_ones, 1, &beta, dev_out, 1);

下面是一个完整的例子:

$ cat t315.cu
#include <vector>
#include <cublas_v2.h>
#include <iostream>

const long idim1 = 8;
const long idim2 = 8;

double* vec2d_to_colfirst_array(std::vector<std::vector<double>> in){
    long dim1 = in.size();
    long dim2 = in[0].size();
    long k = 0;
    double *res = new double[dim1*dim2];
    for (int i = 0; i < dim1; i++)
      for (int j = 0; j < dim2; j++) res[k++] = in[i][j];
    return res;
}


std::vector<double> test(std::vector<std::vector<double>> in)
{
    std::vector<double> out;
    long in_m = in.size();
    long in_n = in[0].size();
    cudaError_t cudaStat;
    cublasStatus_t stat;
    cublasHandle_t handle;
    // This just converts a vector-of-vectors into a col-first array
    double* p_in = vec2d_to_colfirst_array(in);
    double* p_ones = new double[in_n];
    double* p_out = new double[in_m];
    std::fill(p_ones, p_ones + in_n, 1.0);
    double* dev_in;
    double* dev_ones;
    double* dev_out;
    cudaStat = cudaMalloc((void**)&dev_in, in_m * in_n * sizeof(double));
    cudaStat = cudaMalloc((void**)&dev_ones, in_n * sizeof(double));
    cudaStat = cudaMalloc((void**)&dev_out, in_m * sizeof(double));
    stat = cublasCreate(&handle);
    cudaStat = cudaMemcpy(dev_in, p_in, in_m*in_n * sizeof(double), cudaMemcpyHostToDevice);
    cudaStat = cudaMemcpy(dev_ones, p_ones, in_n * sizeof(double), cudaMemcpyHostToDevice);
    double alpha = 1.0;
    double beta = 0.0;
    stat = cublasDgemv(handle, CUBLAS_OP_N, in_m, in_n, &alpha, dev_in, in_m, dev_ones, 1, &beta, dev_out, 1);
    cudaStat = cudaMemcpy(p_out, dev_out, in_m * sizeof(double), cudaMemcpyDeviceToHost);
    out.assign(p_out, p_out + in_m);
    cudaFree(dev_in);
    cudaFree(dev_ones);
    cudaFree(dev_out);
    cublasDestroy(handle);

    free(p_in);
    free(p_ones);
    free(p_out);
    return out;
}

int main(){

  std::vector<double> a(idim2, 1.0);
  std::vector<std::vector<double>> b;
  for (int i = 0; i <  idim1; i++) b.push_back(a);
  std::vector<double> c = test(b);
  for (int i = 0; i < c.size(); i++) std::cout << c[i] << ",";
  std::cout << std::endl;
}

$ nvcc -std=c++11 -o t315 t315.cu -lcublas
t315.cu(24): warning: variable "cudaStat" was set but never used

t315.cu(25): warning: variable "stat" was set but never used

$ cuda-memcheck ./t315
========= CUDA-MEMCHECK
8,8,8,8,8,8,8,8,
========= ERROR SUMMARY: 0 errors
$

请注意,我认为 free() 不是与 new 一起使用的正确 API,但这似乎不是您的问题或问题的症结所在。

【讨论】:

  • 啊,我傻了,谢谢你指出来。在阅读文档时,我很着急,看到y = α op ( A ) x + β y 中的y 是另一个纯输入向量,甚至没有考虑结果会去哪里。 free() 部分来自我复制的示例代码,我将替换为 delete
猜你喜欢
  • 1970-01-01
  • 2020-10-29
  • 1970-01-01
  • 1970-01-01
  • 2020-03-16
  • 2020-01-17
  • 2016-01-23
  • 2017-11-08
相关资源
最近更新 更多