2016-08-24 59 views
0

我已经通读了很多documentation,但是如果您发现某些我错过了可以解释我的问题的信息,我会很高兴。对于背景,我正在使用3.2.7 Eigen库在Visual Studio 2015中的x86 Windows 10上进行编译。 3.2.7版本是从5月份开始的,虽然从那时起已经有发布,但我还没有看到changelog中的任何内容,这表明我的问题已得到解决。特征库中的大块系数方式乘法失败C++

该问题似乎只出现在特定大小以上的矩阵上。我不知道这是否是我的系统特有的某些副产品或Eigen固有的东西。

以下代码在调试和发布模式下都会产生访问冲突。

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 
} 

我相信系数态乘法的分配是安全的,因为其结果应该是aliased

当mx1Rows减少到值254时,此问题变得很有趣,则不会出现访问冲突。这是正确的,256乘以254的mx2尺寸会产生问题,但255乘以254的尺寸不会。如果我增加列大小,我也可以得到访问冲突,所以问题可能与条目总数有关。即使mx1和mx2填充了值,也会出现问题,填充矩阵不必重现问题。

未将topRows()块分配给temp的类似代码不会在发布模式下产生访问冲突。我相信还有更多的东西,因为我最初在代码中发现了这个问题,这个代码是相当复杂的,它只在一定数量的循环之后出现(矩阵大小在循环之间是一致的)。在我的代码中有太多的事情,我无法隔离访问冲突仅在一定数量的循环之后出现的条件。

我很好奇,想知道什么是

1)我在一些明显错误的方式使用本征?

2)你能重现这个问题吗? (你的环境细节是什么?)

3)这是Eigen库中的一个bug吗?

通过将块分配给临时矩阵而不是块可以很容易地解决此问题,即使它效率低下,所以我对此不感兴趣。

+1

看起来像一个错误,假设负1索引是允许的,他们似乎是。如果初始化矩阵,您将看到即使在mx1Rows和cols值较小的情况下,也会将垃圾放入数组mult中。运行'temp.array()。eval()。array()'可以正常工作。您不会在数据之外进行别名。它在x64中执行但不正确。它只是没有错。 – doug

回答

2

的问题是,temp引用由mx2举行的系数,但在最后分配,表达被评估之前mx2第一次调整。因此,在表达式的实际评估期间,temp引用垃圾。更确切地说,这里是实际产生(以简化的方式):

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); 

这就是所谓的aliasing issue

您可以通过在临时表达式计算解决方法:

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

另一个解决方案是mx2.topRows(.)复制到一个真正的MatrixXd保持它自己的内存:

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

另一种解决方案是,以评估到temp,然后调整大小:

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

这类问题总是发生在您保留对其他地方操作的对象(而不是对象)的引用的情况下。 – Walter

+0

除非Eigen中没有容量概念(比如STL中字符串的容量和大小),否则我认为分数乘法不需要重新分配。这基本上是问题吗? LHS不再具有相同的尺寸,因此它被重新分配,这成为一个问题,因为RHS不使用别名矩阵乘法,因为使用了数组接口。如果是这种情况,那么或许下面的情况会成功而没有重新分配或腐败? mx2.topRows(mx1Rows)= mx2.topRows(mx1Rows).array()* mx1.array(); –

+0

这正是我提出的最新解决方案,但使用'temp'作为快捷方式。此外,请记住,默认情况下,MatrixXd具有列主存储器,所以“temp”列不会顺序存储在内存中。因此,在保留最后一个字节之前,'conservativeResize'首先需要通过内存拷贝打包列。当然,你也可以省略调整大小,在剩余的计算中使用'temp'而不是'mx2'。 – ggael

-2

看起来像一个影响小尺寸的错误。删除错误诱导行中的注释以获得正确的结果。

正确。正如ggael的回答指出的那样,这是走样。它是使用auto经常遇到的类型,用于创建稍后在同一对象上使用的临时文件。

#include <iostream> 
#include <Eigen/Dense> 

int main() 
{//this has an access violation at the assignment of mx2 
    //const int mx1Rows = 255, cols = 254; 
    const int mx1Rows = 3, cols = 2; 
    Eigen::MatrixXd mx1(mx1Rows, cols); 
    int value = 0; 
    for (int j = 0; j < cols; j++) 
     for (int i = 0; i < mx1Rows; i++) 
      mx1(i,j)=value++; 
    Eigen::MatrixXd mx2(mx1Rows + 1, cols); 
    for (int j = 0; j < cols; j++) 
     for (int i = 0; i < mx1Rows+1; i++) 
      mx2(i,j)=value++; 
    Eigen::Block<Eigen::MatrixXd, -1, -1> temp = mx2.topRows(mx1Rows); 
    mx2 = temp.array()/*.eval().array()*/ * mx1.array();r 
    std::cout << mx2.array() << std::endl; 
} 

// with /*.eval().array()*/ uncommented 
//0 30 
//7 44 
//16 60 

// Original showing bug 
//-0 -4.37045e+144 
//-1.45682e+144 -5.82726e+144 
//-2.91363e+144 -7.28408e+144 
+0

这并不能解释问题,而仅仅是以适合评论的方式对其进行评论,而不是回答。 – Walter

+0

@Walter同意。这是使用auto时出现在多行“临时”中的混叠类型。有效地'Eigen :: Block temp'可以做到这一点,而不是声明临时的MatrixXd并使用右侧的Block。我应该看到这一点。 – doug