Использование RViennaCL для умножения матриц

Я новичок, использующий 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 а также wrap функции внутри Rcpp, адаптируя его к viennacl::matrix дело.

Вот мой 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 ответ

Краткий ответ: да. RViennaCl предоставляет только заголовки ViennaCl, но не as() а также wrap() функции для связи между структурами данных R и C++. И gpuR Пакет, в котором используется RViennaCl, использует другой подход, предоставляя 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 для этого.

Вместо того чтобы писать as() а также wrap() для ViennaCL вы также можете комбинировать доступные конвертеры с пакетом, который преобразует в и из структур данных R. Так что в пакете, который использует 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 в частности.

Другие вопросы по тегам