我一直在使用Eigen 3.2中的ConjugateGradient
求解器,并决定尝试升级到Eigen 3.3.3,希望能从新的多线程功能中受益。
可悲的是,当我使用GCC 4.8.4启用-fopenmp
时,求解器似乎更慢(~10%)。看看xosview,我看到所有8个cpus都在使用,但性能却较慢......
经过一些测试,我发现如果我禁用编译器优化(使用-O0
而不是-O3
),那么-fopenmp
会使解算器加速约50%。
当然,仅仅为了从多线程中受益而禁止优化并不值得,因为总体来说它会更慢。
根据https://stackoverflow.com/a/42135567/7974125的建议,我存储了完整的稀疏矩阵并将Lower|Upper
作为UpLo
参数传递。
我也试过了3个预处理器中的每一个,并尝试使用RowMajor矩阵,但没有用。
还有什么可以尝试获得多线程和编译器优化的全部好处吗?
我无法发布我的实际代码,但这是使用Eigen's documentation中拉普拉斯示例的快速测试,但使用ConjugateGradient
而不是SimplicialCholesky
进行了一些更改。 (这两个求解器都与SPD矩阵一起使用。)
#include <Eigen/Sparse>
#include <bench/BenchTimer.h>
#include <iostream>
#include <vector>
using namespace Eigen;
using namespace std;
// Use RowMajor to make use of multi-threading
typedef SparseMatrix<double, RowMajor> SpMat;
typedef Triplet<double> T;
// Assemble sparse matrix from
// https://eigen.tuxfamily.org/dox/TutorialSparse_example_details.html
void insertCoefficient(int id, int i, int j, double w, vector<T>& coeffs,
VectorXd& b, const VectorXd& boundary)
{
int n = int(boundary.size());
int id1 = i+j*n;
if(i==-1 || i==n) b(id) -= w * boundary(j); // constrained coefficient
else if(j==-1 || j==n) b(id) -= w * boundary(i); // constrained coefficient
else coeffs.push_back(T(id,id1,w)); // unknown coefficient
}
void buildProblem(vector<T>& coefficients, VectorXd& b, int n)
{
b.setZero();
ArrayXd boundary = ArrayXd::LinSpaced(n, 0,M_PI).sin().pow(2);
for(int j=0; j<n; ++j)
{
for(int i=0; i<n; ++i)
{
int id = i+j*n;
insertCoefficient(id, i-1,j, -1, coefficients, b, boundary);
insertCoefficient(id, i+1,j, -1, coefficients, b, boundary);
insertCoefficient(id, i,j-1, -1, coefficients, b, boundary);
insertCoefficient(id, i,j+1, -1, coefficients, b, boundary);
insertCoefficient(id, i,j, 4, coefficients, b, boundary);
}
}
}
int main()
{
int n = 300; // size of the image
int m = n*n; // number of unknowns (=number of pixels)
// Assembly:
vector<T> coefficients; // list of non-zeros coefficients
VectorXd b(m); // the right hand side-vector resulting from the constraints
buildProblem(coefficients, b, n);
SpMat A(m,m);
A.setFromTriplets(coefficients.begin(), coefficients.end());
// Solving:
// Use ConjugateGradient with Lower|Upper as the UpLo template parameter to make use of multi-threading
BenchTimer t;
t.reset(); t.start();
ConjugateGradient<SpMat, Lower|Upper> solver(A);
VectorXd x = solver.solve(b); // use the factorization to solve for the given right hand side
t.stop();
cout << "Real time: " << t.value(1) << endl; // 0=CPU_TIMER, 1=REAL_TIMER
return 0;
}
结果输出:
// No optimization, without OpenMP
g++ cg.cpp -O0 -I./eigen -o cg
./cg
Real time: 23.9473
// No optimization, with OpenMP
g++ cg.cpp -O0 -I./eigen -fopenmp -o cg
./cg
Real time: 17.6621
// -O3 optimization, without OpenMP
g++ cg.cpp -O3 -I./eigen -o cg
./cg
Real time: 0.924272
// -O3 optimization, with OpenMP
g++ cg.cpp -O3 -I./eigen -fopenmp -o cg
./cg
Real time: 1.04809
答案 0 :(得分:1)
您的问题太小,无法从多线程中获益。稀疏矩阵预计至少要大一个数量级。在这种情况下,应调整Eigen的代码以减少线程数。
此外,我猜您只有4个物理内核,因此使用OMP_NUM_THREADS=4 ./cg
运行可能有所帮助。