Armadillo C ++:如何有效地分配给submat

时间:2017-08-30 11:46:53

标签: c++ armadillo

我想将矩阵的值分配给另一个矩阵的submat A.submat(ni1, ni2, nk1, nk2) = B;这似乎很慢。我想知道它为什么这么慢,有没有办法改进它?

这是我的测试代码(因为函数“XForwarDifference”需要在我的项目中调用数百万次,我需要更好地分析它)

#include <armadillo>
#include <chrono>
#include <iostream>
using ms = std::chrono::milliseconds;
using ns = std::chrono::nanoseconds;
using get_time = std::chrono::steady_clock;

namespace {
  const arma::ivec::fixed<5> iforward = {-1, 0, 1, 2, 3};
  const double MCA_1 = -0.30874;
  const double MCA0 = -0.6326;
  const double MCA1 = 1.2330;
  const double MCA2 = -0.3334;
  const double MCA3 = 0.04168;
}

arma::mat XForwardDifference(arma::mat& mat,
                             const int& ni1,
                             const int& ni2,
                             const int& nk1,
                             const int& nk2,
                             const double& dx)
{
  arma::mat ret(size(mat));
  double sign_dx = 1./dx;
  double mca_1= sign_dx * MCA_1;
  double mca0 = sign_dx * MCA0;
  double mca1 = sign_dx * MCA1;
  double mca2 = sign_dx * MCA2;
  double mca3 = sign_dx * MCA3;
  auto t1 = get_time::now();
  auto m1 = mat.submat(ni1+iforward(0), nk1, ni2+iforward(0), nk2);
  auto t2 = get_time::now();
  auto m2 = mca_1 * m1;
  auto t3 = get_time::now();
  auto m3 = m2 + m2;
  auto t4 = get_time::now();
  mat.submat(ni1, nk1, ni2, nk2) = m3;
  auto t5 = get_time::now();
  std::cout << std::chrono::duration_cast<ns>(t2-t1).count() << std::endl;
  std::cout << std::chrono::duration_cast<ns>(t3-t2).count() << std::endl;
  std::cout << std::chrono::duration_cast<ns>(t4-t3).count() << std::endl;
  std::cout << std::chrono::duration_cast<ns>(t5-t4).count() << std::endl;


  // ret.submat(ni1, nk1, ni2, nk2) =
  //   mca_1* mat.submat(ni1+iforward(0), nk1, ni2+iforward(0), nk2) +
  //   mca0 * mat.submat(ni1+iforward(1), nk1, ni2+iforward(1), nk2) +
  //   mca1 * mat.submat(ni1+iforward(2), nk1, ni2+iforward(2), nk2) +
  //   mca2 * mat.submat(ni1+iforward(3), nk1, ni2+iforward(3), nk2) +
  //   mca3 * mat.submat(ni1+iforward(4), nk1, ni2+iforward(4), nk2);
  return ret;
}


int main(int argc, char *argv[])
{
  const int len = 3;
  int ni1, ni2, nk1, nk2, ni, nk;
  ni = 200;
  nk = 200;
  ni1 = len;
  ni2 = ni1 + ni - 1;
  nk1 = len;
  nk2 = nk1 + nk - 1;
  const double dx = 1.;
  auto start_time = get_time::now();
  arma::mat mat(ni + 2*len, nk + 2*len);
  mat = XForwardDifference(mat, ni1, ni2, nk1, nk2, dx);
  auto end_time = get_time::now();
  auto diff = end_time - start_time;
  std::cout << "Elapsed time is : "
            << std::chrono::duration_cast<ns>(diff).count()
            << " ns "
            << std::endl;
  return 0;
}

输出结果为:

180
116
110
851123
Elapsed time is : 961975 ns 

您可以看到mat.submat(ni1, nk1, ni2, nk2) = m3;涵盖了大部分已用时间。

hbrerkere给出了原因:

  

Armadillo将所有操作排队,直到将结果分配给矩阵或子矩阵。这就是为什么分配到submat似乎需要更长时间。它实际上是在分配时进行乘法和加法,而不是之前。另外,不要将auto关键字与Armadillo矩阵和表达式一起使用,因为这会导致问题。 - hbrerkere

  auto t1 = get_time::now();
  arma::mat m1 = mat.submat(ni1+iforward(0), nk1, ni2+iforward(0), nk2);
  auto t2 = get_time::now();
  arma::mat m2 = mca_1 * m1;
  auto t3 = get_time::now();
  arma::mat m3 = m2 + m2;
  auto t4 = get_time::now();
  ret = m3;
  auto t5 = get_time::now();

如果按照他的说法修改代码,那么输出现在如下:

391880
356480
373072
113051
Elapsed time is : 1352013 ns 

我也遇到auto会给犰狳带来问题的情况。

auto m1 = mat.submat(ni1, nk1, ni2, nk2) * 2;
cout << size(m1) << endl;

它将打印非常大的不正确的尺寸。

1 个答案:

答案 0 :(得分:0)

在您的代码中似乎没有特定点可以使它变慢。你正在做的事看起来很好。我建议的唯一一点就是你应该尽量减少submat次呼叫的数量,因为它被认为相对昂贵,因为它可能会创建一个临时矩阵。考虑在没有submat的情况下进行数学运算。只需计算指数并自己分配。

鉴于此,我还有另外两条建议:

  1. 如果您坚持认为代码可能有问题或需要改进,可以研究编写代码的其他方法(如果可能)并使用function profiler(如Valgrind)来查看代码的功能最重要的,以及是否可以优化。

  2. 如果您放弃,请考虑学习如何使用C ++进行多线程处理。如今,它非常简单。要么使用OpenMP,这非常容易......但是你必须这样做,因为它很容易在那里犯错误(如high contention);或者使用std::thread,这是一个相对较新的C ++构造,它只是在一个线程中运行一个函数。对于您的情况,由于您的应用程序是重复的,您可以使用它们中的任何一个。您可以使用my simple thread pool实施,一旦完成,就会安排新实例的调用。

  3. 祝你好运。