【问题标题】:RStudio crashes with RCpp with reproducible codesRStudio 与具有可重现代码的 RCpp 崩溃
【发布时间】:2026-02-18 04:20:04
【问题描述】:

@user3759195 写了一篇关于 RStudio 与 RCpp 崩溃的帖子 https://*.com/questions/24322356/rstudio-crashes-and-it-does-not-reproduce,但没有给出任何可重现的案例。 @KevinUshey 在 cmets 中提到我们必须在代码中 PROTECT wrap

我冒昧地发布了两个用 RCpp 编写的 split.data.frame 函数的替代方案:

* 不会导致 RSTUDIO 崩溃的版本 *

//[[Rcpp::export]]
List splitDataFrameCpp(DataFrame x,NumericVector y) {
  int nRows=x.nrows();
  int nCols=x.size();

  std::map<double,vector<double> > z;
  for (int i=0;i<nCols;i++) {
    std::vector<double> tmp=Rcpp::as<std::vector<double> > (x[i]);
    for (int j=0;j<nRows;j++) {
      z[y[j]].push_back(tmp[j]);      
    }
  }

  std::vector<double> yunq=Rcpp::as<std::vector<double> > (sort_unique(y));
  std::map<double, DataFrame> z1;
  for (int i=0;i<int(yunq.size());i++) {
    NumericVector tmp1=wrap(z[yunq[i]]);   // *** DEFINING INSIDE LOOP ***
    tmp1.attr("dim")=Dimension(int(tmp1.size())/nCols,nCols);
    DataFrame tmp2(wrap(tmp1));   // *** DEFINING INSIDE LOOP ***
    tmp2.attr("names")=x.attr("names");
    z1[yunq[i]]=tmp2;
  }
  return wrap(z1);  
}

* 导致 RSTUDIO 崩溃的版本 *

//[[Rcpp::export]]
List splitDataFrameCpp(DataFrame x,NumericVector y) {
  int nRows=x.nrows();
  int nCols=x.size();

  std::map<double,vector<double> > z;
  for (int i=0;i<nCols;i++) {
    std::vector<double> tmp=Rcpp::as<std::vector<double> > (x[i]);
    for (int j=0;j<nRows;j++) {
      z[y[j]].push_back(tmp[j]);      
    }
  }

  std::vector<double> yunq=Rcpp::as<std::vector<double> > (sort_unique(y));
  std::map<double, DataFrame> z1;

  NumericVector tmp1;    // *** DEFINING OUTSIDE LOOP ***
  DataFrame tmp2;    // *** DEFINING OUTSIDE LOOP ***

  for (int i=0;i<int(yunq.size());i++) {
    tmp1=wrap(z[yunq[i]]);
    tmp1.attr("dim")=Dimension(int(tmp1.size())/nCols,nCols);
    tmp2=wrap(tmp1);
    tmp2.attr("names")=x.attr("names");
    z1[yunq[i]]=tmp2;
  }    
  return wrap(z1);      
}

这两个代码之间的主要区别在于,在一种情况下tmp1tmp2 是在循环内定义的,而在另一种情况下是在循环外定义的。

  1. 谁能解释为什么第二个循环会崩溃(以及可以更改什么以不使 RStudio 崩溃)?我仍然是 C++ 的新手,主要通过查看 SO 或 RCpp 库网站上的示例来编写 RCpp - 所以想进一步了解这种行为。

  2. 另外,作为附带的好处,如果有人可以建议更改以使代码更快,那将是很棒的。根据我使用的一些测试用例,目前不会崩溃的代码比 R 的 split.data.frame 函数快 2 到 3 倍左右。

测试用例示例:

> testDF
   V1 V2 V3 V4 V5 V6
1   1  5  4  1  3  2
2   2  1  5  4  1  3
3   2  2  1  5  4  1
4   3  2  2  1  5  4
5   1  3  2  2  1  5
6   4  1  3  2  2  1
7   1  5  4  1  3  2
8   2  1  5  4  1  3
9   2  2  1  5  4  1
10  3  2  2  1  5  4
11  1  3  2  2  1  5
12  4  1  3  2  2  1

> testSp<-c(1,1,1,2,2,2,3,4,4,3,3,5)

> split(testDF,testSp)     OR  > splitDataFrameCpp(testDF,testSp)     
$`1`
  V1 V2 V3 V4 V5 V6
1  1  5  4  1  3  2
2  2  1  5  4  1  3
3  2  2  1  5  4  1

$`2`
  V1 V2 V3 V4 V5 V6
4  3  2  2  1  5  4
5  1  3  2  2  1  5
6  4  1  3  2  2  1

$`3`
   V1 V2 V3 V4 V5 V6
7   1  5  4  1  3  2
10  3  2  2  1  5  4
11  1  3  2  2  1  5

$`4`
  V1 V2 V3 V4 V5 V6
8  2  1  5  4  1  3
9  2  2  1  5  4  1

$`5`
   V1 V2 V3 V4 V5 V6
12  4  1  3  2  2  1

此测试用例的microbenchmark 结果:

> microbenchmark(t1<-split(testDF,testSp),t2<-splitDataFrameCpp(testDF,testSp))
Unit: microseconds
                                   expr     min      lq   median       uq      max neval
             t1 <- split(testDF, test2) 343.181 365.562 372.8760 387.9430 1027.786   100
 t2 <- splitDataFrameCpp(testDF, test2) 177.881 190.315 200.5545 208.4545  870.093   100

* 编辑 *

添加了sessionInfo

> sessionInfo()
R version 3.1.0 (2014-04-10)
Platform: x86_64-w64-mingw32/x64 (64-bit)

locale:
[1] LC_COLLATE=English_United States.1252  LC_CTYPE=English_United States.1252   
[3] LC_MONETARY=English_United States.1252 LC_NUMERIC=C                          
[5] LC_TIME=English_United States.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] microbenchmark_1.3-0

loaded via a namespace (and not attached):
[1] Rcpp_0.11.1 tools_3.1.0

另外,testDF 在 R 中创建为 numeric,而不是 integer

【问题讨论】:

  • 感谢您抽出宝贵时间生成一个可重复的示例——您能补充一些缺失的部分吗? 1.sessionInfo(), 2.testDF 是如何构造的(列是整数还是数字?)FWIW,您发布的第二个版本不会在 Mac OSX Mavericks 上使用 Rcpp 0.11.2、R 3.1.0 崩溃, RStudio 0.98.943.
  • FWIW 它对我来说也不会崩溃,无论是在 RStudio 中还是在 RStudio 之外;
  • 好帖子,好 cmets。一个真正可重现的示例确实需要一个数据集(包括其创建或dput() 调用)和示例调用。
  • @KevinUshey,添加了 sessionInfotestDF 是数字。可能是 Windows 版本的 RStudio 问题?
  • 我可以用Rcpp 0.11.1复制你的段错误,所以我的建议是在CRAN上更新到最新版本的Rcpp(现在是(0.11.2)

标签: r rcpp


【解决方案1】:

对于它的价值,这里有一个完整的例子,你可以sourceCpp()。与 Kevin 和 Romain 所指出的类似,我也不会崩溃。

#include <Rcpp.h>

using namespace Rcpp;
using namespace std;

//[[Rcpp::export]]
List splitDataFrameCppA(DataFrame x,NumericVector y) {
  int nRows=x.nrows();
  int nCols=x.size();

  std::map<double,vector<double> > z;
  for (int i=0;i<nCols;i++) {
    std::vector<double> tmp=Rcpp::as<std::vector<double> > (x[i]);
    for (int j=0;j<nRows;j++) {
      z[y[j]].push_back(tmp[j]);      
    }
  }

  std::vector<double> yunq=Rcpp::as<std::vector<double> > (sort_unique(y));
  std::map<double, DataFrame> z1;
  for (int i=0;i<int(yunq.size());i++) {
    NumericVector tmp1=wrap(z[yunq[i]]);   // *** DEFINING INSIDE LOOP ***
    tmp1.attr("dim")=Dimension(int(tmp1.size())/nCols,nCols);
    DataFrame tmp2(wrap(tmp1));   // *** DEFINING INSIDE LOOP ***
    tmp2.attr("names")=x.attr("names");
    z1[yunq[i]]=tmp2;
  }
  return wrap(z1);  
}


//[[Rcpp::export]]
List splitDataFrameCppB(DataFrame x,NumericVector y) {
  int nRows=x.nrows();
  int nCols=x.size();

  std::map<double,vector<double> > z;
  for (int i=0;i<nCols;i++) {
    std::vector<double> tmp=Rcpp::as<std::vector<double> > (x[i]);
    for (int j=0;j<nRows;j++) {
      z[y[j]].push_back(tmp[j]);      
    }
  }

  std::vector<double> yunq=Rcpp::as<std::vector<double> > (sort_unique(y));
  std::map<double, DataFrame> z1;

  NumericVector tmp1;    // *** DEFINING OUTSIDE LOOP ***
  DataFrame tmp2;    // *** DEFINING OUTSIDE LOOP ***

  for (int i=0;i<int(yunq.size());i++) {
    tmp1=wrap(z[yunq[i]]);
    tmp1.attr("dim")=Dimension(int(tmp1.size())/nCols,nCols);
    tmp2=wrap(tmp1);
    tmp2.attr("names")=x.attr("names");
    z1[yunq[i]]=tmp2;
  }    
  return wrap(z1);      
}


/*** R

testDF <- read.table(textConnection("
1  5  4  1  3  2
2  1  5  4  1  3
2  2  1  5  4  1
3  2  2  1  5  4
1  3  2  2  1  5
4  1  3  2  2  1
1  5  4  1  3  2
2  1  5  4  1  3
2  2  1  5  4  1
3  2  2  1  5  4
1  3  2  2  1  5
4  1  3  2  2  1
"))

testSp <- c(1,1,1,2,2,2,3,4,4,3,3,5)


str(splitDataFrameCppA(testDF, testSp))
str(splitDataFrameCppB(testDF, testSp))

library(microbenchmark)
microbenchmark(split(testDF,testSp),
               splitDataFrameCppA(testDF,testSp),
               splitDataFrameCppB(testDF,testSp))

*/

基准测试大约在您的两个版本之间:

R> library(microbenchmark)

R> microbenchmark(split(testDF,testSp),
+                splitDataFrameCppA(testDF,testSp),
+                splitDataFrameCppB(testDF,testSp))
Unit: microseconds
                               expr     min      lq  median      uq      max neval
              split(testDF, testSp) 687.271 724.748 745.287 791.574 2373.283   100
 splitDataFrameCppA(testDF, testSp) 380.781 393.161 406.686 421.469  491.803   100
 splitDataFrameCppB(testDF, testSp) 377.959 393.391 405.476 429.947 2052.193   100
R> 
R> 

【讨论】: