MinGW 64 中 std::mutex 和 QMutex 的性能(posix 线程版本)
Posted
技术标签:
【中文标题】MinGW 64 中 std::mutex 和 QMutex 的性能(posix 线程版本)【英文标题】:Performance of std::mutex and QMutex in MinGW 64 (posix thread version) 【发布时间】:2015-03-20 15:44:02 【问题描述】:我尝试将我的应用程序(蒙特卡罗模拟)中的QMutex
替换为std::mutex
,令人惊讶的是,计算速度被除以3。互斥锁/解锁性能成本从可以忽略不计上升到大约66%线程时间。
我深入研究了实现源。我最初认为两者都是 Win32 线程的包装器(为std::thread
增加了一层 pthread),但实际上 Qt 没有为互斥锁使用任何内核函数,并且有自己的基于原子变量的内部实现。这个似乎要快得多。
谢谢
【问题讨论】:
【参考方案1】:Qt 没有为互斥锁使用任何内核函数,并且有自己的基于原子变量的内部实现
当然,如果互斥锁已被锁定,Qt 会调用操作系统让线程等待。
这里的想法是,在好的多线程代码中,等待已经锁定的互斥锁的机会非常低; 要优化的常见情况是非竞争互斥体,它必须尽可能快。
因此,QMutex 是围绕 Linux futex
系统调用设计的,并使用原子和无锁编程。在无竞争的情况下,不需要系统调用,只需要一些巧妙的原子编程;在有争议的情况下,系统调用是必要的(等待/唤醒线程),这确实是 Qt 使用的:
有关 QMutex 设计背后的更多信息,请参阅here。
为什么 STL 不使用类似的方法?我不知道。可能是因为 native_handle
函数在所有情况下都应该返回“某物”,即使互斥锁已解锁或锁定但没有争用,所以它总是使用系统调用。
(无论如何,Qt 的性能优于 std::mutex
,我并不感到惊讶。如果没有,QMutex 将成为 std::mutex
的包装器。)
【讨论】:
【参考方案2】:听起来 QMutex 是用自旋锁实现的。回答您的问题:
是的,这是可能的。它有局限性,例如见this answer。 只需使用原子,您就可以编写自旋锁;您无法从标准库中的任何其他内容编写真正的互斥锁。 不知道你的算法是做什么的,很难说。但看起来你可以从阅读这本书中受益:Is Parallel Programming Hard, And, If So, What Can You Do About It?。 Counting 一章似乎与您所描述的大致对应。 TL;DR您必须尝试该章中的不同算法,看看哪种算法更适合您的特定情况。【讨论】:
其实peppe是对的,Qt只是在没有并发锁的情况下使用一个原子标志来加速互斥锁。否则它使用系统互斥锁 我不能使用无锁计数方法,因为这是一个浮点加法。不幸的是,没有原子浮点数。【参考方案3】:有趣的是,我来这里是为了弄清楚为什么 QMutex 这么慢。至少对于非常满足的情况,等待单个对象与 enterCriticalSection 相比显得相当慢。 TBB 包装了它,并且似乎比 Windows 上的 QMutex 快得多。
【讨论】:
以上是关于MinGW 64 中 std::mutex 和 QMutex 的性能(posix 线程版本)的主要内容,如果未能解决你的问题,请参考以下文章
std::mutex 和 std::shared_mutex 之间的区别
std::mutex 锁定函数和 std::lock_guard<std::mutex> 的区别?
c++ 如何将 std::mutex 和 std::lock_guard 与仿函数一起使用?