繁忙旋转以减少上下文切换延迟(java)

时间:2013-01-04 12:30:05

标签: java multithreading performance queue

在我的应用程序中,有几个服务在自己的线程上处理信息,当它们完成时,它们将消息发布到下一个服务,然后继续在其自己的线程上进行工作。消息的切换是通过LinkedBlockingQueue完成的。切换通常需要50-80 us(从将消息放入队列直到消费者开始处理消息)。 为了加速最重要服务的切换,我想使用繁忙的旋转而不是阻塞方法(我有12个处理器内核,并希望将3个专用于这些重要的服务)。 所以..我将LinkedBlockingQueue更改为ConcurrentLinkedQueue

并且做了

for(;;)
{
 Message m = queue.poll();
 if( m != null )
  ....
}

现在..结果是第一个消息传递需要1 us,但是接下来的25个切换的延迟增加,直到达到500 us,然后延迟突然回到1 us并且开始增加..所以我有25次迭代的延迟周期,其中延迟从1 us开始,到500 us结束。 (消息每秒传递大约100次)

平均延迟为250,这并不是我所寻求的性能增益。

我还尝试使用LMAX Disruptor ringbuffer而不是ConcurrentLinkedQueue。这个框架在繁忙的旋转实现和完全不同的队列实现中有自己的构建,但结果是相同的。所以我非常肯定它不是队列的错误或我滥用的东西..

问题是...... Heck在这里发生了什么?为什么我会看到这个奇怪的延迟周期?

干杯!!

4 个答案:

答案 0 :(得分:1)

据我所知,如果线程调度程序检测到此线程正在非常密集地使用CPU,则可以故意暂停一个线程 - 在不同线程之间分配CPU时间更加公平。尝试在队列为空后在消费者中添加LockSupport.park()并在添加消息后在生成器中添加LockSupport.unpark() - 这可能会使延迟变得更少;实际上,与阻塞队列相比实际上是否更好是一个很大的问题。

答案 1 :(得分:0)

如果你真的需要按照你描述的方式完成工作(而不是安德烈·努杜科在1月5日13:22回复的方式),那么你肯定也需要从其他观点来看问题。

只是一些提示:

  1. 尝试检查整体环境(JVM外部)。例如:

  2. JVM中的“问题”

  3. 尝试更改主题优先级:Setting priority to Java's threads

答案 2 :(得分:0)

这只是疯狂的推测(因为正如其他人所提到的,你没有收集有关队列长度,失败的民意调查,无民意调查等的任何信息):

我使用了力量并阅读source of ConcurrentLinkedQueue,或者更确切地说,简单地翻了一两分钟。轮询不是你平凡的O(1)操作。情况可能是你正在遍历多个已经过时的节点,保持为空;并且可能存在其他临时状态,涉及节点链接到它们自己作为下一个节点,作为从队列中过时/删除的指示。可能是由于线程调度,队列开始建立垃圾。尝试按照代码中提到的抽象算法的链接:

Maged M. Michael和Michael L. Scott的

Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue(链接有PDF和伪代码)。

答案 3 :(得分:0)

这是我的2美分。如果您在基于linux / unix的系统上运行,有一种方法可以将某个cpu专用于某个线程。从本质上讲,您可以让操作系统忽略该cpu以进行任何调度。检查cpu的隔离级别