ベクトルの合計の最大要素を素早く見つけるにはどうすればよいでしょうか?
-
21-09-2019 - |
質問
プログラムの最も内側のループに次のコードがあります
struct V {
float val [200]; // 0 <= val[i] <= 1
};
V a[600];
V b[250];
V c[250];
V d[350];
V e[350];
// ... init values in a,b,c,d,e ...
int findmax(int ai, int bi, int ci, int di, int ei) {
float best_val = 0.0;
int best_ii = -1;
for (int ii = 0; ii < 200; ii++) {
float act_val =
a[ai].val[ii] +
b[bi].val[ii] +
c[ci].val[ii] +
d[ci].val[ii] +
e[ci].val[ii];
if (act_val > best_val) {
best_val = act_val;
best_ii = ii;
}
}
return best_ii;
}
それが賢いアルゴリズム (しかし、これが最も興味深いでしょう) であろうと、C++ のトリックや組み込み関数やアセンブラであろうと、私は気にしません。しかし、findmax 関数をより効率的にする必要があります。
よろしくお願いします。
編集:ブランチが最も遅い操作のようです (予想ミス?)。
解決
まあ、私は、アルゴリズムの最適化のための明白な部屋を見ません。最大に達することができないことは明らかであるまで、Theoreticaly 1は唯一の5つのベクトルの合計を計算できますが、これは唯一の5つの数字を合計するために多くのオーバーヘッドへの道を追加します。あなたがスレッドに複数のスレッドおよび割り当ての範囲を使用して試みることができる、しかし、あなたは唯一の200非常に短い作業項目がある場合、スレッド作成のオーバーヘッドを考える必要はあります。
私は多分アセンブラおよびx86上のMMXやSSE命令または(マシン固有)C ++を使用すると、この指示へのアクセスを提供するライブラリが最善の策であると言う傾向があるので。
他のヒント
コンパイラは、ジャンプを切断難易ショートを持っている場合、これは少し役立つかもしれません。
int findmax(int ai, int bi, int ci, int di, int ei) {
float best_val = 0.0;
int best_ii = -1;
float* a_it = &a[ai].val[0]
float* b_it = &b[bi].val[0]
float* c_it = &c[ci].val[0]
float* d_it = &d[di].val[0] // assume typo ci->di
float* e_it = &e[ei].val[0] // assume typo ci->ei
for (int ii = 0; ii < 200; ii++) {
float act_val = *(a_it++) + *(b_it++) + *(c_it++) + *(d_it++) + *(e_it++);
best_val = (act_val <= best_val) ? best_val : act_val; // becomes _fsel
best_ii = (act_val <= best_val) ? best_ii : ii; // becomes _fsel
}
return best_ii;
}
私は少しでこれを投稿します総和テーブルを生成すると、キャッシュミスの面でより速いかもしれません。
int findmax(int ai, int bi, int ci, int di, int ei) {
float best_val = 0.0;
int best_ii = -1;
float* its[] = {&a[ai].val[0], &a[bi].val[0], &a[ci].val[0], &a[di].val[0], &a[ei].val[0] };
V sums;
for (int ii = 0; ii < 200; ii++) {
sums.val[ii] = * (++its[0]);
}
for (int iter = 1 ; iter < 5; ++iter) {
for (int ii = 0; ii < 200; ii++) {
sums.val[ii] += * (++its[iter]);
}
}
}
for (int ii = 0; ii < 200; ii++) {
best_val = (sums.val[ii] <= best_val) ? best_val : sums.val[ii]; // becomes _fsel
best_ii = (sums.val[ii] <= best_val) ? best_ii : ii; // becomes _fsel
}
return best_ii;
}
それぞれの合計を調べずにこれを行う方法は見つからず、これは O(n) 問題になります。ただし、データは直線的に配置されるため、Intel/AMD MMX または SSE 命令が役立つ場合があります。Microsoft の組み込みの実装については、このリンクを参照してください。
http://msdn.microsoft.com/en-us/library/y0dh78ez(VS.71).aspx
コンパイラ等a[ai]
を、コンピューティング、あなたのためにそれらを最適化していない限り、ループの中で彼らはfindmax
の期間固定されていることを考えると(ただしわずかな)あなたにいくつかの時間がかかります。あなたのような何かを試みるかもしれないとの照らします:
int findmax(int ai, int bi, int ci, int di, int ei) {
float best_val = std::numeric_limits<float>::min();
int best_ii = 0;
const V& a(a[ai]);
const V& b(b[bi]);
const V& c(c[ci]);
const V& d(d[di]);
const V& e(e[ei]);
for (int ii = 0; ii < 200; ++ii) {
float act_val = a.val[ii] + b.val[ii] + c.val[ii] +
d.val[ii] + e.val[ii];
if (act_val > best_val) {
best_val = act_val;
best_ii = ii;
}
}
return best_ii;
}
のコードを向上させる他の手段は、異なる(しかし、はるかに速い)findmax
アルゴリズム
一度にすべてのベクトルを反復するようにしてください。ここでは、2つのベクトルの例を以下に示します。
for (float *ap = a[ai].val, *bp = b[bi].val; ap - a[ai].val < 200; ap++, bp ++) {
float act_val = *ap + *bp;
// check for max and return if necessary
}
(例を特定のためとダフのデバイスが、はるかに複雑)巻き戻しループを見てみましょう。それらは私が思い付くことができる唯一の本当のアルゴリズムの最適化されます。
あなたは本当にa
、b
、c
、d
、およびe
に格納されたデータ(値)に関する追加情報なしにはるかに高速よりもそれを得ることができません。あなたは1が最大であるかを決定するために、すべての合計を検査する必要があります。
これは、N番目の要素クエリのために少しさらに悪いますが、幸い、あなたはそのいずれかを尋ねませんでした。