2011-12-06 4 views
1

まず、mutexと条件変数を使用して実装できることはわかっていますが、最も効率的な実装を可能にしたいと考えています。 競合がない場合は、高速パスのセマフォが必要です。 Linuxではfutexで簡単です。例えば、ここで待ってます:Windowsでのセマフォの高速カウント?

if (AtomicDecremenIfPositive(_counter) > 0) return; // Uncontended 
AtomicAdd(&_waiters, 1); 
do 
{ 
    if (syscall(SYS_futex, &_counter, FUTEX_WAIT_PRIVATE, 0, nullptr, nullptr, 0) == -1) // Sleep 
    { 
     AtomicAdd(&_waiters, -1); 
     throw std::runtime_error("Failed to wait for futex"); 
    } 
} 
while (AtomicDecrementIfPositive(_counter) <= 0); 
AtomicAdd(&_waiters, -1); 

とポスト:WindowsはちょうどNtWaitForKeyedEvent()を使用するためにまず

AtomicAdd(&_counter, 1); 
if (Load(_waiters) > 0 && syscall(SYS_futex, &_counter, FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0) == -1) throw std::runtime_error("Failed to wake futex"); // Wake one 

私は思いました。問題は、カーネルに入る前に_counterの値を原子的にチェックしていないため、NtReleaseKeyedEvent()からの復帰を逃す可能性があるため、直接的な代替ではありません。悪い場合は、NtReleaseKeyedEvent()がブロックされます。 最適なソリューションは何ですか?

+0

セマフォは、共有リソースへの同時アクセスの数を制限します。 mutexはアクセスをシリアライズして、同時ユーザーが待機する必要があります。どちらがいいですか? – AJG85

+1

セマフォ。ミューテックスはロックされたスレッドによってロック解除されるはずです。私は他の人が投稿するセマフォを待っているスレッドが必要です。 –

+0

Hmm ..最初の考え:セマフォカウントが整数で表される場合、負の数になるsemaのアトムデクリメントは、呼び出し側が待機する必要があることを示します。 0または負の結果になるsemaのアトミック・インクリメントは、解放する必要がある待機中のスレッドがあることを示します。これは、スレッドのイベントリストを保護するための「スーパーCS」の1つと一緒に、より高速な「カーネルなし」のパスでより良いセマフォを作成するケースがありますか? –

答えて

2

私はこのような何かが動作するはずだと思う:

// bottom 16 bits: post count 
// top 16 bits: wait count 
struct Semaphore { unsigned val; } 

wait(struct Semaphore *s) 
{ 
retry: 
    do 
     old = s->val; 
     if old had posts (bottom 16 bits != 0) 
      new = old - 1 
      wait = false 
     else 
      new = old + 65536 
      wait = true 
    until successful CAS of &s->val from old to new 

    if wait == true 
     wait on keyed event 
     goto retry; 
} 

post(struct Semaphore *s) 
{ 
    do 
     old = s->val; 
     if old had waiters (top 16 bits != 0) 
      // perhaps new = old - 65536 and remove the "goto retry" above? 
      // not sure, but this is safer... 
      new = old - 65536 + 1 
      release = true 
     else 
      new = old + 1 
      release = false 
    until successful CAS of &s->val from old to new 

    if release == true 
     release keyed event 
} 

編集:私はこれがあなたに大いに役立つだろうわからない、と述べました。スレッドプールは通常、スレッドが常にリクエストを処理する準備ができるほど大きくなければなりません。これは、待機するだけでなく、ポストも常に遅い経路をとり、カーネルに行くことを意味します。したがって、セマフォの数をカウントすることはおそらく、ユーザー空間のみのファストパスを気にしない1つのプリミティブです。 Stock Win32セマフォは十分なはずです。それが間違っていると私はうれしく思います!

3

WindowsにはネイティブセマフォがCreateSemaphoreとなっています。一般的なやり方で文書化されたパフォーマンス上の問題があるまでは、脆弱なハードウェアやハードウェア固有の最適化については考慮しないでください。

+1

私は、Windowsクリティカルセクションの代わりにカスタムミューテックスを使用することで、パフォーマンスの向上を達成しました(スリムなrwロックよりもやや速い)ので、同じように大幅な改善が期待されますセマフォー。ネイティブセマフォは、非公式のケースであっても重量のあるカーネル呼び出しです。これはかなり一般的です。不要なカーネル呼び出しは、それが必要かどうかをチェックするアトミック操作よりも〜10倍遅くなります。 –

+0

ノイズでオーバーヘッドが失われていない同期関数の呼び出しが非常に多い場合は、*非常に*低レベルのコードを書いているか、同期関数を完全に不適切に使用しています。 –

+0

これは後者です。デプロイメントマシンの仕様を制御するので、x86とx86-64に特有の問題はありません。いずれにせよ、私は元の質問に戻っていきたいと思います。 –

0

QtにはQMutex、QSemaphoreのようなものがあり、あなたの質問で提示したように実装されています。

実際、私はfutexのものを通常のOS提供の同期プリミティブに置き換えることをお勧めします。とにかく遅い道なので大したことではありません。

+0

Qtには、独自の同期メカニズム(比較とスワップが最も顕著)を構築するために必要なアトミック操作も用意されています。 – Ringding

1

あなたの最初のアイデア(例:クリティカルセクションと条件変数)に投票します。クリティカルセクションは十分速く、スリープ状態になる前にインターロックされた操作を使用します。または、クリティカルセクションの代わりにSRWLockを試すこともできます。条件変数(とSRWLock)は非常に高速です。その唯一の問題はXPに条件がないことですが、おそらくこのプラットフォームをターゲットにする必要はありません。

関連する問題