运行c++代码w/ Rcpp/RcppArmadillo时的内存错误:在使用valgrind进行调试后,无法准确地识别出哪里出了问题

时间:2022-01-08 09:00:01

I've just started studying Rcpp and I'm trying to implement PRIM's algorithm. After tons of help and some reading, I have a version that works well, except for simulated data with n=50 or n=1050 (w/ seed 1984).

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

My RStudio throws the "R session Aborted" screen. From the terminal (I'm using Linux Mint 18.3) I get

我的RStudio抛出“R会话中止”屏幕。从终端(我正在使用Linux Mint 18.3)获得

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

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

After seeking for how to debug my compiled code, I've found:

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

I've also read about lldb, but decided to go with valgrind.

我也读过关于lldb的文章,但我决定选择valgrind。

I've prepared a cod_valgrind_test.R file in which I generate data and compile my C++ files. All those files are in my github repo (https://github.com/allanvc/test), but I decided to reproduce the code of the problematic file (prim_cpp_bug.cpp) here:

我准备了一个cod_valgrind_test。生成数据并编译c++文件的R文件。所有这些文件都在我的github repo中(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://*.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)
    );
}

In my tests, oddly only for n=50 and n=1050 valgrind show me 3 ERRORS when executing the function prim_cpp() from my prim_cpp_bug.cpp file.

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

Running R -d valgrind -f cod_valgrind_test.R from terminal returns:

运行R -d valgrind -f cod_valgrind_test。R从终端的回报:

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

==36904== ERROR SUMMARY: 3个上下文中的3个错误(隐含:0从0)

The problematic lines seem to be:

问题似乎是:

  • 61, 63 and 64
  • 61年、63年和64年

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

== =36904==无效写入大小4== 36904==在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==无效写入大小4== 36904==在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)

== =36904==无效写入大小4== 36904== 0x12315F74: prim_cpp(arma:::Mat) (prim_cpp_bug.cpp:64)

It seems I'm doing some wrong memory allocation for my vectors - maybe when using indices. I think I’ve read somewhere people not recommending using .at() but I'm not sure. As I don't have enough knowledge about C++ and Rcpp/RcppArmadillo to fix that, I would appreciate any help.

似乎我在为我的向量做一些错误的内存分配——也许在使用索引时。我想我在某些地方读过有人不推荐使用。at(),但我不确定。由于我对c++和Rcpp/RcppArmadillo没有足够的了解,所以我非常感谢您的帮助。

My sessionInfo() is below:

我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

1 个解决方案

#1


3  

Since parent is initialized with V elements, the indexing is going out of bounds with the last iteration, where i+1 would be V on line 61 (since indexing starts from 0).

由于父元素是用V元素初始化的,所以索引在最后一次迭代中超出了界限,其中i+1在第61行是V(因为索引从0开始)。

The fact that it doesn't necessarily error for all cases is not a surprise, as in many cases the code will manage to collect some random stuff from memory anyhow. So luckily there were a couple of errors, else the results could simply have been wrong without anyone noticing...

事实上,它不一定在所有情况下都是错误的,这并不令人惊讶,因为在许多情况下,代码将设法从内存中收集一些随机的东西。所以幸运的是出现了一些错误,否则结果很可能是错误的,没有人注意到……

#1


3  

Since parent is initialized with V elements, the indexing is going out of bounds with the last iteration, where i+1 would be V on line 61 (since indexing starts from 0).

由于父元素是用V元素初始化的,所以索引在最后一次迭代中超出了界限,其中i+1在第61行是V(因为索引从0开始)。

The fact that it doesn't necessarily error for all cases is not a surprise, as in many cases the code will manage to collect some random stuff from memory anyhow. So luckily there were a couple of errors, else the results could simply have been wrong without anyone noticing...

事实上,它不一定在所有情况下都是错误的,这并不令人惊讶,因为在许多情况下,代码将设法从内存中收集一些随机的东西。所以幸运的是出现了一些错误,否则结果很可能是错误的,没有人注意到……