使用RViennaCL进行矩阵乘法

时间:2018-03-23 19:22:12

标签: r matrix rcpp viennacl

我是使用RViennaCL套餐的新手,试图进行GPU矩阵乘法。

尝试一个简单的案例我在我创建的包(cpp)中的src文件夹中创建了一个gpuUtils文件,其代码如下:

#include "viennacl/ocl/backend.hpp"
#include "viennacl/linalg/prod.hpp"
#include "viennacl/tools/random.hpp"
#include "viennacl/matrix.hpp"


//' PU matrix multiplication
//'
//' @export
// [[Rcpp::export]]
SEXP matMult(){

  viennacl::tools::uniform_random_numbers<float> randomNumber;

  viennacl::matrix<float> vcl_A(400, 400);
  viennacl::matrix<float> vcl_B(400, 400);

  for (unsigned int i = 0; i < vcl_A.size1(); ++i)
    for (unsigned int j = 0; j < vcl_A.size2(); ++j)
      vcl_A(i,j) = randomNumber();
  for (unsigned int i = 0; i < vcl_B.size1(); ++i)
    for (unsigned int j = 0; j < vcl_B.size2(); ++j)
      vcl_B(i,j) = randomNumber();
  viennacl::matrix<float> vcl_C = viennacl::linalg::prod(vcl_A, vcl_B);
  return Rcpp::wrap(vcl_C);
}

然后我build包裹:

$ R CMD build gpuUtils
* checking for file ‘gpuUtils/DESCRIPTION’ ... OK
* preparing ‘gpuUtils’:
* checking DESCRIPTION meta-information ... OK
* cleaning src
* checking for LF line-endings in source and make files and shell scripts
* checking for empty or unneeded directories
* building ‘gpuUtils_0.0.0.9000.tar.gz’

但是当我尝试安装它时,我得到了:

$ R CMD INSTALL gpuUtils_0.0.0.9000.tar.gz
* installing to library ‘/home/usr/R’
* installing *source* package ‘gpuUtils’ ...
** libs
g++  -I/opt/ohpc/pub/libs/gnu7/R/3.4.2/lib64/R/include -DNDEBUG -I/usr/local/cuda-9.0/include/ -I"/home/usr/R/Rcpp/include" -I"/home/usr/R/RViennaCL/include" -I/usr/local/include   -fpic  -g -O2  -c gpuUtils.cpp -o gpuUtils.o
In file included from /home/usr/R/Rcpp/include/RcppCommon.h:195:0,
                 from /home/usr/R/Rcpp/include/Rcpp.h:27,
                 from /home/usr/R/RViennaCL/include/viennacl/ocl/forwards.h:29,
                 from /home/usr/R/RViennaCL/include/viennacl/ocl/context.hpp:36,
                 from /home/usr/R/RViennaCL/include/viennacl/ocl/backend.hpp:26,
                 from gpuUtils.cpp:1:
/home/usr/R/Rcpp/include/Rcpp/internal/wrap.h: In instantiation of ‘SEXPREC* Rcpp::internal::wrap_dispatch_unknown_iterable(const T&, Rcpp::traits::false_type) [with T = viennacl::matrix<float, viennacl::row_major>; SEXP = SEXPREC*; Rcpp::traits::false_type = Rcpp::traits::integral_constant<bool, false>]’:
/home/usr/R/Rcpp/include/Rcpp/internal/wrap.h:732:43:   required from ‘SEXPREC* Rcpp::internal::wrap_dispatch_unknown(const T&, Rcpp::traits::false_type) [with T = viennacl::matrix<float, viennacl::row_major>; SEXP = SEXPREC*; Rcpp::traits::false_type = Rcpp::traits::integral_constant<bool, false>]’
/home/usr/R/Rcpp/include/Rcpp/internal/wrap.h:770:41:   required from ‘SEXPREC* Rcpp::internal::wrap_dispatch_eigen(const T&, Rcpp::traits::false_type) [with T = viennacl::matrix<float, viennacl::row_major>; SEXP = SEXPREC*; Rcpp::traits::false_type = Rcpp::traits::integral_constant<bool, false>]’
/home/usr/R/Rcpp/include/Rcpp/internal/wrap.h:787:39:   required from ‘SEXPREC* Rcpp::internal::wrap_dispatch_unknown_importable(const T&, Rcpp::traits::false_type) [with T = viennacl::matrix<float, viennacl::row_major>; SEXP = SEXPREC*; Rcpp::traits::false_type = Rcpp::traits::integral_constant<bool, false>]’
/home/usr/R/Rcpp/include/Rcpp/internal/wrap.h:807:52:   required from ‘SEXPREC* Rcpp::internal::wrap_dispatch(const T&, Rcpp::traits::wrap_type_unknown_tag) [with T = viennacl::matrix<float, viennacl::row_major>; SEXP = SEXPREC*]’
/home/usr/R/Rcpp/include/Rcpp/internal/wrap_end.h:30:38:   required from ‘SEXPREC* Rcpp::wrap(const T&) [with T = viennacl::matrix<float, viennacl::row_major>; SEXP = SEXPREC*]’
gpuUtils.cpp:25:26:   required from here
/home/usr/R/Rcpp/include/Rcpp/internal/wrap.h:523:17: error: static assertion failed: cannot convert type to SEXP
                 static_assert(!sizeof(T), "cannot convert type to SEXP");
                 ^~~~~~~~~~~~~
make: *** [gpuUtils.o] Error 1
ERROR: compilation failed for package ‘gpuUtils’
* removing ‘/home/usr/R/gpuUtils’
* restoring previous ‘/home/usr/R/gpuUtils’

这表明viennacl::matrix需要编写自己的wrap函数。

因此,我尝试在as内针对自定义模板wrapRcpp函数执行非常好的vignette,使其适应viennacl::matrix情况。< / p>

这是我的cpp代码:

#include <RcppCommon.h>
#include "viennacl/ocl/backend.hpp"
#include "viennacl/matrix.hpp"


// [[Rcpp::plugins("cpp11")]]
// Provide Forward Declarations
namespace Rcpp {
  namespace traits{
    // Setup non-intrusive extension via template specialization for
    // 'viennacl' class

    // Support for wrap
    template <typename T> SEXP wrap(const viennacl::matrix<T> & obj);

    // Support for as<T>
    template <typename T> class Exporter< viennacl::matrix<T> >;
  }
}

#include <Rcpp.h>

// Define template specializations for as<> and wrap
namespace Rcpp {

  namespace traits{

  // Defined wrap case
  template <typename T> SEXP wrap(const viennacl::matrix<T> & obj){
    const int RTYPE = Rcpp::traits::r_sexptype_traits<T>::rtype ;

    return Rcpp::Matrix< RTYPE >(obj.begin(), obj.end());
  };


  // Defined as< > case
  template<typename T> class Exporter< viennacl::matrix<T> > {
    typedef typename viennacl::matrix<T> OUT ;

    // Convert the type to a valid rtype.
    const static int RTYPE = Rcpp::traits::r_sexptype_traits< T >::rtype ;
    Rcpp::Matrix<RTYPE> mat;

    public:
      Exporter(SEXP x) : mat(x) {
        if (TYPEOF(x) != RTYPE)
          throw std::invalid_argument("Wrong R type for mapped 2D array");
      }
      OUT get() {

       // Need to figure out a way to perhaps do a pointer pass?
        OUT x(mat.size());

        std::copy(mat.begin(), mat.end(), mat.begin());

        return x;
      }
    };
  }
}

//' viennacl matrix templating
//'
//' @export
// [[Rcpp::export]]
void matTemplate(Rcpp::NumericMatrix in_mat){
  viennacl::matrix<double> viennacl_mat = Rcpp::as< viennacl::matrix<double> >(in_mat);
  Rcpp::NumericMatrix out_mat = Rcpp::wrap(viennacl_mat);
}

构建良好:

$ R CMD build gpuUtils
* checking for file ‘gpuUtils/DESCRIPTION’ ... OK
* preparing ‘gpuUtils’:
* checking DESCRIPTION meta-information ... OK
* cleaning src
* checking for LF line-endings in source and make files and shell scripts
* checking for empty or unneeded directories
* building ‘gpuUtils_0.0.0.9000.tar.gz’

但仍然得到同样的错误:

$ R CMD INSTALL gpuUtils_0.0.0.9000.tar.gz
* installing to library ‘/home/usr/R’
* installing *source* package ‘gpuUtils’ ...
** libs
g++  -I/opt/ohpc/pub/libs/gnu7/R/3.4.2/lib64/R/include -DNDEBUG -I/usr/local/cuda-9.0/include/ -I"/home/usr/R/Rcpp/include" -I"/home/usr/R/RViennaCL/include" -I/usr/local/include   -fpic  -g -O2  -c gpuUtils.cpp -o gpuUtils.o
In file included from /home/usr/R/Rcpp/include/RcppCommon.h:195:0,
                 from gpuUtils.cpp:1:
/home/usr/R/Rcpp/include/Rcpp/internal/wrap.h: In instantiation of ‘SEXPREC* Rcpp::internal::wrap_dispatch_unknown_iterable(const T&, Rcpp::traits::false_type) [with T = viennacl::matrix<double, viennacl::row_major>; SEXP = SEXPREC*; Rcpp::traits::false_type = Rcpp::traits::integral_constant<bool, false>]’:
/home/usr/R/Rcpp/include/Rcpp/internal/wrap.h:732:43:   required from ‘SEXPREC* Rcpp::internal::wrap_dispatch_unknown(const T&, Rcpp::traits::false_type) [with T = viennacl::matrix<double, viennacl::row_major>; SEXP = SEXPREC*; Rcpp::traits::false_type = Rcpp::traits::integral_constant<bool, false>]’
/home/usr/R/Rcpp/include/Rcpp/internal/wrap.h:770:41:   required from ‘SEXPREC* Rcpp::internal::wrap_dispatch_eigen(const T&, Rcpp::traits::false_type) [with T = viennacl::matrix<double, viennacl::row_major>; SEXP = SEXPREC*; Rcpp::traits::false_type = Rcpp::traits::integral_constant<bool, false>]’
/home/usr/R/Rcpp/include/Rcpp/internal/wrap.h:787:39:   required from ‘SEXPREC* Rcpp::internal::wrap_dispatch_unknown_importable(const T&, Rcpp::traits::false_type) [with T = viennacl::matrix<double, viennacl::row_major>; SEXP = SEXPREC*; Rcpp::traits::false_type = Rcpp::traits::integral_constant<bool, false>]’
/home/usr/R/Rcpp/include/Rcpp/internal/wrap.h:807:52:   required from ‘SEXPREC* Rcpp::internal::wrap_dispatch(const T&, Rcpp::traits::wrap_type_unknown_tag) [with T = viennacl::matrix<double, viennacl::row_major>; SEXP = SEXPREC*]’
/home/usr/R/Rcpp/include/Rcpp/internal/wrap_end.h:30:38:   required from ‘SEXPREC* Rcpp::wrap(const T&) [with T = viennacl::matrix<double, viennacl::row_major>; SEXP = SEXPREC*]’
gpuUtils.cpp:68:56:   required from here
/home/usr/R/Rcpp/include/Rcpp/internal/wrap.h:523:17: error: static assertion failed: cannot convert type to SEXP
                 static_assert(!sizeof(T), "cannot convert type to SEXP");
                 ^~~~~~~~~~~~~
make: *** [gpuUtils.o] Error 1
ERROR: compilation failed for package ‘gpuUtils’
* removing ‘/home/usr/R/gpuUtils’
* restoring previous ‘/home/usr/R/gpuUtils’

1 个答案:

答案 0 :(得分:2)

简短回答:是的。 RViennaCl仅提供ViennaCl标头,但没有as()wrap()函数用于R和C ++数据结构之间的通信。使用RViennaCl的gpuR包使用不同的方法,通过将适当的对象暴露给R,并使用适当的函数作用于这些对象。当然,您可以使用这些对象在GPU上进行矩阵乘法。

因此,如果您对R和ViennaCl数据结构之间的通信感兴趣,则必须自己编写这些函数。但是,这不是一项简单的任务,因为RViennaCL会修补ViennaCL标头以包含Rcpp.h(例如https://github.com/cdeterman/RViennaCL/blob/master/inst/include/viennacl/ocl/backend.hpp#L29)。这使得无法像上面尝试那样使用通常的扩展机制。我认为RViennaCL包含RcppCommon.h就足够了,这样可以扩展。但我没有尝试过。我已经为此提交了https://github.com/cdeterman/RViennaCL/issues/4

您可以将available converters与转换为R数据结构和从R数据结构转换的包合并,而不是为ViennaCL编写as()wrap()。所以在使用LinkingTo: Rcpp, RViennaCL, RcppEigen的包中,这个功能对我有用:

#define VIENNACL_HAVE_EIGEN
#include <RcppEigen.h>
#include <viennacl/ocl/backend.hpp>
#include <viennacl/matrix.hpp>


//' viennacl matrix crossprod
//'
//' @export
// [[Rcpp::export]]
Eigen::MatrixXd vclCrossprod(const Eigen::MatrixXd &in_densematrix){
  int rows = in_densematrix.rows();
  int cols = in_densematrix.cols();

  viennacl::matrix<double>  viennacl_densematrix(rows, cols);
  viennacl::copy(in_densematrix,  viennacl_densematrix);
  viennacl::matrix<double> viennacl_result = viennacl::linalg::prod(
    viennacl::trans(viennacl_densematrix),
    viennacl_densematrix);

  Eigen::MatrixXd eigen_result(cols, cols);
  viennacl::copy(viennacl_result,  eigen_result);
  return eigen_result;
}

然而,对于我尝试过的例子,这种方法效率不高。如果您正在做一些比矩阵乘法更复杂的事情,这可能会改变。

或者你可以试试我正在研究的RcppArrayFire,如果你主要对GPGPU感兴趣,特别是对ViennaCl不感兴趣。