Eigen3 矩阵乘法性能
Eigen3 matrix multiplication performance
注意:我也在 Eigen 论坛上发布了这个 here
我想用 3x3 矩阵预乘 3xN 矩阵,即转换 3D 点,比如
p_dest = T * p_source
初始化矩阵后:
Eigen::Matrix<double, 3, Eigen::Dynamic> points = Eigen::Matrix<double, 3, Eigen::Dynamic>::Random(3, NUMCOLS);
Eigen::Matrix<double, 3, Eigen::Dynamic> dest = Eigen::Matrix<double, 3, Eigen::Dynamic>(3, NUMCOLS);
int NT = 100;
我评价过这两个版本
// eigen direct multiplication
for (int i = 0; i < NT; i++){
Eigen::Matrix3d T = Eigen::Matrix3d::Random();
dest.noalias() = T * points;
}
和
// col multiplication
for (int i = 0; i < NT; i++){
Eigen::Matrix3d T = Eigen::Matrix3d::Random();
for (int c = 0; c < points.cols(); c++){
dest.col(c) = T * points.col(c);
}
}
NT 重复只是为了计算平均时间
我很惊讶逐列乘法比直接乘法快4/5倍
(如果我不使用 .noalias()
,直接乘法会更慢,但这很好,因为它正在做一个临时副本)
我尝试将 NUMCOLS 从 0 更改为 1000000,并且关系是线性的。
我正在使用 Visual Studio 2013 并在发行版中编译
下图在 X 上显示矩阵的列数,在 Y 上显示单个操作的平均时间,蓝色为 col 乘 col 乘法,红色为矩阵乘法
有什么建议为什么会这样吗?
简答
您正在计时 col 乘法版本中的惰性(因此缺乏)评估,而不是直接版本中的惰性(但已评估)评估。
长答案
让我们看一个完整的 MCVE,而不是代码片段。一、"you're"版本:
void ColMult(Matrix3Xd& dest, Matrix3Xd& points)
{
Eigen::Matrix3d T = Eigen::Matrix3d::Random();
for (int c = 0; c < points.cols(); c++){
dest.col(c) = T * points.col(c);
}
}
void EigenDirect(Matrix3Xd& dest, Matrix3Xd& points)
{
Eigen::Matrix3d T = Eigen::Matrix3d::Random();
dest.noalias() = T * points;
}
int main(int argc, char *argv[])
{
srand(time(NULL));
int NUMCOLS = 100000 + rand();
Matrix3Xd points = Matrix3Xd::Random(3, NUMCOLS);
Matrix3Xd dest = Matrix3Xd(3, NUMCOLS);
Matrix3Xd dest2 = Matrix3Xd(3, NUMCOLS);
int NT = 200;
// eigen direct multiplication
auto beg1 = std::chrono::high_resolution_clock::now();
for (int i = 0; i < NT; i++)
{
EigenDirect(dest, points);
}
auto end1 = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed_seconds = end1-beg1;
// col multiplication
auto beg2 = std::chrono::high_resolution_clock::now();
for(int i = 0; i < NT; i++)
{
ColMult(dest2, points);
}
auto end2 = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed_seconds2 = end2-beg2;
std::cout << "Direct time: " << elapsed_seconds.count() << "\n";
std::cout << "Col time: " << elapsed_seconds2.count() << "\n";
std::cout << "Eigen speedup: " << elapsed_seconds2.count() / elapsed_seconds.count() << "\n\n";
return 0;
}
使用此代码(并启用 SSE),我得到:
Direct time: 0.449301
Col time: 0.10107
Eigen speedup: 0.224949
与您抱怨的 4-5 减速一样。为什么?!?!在我们得到答案之前,让我们稍微修改一下代码,以便将 dest
矩阵发送到 ostream
。将 std::ostream outPut(0);
添加到 main()
和 的开头,然后 结束计时器添加 outPut << dest << "\n\n";
和 outPut << dest2 << "\n\n";
。 std::ostream outPut(0);
不输出任何内容(我很确定 badbit 已设置),但它确实导致 Eigens operator<<
为 called,这会强制对矩阵进行评估。
注意: 如果我们使用 outPut << dest(1,1)
那么 dest
的计算量仅足以输出 col 乘法方法中的单个元素。
然后我们得到
Direct time: 0.447298
Col time: 0.681456
Eigen speedup: 1.52349
结果如预期。请注意,Eigen 直接方法花费了完全相同的时间(这意味着即使没有添加 ostream
也进行了评估),而 col 方法突然花费了更长的时间。
注意:我也在 Eigen 论坛上发布了这个 here
我想用 3x3 矩阵预乘 3xN 矩阵,即转换 3D 点,比如 p_dest = T * p_source
初始化矩阵后:
Eigen::Matrix<double, 3, Eigen::Dynamic> points = Eigen::Matrix<double, 3, Eigen::Dynamic>::Random(3, NUMCOLS);
Eigen::Matrix<double, 3, Eigen::Dynamic> dest = Eigen::Matrix<double, 3, Eigen::Dynamic>(3, NUMCOLS);
int NT = 100;
我评价过这两个版本
// eigen direct multiplication
for (int i = 0; i < NT; i++){
Eigen::Matrix3d T = Eigen::Matrix3d::Random();
dest.noalias() = T * points;
}
和
// col multiplication
for (int i = 0; i < NT; i++){
Eigen::Matrix3d T = Eigen::Matrix3d::Random();
for (int c = 0; c < points.cols(); c++){
dest.col(c) = T * points.col(c);
}
}
NT 重复只是为了计算平均时间
我很惊讶逐列乘法比直接乘法快4/5倍
(如果我不使用 .noalias()
,直接乘法会更慢,但这很好,因为它正在做一个临时副本)
我尝试将 NUMCOLS 从 0 更改为 1000000,并且关系是线性的。
我正在使用 Visual Studio 2013 并在发行版中编译
下图在 X 上显示矩阵的列数,在 Y 上显示单个操作的平均时间,蓝色为 col 乘 col 乘法,红色为矩阵乘法
有什么建议为什么会这样吗?
简答
您正在计时 col 乘法版本中的惰性(因此缺乏)评估,而不是直接版本中的惰性(但已评估)评估。
长答案
让我们看一个完整的 MCVE,而不是代码片段。一、"you're"版本:
void ColMult(Matrix3Xd& dest, Matrix3Xd& points)
{
Eigen::Matrix3d T = Eigen::Matrix3d::Random();
for (int c = 0; c < points.cols(); c++){
dest.col(c) = T * points.col(c);
}
}
void EigenDirect(Matrix3Xd& dest, Matrix3Xd& points)
{
Eigen::Matrix3d T = Eigen::Matrix3d::Random();
dest.noalias() = T * points;
}
int main(int argc, char *argv[])
{
srand(time(NULL));
int NUMCOLS = 100000 + rand();
Matrix3Xd points = Matrix3Xd::Random(3, NUMCOLS);
Matrix3Xd dest = Matrix3Xd(3, NUMCOLS);
Matrix3Xd dest2 = Matrix3Xd(3, NUMCOLS);
int NT = 200;
// eigen direct multiplication
auto beg1 = std::chrono::high_resolution_clock::now();
for (int i = 0; i < NT; i++)
{
EigenDirect(dest, points);
}
auto end1 = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed_seconds = end1-beg1;
// col multiplication
auto beg2 = std::chrono::high_resolution_clock::now();
for(int i = 0; i < NT; i++)
{
ColMult(dest2, points);
}
auto end2 = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed_seconds2 = end2-beg2;
std::cout << "Direct time: " << elapsed_seconds.count() << "\n";
std::cout << "Col time: " << elapsed_seconds2.count() << "\n";
std::cout << "Eigen speedup: " << elapsed_seconds2.count() / elapsed_seconds.count() << "\n\n";
return 0;
}
使用此代码(并启用 SSE),我得到:
Direct time: 0.449301
Col time: 0.10107
Eigen speedup: 0.224949
与您抱怨的 4-5 减速一样。为什么?!?!在我们得到答案之前,让我们稍微修改一下代码,以便将 dest
矩阵发送到 ostream
。将 std::ostream outPut(0);
添加到 main()
和 的开头,然后 结束计时器添加 outPut << dest << "\n\n";
和 outPut << dest2 << "\n\n";
。 std::ostream outPut(0);
不输出任何内容(我很确定 badbit 已设置),但它确实导致 Eigens operator<<
为 called,这会强制对矩阵进行评估。
注意: 如果我们使用 outPut << dest(1,1)
那么 dest
的计算量仅足以输出 col 乘法方法中的单个元素。
然后我们得到
Direct time: 0.447298
Col time: 0.681456
Eigen speedup: 1.52349
结果如预期。请注意,Eigen 直接方法花费了完全相同的时间(这意味着即使没有添加 ostream
也进行了评估),而 col 方法突然花费了更长的时间。