Search code examples
c++matrixeigenaccess-violation

Large block coefficient-wise multiplication fails in Eigen library C++


I've read through a lot of the documentation, however if you find something which I've missed that can explain away my issue I'll be pleased. For background, I'm compiling on x86 Windows 10 in Visual Studio 2015 using the 3.2.7 Eigen Library. The 3.2.7 version is from May and while there have been releases since then, I haven't seen anything in the changelog that would indicate my issue has been solved.

The issue seems to only appear for matrices above a certain size. I don't know if this is a byproduct of something specific to my system or something inherent to Eigen.

The following code produces an access violation in both Debug and Release mode.

int mx1Rows = 255, cols = 254;
{//this has an access violation at the assignment of mx2
        Eigen::MatrixXd mx1(mx1Rows, cols);
        Eigen::MatrixXd mx2(mx1Rows + 1, cols);
        Eigen::Block<Eigen::MatrixXd, -1, -1, false> temp = mx2.topRows(mx1Rows);
        mx2 = temp.array() * mx1.array();//error
}

I believe the assignment of the coefficient-wise multiplication to be safe since the result should be aliased.

This issue becomes interesting when mx1Rows is reduced to the value 254, then the access violation doesn't appear. That's correct, the mx2 dimensions of 256 by 254 produce the problem but the dimensions 255 by 254 don't. If I increase the column size I can also get the access violation, so the problem likely has something to do with the total number of entries. The issue appears even if mx1 and mx2 are filled with values, having filled matrixes is not necessary to reproduce the issue.

Similar code that does not assign the topRows() block to temp does not produce the access violation in Release mode. I believe there is something more to this since I originally identified this issue in code that was considerably more complex and it only appeared after a certain number of loops (the matrix sizes were consistent between loops). There is so much going on in my code that I haven't been able to isolate the conditions under which the access violation only appears after a certain number of loops.

What I am curious to know is

1) Am I using Eigen in some obviously wrong way?

2) Are you able to reproduce this issue? (what is your environment particulars?)

3) Is this a bug in the Eigen library?

It's easy enough to work around this problem by assigning the block to a temporary matrix instead of a block, even if it is inefficient, so I'm not interested in hearing about that.


Solution

  • The problem is that temp references the coefficients held by mx2, but in the last assignment, mx2 is first resized before the expression gets evaluated. Therefore, during the actual evaluation of the expression, temp references garbage. More precisely, here is what is actually generated (in a simplified manner):

    double* temp_data = mx2.data;
    free(mx2.data);
    mx2.data = malloc(sizeof(double)*mx1Rows*cols);
    for(j=0;j<cols;++j)
      for(i=0;i<mx1Rows;++i)
        mx2(i,j) = temp_data[i+j*(mw1Rows+1)] * mx1(i,j);
    

    This is called an aliasing issue.

    You can workaround by evaluating the expression in a temporary:

    mx2 = (temp.array() * mx1.array()).eval();
    

    Another solution is to copy mx2.topRows(.) into a true MatrixXd holding its own memory:

    MatrixXd temp = mx2.topRows(mx1Rows);
    mx2 = temp.array() * mx1.array();
    

    Yet another solution is to evaluate into temp and resize afterward:

    Block<MatrixXd, -1, -1, false> temp = mx2.topRows(mx1Rows);
    temp = temp.array() * mx1.array();
    mx2.conservativeResize(mx1Rows,cols);