c++ - 使用 Rcpp/RcppArmadillo : Can't identify exactly what is wrong after debugging with valgrind 运行我的 C++ 代码时出现内存错误

标签 c++ r rcpp

我刚刚开始研究 Rcpp,我正在尝试实现 PRIM 的算法。在大量帮助和一些阅读之后,我有一个运行良好的版本,除了 n=​​50 或 n=1050(w/seed 1984)的模拟数据。

我的 RStudio 抛出“R session 中止”屏幕。从终端(我使用的是 Linux Mint 18.3)我得到

**** Error in `/usr/lib/R/bin/exec/R': double free or corruption (!prev): 0x00000000039f8690 ***

在寻找如何调试编译后的代码后,我发现:

我也读过 lldb,但决定使用 valgrind。

我准备了一个 cod_valgrind_test.R 文件,我在其中生成数据并编译我的 C++ 文件。所有这些文件都在我的 github 存储库 ( https://github.com/allanvc/test ) 中,但我决定在此处重现有问题的文件 (prim_cpp_bug.cpp) 的代码:

#include <iostream>
//#include <Rcpp.h>
#include <RcppArmadillo.h>

//using namespace Rcpp;
//using namespace arma;
using namespace std;


// [[Rcpp::depends(RcppArmadillo)]]


// [[Rcpp::export]]
Rcpp::List prim_cpp(arma::mat x)
{


    int V = x.n_cols;

    arma::uvec parent(V);
    parent.at(0) = 0;

    double max_value = x.max()+1;

    int v = 0;

    int idxmin_geral = 0;

    arma::uvec min_subnot;

    arma::mat new_m;

    arma::uvec from(V-1);
    //from.at(0) = 0;

    arma::uvec to(V-1);



    for(int i=0; i < V; i++)
    {
        // "deleting" the row for current vertex by setting the maximum to all entries in this row
        x.row(v).fill(max_value); // better than using loop

        // insert object x.col(v) at col i of new_m matrix
        new_m.insert_cols(i,x.col(v)); //see arma.sourceforge.net 

        //cout << new_m << endl;

        // obtain the minimum index from the selected columns
        idxmin_geral = new_m.index_min();

        // obtain the subscript notation from index based on reduced dimensions ***
        min_subnot = arma::ind2sub(arma::size(new_m.n_rows, new_m.n_cols),
                                    idxmin_geral);
        // *** adapted from @coatless
        // https://stackoverflow.com/questions/48045895/how-to-find-the-index-of-the-minimum-value-between-two-specific-columns-of-a-mat                                    

        v = min_subnot.at(0);
        parent.at(i+1) = v; // -----> !! this is line 61 <-----

        to.at(i) = min_subnot.at(0); // -----> !! this is line 63 <-----
        from.at(i) = parent.at(min_subnot.at(1)); // -----> !! this is line 64 <-----

        // "deleting" the row for current vertex by setting maximum to all entries in this row
        // now, in the new matrix
        new_m.row(v).fill(max_value); //better than using loop

    }
    /*
     * add 1 to the final vectors - preparing R output
    */
    return Rcpp::List::create(
    Rcpp::Named("dist",x),
    Rcpp::Named("parent",parent),
    Rcpp::Named("from",from+1),
    Rcpp::Named("to",to+1)
    );
}

在我的测试中,奇怪的是只有 n=50 和 n=1050 valgrind 在从我的 prim_cpp_bug.cpp 文件执行函数 prim_cpp() 时向我显示 3 个错误。

从终端返回运行 R -d valgrind -f cod_valgrind_test.R:

==36904== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)

有问题的行似乎是:

  • 61、63 和 64

==36904== Invalid write of size 4 ==36904== at 0x12315F5D: prim_cpp(arma::Mat) (prim_cpp_bug.cpp:61)

(...)

==36904== Invalid write of size 4 ==36904== at 0x12315F63: prim_cpp(arma::Mat) (prim_cpp_bug.cpp:63)

(...)

==36904== Invalid write of size 4 ==36904== at 0x12315F74: prim_cpp(arma::Mat) (prim_cpp_bug.cpp:64)

似乎我为我的 vector 做了一些错误的内存分配 - 可能是在使用索引时。我想我读过一些人不推荐使用 .at() 但我不确定。由于我没有足够的 C++ 和 Rcpp/RcppArmadillo 知识来解决这个问题,我将不胜感激任何帮助。

我的 sessionInfo() 如下:

R version 3.4.3 (2017-11-30)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Linux Mint 18.3

Matrix products: default
BLAS: /usr/lib/libblas/libblas.so.3.6.0
LAPACK: /usr/lib/lapack/liblapack.so.3.6.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8       
 [4] LC_COLLATE=en_US.UTF-8     LC_MONETARY=pt_BR.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=pt_BR.UTF-8       LC_NAME=C                  LC_ADDRESS=C              
[10] LC_TELEPHONE=C             LC_MEASUREMENT=pt_BR.UTF-8 LC_IDENTIFICATION=C       

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

loaded via a namespace (and not attached):
[1] compiler_3.4.3 tools_3.4.3    yaml_2.1.16

最佳答案

由于 parent 是用 V 元素初始化的,因此索引在最后一次迭代中越界,其中 i+1 将是V 第 61 行(因为索引从 0 开始)。

它不一定在所有情况下都出错这一事实不足为奇,因为在许多情况下,代码无论如何都会设法从内存中收集一些随机的东西。所以幸运的是有几个错误,否则结果可能只是错误而没有人注意到......

关于c++ - 使用 Rcpp/RcppArmadillo : Can't identify exactly what is wrong after debugging with valgrind 运行我的 C++ 代码时出现内存错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48225516/

相关文章:

r - 包 X 的错误 : function is not available for . Call()

c++ - 通过 Rcpp 从 R 调用 QuantLib

c++ - 使用 stat 检测文件是否存在(慢?)

c++ - C++中的二维数组加法

r - 如何减少 R 列表中的值

r - 如何在 ubuntu 12.04 中安装 R 的预测包?

R循环查找数据帧中的最小值和最大值

r - 在 R 中找不到指定的模块

c++ - Linux进程的默认分配堆大小

c++ - 我可以在 C++ 中使用未标记值的枚举吗?