2017-10-24 5 views
0

問題:アトミック命令によるマップアクセスの安全性を保証するには、2つの異なるアトミックで命令の並べ替えが可能ですか?

私はリソースと呼ばれるstd::mapのオブジェクトを持っています。リソースは、リソース内のメンバーのセット/取得操作をサポートするオブジェクトです。また、set OR get操作は、パフォーマンス重視の他の多くのことを行います。 マップ自体からリソースを削除する操作もサポートする必要があります。したがって、set/get操作でリソースオブジェクトが実行されている可能性があります。リソース自体が削除され、メモリが破損する可能性があります。 リソースの削除操作は非常にまれです。 10億回にも及ぶ。

スレッドの一貫性を実現するためにpthread読み書きロックを使用しようとしましたが、パフォーマンスに影響します。後でこの問題を解決するためにアトミックを試みました。ここにコードがあります。

std::atomic<bool> g_changeInProgress{false}; // used to block all reader threads 
std::atomic<int> g_readers{0}; // used to block delete thread 

リーダースレッド

LOOP: 
    while(g_changeInProgress) { 
     std::this_thread::yield(); // give opportunity to schedule to other threads 
    }  
    g_readers++; 
    if(g_changeInProgress) { 
     g_readers--; 
     goto LOOP; 
    } 
// DO SET/GET opration with the resource 
// this portion should not execute in parallel to delete 
g_readers--; 

削除スレッド

g_changeInProgress = true; 
while(g_readers) {} // busy loop untill no readers left 
/* Delete the Resource here */ 
g_changeInProgress = false; 

このコードスニペットは、私のために罰金と読み書きロックのpthreadよりもはるかに高速に動作するようです。 質問:スレッドを削除するには、コンパイラがこのコードをひどく失敗させるような命令を並べ替える可能性はありますか?

これより軽いアトミックロックの実装は可能ですか?

+3

これらのビジーループは*ひどい*です。利回りは、他のスレッドが得られない可能性があるため、役に立たない。とりわけ、CPUリソースを浪費して、クロールを待っているスレッドを遅くすることがあります。そして、最悪の場合、最終的に待機を止めると、最悪の場合に誤った予測分岐が発生し、最悪の場合に大きなパフォーマンスペナルティが発生します。あなたがパフォーマンス* at * * all *を気にしているなら、このようなリモートでもコードを書いてはいけません。 –

+0

@DavidSchwartz洞察をいただきありがとうございます。私のユースケースでは、削除が来たときにパフォーマンスを妥協するのは大丈夫です。そのまれなイベントです。このコードを使用するのはまだ良い考えではないと思いますか?即興化の提案はありますか? – piyush

+0

あなたのリーダースレッドは同じ問題を抱えて同じループをしています。このすべての複雑さの根拠は何ですか? 「収穫」がどれくらい重いか分かりますか?そして、それは何にも降伏するものがなければ事態を悪化させます。 –

答えて

1

std::atomicのデフォルトのメモリ順序は逐次整合性です。つまり、アトミックなロード/ストアに関して、単一のスレッド内でロードまたはストアを並べ替えることはできません。他のスレッドは、同じアトミック変数にアクセスして順序を確立します。シーケンシャルな整合性は、すべてのスレッドにわたって、すべての場所にわたって、すべてのロードおよびストアの単一の順序付けを構成できることを示しています。したがって、実際にあなたが想像していることは保証されています。参照:http://en.cppreference.com/w/cpp/atomic/memory_orderおよびhttp://en.cppreference.com/w/cpp/language/memory_modelを参照してください。

メモリの順序を厳密にすることでパフォーマンスを向上させることができます。

複数のライターが存在する場合、このコードは失敗します。そうでなければ私はそれが動作すると思うが、私はそうであることを証明しようとしなかった。一般的に私はコードの各行で明白な不変量を持つのが好きで、ここでは完全にはっきりしていません。私はあなたが通過して、明示的にメモリ順序付けプロパティを確立した後の正確さを証明するようにそれらを明示することをお勧めします。

より良い方法は、上位のビットをいくつか指定して1つの32ビットまたは64ビットの整数を使用して、状態を追跡し、次にリーダの数を追跡するために下位ビットを使用することです。状態を更新するには、std::atomic<T>::compare_exchange_weak操作とfetch_*操作を使用します。 Halideへのこのプルリクエストのの実装:https://github.com/halide/Halide/pull/2420/files#diff-e05149e4d7a77708058562163bf8984d

+0

"これは、アトミックロード/ストアに関して単一のスレッド内でロードまたはストアを並べ替えることができないことを意味します。 ** g_changeInPrgoress **お​​よび** g_readers **。 In Deleteスレッドの1行目と2行目をシャッフルできないことを示唆していますか?このコードは1人の作家のみを対象としています。 – piyush

+0

これらの2行を並べ替えることはできません。メモリ順序付け仕様には、「現在のスレッド内のすべての書き込みは、同じ原子変数を取得する他のスレッドで見ることができます」という文があります。その文の最初の2つの単語には、* other * atomicsへの書き込みが含まれます。 (アトミックの割り当てよりも大きなセットが含まれますが、そのセットにはコメントよりも多くの単語が必要です)。 –

関連する問題