我一直在寻找提高处理从 rabbitmq 队列接收到的消息速度的方法。我发现的唯一方法是让多个线程做同样的事情——接收和处理。这给了我一些利润。在我创建 4 个线程后,速度翻了两番。由于我有 8 核处理器,我决定将线程数增加到 8。但这并没有提高性能。YourKit 显示只使用了 50% 的 CPU。有人可能会说我的应用程序是轻量级的,但我可以说它不能做比它做的更多的工作,无论我产生更多的事情要做。为什么这不起作用?
5 回答
有许多不同的问题会限制给定系统上某些应用程序的最大速度。例如,它可能受到内存带宽、阿姆达尔定律效应(非并行代码所需的时间,包括同步块)、I/O 带宽和缓存空间的限制。
如果您想进一步改进,您需要进行一些测量和分析以找到时间的去向,然后继续努力。
简短(而且不是特别有用)的答案是“开销和瓶颈”。
例如:
在 Java 中创建线程相对昂贵。如果线程完成的工作量不大,则创建线程的开销可能会超过好处。
线程之间的上下文切换相对昂贵,尤其是考虑到与内存相关的开销(例如缓存未命中、TLB 未命中)时。(当将本机线程分配给内核时,这些开销实际上会受到影响。如果操作系统可以以某种方式将本机线程连续保持在单个内核上(即同一内核上没有其他线程),那么它可以使用自旋锁......并且避免上下文切换。但是您拥有的 Java 线程越多,操作系统执行此操作的可能性就越小。)
线程可能会花费大部分时间等待 I/O 完成。I/O 系统的吞吐量或某些外部服务的速度/延迟可能是瓶颈。
您可能对数据结构有争议;例如,需要独占访问权限才能安全读取或更新(例如)共享 Map 的线程。如果线程经常需要等待其他人释放锁,那么你就有了瓶颈。
您的计算可能主要受“馈送”线程的成本支配。例如,如果有一个主线程将“工作”分配给工作线程,那么主线程的活动可能是瓶颈;即它可能无法提供足够的工作来让工人忙碌。
由于您的标签暗示您正在使用消息队列,因此这可能是瓶颈,特别是如果消息很大或者每个消息完成的“工作”相对较小。
(使用单独的消息队列服务可能会增加上下文切换、增加 I/O 延迟、增加协议开销等。对于小型系统来说,这不是提高性能的自动途径。)
您也有可能拥有“超线程”内核而不是真正的内核,或者操作系统正在阻止您的 JVM 使用所有内核。
如果 CPU 或等待 IO 是您的瓶颈,那么添加独立线程会产生很大的不同。
如果你有一个共享资源是一个瓶颈,例如你的 L3 缓存、你的网络适配器、你的内核,那么添加线程将无济于事,因为 CPU 不是问题。事实上,它通常会通过增加开销使情况变得更糟。
我的应用是轻量级的
在这种情况下,CPU 不太可能是您的问题,并且您可以很好地看到超过 1 个 CPU 的加速。您很可能正在加速 RabbitMQ 使用的 CPU。理想情况下,它应该更有效,这应该没什么帮助。恕我直言,更有效的消息传递解决方案不会因多个 CPU 获得太多收益,因为它们不会成为 CPU 的瓶颈。
一种或另一种方式,您只使用 4 个内核。有很多东西可以阻止您通过加倍线程来使性能加倍,但是从您的 4 线程成功来看,您已经克服了所有这些问题。我猜你的代码中有一个错误来触发 8 个线程,它只启动了 4 个。(即使使用超线程,你也会得到一些改进。即使有每一个可能的问题,你也会得到一些改进.) 否则,我会选择 TJCrowder 和 Stephen C:我不认为你真的有 8 个内核。
我会尝试使用不同数量的线程:3、5、6。看看有什么变化。我想你很快就会发现这个问题。
公平地说,Java:如果您编写线程安全代码并避免瓶颈,它可以很好地处理线程,正如您已经注意到从一个线程到 4 个线程一样。我一直发现开销成本是微不足道的。