我正在编写一个R包,它使用Thrust来处理内存分配并避免编写我自己的CUDA内核。
在某些情况下,我从设备代码而不是从主机代码调用cuBLAS例程。这会更改编译要求。虽然代码使用下面的nvcc
命令进行编译,但可能需要显式调用主机链接器(g++
)。如何修改当前构建过程以实现此目的?
我正在使用的步骤是:
使用max.o
开关
-dc
)
创建一个库(libmax.a
)以与
使用somePackage.o
开关
-c
)的输出文件
使用somePackage.so
开关
libmax.a
的共享库(-shared
)
醇>
如下所示的工作示例:
iterator.h:这定义了一些类型,包括strideAccessor
。
max.h:max.cu中的函数声明
max.cu:定义一个函数,该函数查找维度为n
的每个d
级联数组中最大元素的索引。
somePackage.cu:处理R / C ++接口的包装器
$ cat iterator.h
#ifndef ITER_H
#define ITER_H
#include <thrust/host_vector.h>
#include <thrust/device_vector.h>
#include <thrust/iterator/transform_iterator.h>
#include <thrust/iterator/permutation_iterator.h>
#include <thrust/tuple.h>
#include <thrust/iterator/zip_iterator.h>
typedef thrust::device_vector<int> ivec_d;
typedef thrust::device_vector<double> fvec_d;
typedef thrust::device_vector<int>::iterator intIter;
typedef thrust::device_vector<double>::iterator realIter;
typedef thrust::host_vector<int> ivec_h;
typedef thrust::host_vector<double> fvec_h;
typedef thrust::counting_iterator<int> countIter;
//Used for generating rep( (1:len)*incr, times=infinity)
struct stride: public thrust::unary_function<int, int>{
int incr;
__host__ __device__ stride(int incr=1): incr(incr){}
__host__ __device__ int operator()(int x){
return x*incr;
}
};
typedef thrust::transform_iterator<stride, countIter> strideIter;
typedef thrust::permutation_iterator<realIter, strideIter> strideAccessor;
#endif
$ cat max.h
#include "iterator.h"
void cublas_max(fvec_d &x, ivec_d &result, int n, int d);
$ cat max.cu
#include "iterator.h"
#include <thrust/functional.h>
#include <thrust/transform.h>
#include <cublas_v2.h>
#include <iostream>
struct whichMax : thrust::unary_function<double, int>{
int dim;
__host__ __device__ whichMax(int dim): dim(dim){}
__host__ __device__ int operator()(double &vec){
cublasHandle_t handle;
cublasCreate_v2(&handle);
int incx=1, n = dim, result =0;
double *vec_ptr = thrust::raw_pointer_cast(&vec);
//find the first index of a maximal element
cublasIdamax(handle, n, vec_ptr, incx, &result);
cublasDestroy_v2(handle);
return result;
}
};
void cublas_max(fvec_d &x, ivec_d &result, int n, int d){
stride f(d);
strideIter siter = thrust::transform_iterator<stride, countIter>(thrust::make_counting_iterator<int>(0), f);
strideAccessor stridex = thrust::permutation_iterator<realIter, strideIter>(x.begin(), siter);
whichMax g(d);
//find the index of maximum for each of n subvectors
thrust::copy(result.begin(), result.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
thrust::transform(stridex, stridex + n, result.begin(), g);
thrust::copy(result.begin(), result.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
}
$ cat somePackage.cu
#include "iterator.h"
#include "max.h"
#include <thrust/host_vector.h>
#include <thrust/device_vector.h>
#include <R.h>
#include <Rinternals.h>
#include <Rmath.h>
#include <iostream>
extern "C" SEXP Rcublas_max(SEXP x, SEXP n, SEXP dim){
double *xptr = REAL(x);
int N = INTEGER(n)[0], D = INTEGER(n)[0];
fvec_d dx(xptr, xptr+N*D);
ivec_d dresult(N);
cublas_max(dx, dresult, N, D);
ivec_h hresult(N);
thrust::copy(dresult.begin(), dresult.end(), hresult.begin());
SEXP indices = PROTECT(allocVector(INTSXP, N));
for(int i=0; i<N; ++i)
INTEGER(indices)[i] = hresult[i];
UNPROTECT(1);
return indices;
}
$ make
nvcc -dc -arch=sm_35 -Xcompiler -fPIC -lcublas_device -lcublas_device max.cu -o max.o
nvcc -lib -arch=sm_35 -Xcompiler -fPIC -lcublas_device -lcublas_device max.o -o libmax.a
nvcc -c -arch=sm_35 -Xcompiler -fPIC -lcublas_device somePackage.cu -lmax -I/home/emittman/src/R-3.3.1/builddir/include -I. -o somePackage.o
nvcc -shared -arch=sm_35 -Xcompiler -fPIC -lcublas_device somePackage.o -I/home/emittman/src/R-3.3.1/builddir/include -I. -L. -lcublas_device -lmax -o somePackage.so
ptxas info : 'device-function-maxrregcount' is a BETA feature
答案 0 :(得分:1)
我使用Rcpp创建了一个R包,从C ++共享库调用一些外部函数,然后调用CUDA内核来执行所需的计算。
您要在此处尝试将CUDA代码编译为静态库,然后将其链接到R包(它本身将编译为共享库)。我的方法与你的方法不同,我正在为我的方法提供描述,只是为了给你一个不同的想法。
这是一个简化的例子。
包含CUDA代码的共享库的kernels.cu:
__global__
void my_cuda_kernel( ... ) {
// ......
}
包含CUDA代码的共享库的main.cu:
extern "C" {
void do_cuda_work( ... ) {
thrust::copy( ... );
my_cuda_kernel <<< ... >>> ( ... );
}
}
R包中的package.cpp:
extern void do_cuda_work( ... );
// [[Rcpp::export]]
void call_cuda_code( ... ) {
do_cuda_work( ... );
}
要将CUDA代码编译到共享库中,您需要使用:
nvcc -arch=sm_35 -dc ... kernels.cu -o kernels.o
nvcc -arch=sm_35 -dc ... main.cu -o main.o
nvcc --shared -arch=sm_35 ... kernels.o main.o ... libMyCUDALibrary.so
请注意,要使单独的编译工作,您需要为编译器和链接器指定-arch=sm_35
,为编译器指定-dc
。成功创建共享库后,将R包链接到它是非常简单的。 (但是,您可能需要在R包的Makevars
文件夹下创建一个src
文件来指定包含和库路径,也可以指定RPATH:
CXX_STD= CXX11
PKG_CPPFLAGS= -I../../../CPP/include
PKG_LIBS= -L../../../CPP/bin/Release -lMyCUDALibrary -Wl,-rpath=$$HOME/MyCUDALibrary/CPP/bin/Release `$(R_HOME)/bin/Rscript -e "Rcpp::LdFlags()"`