Building a tiny R package with CUDA and Rcpp

后端 未结 2 1756
谎友^
谎友^ 2020-12-09 05:07

I\'m working on a tiny R package that uses CUDA and Rcpp, adapted from the output of Rcpp.package.skeleton(). I will first describe what happens on the master b

相关标签:
2条回答
  • 2020-12-09 05:52

    Several packages on CRAN use GPUs via CUDA:

    • cudaBayesreg
    • gmatrix
    • gputools
    • iFes
    • permGPU
    • rpud

    I would start with these.

    0 讨论(0)
  • 2020-12-09 06:05

    Going through your package there are multiple aspects that need to be changed.

    1. You shouldn't use a 'Makefile' but a 'Makevars' file instead to improve compatibility for multiple architecture builds.
    2. Try to follow the standard variable names (e.g. CPPC should be CXX), this makes everything play together much better.
    3. Don't try and compile the shared object yourself, there are good macros within the base R makefile that make this much simpler (e.g. PKG_LIBS, OBJECTS, etc.)
    4. With multiple compilers, you will want to use the OBJECTS macro. Here you will override R's base attempt to set the object files to be linked (make sure you include them all).
    5. You also need (AFAIK) to make CUDA functions available with extern "C". You will prefix both the function in the .cu file and when you declare it at the start of your cpp file.

    The following Makevars worked for me whereby I modified my CUDA_HOME, R_HOME, and RCPP_INC (switched back for you). Note, this is where a configure file is recommended to make the package as portable as possible.

    CUDA_HOME = /usr/local/cuda
    R_HOME = /apps/R-3.2.0
    CXX = /usr/bin/g++
    
    # This defines what the shared object libraries will be
    PKG_LIBS= -L/usr/local/cuda-7.0/lib64 -Wl,-rpath,/usr/local/cuda-7.0/lib64 -lcudart -d
    
    
    #########################################
    
    R_INC = /usr/share/R/include
    RCPP_INC = $(R_HOME)/library/Rcpp/include
    
    NVCC = $(CUDA_HOME)/bin/nvcc
    CUDA_INC = $(CUDA_HOME)/include 
    CUDA_LIB = $(CUDA_HOME)/lib64
    
    LIBS = -lcudart -d
    NVCC_FLAGS = -Xcompiler "-fPIC" -gencode arch=compute_20,code=sm_20 -gencode arch=compute_30,code=sm_30 -gencode arch=compute_35,code=sm_35 -I$(R_INC)
    
    ### Define objects
    cu_sources := $(wildcard *cu)
    cu_sharedlibs := $(patsubst %.cu, %.o,$(cu_sources))
    
    cpp_sources := $(wildcard *.cpp)
    cpp_sharedlibs := $(patsubst %.cpp, %.o, $(cpp_sources))
    
    OBJECTS = $(cu_sharedlibs) $(cpp_sharedlibs)
    
    all : rcppcuda.so
    
    rcppcuda.so: $(OBJECTS)
    
    %.o: %.cpp $(cpp_sources)
            $(CXX) $< -c -fPIC -I$(R_INC) -I$(RCPP_INC)
    
    %.o: %.cu $(cu_sources)
            $(NVCC) $(NVCC_FLAGS) -I$(CUDA_INC) $< -c
    

    A follow-up point (as you say this is a learning exercise):

    A. You aren't using one of the parts of Rcpp that make it such a wonderful package, namely 'attributes'. Here is how your cpp file should look:

    #include <Rcpp.h>
    using namespace Rcpp;
    
    extern "C"
    void someCUDAcode();
    
    //[[Rcpp::export]]
    SEXP someCPPcode(SEXP r) {
      S4 c(r);
      double *x = REAL(c.slot("x"));
      int *y = INTEGER(c.slot("y"));
      x[0] = 500.0;
      y[1] = 1000;
      someCUDAcode();
      return R_NilValue;
    }
    

    This will automatically generate the corresponding RcppExports.cpp and RcppExports.R files and you no longer need a .Call function yourself. You just call the function. Now .Call('someCPPcode', r) becomes someCPPcode(r) :)

    For completeness, here is the updated someCUDAcode.cu file:

    __global__ void mykernel(int a){
      int id = threadIdx.x;
      int b = a;
      b++;
      id++;
    }
    
    
    extern "C"
    void someCUDAcode() {
      mykernel<<<1, 1>>>(1);
    }
    

    With respect to a configure file (using autoconf), you are welcome to check out my gpuRcuda package using Rcpp, CUDA, and ViennaCL (a C++ GPU computing library).

    0 讨论(0)
提交回复
热议问题