R 检查导致SEG故障的cpp代码
我有一些cpp代码,它在一个调用了大约80k次的R函数中运行。它的测试套件全面且通过测试。它似乎运行良好的第一次调用60K次,然后在中间的某个地方,我得到一个Sebug。R 检查导致SEG故障的cpp代码,r,rcpp,R,Rcpp,我有一些cpp代码,它在一个调用了大约80k次的R函数中运行。它的测试套件全面且通过测试。它似乎运行良好的第一次调用60K次,然后在中间的某个地方,我得到一个Sebug。 *** Error in `/usr/lib/R/bin/exec/R': malloc(): memory corruption: 0x00000000047150f0 *** ======= Backtrace: ========= /lib/x86_64-linux-gnu/libc.so.6(+0x77725)[0x7
*** Error in `/usr/lib/R/bin/exec/R': malloc(): memory corruption: 0x00000000047150f0 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x77725)[0x7f684462e725]
/lib/x86_64-linux-gnu/libc.so.6(+0x819be)[0x7f68446389be]
/lib/x86_64-linux-gnu/libc.so.6(__libc_malloc+0x54)[0x7f684463a5a4]
/usr/lib/R/lib/libR.so(Rf_allocVector3+0x70d)[0x7f6844cd617d]
... # more
下面是我的一些代码作为例子,你能看到它有什么问题吗
返回一个LogicalVector
(例如TRUE
/FALSE
向量),其中前导NA
s标记为TRUE
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
LogicalVector leading_na(IntegerVector x) {
int n = x.size();
LogicalVector leading_na(n);
int i = 0;
while(x[i] == NA_INTEGER) {
leading_na[i] = TRUE;
i++;
}
return leading_na;
}
// [[Rcpp::export]]
LogicalVector trailing_na(IntegerVector x) {
LogicalVector trailing_na = leading_na(rev(x));
return rev(trailing_na);
}
从zoo软件包复制na.locf(x,na.rm=TRUE)的功能:
// [[Rcpp::export]]
IntegerVector na_locf(IntegerVector x) {
int n = x.size();
LogicalVector lna = leading_na(x);
for(int i = 0; i<n; i++) {
if((i > 0) & (x[i] == NA_INTEGER) & (lna[i] != TRUE)) {
x[i] = x[i-1];
}
}
return x;
}
为了解决主要问题,您会遇到看似随机的segfaults,因为您的代码包含未定义的行为——特别是数组边界冲突。由于您之前注意到C++是相当新的,因此,至少要阅读讨论这个特定错误的第一个答案是值得的。对于其他语言的C或C++来说,UB可能是一个很难理解的概念,主要是因为它并不总是以错误的形式表现出来。该行为实际上是未定义的,因此无法知道结果是什么,也不应该期望该行为在平台、编译器甚至编译器版本之间保持一致 我将使用您的
leading_na
功能进行演示,但是max_x_pos
功能存在相同的问题:
// [[Rcpp::export]]
Rcpp::LogicalVector leading_na(Rcpp::IntegerVector x) {
int n = x.size();
Rcpp::LogicalVector leading_na(n);
int i = 0;
while (x[i] == NA_INTEGER) {
// ^^^^
Rcpp::Rcout << i << "\n";
leading_na[i] = TRUE;
i++;
}
return leading_na;
}
但是,当我们用执行相同元素访问的at()
成员函数替换操作符[]
时,错误显而易见:
// [[Rcpp::export]]
Rcpp::LogicalVector leading_na2(Rcpp::IntegerVector x) {
int n = x.size();
Rcpp::LogicalVector leading_na(n);
int i = 0;
while (x.at(i) == NA_INTEGER) {
Rcpp::Rcout << i << "\n";
leading_na[i] = TRUE;
i++;
}
return leading_na;
}
请注意,由at
提供的附加边界检查确实会带来轻微的性能成本,因为此检查发生在运行时,因此即使在开发阶段使用at
而不是操作符[]
也是一个好主意,一旦您的代码经过彻底测试,如果需要更好的性能,那么最好恢复到操作符[]
至于解决方案,第一种方法是保持
while
循环,只需添加对i
值的检查:
while (i < n && x[i] == NA_INTEGER) {
leading_na[i] = TRUE;
i++;
}
在这种情况下,选择使用while
循环或for
循环并不重要,只要您选择的内容编写正确
还有一种(或两种)选择是使用迭代器而不是索引,在这种情况下,您可以使用while
循环或for
循环:
// [[Rcpp::export]]
Rcpp::LogicalVector leading_na5(Rcpp::IntegerVector x) {
int n = x.size();
Rcpp::LogicalVector leading_na(n);
Rcpp::IntegerVector::const_iterator it_x = x.begin();
Rcpp::LogicalVector::iterator first = leading_na.begin(),
last = leading_na.end();
/*
while (first != last && *it_x++ == NA_INTEGER) {
*first++ = TRUE;
}
*/
for ( ; first != last && *it_x == NA_INTEGER; ++first, ++it_x) {
*first = TRUE;
}
return leading_na;
}
尽管迭代器是非常有用的设备,但我不确定在这种特殊情况下,迭代器是否比手动索引有任何好处,因此我建议使用前两种方法之一
与segfault无关,代码还有一些其他方面值得解决
&
和|
分别执行原子逻辑and和原子逻辑OR,而&
和|
分别执行向量化逻辑and和向量化逻辑OR。在C++中,&
与使用&
具有相同的效果,但是您需要解决这个问题,因为您的目的是使用逻辑运算,而不是按位运算李>
x[i]==NA_INTEGER
确实可以测试x[i]
是否为NA
,但并非所有类型的行为都是这样的。IIRC,用NA_REAL检验任何事物是否相等总是错误的,即使是NA_REAL==NA_REAL;对于非整数算术类型(数字和复杂(REALSXP
/CPLXSXP
),您很可能还需要检查值是否为NaN
)。Rcpp根据对象类型提供了几种不同的方法。对于任何存储类型的向量,Rcpp::is_na(x)
将返回与x
大小相同的逻辑向量。对于原子值,我通常使用Rcpp::traits::is_na(x[I])
-REALSXP
表示double
,INTSXP
表示int
,CPLXSXP
表示Rcomplex
,等等。但是,我认为您可以等效地使用向量对应的静态成员函数,例如,Rcpp::NumericVector::is_na(x[I])
,在这种情况下,您不需要记住各种SEXPTYPE
s李>
严格来说,C++或C中没有<代码>真<代码> >代码>伪<代码>;这些(大概)是由R的API提供的方便TypeDef,所以请注意它们并不存在于R的后端之外。当然,可以在您的Rcpp代码中随意使用它们,因为它们的行为显然符合预期,但大多数人即使在使用Rcpp时也坚持标准true
和false
李>
leading_na
函数声明了一个局部变量,也被命名为leading_na
,这有点让人困惑,或者至少是非正统的李>
std::size_t
(标准C++)或R_xlen_t
(特定于R API),例如在以下表达式中:int n=x.size()代码>。这些是无符号整数类型,其大小应足以存储任何对象的长度,其中asint
是一种有符号整数类型,它可能足够,也可能不够(通常是)。99.9%的情况下,最糟糕的情况是,在使用int
s时,您会收到一些额外的编译器警告(而不是错误),而不是在表达式中使用for(int i=0;i
等其他两个警告。在极少数情况下,可能会有更严重的影响,如信号
leading_na2(rep(NA, 5))
# 0
# 1
# 2
# 3
# 4
# Error: index out of bounds
while (i < n && x[i] == NA_INTEGER) {
leading_na[i] = TRUE;
i++;
}
for (int i = 0; i < n && x[i] == NA_INTEGER; i++) {
leading_na[i] = TRUE;
}
// [[Rcpp::export]]
Rcpp::LogicalVector leading_na5(Rcpp::IntegerVector x) {
int n = x.size();
Rcpp::LogicalVector leading_na(n);
Rcpp::IntegerVector::const_iterator it_x = x.begin();
Rcpp::LogicalVector::iterator first = leading_na.begin(),
last = leading_na.end();
/*
while (first != last && *it_x++ == NA_INTEGER) {
*first++ = TRUE;
}
*/
for ( ; first != last && *it_x == NA_INTEGER; ++first, ++it_x) {
*first = TRUE;
}
return leading_na;
}