2017-05-06 19 views
1

私は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 
+2

omp_set_num_threadsを使用して別のスレッド数にopenmpを試してみる必要があります。多分メモリはボトルネックです。メモリにアクセスするために戦う8スレッドを開始し、パフォーマンスを低下させます。 –

答えて

1

あなたの問題は、マルチスレッドから任意の利益を期待するには小さすぎるし。スパース行列は、少なくとも1桁大きいと予想されます。この場合、Eigenのコードを調整してスレッド数を減らす必要があります。

さらに、物理コアが4つしかないので、OMP_NUM_THREADS=4 ./cgで実行すると役立つかもしれません。

+0

ハイパースレッディング付きの4コアを使用する場合は、omp_places = coresを使用してください(ただし、g ++ウィンドウでは実装されていません)。 – tim18

+0

明確にするために、私の質問のサンプルコードは90000 * 90000マトリックスを使用しています。 'int n = 300;という行です。 //画像の大きさは誤解を招くかもしれません。未知数の実際の数はn * nピクセルです。 90000 * 90000はまだ小さすぎますか? – Leon

+0

cgのシーケンシャル性に応じて1秒しか実行されず、並列性が制限されているタスクが並列性から得られない場合は、驚くことではありません。 – tim18

関連する問題