В настоящее время я пытаюсь распараллелить существующую иерархическую схему выборки MCMC. Большая часть моего (теперь последовательного) исходного кода написана в RcppArmadillo, поэтому я хотел бы придерживаться этой структуры для распараллеливания.Rcpp вызывает segfault RcppArmadillo не
Прежде чем начать с распараллеливания моего кода, я прочитал пару сообщений в блоге на Rcpp/Openmp. В большинстве этих сообщений в блоге (например, Drew Schmidt, wrathematics) авторы предупреждают о проблеме безопасности потоков, структур данных R/Rcpp и Openmp. В нижней строке всех сообщений, которые я прочитал до сих пор, R и Rcpp не являются потокобезопасными, не вызывайте их из параллельной прагмы omp.
Из-за этого, в следующем примере Rcpp вызывает Segfault, когда вызывается из R:
#include <Rcpp.h>
#include <omp.h>
using namespace Rcpp;
double rcpp_rootsum_j(Rcpp::NumericVector x)
{
Rcpp::NumericVector ret = sqrt(x);
return sum(ret);
}
// [[Rcpp::export]]
Rcpp::NumericVector rcpp_rootsum(Rcpp::NumericMatrix x, int cores = 2)
{
omp_set_num_threads(cores);
const int nr = x.nrow();
const int nc = x.ncol();
Rcpp::NumericVector ret(nc);
#pragma omp parallel for shared(x, ret)
for (int j=0; j<nc; j++)
ret[j] = rcpp_rootsum_j(x.column(j));
return ret;
}
Как Дрю объясняет в своем блоге, то выдаёт ошибку сегментации происходит из-за «скрытой» копии, что делает Rcpp в обращении к ret[j] = rcpp_rootsum_j(x.column(j));
.
Поскольку я заинтересован в поведении RcppArmadillo в случае распараллеливания, у меня есть пример преобразованного Дрю:
//[[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>
#include <omp.h>
double rcpp_rootsum_j_arma(arma::vec x)
{
arma::vec ret = arma::sqrt(x);
return arma::accu(ret);
}
// [[Rcpp::export]]
arma::vec rcpp_rootsum_arma(arma::mat x, int cores = 2)
{
omp_set_num_threads(cores);
const int nr = x.n_rows;
const int nc = x.n_cols;
arma::vec ret(nc);
#pragma omp parallel for shared(x, ret)
for (int j=0; j<nc; j++)
ret(j) = rcpp_rootsum_j_arma(x.col(j));
return ret;
}
Интересно, что семантически эквивалентный код не вызывает Segfault.
Вторая вещь, которую я заметил во время моего исследования в том, что вышеупомянутое заявление (R и Rcpp не поточно, не называйте их изнутри OMP параллельно прагме), кажется, не всегда имеет место, чтобы быть правдой , Например, вызов в следующем примере не вызывает segfault, хотя мы читаем и записываем структуры данных Rcpp.
#include <Rcpp.h>
#include <omp.h>
// [[Rcpp::export]]
Rcpp::NumericMatrix rcpp_sweep_(Rcpp::NumericMatrix x, Rcpp::NumericVector vec)
{
Rcpp::NumericMatrix ret(x.nrow(), x.ncol());
#pragma omp parallel for default(shared)
for (int j=0; j<x.ncol(); j++)
{
#pragma omp simd
for (int i=0; i<x.nrow(); i++)
ret(i, j) = x(i, j) - vec(i);
}
return ret;
}
Мои вопросы
-
Почему код из первого примера вызывает Segfault, но код из примера два и три не? - Как узнать, может ли безопасный вызов метода (
arma::mat.col(i)
) или если небезопасно вызывать метод (Rcpp::NumericMatrix.column(i)
)? Должен ли я каждый раз читать исходный код среды? -
Любые предложения о том, как избежать таких «непрозрачных» ситуаций, как в примере один?
Это может быть чистое совпадение, что мой пример RcppArmadillo не подведет. См. Комментарии Dirks ниже.
EDIT 1
В своем ответе и в обоих своих комментариях Dirk настоятельно рекомендует более внимательно изучить примеры в Rcpp галерее.
Вот мои первоначальные предположения:
- Извлечение строк, столбцов и т.д. внутри прагма OpenMP, как правило, не поточно, так как он может перезвонить в R, чтобы выделить новое пространство в памяти для скрытой копии.
- Поскольку RcppArmadillo полагается на ту же модель облегченного/прокси для структур данных, что и Rcpp, мое первое предположение также относится к RcppArmadillo.
- Структуры данных из пространства имен std должны быть как можно более безопасными, поскольку они не используют одну и ту же облегченную/прокси-схему.
- Примитивные типы данных также не должны вызывать проблем, поскольку они живут в стеке и не требуют, чтобы R выделяла и управляла памятью.
arma::mat temp_row_sub = temp_mat.rows(x-2, x+2);
interMatrix(_, i) = MAT_COV(_, index_asset); // 3rd code example 3rd method
thread_sum += R::dlnorm(i+j, 0.0, 1.0, 0); // subsection OpenMP support
На мой взгляд, первый и второй пример явно мешают мои предположения, сделанные в точке о ne и два. Пример три также дает мне головную боль, так как для меня это выглядит как вызов к R ...
Мои обновленные вопросы
- Где разница между, например один/два, и мой первый пример кода?
- Где я заблудился в своих предположениях?
Любые рекомендации, помимо RcppGallery и GitHub, о том, как лучше понять взаимодействие Rcpp и OpenMP?
Спасибо за ваш быстрый ответ! Возможно, мой вопрос сформулирован неоднозначно: я не хочу обвинять Rcpp в этом поведении, и я знаю, что существует множество R-пакетов с рабочим кодом openmp. Моя проблема просто: я просто ** не понимаю, почему ** безопасно вызывать arma :: col (i) или Rcpp: :(i, j), но не Rcpp ::. Colum (i). К сожалению, ни один из цитированных источников явно не затрагивает эту проблему; то же самое касается комментариев по GitHub [поиск: Rcpp & Openmp]. – Thomas
Неправильное предположение: просто потому, что ваш второй подход не срабатывает сразу же, не доказывает, что он нормальный или рекомендуется. Объекты RcppArmadillo _still_ используют R-память по нашему выбранному (эффективному) подходу. Поэтому, пожалуйста, прочитайте приведенные мной ссылки. –
Еще раз спасибо за ваше время! В выходные я более внимательно изучил примеры OpenMP в галерее Rcpp, которые вы указали мне. Так как некоторые из примеров еще больше смутили меня, я редактировал свой оригинальный вопрос. – Thomas