c++ 使用Eigen::Block时防止Eigen创建临时文件

svmlkihl  于 2023-05-02  发布在  其他
关注(0)|答案(1)|浏览(420)

我使用特征库和它的块运算来计算矩阵B和C之间的矩阵乘法,并将结果保存到矩阵A中。B具有N行和M列,C具有M行和P列,其中P是1和max_cols之间的数字。所以A将有N行和P列。我预先分配矩阵A,B,C,然后进入迭代算法,我需要修改它们的值并计算乘法。我需要这最后一步越快越好。由于我不能提前知道P,但我知道max_cols,我预先为A和C分配max_colscols,然后我使用块运算计算矩阵乘法,考虑到我在该迭代中需要的cols数量(P):
A.block(0,0,N,P).noalias() = B*C.block(0,0,M,P);
为了避免创建临时变量,我对矩阵A使用noalias()。我还使用Eigen::internal::set_is_malloc_allowed(false)来检查是否没有创建临时文件。
我看到当N和M等于150时,程序不会因为EIGEN_RUNTIME_NO_MALLOCAssert而停止,而当N和M等于180时,程序会停止。有人能解释一下吗?你能建议我如何解决这个问题或另一种方法来完成我的任务吗?
下面是一个简化的代码来重现这个问题:

#define EIGEN_RUNTIME_NO_MALLOC 
#include <Eigen/Dense>
#include <eigen3/Eigen/Eigen>

int main(int argc, char** argv)
{
  uint N, M, P, max_cols;

  // Initial allocation
  N = 150;
  M = 150;
  P = 10;
  max_cols = 50;

  Eigen::MatrixXd B = Eigen::MatrixXd::Random(N, M);
  Eigen::MatrixXd C = Eigen::MatrixXd::Random(M, max_cols);
  Eigen::MatrixXd A(N,max_cols);

  // 
  //Here some code that set the values of the first P columns of C that I will consider into the multiplication
  //

  // This code should be executed as fast as possible without temporaries
  Eigen::internal::set_is_malloc_allowed(false);

  A.block(0,0,N,P).noalias() = B*C.block(0,0,M,P);  // no assertion

  Eigen::internal::set_is_malloc_allowed(true);
  // until here

  std::cout<<"first multiplication OK"<<std::endl;

  N = 180;
  M = 180;

  Eigen::MatrixXd B2 = Eigen::MatrixXd::Random(N, M);
  Eigen::MatrixXd C2 = Eigen::MatrixXd::Random(M, max_cols);
  Eigen::MatrixXd A2(N,max_cols);

  // 
  //Here some code that set the values of the first P columns of C that I will consider into the multiplication
  //

  // This code should be executed as fast as possible without temporaries
  Eigen::internal::set_is_malloc_allowed(false);

  A2.block(0,0,N,P).noalias() = B2*C2.block(0,0,M,P);  // assertion

  Eigen::internal::set_is_malloc_allowed(true);
  // until here

  std::cout<<"second multiplication OK"<<std::endl;

  return 0;
}
ycl3bljg

ycl3bljg1#

与所有优化BLAS库一样,Eigen使用分块算法进行大型矩阵乘法。我没有深入研究实现,但块大小似乎主要是3级缓存大小的函数。
超过certain limit(默认值为128 kiB),这些块不再在堆栈上分配。您可以通过使用-DEIGEN_STACK_ALLOCATION_LIMIT=$((256*1024))或您认为合适的任何限制进行编译来提高限制。但是要注意,主线程以外的线程可能具有有限的堆栈空间。
无论如何,我不会打扰的。我刚刚做了一个基准测试,没有发现性能上的可测量差异。与计算本身相比,这种类型的动态分配是廉价的。
顺便说一句:你可以把块表达式写成A2.topLeftCorner(N,P)

相关问题