2013-12-13 3 views
1

В WinAPI есть пара функций WaitForSingleObject() и ReleaseMutex(). Также существует семейство функций Interlocked *(). Я решил проверить производительность между захватом одного мьютекса и обменом переплетенной переменной.WaitForSingleObject vs 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(); 

Я измерил производительности между этими двумя методами и выяснили, что использование взаимно сблокированы *() составляет около 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; 
} 
+1

Мьютекс - это объект ядра для кросс-технологической синхронизации, поэтому каждая блокировка/разблокировка включает контекстный переключатель. [См. Здесь] (http://msdn.microsoft.com/en-us/magazine/cc163726.aspx) для соответствующего обсуждения. –

+1

Насколько я знаю, Mutex больше подходит для синхронизации между несколькими процессами. Вы также можете попробовать критический раздел. – Paul

+0

Как вы оценили производительность? Были ли какие-либо фактические споры на замках? –

ответ

3

WaitForSingleObject не должен быть быстрее. Он охватывает гораздо более широкий спектр сценариев синхронизации, в частности, вы можете ждать на ручках, которые не «принадлежат» вашему процессу и, следовательно, межпроцессорной синхронизации. Принимая во внимание это только только 38% медленнее в соответствии с вашим тестом.

Если у вас есть все, что находится внутри вашего процесса, и каждый наносекундный счет, InterlockedXxx может быть лучшим вариантом, но это определенно не совсем превосходный.

Кроме того, вы можете ознакомиться с API Slim Reader/Writer (SRW) Locks. Возможно, вы сможете построить аналогичный класс/функции, основанный исключительно на InterlockedXxx с немного лучшей производительностью, однако дело в том, что с помощью SRW вы будете готовы к использованию из коробки, с документированным поведением, стабильной и с достойной производительностью в любом случае.

3

Вы не сравниваете эквивалентные блокировки, поэтому неудивительно, что производительность настолько отличается.

Мьютекс позволяет блокировать перекрестный процесс, это, вероятно, один из самых дорогих способов блокировки благодаря гибкости, которую он обеспечивает. Обычно он помещает ваш поток в сон, когда вы блокируете блокировку, и это не использует процессор, пока вы не проснетесь, получив блокировку. Это позволяет другому коду использовать процессор.

Ваш код InterlockedCompareExchange() - это простая прямая блокировка. Вы зажжете процессор, ожидающий блокировки.

Вы также можете посмотреть в Critical Sections (меньше накладных расходов, чем мьютекс) и Slim Reader/Writer Locks (которые могут быть использованы для взаимного исключения, если вы всегда получить эксклюзивную блокировку и которые обеспечивают дробно более высокую производительность, чем критические секции не-оспариваемого использования , согласно моим испытаниям).

Возможно, вы также захотите прочитать Kenny Kerr's "The Evolution of Synchronization in Windows and C++" и материал, связанный с замком Preshing, here и here.

Смежные вопросы