2014-10-07 4 views
1

В следующем примере игрушки для parallelFor работает отлично (f2 является распараллеливаемым f1):Сожительство из RcppArmadillo и RcppParallel

// [[Rcpp::depends(RcppParallel)]] 
// [[Rcpp::depends(RcppArmadillo)]] 
#include <RcppArmadillo.h> 
#include <RcppParallel.h> 
#include <iostream> 
#define vector NumericVector 

using namespace Rcpp; 
using namespace RcppParallel; 


// compute values i/i+1 for i = 0 to n-1 
// [[Rcpp::export]] 
vector f1(int n) { 
    vector x(n); 
    for(int i = 0; i < n; i++) x[i] = (double) i/ (i+1); 
    return x; 
} 

struct mytry : public Worker { 
    vector output; 

    mytry(vector out) : output(out) {} 

    void operator()(std::size_t begin, std::size_t end) { 
    for(int i = begin; i < end; i++) output[i] = (double) i/ (i+1); 
    } 

}; 

// [[Rcpp::export]] 
vector f2(int n) { 
    vector x(n); 
    mytry A(x); 
    parallelFor(0, n, A); 
    return x; 
} 

Однако, если я заменю #define vector NumericVector на #define vector arma::vec это больше не работает. Коды компилируются и запускаются, f1 в порядке, но вектор, возвращаемый f2, просто содержит неинициализированные значения.

Большое спасибо за любое разъяснение.

+1

Должен ли 'arma :: vec' быть ссылочным членом класса? (Это работает для случая «Rcpp», поскольку векторы R являются прокси-объектами по указателям, поэтому вы все равно получаете доступ к одному объекту) –

+0

Спасибо, я попробую. – Elvis

+1

@KevinUshey Это нормально, два хорошо размещенных '&' и он работает: Точно, замените 'vector output'' на 'vector & output;' и 'mytry (vector out): output (out) {}' by 'mytry (vector & out): output (out) {} '. Вы сформулируете свой комментарий в качестве ответа? Я буду рад принять это. Поскольку я вряд ли буду единственным, кто однажды встретит эту проблему, я думаю, что это того стоит. – Elvis

ответ

3

Проблема здесь - ваш класс должен брать вектор по ссылке, а не по значению.

Это связано с тем, что при использовании RcppParallel вы обычно размещаете память для объекта где-то, а затем заполняете этот объект - поэтому работники параллельного доступа должны брать ссылку на тот объект, который хотите заполнить.

Так что ваш работник должен выглядеть (как вы заметили):

struct mytry : public Worker { 
    vector& output; 

    mytry(vector& out) : output(out) {} 

    void operator()(std::size_t begin, std::size_t end) { 
    for(int i = begin; i < end; i++) output[i] = (double) i/ (i+1); 
    } 

Обратите внимание, что это работает (возможно, удивительно) для векторов Rcpp, потому что они уже просто «прокси» объекты - только объекты инкапсулирования указатель на данные. Когда вы передаете вектор Rcpp по значению, вы копируете указатель (а не базовые данные!) Плюс некоторые дополнительные векторные биты (например, длину вектора), поэтому «копия» сохраняет ссылку на одну и ту же структуру данных.

Когда вы используете более «классический» вектор, например. arma::vec или std::vector, при передаче тому, что по значению для работника вы действительно копируете целый новый вектор в класс, а затем заполняете этот (временный, скопированный) вектор - поэтому исходный вектор никогда не заполняется.

Смежные вопросы