1

WinAPI の下には、WaitForSingleObject() と ReleaseMutex() 関数のペアがあります。また、Interlocked*() 関数ファミリもあります。単一のミューテックスのキャプチャとインターロックされた変数の交換の間のパフォーマンスをチェックすることにしました。

HANDLE mutex;
WaitForSingleObject(mutex, INFINITE);
// ..
ReleaseMutex(mutex);

// 0 unlocked, 1 locked
LONG lock = 0;
while(InterlockedCompareExchange(&lock, 1, 0))
  SwitchToThread();
// ..
InterlockedExchange(&lock, 0);
SwitchToThread();

これら 2 つの方法のパフォーマンスを測定したところ、Interlocked*() を使用した方が約 38% 高速であることがわかりました。なぜそうなのですか?

これが私のパフォーマンステストです:

#include <windows.h>
#include <iostream>
#include <conio.h>
using namespace std;

LONG interlocked_variable   = 0; // 0 unlocked, 1 locked
int run                     = 1;

DWORD WINAPI thread(LPVOID lpParam)
{
    while(run)
    {
        while(InterlockedCompareExchange(&interlocked_variable, 1, 0))
            SwitchToThread();
        ++(*((unsigned int*)lpParam));
        InterlockedExchange(&interlocked_variable, 0);
        SwitchToThread();
    }

    return 0;
}

int main()
{
    unsigned int num_threads;
    cout << "number of threads: ";
    cin >> num_threads;
    unsigned int* num_cycles = new unsigned int[num_threads];
    DWORD s_time, e_time;

    s_time = GetTickCount();
    for(unsigned int i = 0; i < num_threads; ++i)
    {
        num_cycles[i] = 0;
        HANDLE handle = CreateThread(NULL, NULL, thread, &num_cycles[i], NULL, NULL);
        CloseHandle(handle);
    }
    _getch();
    run = 0;
    e_time = GetTickCount();

    unsigned long long total = 0;
    for(unsigned int i = 0; i < num_threads; ++i)
        total += num_cycles[i];
    for(unsigned int i = 0; i < num_threads; ++i)
        cout << "\nthread " << i << ":\t" << num_cycles[i] << " cyc\t" << ((double)num_cycles[i] / (double)total) * 100 << "%";
    cout << "\n----------------\n"
        << "cycles total:\t" << total
        << "\ntime elapsed:\t" << e_time - s_time << " ms"
        << "\n----------------"
        << '\n' << (double)(e_time - s_time) / (double)(total) << " ms\\op\n";

    delete[] num_cycles;
    _getch();
    return 0;
}
4

2 に答える 2

4

WaitForSingleObject速くする必要はありません。より広い範囲の同期シナリオをカバーしています。特に、プロセスに「属していない」ハンドルを待機できるため、プロセス間同期が可能です。これらすべてを考慮すると、テストによると、わずか38% 遅くなります。

プロセス内にすべてがあり、すべてのナノ秒がカウントInterlockedXxxされる場合は、より良いオプションかもしれませんが、絶対に優れているわけではありません。

さらに、Slim Reader/Writer (SRW) Locks API も参照してください。InterlockedXxxおそらく、わずかに優れたパフォーマンスで純粋に基づいて同様のクラス/関数を構築できるでしょうが、要点は、SRW を使用すると、箱から出してすぐに使用できるようになり、動作が文書化され、安定していて、とにかくまともなパフォーマンスが得られることです。

于 2013-12-13T10:42:40.787 に答える
3

同等のロックを比較しているわけではないので、パフォーマンスが大きく異なることは驚くべきことではありません。

ミューテックスは、クロス プロセス ロックを可能にします。ミューテックスが提供する柔軟性のために、おそらく最も高価なロック方法の 1 つです。通常、ロックをブロックするとスレッドがスリープ状態になり、ロックを取得して起動するまでCPUを使用しません。これにより、他のコードが CPU を使用できるようになります。

InterlockedCompareExchange() コードは単純なスピン ロックです。ロックを待っているCPUを燃やします。

また、クリティカル セクション(Mutex よりもオーバーヘッドが少ない) とスリム リーダー/ライター ロック(常に排他ロックを取得する場合に相互排除に使用でき、非競合のクリティカル セクションよりもわずかに高速なパフォーマンスを提供する) を調べることもできます。私のテストによると、使用してください)。

Kenny Kerr の"The Evolution of Synchronization in Windows and C++"と Preshing のロック関連の記事 (こちらこちら) もお読みください。

于 2013-12-13T10:48:39.590 に答える