...
オリジナル機能、参照のための(私の正気のためのいくつかの以降の整理整頓を持つ):
void MultiDiagonalSymmetricMatrix::CholeskyBackSolve(float *x, float *b){
//We want to solve L D Lt x = b where D is a diagonal matrix described by Diagonals[0] and L is a unit lower triagular matrix described by the rest of the diagonals.
//Let D Lt x = y. Then, first solve L y = b.
float *y = new float[n];
float **d = IncompleteCholeskyFactorization->Diagonals;
unsigned int *s = IncompleteCholeskyFactorization->StartRows;
unsigned int M = IncompleteCholeskyFactorization->m;
unsigned int N = IncompleteCholeskyFactorization->n;
unsigned int i, j;
for(j = 0; j != N; j++){
float sub = 0;
for(i = 1; i != M; i++){
int c = (int)j - (int)s[i];
if(c < 0) break;
if(c==j) {
sub += d[i][c]*b[c];
} else {
sub += d[i][c]*y[c];
}
}
y[j] = b[j] - sub;
}
//Now, solve x from D Lt x = y -> Lt x = D^-1 y
// Took this one out of the while, so it can be parallelized now, which speeds up, because division is expensive
#pragma omp parallel for
for(j = 0; j < N; j++){
x[j] = y[j]/d[0][j];
}
while(j-- != 0){
float sub = 0;
for(i = 1; i != M; i++){
if(j + s[i] >= N) break;
sub += d[i][j]*x[j + s[i]];
}
x[j] -= sub;
}
delete[] y;
}
ため(だけO(N)であることにもかかわらず)スピードブーストを与える並列除算についてのコメントの、私は機能自体が多く呼び出されますと仮定しています。なぜメモリを割り当てるのですか? x
を__restrict__
とマークしてy
をx
に変更してください(__restrict__
はGCCの拡張子であり、C99から取ったものです)。define
を使用してもかまいません。
同様に、署名を変更することはできないと思いますが、関数には1つのパラメータしか取らずに変更することができます。 x
またはy
が設定されている場合、b
は使用されません。つまり、〜N * M回実行される最初のループの分岐を取り除くこともできます。 2つのパラメータが必要な場合は、最初にmemcpy
を使用してください。
d
はなぜポインタの配列ですか?それがなければならない?これは元のコードではあまりにも深いようですので触れませんが、格納された配列を平坦化する可能性がある場合は、逆参照、追加、逆参照よりも)。
ので、新しいコード:他に何も、ブーストされていない場合
void MultiDiagonalSymmetricMatrix::CholeskyBackSolve(float *__restrict__ x){
// comments removed so that suggestions are more visible. Don't remove them in the real code!
// these definitions got long. Feel free to remove const; it does nothing for the optimiser
const float *const __restrict__ *const __restrict__ d = IncompleteCholeskyFactorization->Diagonals;
const unsigned int *const __restrict__ s = IncompleteCholeskyFactorization->StartRows;
const unsigned int M = IncompleteCholeskyFactorization->m;
const unsigned int N = IncompleteCholeskyFactorization->n;
unsigned int i;
unsigned int j;
for(j = 0; j < N; j++){ // don't use != as an optimisation; compilers can do more with <
float sub = 0;
for(i = 1; i < M && j >= s[i]; i++){
const unsigned int c = j - s[i];
sub += d[i][c]*x[c];
}
x[j] -= sub;
}
// Consider using processor-specific optimisations for this
#pragma omp parallel for
for(j = 0; j < N; j++){
x[j] /= d[0][j];
}
for(j = N; (j --) > 0;){ // changed for clarity
float sub = 0;
for(i = 1; i < M && j + s[i] < N; i++){
sub += d[i][j]*x[j + s[i]];
}
x[j] -= sub;
}
}
まあそれは、整然と見ていると、メモリの割り当てと縮小の分岐の欠如です。 s
を最後にUINT_MAX
の値を追加するように変更できる場合は、より多くのブランチ(〜N * M回実行するi<M
の両方のチェック)を削除することができます。
これ以上ループを並列化することはできません。ループを組み合わせることはできません。ブーストは、他の答えに示唆されているように、d
を並べ替えることになります。例外を除いて... d
を再配置するために必要な作業は、ループを実行する作業とまったく同じキャッシュ問題を持ちます。そして、メモリを割り当てる必要があります。良くない。さらに最適化するオプションは、IncompleteCholeskyFactorization->Diagonals
自体の構造を変更することです。これはおそらく多くの変更を意味するか、データをこの順序でうまく処理する別のアルゴリズムを見つけることになります。
さらに進めたい場合は、コードのかなりの部分に最適化が必要です(悪いことではありません; Diagonals
がポインタの配列である理由がない限り、リファクタリング)。
コンテキストを追加できますか? "d [] []とx []はfloatの配列です" - __restrict__と宣言されていますか? 'i'と' j'は実際にどれだけ大きなものになるのですか? "このループは再帰的です..." - より多くのコンテキスト、してください。 –
最適化されたコンパイラ出力はどのように見えますか? – andy256
また、ホットスポットがどこにあるかを見るためにプロファイラを使い始めます。 –