为什么CRITICAL_SECTION性能在Win8上变差

时间:2018-09-04 16:38:12

标签: c++ c++11 winapi critical-section stdmutex

在Windows 8及更高版本上,CRITICAL_SECTION的性能似乎变差了。 (请参见下图)

测试非常简单:某些并发线程分别进行300万个锁以专门访问变量。您可以在问题的底部找到C ++程序。我在Windows Vista,Windows 7,Windows 8,Windows 10(x64,VMWare,Intel Core i7-2600 3.40GHz)上运行该测试。

结果在下图上。 X轴是并发线程数。 Y轴是以秒为单位的经过时间(越低越好)。

Test results

我们所看到的:

  • SRWLock在所有平台上的性能大致相同
  • CriticalSection的性能相对于Windows 8及更高版本上的SRWL而言变差

问题:有人可以解释为什么CRITICAL_SECTION性能在Win8和更高版本上变得更差吗?


一些注意事项:

  • 在真实计算机上的结果几乎相同-CS在Win8及更高版本上比std :: mutex,std :: recursive_mutex和SRWL都差得多。但是,我没有机会在具有相同CPU的不同操作系统上运行测试。
  • Windows Vista的
  • std::mutex实现基于CRITICAL_SECTION,而Win7及更高版本的std::mutex基于SWRL。它对于MSVS17和15都是正确的(确保在安装MSVC ++时搜索primitives.h文件并寻找stl_critical_section_vistastl_critical_section_win7类)这解释了std :: mutex性能之间的区别赢得Vista和其他人。
  • 正如评论中所说,std::mutex是一个包装器,因此,相对于SRWL的某些开销的可能解释可能是包装器代码引入的开销。

#include <chrono>
#include <iostream>
#include <mutex>
#include <string>
#include <thread>
#include <vector>

#include <Windows.h>

const size_t T = 10;
const size_t N = 3000000;
volatile uint64_t var = 0;

const std::string sep = ";";

namespace WinApi
{
    class CriticalSection
    {
        CRITICAL_SECTION cs;
    public:
        CriticalSection() { InitializeCriticalSection(&cs); }
        ~CriticalSection() { DeleteCriticalSection(&cs); }
        void lock() { EnterCriticalSection(&cs); }
        void unlock() { LeaveCriticalSection(&cs); }
    };

    class SRWLock
    {
        SRWLOCK srw;
    public:
        SRWLock() { InitializeSRWLock(&srw); }
        void lock() { AcquireSRWLockExclusive(&srw); }
        void unlock() { ReleaseSRWLockExclusive(&srw); }
    };
}

template <class M>
void doLock(void *param)
{
    M &m = *static_cast<M*>(param);
    for (size_t n = 0; n < N; ++n)
    {
        m.lock();
        var += std::rand();
        m.unlock();
    }
}

template <class M>
void runTest(size_t threadCount)
{
    M m;
    std::vector<std::thread> thrs(threadCount);

    const auto start = std::chrono::system_clock::now();

    for (auto &t : thrs) t = std::thread(doLock<M>, &m);
    for (auto &t : thrs) t.join();

    const auto end = std::chrono::system_clock::now();

    const std::chrono::duration<double> diff = end - start;
    std::cout << diff.count() << sep;
}

template <class ...Args>
void runTests(size_t threadMax)
{
    {
        int dummy[] = { (std::cout << typeid(Args).name() << sep, 0)... };
        (void)dummy;
    }
    std::cout << std::endl;

    for (size_t n = 1; n <= threadMax; ++n)
    {
        {
            int dummy[] = { (runTest<Args>(n), 0)... };
            (void)dummy;
        }
        std::cout << std::endl;
    }
}

int main()
{
    std::srand(time(NULL));
    runTests<std::mutex, WinApi::CriticalSection, WinApi::SRWLock>(T);
    return 0;
}

该测试项目是在Microsoft Visual Studio 17(15.8.2)上作为Windows控制台应用程序构建的,具有以下设置:

  • 使用MFC:在静态库中使用MFC
  • Windows SDK版本:10.0.17134.0
  • 平台工具集:Visual Studio 2017(v141)
  • 优化:O2,Oi,Oy-,GL

1 个答案:

答案 0 :(得分:1)

请参见Windows Critical Section - how to disable spinning completely 从Windows 8开始,Microsoft更改了Critical Section的默认行为的实现(即使文档中也没有任何措辞)(如果使用InitializeCriticalSection(&cs),则将启用未记录的动态旋转调整算法,从而发生旋转)。在这里查看我的评论:https://randomascii.wordpress.com/2012/06/05/in-praise-of-idleness/#comment-57420

对于您的测试,请尝试使用InitializeCriticalSectionAndSpinCount(&cs,1)而不是InitializeCriticalSection(&cs)。这应该使它的行为与Windows 7相似,尽管该区域还有很多其他更改。