考虑以下串行示例函数:
// [[Rcpp::plugins(cpp20)]]
#include <Rcpp.h>
// [[Rcpp::export]]
Rcpp::NumericVector example_fun(int n) {
Rcpp::NumericVector result (n);
for(int i = 0; i < n; ++i) {
result[i] = something();
}
return result;
}
使用 OpenMP 并行化此循环需要使用例如
std::vector
或 RcppParallel::RVector
,因为 Rcpp 向量不是线程安全的。对应的并行std::vector
版本是
// [[Rcpp::plugins(cpp20)]]
// [[Rcpp::plugins(openmp)]]
#include <Rcpp.h>
#include <omp.h>
#include <vector>
// [[Rcpp::export]]
std::vector<double> example_fun(int n) {
std::vector<double> result (n);
#pragma omp parallel for
for(int i = 0; i < n; ++i) {
result[i] = something();
}
return result;
}
与
RcppParallel::RVector<double> example_fun(int n)
类似,用于 RcppParallel::RVector
。
如果我理解正确的话,导出
Rcpp::NumericVector
可以使数据可供 R 使用,而无需复制它,因为它本质上是 R 的本机数据类型。我想知道的是,导出 std::vector
或 RcppParallel::RVector
在内部如何工作?向量被复制了吗?是不是感动了?是否需要类型转换?重要的是,这两个选项之一是否明显比另一个更有效?
作为一个快速的附加问题,我还想知道,Rcpp 胎面安全问题是否也适用于矢量化 simd 循环:
#pragma omp simd
或 #pragma omp parallel for simd
?
谢谢。
您可能会让自己的事情变得复杂化。它有助于退后一步,您还可以检查 R 在您玩时所做的事情,例如内存分析选项很好!
简而言之,R 使用
SEXP
类型,并且这些类型具有“原生”整数和双精度向量——您可以从 R 中以 integer(3)
和 double(4)
的形式访问它们,分别创建和分配三元素向量和四元素向量.
现在,使用
Rcpp::IntegerVector
和 Rcpp::NumericVector
执行绝对等效的步骤。它使用 R 自己的分配器,并且生成的对象对于 R 来说与在 R 中创建的对象无法区分。(当然,通过 R 本身的 C API 是相同的,Rcpp 访问它。)
另一方面,C++ STL 对象(如
std::vector
)或贡献类型(如 RVector
)使用在别处分配的 内存(这有助于 例如 与 RcppParallel
,正如您所注意到的,并保持线程安全),因此对于每个其中我们必须将其复制到 R 数据结构中。这确实是它的要点。 “已经像 R 自己的一样”的数据不需要复制。其他一切都需要副本。
(总的来说,你的示例中没有任何地方有 C++20。甚至连 C++11 也没有。你写的东西可能是在十多年前构建的,当时 C++98(!!) 仍然是默认值。生活就是现在在当前的 R 和当前的编译器下好多了,所以我几乎从不设置标准插件,因为如果您使用的是足够当前的系统,C++14 或 C++17 已经是默认值。)