我使用http://www.research.ibm.com/people/m/michael/ieeetpds-2004.pdf中解释的危险指针方法实现了一个无锁队列,使用GCC CAS指令实现,并将pthread本地存储用于线程本地结构。我现在试图评估我写的代码的性能,特别是我试图在这个实现和使用锁(pthread互斥)来保护队列的实现之间做一个比较。
我之所以在这里问这个问题,是因为我试着将它与"锁定"队列进行比较,我发现相对于无锁实现,它具有更好的性能。我尝试的唯一测试是在4核x86_64机器上创建4个线程,对队列进行10,000.000次随机操作,它比无锁版本快得多。
我想知道你是否可以建议我遵循一种方法,即我必须在队列上测试什么样的操作,以及我可以使用什么样的工具来查看我的无锁代码在哪里浪费时间。
我还想了解是否有可能因为4个线程不足以看到重大改进而导致无锁队列的性能更差…
谢谢
第一点:无锁编程不一定能提高速度。无锁编程(如果做得正确)保证向前发展。当您使用锁时,一个线程在持有互斥锁时可能崩溃(例如,进入无限循环)。当/如果发生这种情况时,等待该互斥锁的其他线程不能再取得任何进展。如果互斥锁是正常操作的核心,那么在完成任何其他工作之前,您可能很容易不得不重新启动整个进程。使用无锁编程,就不会出现这种情况。其他线程可以继续前进,而不管任何一个线程1发生了什么。
也就是说,是的,您希望的事情之一通常是更好的性能——但是要看到它,您可能需要四个以上的线程。与基于锁的队列相比,在几十到几百个线程的范围内,无锁代码更有可能显示出改进的性能。然而,要真正发挥作用,您不仅需要更多的线程,还需要更多的内核——至少根据我目前所看到的,使用四个内核和编写良好的代码,对于无锁编程来说,不太可能有足够的锁争用来显示太多(如果有的话)性能优势。底线:更多的线程(至少几十个)将提高无锁队列显示性能优势的机会,但是只有四个核心,如果基于锁的队列仍然保持不变,那就不足为奇了。如果您添加了足够多的线程和内核,那么无锁版本几乎不可避免地会胜出。所需的线程和内核的确切数量很难预测,但您应该至少考虑几十个。
1至少对于互斥锁来说是这样的。像吞噬了所有系统资源的fork-bomb这样的东西可能会剥夺其他线程完成任何事情所需的足够资源——但是注意配额之类的东西通常也可以防止这种情况发生。
问题实际上是您正在优化的工作负载。如果拥塞很少,那么现代操作系统上的锁结构可能不会太糟糕。只要它们在快速路径上,它们主要在引擎盖下使用CAS指令。因为这些都是经过优化的,所以用你自己的代码很难打败它们。
我们自己的实现只能在拥塞部分取得实质性的胜利。如果平均队列长度远远大于并行处理队列的线程数量,那么对队列进行随机操作(您的问题不太精确)可能无法做到这一点。因此,您必须确保队列是短的,这可以通过在队列太长或太短时引入对选择的随机操作的偏差来实现。然后,我还会让系统的线程数至少是内核数的两倍。这将确保等待时间(内存)不会对锁版本有利。
我认为最好的方法是用锁识别应用程序中的热点通过分析代码。引入无锁机制并再次测量。正如其他海报所提到的,可能不会有明显的改善在较低的规模(线程数,应用程序规模,内核数),但您可能在扩展系统时看到吞吐量的改进。这是因为死锁消除了各种情况,线程总是在向前推进。
另一种看待无锁方案优势的方式是对某些人来说区段一将系统状态与应用程序性能解耦,因为存在是否不涉及内核/调度器,并且大部分代码是用户代码
对于严重竞争的锁,线程阻塞并被调度一次获得锁,这基本上意味着它们在运行结束时被放置队列(用于特定优先级)。这无意中将应用程序链接到系统应用程序的状态和响应时间现在取决于运行队列的长度。
我的2美分。