13

C ++ 0xドラフトには、CPU /チップレベルのフェンスの概念とは非常に異なるように見えるフェンスの概念があります。つまり、Linuxカーネルの人がフェンスに期待することを言います。問題は、ドラフトが本当に極端に制限されたモデルを暗示しているのか、それとも言い回しが貧弱で実際に真のフェンスを暗示しているのかということです。

たとえば、29.8フェンスでは、次のように記述されます。

リリースフェンスAは、AがXの前にシーケンスされ、XがMを変更し、YがBの前にシーケンスされ、Yが値を読み取るように、両方が何らかのアトミックオブジェクトMで動作するアトミック操作XおよびYが存在する場合、取得フェンスBと同期します。 Xによって書き込まれた値、または仮想のリリースシーケンスXのいずれかのサイドエフェクトによって書き込まれた値は、リリース操作の場合に先頭になります。

これらの用語atomic operationsとを使用しatomic objectます。ドラフトで定義されているそのような不可分操作とメソッドがありますが、それはそれらだけを意味するのでしょうか?リリースフェンスはストアフェンスのように聞こえます。フェンスの前にすべてのデータの書き込みを保証しないストアフェンスはほとんど役に立たない。ロード(取得)フェンスとフルフェンスについても同様です。

それで、C ++ 0xの適切なフェンスと言い回しのフェンス/バリーは非常に貧弱ですか、それとも説明されているように非常に制限されている/役に立たないのですか?


C ++に関して、私がこの既存のコードを持っているとしましょう(GCCで__sync_synchronizeを使用する代わりに、フェンスが現在高レベルの構造として利用可能であると仮定します):

Thread A:
b = 9;
store_fence();
a = 5;

Thread B:
if( a == 5 )
{
  load_fence();
  c = b;
}

a、b、cが、プラットフォーム上にアトミックコピーを持つサイズであると想定します。上記は、cこれまでにのみ割り当てられることを意味します9a==5スレッドBがいつ表示されるかは関係ありませんが、スレッドBが表示されるときにも表示されることに注意してくださいb==9

同じ関係を保証するC++0xのコードは何ですか?


答え:私の選んだ答えとすべてのコメントを読むと、状況の要点がわかります。C ++ 0xは、フェンスでアトミックを使用するように強制しているように見えますが、通常のハードウェアフェンスにはこの要件がありません。多くの場合、とがである限り、これを使用して並行アルゴリズムを置き換えることができsizeof(atomic<T>) == sizeof(T)ますatomic<T>.is_lock_free() == true

残念ながら、それはis_lock_freeconstexprではありません。これにより、で使用できるようになりますstatic_assert。ロックの使用に退化することは一般的に悪い考えです。ミューテックスをatomic<T>使用するアトミックアルゴリズムは、ミューテックスで設計されたアルゴリズムと比較して、ひどい競合の問題を抱えています。

4

2 に答える 2

17

フェンスは、すべてのデータの順序付けを提供します。ただし、1 つのスレッドからのフェンス操作が 2 番目に見えることを保証するには、フラグにアトミック操作を使用する必要があります。そうしないと、データ競合が発生します。

std::atomic<bool> ready(false);
int data=0;

void thread_1()
{
    data=42;
    std::atomic_thread_fence(std::memory_order_release);
    ready.store(true,std::memory_order_relaxed);
}

void thread_2()
{
    if(ready.load(std::memory_order_relaxed))
    {
        std::atomic_thread_fence(std::memory_order_acquire);
        std::cout<<"data="<<data<<std::endl;
    }
}

thread_2読み取りreadyが である場合true、フェンスはdataが安全に読み取れることを保証し、出力は になりますdata=42readyが であると読み取られた場合、が適切なフェンスを発行したfalseことを保証できないため、スレッド 2 のフェンスは必要な順序の保証を提供しません --- inが省略された場合、 へのアクセスはデータ競合になり、未定義になります。フェンスがあっても振る舞い。thread_1ifthread_2data

明確化: Astd::atomic_thread_fence(std::memory_order_release)は一般に店舗フェンスと同等であり、そのように実装される可能性があります。ただし、1 つのプロセッサに単一のフェンスを設定してもメモリの順序付けは保証されません。2 番目のプロセッサに対応するフェンスが必要であり、取得フェンスが実行されたときにリリース フェンスの効果がその 2 番目のプロセッサに表示されることを知る必要がありますCPU A が取得フェンスを発行し、5 秒後に CPU B がリリース フェンスを発行した場合、そのリリース フェンスは取得フェンスと同期できないことは明らかです。他の CPU でフェンスが発行されたかどうかを確認する何らかの手段がない限り、CPU A のコードは、CPU B のフェンスの前または後にフェンスを発行したかどうかを判断できません。

フェンスが見られたかどうかを確認するためにアトミック操作を使用するという要件は、データ競合ルールの結果です。順序関係がなければ、複数のスレッドから非アトミック変数にアクセスすることはできないため、非アトミック変数を使用することはできません。順序関係をチェックするアトミック変数。

もちろん、ミューテックスなどのより強力なメカニズムを使用することもできますが、ミューテックスがフェンスを提供するため、別のフェンスが無意味になります。

緩和されたアトミック操作は、最新の CPU での単純なロードとストアである可能性がありますが、アトミック性を確保するための追加のアライメント要件が必要になる可能性があります。

プロセッサ固有のフェンスを使用するように記述されたコードは、(同期されたデータへのアクセスに使用される操作ではなく) 同期のチェックに使用される操作がアトミックである場合、C++0x フェンスを使用するように簡単に変更できます。既存のコードは、特定の CPU での単純なロードとストアのアトミック性に依存している可能性がありますが、C++0x への変換では、順序の保証を提供するために、これらのチェックにアトミック操作を使用する必要があります。

于 2011-04-05T08:09:36.397 に答える
2

私の理解では、それらは適切なフェンスです。結局のところ、実際のハードウェアに見られる機能にマッピングすることを意図しており、同期アルゴリズムの効率的な実装を可能にするという状況証拠があります。おっしゃる通り、特定の値にのみ適用されるフェンスは 1. 役に立たない 2. 現在のハードウェアにはありません。

そうは言っても、あなたが引用したAFAICSのセクションでは、フェンスとアトミック操作の間の「同期」関係について説明しています。これが何を意味するかの定義については、セクション 1.10マルチスレッド実行とデータ競合を参照してください。繰り返しますが、AFAICS、これはフェンスがアトミック オブジェクトにのみ適用されることを意味するものではありませんが、通常のロードとストアが通常の方法 (一方向のみ) でフェンスを取得および解放する間、アトミック ロード/ない場合があります。

に関して。アトミック オブジェクト、私の理解では、Linux がサポートするすべてのターゲットで、sizeof() <= sizeof(*void) がアトミックである適切に整列されたプレーンな整数変数であるため、Linux は通常の整数を同期変数として使用します (つまり、Linux カーネルのアトミック操作は、通常の整数変数)。C++ はそのような制限を課したくないため、個別のアトミック整数型を使用します。また、アトミック整数型に対する C++ 操作はバリアを暗示しますが、Linux カーネルではすべてのバリアが明示的です (コンパイラがアトミック型をサポートしていないため、これは明らかです)。

于 2011-04-05T08:04:34.043 に答える