gpt4 book ai didi

c++ - 是否可以在不复制非常大的 std::vector 的情况下执行 Rcpp::wrap

转载 作者:行者123 更新时间:2023-12-04 14:48:04 25 4
gpt4 key购买 nike

我有一个 Rcpp 函数,它读取大型 BAM 文件(1-20GB,使用 htslib )并创建几个非常长的 std::vector s(最多 80M 个元素)。阅读之前不知道元素的数量,所以我不能使用 Rcpp::IntegerVectorRcpp::CharacterVector .据我了解,当我 Rcpp::wrap为了进一步使用它们,创建了拷贝。在这种情况下,有没有办法加快数据从 C++ 到 R 的传输?是否有可以在 Rcpp 函数中创建的数据结构,请尽快 push_back元素为 std::vector是,并通过引用传递给 R?

以防万一,这是我目前创建它们的方式:

std::vector<std::string> seq, xm;
std::vector<int> rname, strand, start;

下面是我如何包装和返回它们:

Rcpp::IntegerVector w_rname = Rcpp::wrap(rname);
w_rname.attr("class") = "factor";
w_rname.attr("levels") = chromosomes; // chromosomes contain names of the reference sequences from BAM

Rcpp::IntegerVector w_strand = Rcpp::wrap(strand);
w_strand.attr("class") = "factor";
w_strand.attr("levels") = strands; // std::vector<std::string> strands = {"+", "-"};

Rcpp::DataFrame res = Rcpp::DataFrame::create(
Rcpp::Named("rname") = w_rname,
Rcpp::Named("strand") = w_strand,
Rcpp::Named("start") = start,
Rcpp::Named("seq") = seq,
Rcpp::Named("XM") = xm
);

return(res);

编辑 1(2021.10.19):

感谢大家的意见,我需要更多的时间来检查是否stringfish可以使用,但我从 cpp11 包 vignettes 运行了一个稍微修改的测试,将它与 std::vector 进行比较.这是代码和结果(显示 std::vector<int> 仍然更快,尽管它必须在返回时被 Rcpp::wrap ped):

Rcpp::cppFunction('
#include <Rcpp.h>
using namespace Rcpp;

//[[Rcpp::export]]
std::vector<int> stdint_grow_(SEXP n_sxp) {
R_xlen_t n = REAL(n_sxp)[0];
std::vector<int> x;
R_xlen_t i = 0;
while (i < n) {
x.push_back(i++);
}

return x;
}')

library(cpp11test)
grid <- expand.grid(len = 10 ^ (0:7), pkg = c("cpp11", "stdint"), stringsAsFactors = FALSE)
b_grow <- bench::press(.grid = grid,
{
fun = match.fun(sprintf("%sgrow_", ifelse(pkg == "cpp11", "", paste0(pkg, "_"))))
bench::mark(
fun(len)
)
}
)[c("len", "pkg", "min", "mem_alloc", "n_itr", "n_gc")]

print(b_grow, n=Inf)

# A tibble: 12 × 6
len pkg min mem_alloc n_itr n_gc
<dbl> <chr> <bch:tm> <bch:byt> <int> <dbl>
1 100 cpp11 1.9µs 1.89KB 9999 1
2 1000 cpp11 6.1µs 16.03KB 9999 1
3 10000 cpp11 58.11µs 256.22KB 7267 12
4 100000 cpp11 488.15µs 2MB 815 11
5 1000000 cpp11 4.34ms 16MB 88 14
6 10000000 cpp11 97.39ms 256MB 4 5
7 100 stdint 1.6µs 2.93KB 10000 0
8 1000 stdint 3.36µs 6.45KB 9998 2
9 10000 stdint 19.87µs 41.6KB 9998 2
10 100000 stdint 181.88µs 393.16KB 2571 4
11 1000000 stdint 1.91ms 3.82MB 213 3
12 10000000 stdint 36.09ms 38.15MB 9 1

编辑 2:

std::vector<std::string>cpp11::writable::strings 稍慢在这些测试条件下,但内存效率更高:

Rcpp::cppFunction('
#include <Rcpp.h>
using namespace Rcpp;

//[[Rcpp::export]]
std::vector<std::string> stdstr_grow_(SEXP n_sxp) {
R_xlen_t n = REAL(n_sxp)[0];
std::vector<std::string> x;
R_xlen_t i = 0;
while (i++ < n) {
std::string s (i, 33);
x.push_back(s);
}

return x;
}')

cpp11::cpp_source(code='
#include "cpp11/strings.hpp"

[[cpp11::register]] cpp11::writable::strings cpp11str_grow_(R_xlen_t n) {
cpp11::writable::strings x;
R_xlen_t i = 0;
while (i++ < n) {
std::string s (i, 33);
x.push_back(s);
}

return x;
}
')

library(cpp11test)
grid <- expand.grid(len = 10 ^ (0:5), pkg = c("cpp11str", "stdstr"), stringsAsFactors = FALSE)
b_grow <- bench::press(.grid = grid,
{
fun = match.fun(sprintf("%sgrow_", ifelse(pkg == "cpp11", "", paste0(pkg, "_"))))
bench::mark(
fun(len)
)
}
)[c("len", "pkg", "min", "mem_alloc", "n_itr", "n_gc")]

print(b_grow, n=Inf)

# A tibble: 12 × 6
len pkg min mem_alloc n_itr n_gc
<dbl> <chr> <bch:tm> <bch:byt> <int> <dbl>
1 1 cpp11str 1.22µs 0B 10000 0
2 10 cpp11str 3.02µs 0B 9999 1
3 100 cpp11str 22µs 1.89KB 9997 3
4 1000 cpp11str 765.28µs 541.62KB 602 2
5 10000 cpp11str 66.69ms 47.91MB 8 0
6 100000 cpp11str 6.83s 4.62GB 1 0
7 1 stdstr 1.38µs 2.49KB 10000 0
8 10 stdstr 1.86µs 2.49KB 10000 0
9 100 stdstr 16.44µs 3.32KB 10000 0
10 1000 stdstr 898.23µs 10.35KB 511 0
11 10000 stdstr 73.55ms 80.66KB 7 0
12 100000 stdstr 7.54s 783.79KB 1 0

解决方案(2022.01.12):

...对于那些有类似问题的人。在这种特殊情况下,我不需要使用 std::vector R.So 中的数据 XPtr轻松解决了我的问题,将 BAM 加载时间缩短了近两倍。指针被创建:

std::vector<std::string>* seq = new std::vector<std::string>;
std::vector<std::string>* xm = new std::vector<std::string>;

然后存储为 data.frame属性:

Rcpp::DataFrame res = Rcpp::DataFrame::create(                                
Rcpp::Named("rname") = w_rname,
Rcpp::Named("strand") = w_strand,
Rcpp::Named("start") = start
);

Rcpp::XPtr<std::vector<std::string>> seq_xptr(seq, true);
res.attr("seq_xptr") = seq_xptr;

Rcpp::XPtr<std::vector<std::string>> xm_xptr(xm, true);
res.attr("xm_xptr") = xm_xptr;

并在其他地方重用如下:

Rcpp::XPtr<std::vector<std::string>> seq((SEXP)df.attr("seq_xptr"));
Rcpp::XPtr<std::vector<std::string>> xm((SEXP)df.attr("xm_xptr"));

最佳答案

我们使用 std::vector<>因为其强大的实现加上出色的性能(因为在任何比较中通常很难看到 std::vector<> 被打败)。但它使用自己的分配器来分配 R 之外的内存。

Rcpp将对象返回给 R,这些对象与 R 创建的对象无法区分,因为它们使用 R 自己的数据结构,并且需要将最终拷贝复制到 R 使用、拥有和分配的内存中。如果您想使用当前接口(interface),根本无法绕过它将所有元素返回给 R。

R 现在有 ALTREP 允许替代/外部表示,所以你可以做一些不同的东西,但实际上这有点困难,因为 ALTREP 的 API 仍然有些不完整和变化。一些软件包是使用 ALTREP 构建的,但我现在没有想到适合您的特定用例。

编辑:对于您的字符串 vector ,您可以(并且应该)尝试 stringfish Travers 包装。它对字符串使用 ALTREP,这可能是您更大的性能障碍。对于 int vector 我没有其他选择,但也许是最终的 memcpy也没有那么痛苦(与内部处理不同的字符串相反,这使得它们更昂贵)。

关于c++ - 是否可以在不复制非常大的 std::vector 的情况下执行 Rcpp::wrap,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69612309/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com