我想将矩阵的值分配给另一个矩阵的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;
它将打印非常大的不正确的尺寸。
答案 0 :(得分:0)
在您的代码中似乎没有特定点可以使它变慢。你正在做的事看起来很好。我建议的唯一一点就是你应该尽量减少submat
次呼叫的数量,因为它被认为相对昂贵,因为它可能会创建一个临时矩阵。考虑在没有submat
的情况下进行数学运算。只需计算指数并自己分配。
鉴于此,我还有另外两条建议:
如果您坚持认为代码可能有问题或需要改进,可以研究编写代码的其他方法(如果可能)并使用function profiler(如Valgrind)来查看代码的功能最重要的,以及是否可以优化。
如果您放弃,请考虑学习如何使用C ++进行多线程处理。如今,它非常简单。要么使用OpenMP,这非常容易......但是你必须这样做,因为它很容易在那里犯错误(如high contention);或者使用std::thread
,这是一个相对较新的C ++构造,它只是在一个线程中运行一个函数。对于您的情况,由于您的应用程序是重复的,您可以使用它们中的任何一个。您可以使用my simple thread pool实施,一旦完成,就会安排新实例的调用。