私はEigen 3.2のConjugateGradient
ソルバを使っていました。新しいマルチスレッド機能の恩恵を受けるためにEigen 3.3.3へのアップグレードを試みました。 。Eigen 3.3共役勾配がGCCコンパイラの最適化でマルチスレッド化されたときに遅くなる
悲しいことに、私が-fopenmp
をGCC 4.8.4で有効にすると、ソルバーの速度が遅くなり(〜10%)なります。 xosviewを見ると、
-fopenmp
をスピードアップするん(
-O0
代わり
-O3
の使用)ことを発見...私はすべての8つのCPUが使用されていることがわかり、まだパフォーマンスが遅くなりますソルバーは〜50%です。
もちろん、マルチスレッドの恩恵を受けるだけで最適化を無効にする価値はありません。なぜなら、それは全体的にはさらに遅くなるからです。
https://stackoverflow.com/a/42135567/7974125からアドバイスを受けて、UpLo
パラメータとしてLower|Upper
を渡しています。
私は3つの前提条件をそれぞれ試してみましたが、RowMajor行列を使っても無駄です。
マルチスレッド化とコンパイラ最適化の両方の利点を得るために他に何かがありますか?
は、私は私の実際のコードを投稿することはできませんが、これはいくつかの変更がConjugateGradient
代わりのSimplicialCholesky
使用するため除いて、Eigen's documentationからラプラシアンの例を使用して簡単なテストです。 (これらのソルバーの両方が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
omp_set_num_threadsを使用して別のスレッド数にopenmpを試してみる必要があります。多分メモリはボトルネックです。メモリにアクセスするために戦う8スレッドを開始し、パフォーマンスを低下させます。 –