本征库 C++ 中的大块系数乘法失败
Large block coefficient-wise multiplication fails in Eigen library C++
我已经阅读了很多 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 表明我的问题已经解决的内容。
问题似乎只出现在超过一定大小的矩阵上。我不知道这是我系统特有的副产品还是 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 x 254 的 mx2 尺寸会产生问题,但 255 x 254 的尺寸不会。如果我增加列大小,我也会遇到访问冲突,因此问题可能与条目总数有关。即使 mx1 和 mx2 填充了值,问题也会出现,填充矩阵不需要重现问题。
未将 topRows() 块分配给 temp 的类似代码不会在发布模式下产生访问冲突。我相信还有更多原因,因为我最初在代码中发现这个问题要复杂得多,并且它只在一定数量的循环之后出现(矩阵大小在循环之间是一致的)。我的代码中发生了太多事情,以至于我无法隔离仅在一定数量的循环后才会出现访问冲突的条件。
我想知道的是
1) 我是不是以某种明显错误的方式使用了 Eigen?
2) 你能重现这个问题吗? (您的环境详情是什么?)
3) 这是 Eigen 库中的错误吗?
通过将块分配给临时矩阵而不是块来解决这个问题很容易,即使它效率低下,所以我不想听这个。
看起来像一个也会影响小尺寸的错误。删除错误诱导行中的注释以获得正确的结果。
更正。正如 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
问题是 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);
我已经阅读了很多 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 表明我的问题已经解决的内容。
问题似乎只出现在超过一定大小的矩阵上。我不知道这是我系统特有的副产品还是 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 x 254 的 mx2 尺寸会产生问题,但 255 x 254 的尺寸不会。如果我增加列大小,我也会遇到访问冲突,因此问题可能与条目总数有关。即使 mx1 和 mx2 填充了值,问题也会出现,填充矩阵不需要重现问题。
未将 topRows() 块分配给 temp 的类似代码不会在发布模式下产生访问冲突。我相信还有更多原因,因为我最初在代码中发现这个问题要复杂得多,并且它只在一定数量的循环之后出现(矩阵大小在循环之间是一致的)。我的代码中发生了太多事情,以至于我无法隔离仅在一定数量的循环后才会出现访问冲突的条件。
我想知道的是
1) 我是不是以某种明显错误的方式使用了 Eigen?
2) 你能重现这个问题吗? (您的环境详情是什么?)
3) 这是 Eigen 库中的错误吗?
通过将块分配给临时矩阵而不是块来解决这个问题很容易,即使它效率低下,所以我不想听这个。
看起来像一个也会影响小尺寸的错误。删除错误诱导行中的注释以获得正确的结果。
更正。正如 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
问题是 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);