2015-06-11 8 views
5

कलात्मक http://gallery.rcpp.org/articles/parallel-distance-matrix/ से प्रेरित, मैं multithreads का उपयोग कर बैकटेस्टिंग के लिए उच्च-आयामी पैरामीट्रिक स्थान में ब्रूट-बल खोज चलाने के लिए RcppParallel का उपयोग करने का प्रयास करता हूं। मैं struct भाग में स्वयं परिभाषित फ़ंक्शन को कॉल करने के तरीके में फंस गया हूं। विचार इस तरह है:RcppParallel में उपयोगकर्ता द्वारा परिभाषित फ़ंक्शन को कैसे कॉल करें?

पहले, आर में एक पैरामीट्रिक मैट्रिक्स NumericMatrix params_mat बनाने पहले, और इस तरह के रूप में List Data_1, NumericVector Data_2, CharacterVector Data_3, ...List, NumericVector, CharacterVector डेटाप्रकार साथ backtesting डेटा, है, जो प्रत्येक पैरामीट्रिक परिदृश्य params_vec के लिए स्थिर रहे हैं का उपयोग करें (ध्यान दें कि यह पंक्ति है params_mat)।

अगला, बैकटेस्टिंग फ़ंक्शन को परिभाषित करें जो एक वेक्टर आउटपुट करता है जिसमें रणनीति प्रदर्शन का मूल्यांकन करने के लिए 3 प्रमुख चर शामिल होते हैं।

यहां मेरे params_mat और Backtesting_Fun का एक उदाहरण है जो क्रमशः आर और आरसीपीपी में चलाया जा सकता है।

// [[Rcpp::depends(RcppParallel)]] 
#include <RcppParallel.h> 
using namespace RcppParallel; 

RVector<double> Backtesting_Fun (const RVector<double> Data_1, const RVector<double> Data_2, 
           const RVector<string> Data_3,..., const RVector<double> params_vec) 
{ 
    // Main function parts to run backtesting for each params_vec scenario. 
    ... etc; 

    // save 3 key result variables together with each params_vec 
    ... etc; 

    return res; 
} 

struct Backtest_parallel : public Worker 
{  
    // input matrix to read from 
    const RVector<List> Data_1; 
    const RVector<double> Data_2; 
    const RVector<string> Data_3; 
    ... 
    const RMatrix<double> params_mat; 

    // output matrix to write to 
    RMatrix<double> rmat; 

    // initialize from Rcpp input and output matrixes (the RMatrix class 
    // can be automatically converted to from the Rcpp matrix type) 
    Backtest_parallel(const List Data_1, const NumericVector Data_2, 
    const CharacterVector Data_3, ..., const NumericMatrix params_mat) 
     : Data_1(Data_1), Data_2(Data_2), Data_3(Data_3), ..., params_mat(params_mat) {} 

    // function call operator that work for the specified range (begin/end) 
    void operator()(std::size_t begin, std::size_t end) 
    { 
     for (std::size_t ii = begin; ii < end; i++) 
     { 
     // params rows that we will operate on 
     RMatrix<double>::Row params_row = params_mat.row(ii); 

     // Run the backtesting function defined above 
     RVector<double> res = Backtesting_Fun(Data_1, Data_2, ..., params_row) 
     for (std::size_t jj = 0; jj < res.length(); jj++) 
     { 
      // write to output matrix 
      rmat(ii,jj) = res[jj]; 
     } 
     } 
    } 
}; 

// [[Rcpp::export]] 
NumericMatrix rcpp_parallel_backtest(List Data_1, NumericVector Data_2, CharacterVector Data_3, 
            ..., NumericMatrix params_mat) 
{  
    // allocate the matrix we will return 
    NumericMatrix rmat(params_mat.nrow(), params_mat.nrow()+3); 

    // create the worker 
    Backtest_parallel backtest_parallel(Data_1, Date_2, ..., params_mat); 

    // call it with parallelFor 
    parallelFor(0, rmat.nrow(), backtest_parallel); 

    return rmat; 
} 

यहाँ मेरी सवाल कर रहे हैं:

//[[Rcpp::export]] 
NumericMatrix data_frame_rcpp(const Rcpp::List& list_params) 
{ 
    NumericMatrix res = list_params[0]; 
    return res; 
} 

# R codes to generate params_mat 
params <- expand.grid (x_1=seq(1,100,1), x_2=seq(3,100,2), ..., x_n=seq(4,200,1));       
list_params = list(ts(params)); 
tmp_params_data = data_frame_rcpp(list_params);            
params_mat = matrix(tmp_params_data, ncol = ncol(tmp_params_data), dimnames = NULL); 
params_vec = params_mat[ii,]; 

# User-defined Rcpp codes for backtesting 
NumericVector Backtesting_Fun (List Data_1, NumericVector Data_2, CharacterVector Data_3, ..., NumericVector params_vec) 
{ 
    // Main function parts to run backtesting for each params_vec scenario. 
    ... etc 

    // save 3 key result variables together with each params_vec (just a simple illustration). 
    NumericVector res = NumericVector::create(params_vec[0],...,params_vec[size-1], 
              key_1, key_2, key_3); 
    return res; 
} 

निश्चित रूप से हम पुनर्लेखन के लिए/RVector/RMatrix प्रकार के साथ मूल Rcpp Backtesting_Fun संशोधित, और उसके बाद निम्न RcppParallel कोड का उपयोग कॉल करने के लिए struct Backtest_parallel में Backtesting_Fun जरूरत

  1. RVector में List डेटाटाइप, या RcppParallel में List रखने के लिए कोई विशिष्ट कंटेनर है;

  2. Backtesting_Fun में, इनपुट RVector/RMatrix प्रकार होना चाहिए, इसका मतलब यह है कि हम वास्तव में RVector में NumericVector साथ मूल Rcpp मुख्य कोड में परिवर्तित करने की आवश्यकता है?

या आरसीपीपीएरल में मेरे मामले के लिए समांतर कंप्यूटिंग करने का कोई बेहतर तरीका है? अग्रिम में धन्यवाद।

संपादित:

  1. मैं http://gallery.rcpp.org/articles/parallel-matrix-transform/, http://gallery.rcpp.org/articles/parallel-inner-product/ में RcppPararrel के बारे में अन्य उदाहरण को देखो, struct operator() में आम विचार है, operator() के लिए डेटा इनपुट में हेरफेर करने के संकेत दिए गए उपयोग करने के लिए है तो वहाँ किसी भी तरह से है पॉइंटर इनपुट के साथ मेरे मामले में एक उपयोगकर्ता परिभाषित समारोह बनाने के लिए?

  2. ऊपर जिस तरह से काम नहीं करता है, तो यह संभव wrap उपयोग करने के लिए operator() में वापस Rcpp डेटाप्रकार है, अर्थात में RVector/RMatrix कन्वर्ट करने के लिए, NumericVector.. ताकि उपयोगकर्ता परिभाषित समारोह Backtesting_Fun के इनपुट प्रकार में कोई बदलाव नहीं कर सकते हैं।

+0

यदि आप एक छोटे, पूर्ण (अपने कार्यों में नहीं ...) उदाहरण प्रदान करते हैं तो शायद आपको उत्तर पाने की अधिक संभावना होगी। – nrussell

+1

सुझाव @nrussell के लिए धन्यवाद, मैं जल्द ही सरल और सटीक उदाहरण के साथ प्रश्न अपडेट करूंगा – Alvin

उत्तर

3

मैं मैं इस सवाल को हल करने का एक वैकल्पिक तरीका मिल सकता है लगता है: चाबियाँ एक धागा सुरक्षित accessors का उपयोग struct भीतर चर को रोकने के लिए, और बाहर मुख्य कार्य में RVector/RMatrix रहने के लिए कर रहे हैं ताकि parallelFor ठीक काम कर सकते हैं, जो इस समांतर अलगाव में सबसे महत्वपूर्ण हिस्सा है।यहाँ मेरी तरीके हैं:

  1. List डेटाप्रकार से छुटकारा: इसके बजाय, हम NumericVector/NumericMatrix कंटेनर का उपयोग करके List चर परिवर्तित कर सकते हैं और उसके संगत सूचकांक रिकॉर्ड ताकि subvector/submatrix एक ही तत्व इंगित करेगा क्योंकि यह एक सूची के तत्व के रूप में था।

  2. Convert RVector/RMatrixarma::vec/arma::mat में: जैसा कि RcppParallel Github में उल्लेख किया है, C++ Armadillo धागा सुरक्षित struct के ऑपरेटर में हैं। यहां, मैं इस विचार का उपयोग करके Parallel Distance Matrix Calculation with RcppParallel में दिए गए उदाहरण को संशोधित करता हूं, जो लगभग एक ही परीक्षण गति बनी हुई है।

    struct JsDistance : public Worker 
    { 
        const RMatrix<double> tmp_MAT; // input matrix to read from 
        RMatrix<double> tmp_rmat;  // output matrix to write to 
        std::size_t row_size, col_size; 
    
        // Convert global input/output into RMatrix/RVector type 
        JsDistance(const NumericMatrix& matrix_input, NumericMatrix& matrix_output, 
          std::size_t row_size, std::size_t col_size) 
        : tmp_MAT(matrix_input), tmp_rmat(matrix_output), row_size(row_size), col_size(col_size) {} 
    
        // convert RVector/RMatrix into arma type for Rcpp function 
        // and the follwing arma data will be shared in parallel computing 
        arma::mat convert() 
        { 
        RMatrix<double> tmp_mat = tmp_MAT; 
        arma::mat MAT(tmp_mat.begin(), row_size, col_size, false); 
        return MAT; 
        } 
    
    
        void operator()(std::size_t begin, std::size_t end) 
        { 
        for (std::size_t i = begin; i < end; i++) 
        { 
         for (std::size_t j = 0; j < i; j++) 
         { 
         // rows we will operate on 
         arma::mat MAT = convert(); 
         arma::rowvec row1 = MAT.row(i);   // get the row of arma matrix 
         arma::rowvec row2 = MAT.row(j); 
    
         // compute the average using std::tranform from the STL 
         std::vector<double> avg(row1.n_elem); 
         std::transform(row1.begin(), row1.end(), // input range 1 
             row2.begin(),    // input range 2 
             avg.begin(),    // output range 
             average);     // function to apply 
    
         // calculate divergences 
         double d1 = kl_divergence(row1.begin(), row1.end(), avg.begin()); 
         double d2 = kl_divergence(row2.begin(), row2.end(), avg.begin()); 
    
         // write to output matrix 
         tmp_rmat(i,j) = sqrt(.5 * (d1 + d2)); 
         } 
        } 
        } 
    }; 
    
    // [[Rcpp::export]] 
    NumericMatrix rcpp_parallel_js_distance_modify(const Rcpp::NumericMatrix& matrix_input, int N_cores) 
    { 
        // allocate the matrix we will return 
        NumericMatrix matrix_output(matrix_input.nrow(), matrix_input.nrow()); 
        std::size_t row_size = matrix_input.nrow(); 
        std::size_t col_size = matrix_input.ncol(); 
    
        // create the worker 
        JsDistance jsDistance(matrix_input, matrix_output, row_size, col_size); 
    
        // call it with parallelFor 
        parallelFor(0, matrix_input.nrow(), jsDistance, matrix_input.nrow()/N_cores);   // parallelFor with grain size setting 
    
        return matrix_output; 
    } 
    
    // Example compare: 
    n_row = 1E3; 
    n_col = 1E2; 
    m = matrix(runif(n_row*n_col), nrow = n_row, ncol = n_col); 
    m = m/rowSums(m); 
    
    res <- benchmark(rcpp_parallel_js_distance(m, 6), 
         rcpp_parallel_js_distance_orignal(m, 6), 
         order="relative") 
    res[,1:4]; 
    
    #test         #elapsed #relative 
    rcpp_parallel_js_distance_orignal(m, 6) 128.069 1.000 
    rcpp_parallel_js_distance(m, 6)   129.210 1.009 
    

हम देख सकते हैं, operator भीतर डेटाप्रकार C++ arma हो जाएगा, और अब हम सुरक्षित रूप से और तेजी सीधे ही संकेत के बजाय वस्तु का उपयोग करके अपने उपयोगकर्ता परिभाषित समारोह कॉल कर सकते हैं, जो नहीं हो सकता है सामान्य या आसानी से डिजाइन किया गया।

अब, यह parallelFor संरचना समानांतर कंप्यूटिंग में अतिरिक्त प्रतिलिपि के बिना समान डेटा स्रोत साझा करेगी, और फिर हम उपरोक्त प्रश्न में उल्लिखित विचार का उपयोग करके बैकटेस्टिंग के लिए कुछ थोड़ा बदलाव कर सकते हैं।

संबंधित मुद्दे