Использование 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 в частности.