2009-09-03 14 views
3

は、私はそれはいくつかの巧妙なアルゴリズムになるかどうか気にしない(これは最も興味深いものになるだろう)、またはどのように素早くベクトルの和の最大要素を見つけるか?

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関数をより効率的にする必要があります。

事前に大変感謝しています。

編集: 枝が最も遅い操作(誤予測?)であるようです。

+0

あなたは私たちに、外側のループについての詳細を伝えることはできますか?多分それと組み合わせて、最適化の可能性が増えます。 – SebastianK

+1

マイクロ最適化。コンパイラによって処理される可能性がありますが、実際には害はありません。また、私は+++に切り替える時に、驚くべきベンチマークを見ました。そうすれば、値はインクリメントする前にコピーされません。 – krdluzni

答えて

2

まあ、私は、アルゴリズムの最適化のための明白な部屋を見ません。理論的には、最大に達することができないことが明らかになるまで、5つのベクトルの合計を計算することしかできませんでしたが、これは5つの数値を加算するだけのオーバーヘッドにつながります。複数のスレッドを使用してスレッドに範囲を割り当てることもできますが、非常に短い作業項目が200個しかない場合はスレッド作成のオーバーヘッドについて考える必要があります。

私は、x86上でAssemblerとMMXまたはSSE命令を使用するか、おそらくこの命令へのアクセスを提供するライブラリ(マシン固有のC++)を使用することが最善の策だと言います。

+0

"非常に短い作業項目しかありません。彼はコードが最も内側のループにあると言いますが、ai、biなどのさまざまな組み合わせのためにコードを実行している場合は、おそらくマルチスレッドでこの関数より高いレベルで作業を中断できます。ベクトルの内容と5つのパラメータの各セットが以前の計算結果に依存するかどうかによって異なります。また、それぞれの呼び出しを作成するのではなく、ワーカースレッドのプールを維持できるので、スレッドの通信オーバーヘッドほどのスレッド作成オーバーヘッドではありません。 –

+0

あなたが方程式にスレッディングを導入している場合は、これが実際に役立つかどうかを検討する必要があります。これは、アプリケーションのより大きな目的と実行される場所によって異なります。 – krdluzni

+0

しかし、マルチスレッド化は、このアルゴリズムを「より効率的」にすることはなく、潜在的に速いだけです。結果を計算するのに必要なCPUサイクル数を減らすことはありません。マルチスレッドは通常、マシン上にアイドルコアがある場合にのみ役立ちます。多くのアプリを実行しているサーバーでは、非常にうまくいかない可能性があります。 –

0

あなたは本当にabcd、およびeに格納されたデータ(値)に関する追加情報なしにそのはるかに速くよりも取得することはできません。どの合計が最大かを判断するためには、合計を調べなければなりません。

N番目の要素のクエリでは少し悪くなりますが、幸いなことに、それを聞かなかったのです。

1

すべてのベクトルを一度に繰り返してみてください。ここでは、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 
} 
2

私はこのO(n)の問題を作り、それぞれの合計を検討せずにこれを行うにはどのような方法が表示されません。しかし、データが線形にレイアウトされているため、Intel/AMD MMXまたはSSE命令が役立ちます。組み込み関数のMicrosoftの実装のために、このリンクを参照してください:

http://msdn.microsoft.com/en-us/library/y0dh78ez(VS.71).aspx

+0

具体的には、実際には4つの浮動加算を同時に行い、float [4]に相当するXMMレジスタに結果をダンプするaddps(packed addition)命令が必要です。これらのうちのいくつかを保存すると、並列比較を行うためにmaxps(パックド・マックス)を使ってゲインを得ることもできます。明らかに、最後のいくつかの比較は、SSEではなく単精度浮動小数点演算で行う必要があります。 –

2

コンパイラがそれらを最適化しない限り、ループ内の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アルゴリズムにつながる、データが表される方法を変更することがあります。

+0

合意すると、関数内に最適化の余地はあまりありませんが、同じ最大値を複数回見つけたり、ショートカットを見つけることができるようにデータが配置されたりします。全体的なコード。 – DeusAduro

+2

合理的なコンパイラがこの最適化を自動的に実行します。 –

+1

best_valは負の無限大に初期化する必要があります –

4

コンパイラが困難短いジャンプをカットをしている場合、これは少し役立つかもしれない:和テーブルを生成

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; 
} 
+0

私の方法を空想しない場合は、bet_valとbest_iiを設定する_fselメソッドを試してください –

1

ループ巻き戻し(特定の、しかしはるかに複雑な例のDuffのデバイス)を見てください。それらが私が思い付く唯一の本当のアルゴリズムの最適化です。

Loop_unwinding

Duff's_device

+0

ループが常に同じ長さ(この場合は200)の場合、Duffのデバイスは実際には必要ありません。アンロールする長さとして200のファクタを使用するか、または非ファクタを使用しますが、ループの途中で単一のジャンプを開始します。 –

+0

あなたはそうです、あなたはそうではありませんが、私はそれが巻き戻しの興味深い例として役立つと思いました。すべての正直なところ、Duffのデバイスは、通常の巻き戻しよりもずっと多くのことがあり、私はそれを私のポストから取り除くことを考えています。 – krdluzni

+1

私はダフのデバイスを見てみんな*好きですが、絶対に必要な場合を除き*使用しないことを知っている限り。そしておそらくそれでは:-) –

関連する問題