【问题标题】:WaitForSingleObject vs Interlocked*WaitForSingleObject 与联锁*
【发布时间】:2013-12-13 10:31:09
【问题描述】:

在 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();

我测量了这两种方法之间的性能,发现使用 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;
}

【问题讨论】:

  • 互斥锁是用于跨进程同步的内核对象,因此每次锁定/解锁都涉及上下文切换。 See here 进行相关讨论。
  • 据我所知,Mutex 更适合多进程之间的同步。您可能还想尝试一个关键部分。
  • 您是如何测试性能的?锁有任何实际的争用吗?
  • 尝试在锁内进行耗时 100 毫秒的 CPU 密集型操作的测试。启动 32 个线程以进行一些争用。看看会发生什么。
  • @MartinJames 我已经添加了我的性能测试源代码。内部线程通过 WaitFor*() 更改 Interlocked*() 以查看 WaitFor*() 性能。首先输入要创建的线程数,然后按回车,等待一段时间,然后再次按回车。您将能够看到统计数据。

标签: c++ winapi interlocked waitforsingleobject


【解决方案1】:

WaitForSingleObject 不必更快。它涵盖了更广泛的同步场景,特别是您可以等待不“属于”您的进程的句柄,从而实现进程间同步。考虑到所有这些因素,根据您的测试,它慢了 38%。

如果您的流程中包含所有内容并且每一纳秒都很重要,InterlockedXxx 可能是一个更好的选择,但它绝对不是绝对优越的选择。

此外,您可能还想查看Slim Reader/Writer (SRW) Locks API。您也许可以完全基于 InterlockedXxx 构建一个类似的类/函数,但性能稍好一些,但关键是,使用 SRW,您可以开箱即用,具有记录的行为,稳定且性能良好无论如何。

【讨论】:

    【解决方案2】:

    您没有比较等效锁,因此性能如此不同也就不足为奇了。

    互斥锁允许跨进程锁定,由于它提供的灵活性,它可能是最昂贵的锁定方法之一。当您阻塞锁时,它通常会使您的线程进入睡眠状态,并且在您醒来并获得锁之前不使用 CPU。这允许其他代码使用cpu。

    您的 InterlockedCompareExchange() 代码是一个简单的自旋锁。你会烧掉 CPU 等待你的锁。

    您可能还想研究Critical Sections(比互斥锁更少的开销)和Slim Reader/Writer Locks(如果您始终获得独占锁,则可用于互斥,并且提供比非关键部分更快的性能) -根据我的测试,有争议的使用)。

    您可能还想阅读 Kenny Kerr 的 "The Evolution of Synchronization in Windows and C++" 和 Preshing 的与锁相关的东西,herehere

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-04
      • 1970-01-01
      • 2018-10-12
      • 1970-01-01
      • 2011-04-18
      相关资源
      最近更新 更多