3

以下に示す一般的な構造のプログラムがあります。基本的に、私はオブジェクトのベクトルを持っています。各オブジェクトにはメンバー ベクトルがあり、そのうちの 1 つは、より多くのベクトルを含む構造体のベクトルです。マルチスレッド化により、オブジェクトは並行して操作され、メンバー ベクター要素へのアクセスと変更を多く含む計算が実行されます。1 つのオブジェクトは、一度に 1 つのスレッドによってのみアクセスされ、処理のためにそのスレッドのスタックにコピーされます。

問題は、プログラムが 16 コアまでスケールアップできないことです。この問題は、誤った共有および/またはキャッシュの無効化である可能性があると思われ、アドバイスを受けています。これが本当なら、原因は互いに近すぎるメモリを割り当てるベクトルにあると思われます.両方の問題は(簡単に言えば)異なるプロセッサによって同時にアクセスされる近接メモリアドレスによって引き起こされると私は理解しているからです. この推論は理にかなっていますか、これが起こる可能性はありますか? もしそうなら、.reserve() を使用してメンバー ベクトルをパディングして余分な容量を追加し、ベクトル配列間に空のメモリの大きなスペースを残すことで、この問題を解決できるようです。それで、これはすべて意味がありますか?私は完全にここで昼食をとっていますか?

struct str{
    vector <float> a;   vector <int> b;      vector <bool> c;  };

class objects{
    vector <str> a;     vector <int> b;      vector <float> c;  
    //more vectors, etc ...
    void DoWork();            //heavy use of vectors
};    

main(){
    vector <object> objs;
    vector <object> p_objs = &objs;

    //...make `thread_list` and `attr`
    for(int q=0; q<NUM_THREADS; q++)
        pthread_create(&thread_list[q], &attr, Consumer, p_objs );
    //...
}

void* Consumer(void* argument){
     vector <object>* p_objs = (vector <object>*) argument ;
     while(1){
         index = queued++;  //imagine queued is thread-safe global
         object obj = (*p_objs)[index]        
         obj.DoWork();
         (*p_objs)[index] = obj;
}
4

1 に答える 1

2

さて、スレッド 0 で最後にコピーされたベクトルはobjs[0].cです。スレッド 1 で最初にコピーされたベクトルはobjs[1].a[0].aです。そのため、割り当てられたデータの 2 つのブロックがたまたま同じキャッシュ ライン (64 バイト、またはその CPU の実際の値) を占有している場合、誤った共有が発生します。

もちろん、関連する 2 つのベクトルについても同じことが当てはまりますが、具体的な例として、スレッド 0 が最初に実行され、スレッド 1 が割り当てを開始する前にその割り当てを行い、アロケーターが連続した割り当てを隣接させる傾向があると仮定しました。 .

reserve()実際に操作しているそのブロックの部分が同じキャッシュラインを占有するのを防ぐ可能性があります。別のオプションは、スレッドごとのメモリ割り当てです。これらのベクトルのブロックが異なるプールから割り当てられている場合、プールがそうでない限り、同じ行を占有することはできません。

スレッドごとのアロケーターがない場合DoWork、ベクトルを大量に再割り当てすると、メモリ アロケーターで競合が発生する可能性があります。または、 によって使用される他の共有リソースでの競合である可能性がありますDoWork。基本的に、各スレッドがその時間の 1/K を、グローバルな排他的アクセスを必要とする処理に費やしていると想像してください。次に、特定の数 J <= K までは適度に並列化されているように見えるかもしれません。この時点では、コアがアイドル時間のかなりの部分を費やしているため、排他的アクセスを取得すると速度が大幅に低下します。K コアを超えると、コアを追加してもほとんど改善されません。これは、共有リソースがこれ以上高速に動作しないためです。

このばかげた終わりに、1/K の時間をグローバル ロックの保持に費やし、その時間の (K-1)/K を I/O で待機する作業を想像してみてください。次に、問題はほぼ K スレッド (コアの数に関係なく) まで途方もなく並列であるように見え、その時点で停止します。

したがって、真の共有を除外するまで、偽の共有に注目しないでください;-)

于 2011-12-16T11:00:05.997 に答える