3

私は以下の一般的な構造のプログラムを持っています。基本的に、私はオブジェクトのベクトルを持っています。各オブジェクトにはメンバーベクトルがあり、そのうちの1つはより多くのベクトルを含む構造体のベクトルです。マルチスレッド化により、オブジェクトは並列に動作し、メンバーベクトル要素のアクセスと修正が多く必要な計算が行われます。 1つのオブジェクトは、一度に1つのスレッドのみが処理し、処理のためにそのスレッドのスタックにコピーされます。C++ `.reserve()`を使用してマルチスレッドキャッシュの無効化と偽の共有から保護するための `std :: vector`sを埋めてください

問題は、プログラムが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; 
} 
+0

;-)を共有を除外するまで

ので、偽の共有に焦点を当てていませんか? –

+0

@Alexander私は虚偽の共有、キャッシュの無効化、一般的なメモリなどの概念がかなり不安定であるため、理論的な観点からもアイデアに興味があります。言い換えれば、私は自分の状況を確認しようとしています。 –

+0

指定したコードは有効ではありません。C++。どのくらい正確に16コアまで拡張できませんか?それは単に遅いですか?もしそうなら、どれくらい遅くなりますか?それは具体的には16コアですか、それとも8コア、4コア、または2コアにも適用されますか? –

答えて

2

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

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

reserve()実際に動作しているブロックの部分が同じキャッシュラインを占有するのを防ぐことができます。もう1つの選択肢は、スレッド毎のメモリ割り当てです。もしそれらのベクトルのブロックが異なるプールから割り当てられるならば、プールがそうしなければ同じ行を占めることはできません。

スレッド単位のアロケータがない場合は、DoWorkが多くのベクトルを再割り当てすると、メモリアロケータで競合する可能性があります。または、DoWorkによって使用される他の共有リソースについて競合する可能性があります。基本的には、各スレッドが1/Kの時間をグローバル排他アクセスを必要とする処理に費やしているとします。コアがかなりの時間アイドル時間を費やしているため、排他的アクセスを獲得することがスピードアップに大きく影響する場合、特定の数J < = Kまで合理的にうまく並列化するように見えるかもしれません。 Kコアを超えては、共有リソースがそれ以上速く動作することができないため、余分なコアを使用しても改善はほとんどありません。

この不合理な終わりに、グローバルロックを保持している時間の1/KとI/Oを待っている時間の(K-1)/ Kを費やす作業を想像してください。それで問題は(コアの数に関係なく)ほぼKスレッドまで恥ずかしく並行しているように見え、その時点で死んでしまう。あなたはそれを試してみました

+0

です。だから、アロケータ*は連続した割り当てを隣り合わせにしません。それで、2つのベクトルが隣接して割り振られる確率を見積もることは可能ですか?また、スレッドごとのメモリ割り当てを行うにはどうすればよいですか? –

+0

おそらくそれは、連続して作られた2つの空ではないベクトルのアドレス '&v [0]'を見て、それと同じ方法で近くのベクトルブロックの割合を見積もることでテストできます。 。メモリアロケータの設定は実装固有です。私はAmazonの環境がスレッドごとのプールをデフォルトで持っているのかどうか、どうしたらいいのか、どう対処するのか分かりませんが、通常は異なる 'malloc' /' free'実装を使ってライブラリとリンクします。 –

+0

私はコードを修正する必要がありますか、それとも単にライブラリを持っていてg ++コマンドをリンクするように設定するのでしょうか?以前は、運がないとか、tcmallocを実装しようとしましたが、それは基本的にはあなたが指しているものですか?私はちょうど今アマゾンでUbuntuを実行しているので、おそらく私は環境でそれを設定できるでしょうか? –

関連する問題