1

我一直在寻找提高处理从 rabbitmq 队列接收到的消息速度的方法。我发现的唯一方法是让多个线程做同样的事情——接收和处理。这给了我一些利润。在我创建 4 个线程后,速度翻了两番。由于我有 8 核处理器,我决定将线程数增加到 8。但这并没有提高性能。YourKit 显示只使用了 50% 的 CPU。有人可能会说我的应用程序是轻量级的,但我可以说它不能做比它做的更多的工作,无论我产生更多的事情要做。为什么这不起作用?

4

5 回答 5

4

有许多不同的问题会限制给定系统上某些应用程序的最大速度。例如,它可能受到内存带宽、阿姆达尔定律效应(非并行代码所需的时间,包括同步块)、I/O 带宽和缓存空间的限制。

如果您想进一步改进,您需要进行一些测量和分析以找到时间的去向,然后继续努力。

于 2013-07-09T14:02:36.937 回答
3

简短(而且不是特别有用)的答案是“开销和瓶颈”。

例如:

  • 在 Java 中创建线程相对昂贵。如果线程完成的工作量不大,则创建线程的开销可能会超过好处。

  • 线程之间的上下文切换相对昂贵,尤其是考虑到与内存相关的开销(例如缓存未命中、TLB 未命中)时。(当将本机线程分配给内核时,这些开销实际上会受到影响。如果操作系统可以以某种方式将本机线程连续保持在单个内核上(即同一内核上没有其他线程),那么它可以使用自旋锁......并且避免上下文切换。但是您拥有的 Java 线程越多,操作系统执行此操作的可能性就越小。)

  • 线程可能会花费大部分时间等待 I/O 完成。I/O 系统的吞吐量或某些外部服务的速度/延迟可能是瓶颈。

  • 您可能对数据结构有争议;例如,需要独占访问权限才能安全读取或更新(例如)共享 Map 的线程。如果线程经常需要等待其他人释放锁,那么你就有了瓶颈。

  • 您的计算可能主要受“馈送”线程的成本支配。例如,如果有一个主线程将“工作”分配给工作线程,那么主线程的活动可能是瓶颈;即它可能无法提供足够的工作来让工人忙碌。


由于您的标签暗示您正在使用消息队列,因此这可能是瓶颈,特别是如果消息很大或者每个消息完成的“工作”相对较小。

(使用单独的消息队列服务可能会增加上下文切换、增加 I/O 延迟、增加协议开销等。对于小型系统来说,这不是提高性能的自动途径。)


您也有可能拥有“超线程”内核而不是真正的内核,或者操作系统正在阻止您的 JVM 使用所有内核。

于 2013-07-09T14:16:24.130 回答
1

如果 CPU 或等待 IO 是您的瓶颈,那么添加独立线程会产生很大的不同。

如果你有一个共享资源是一个瓶颈,例如你的 L3 缓存、你的网络适配器、你的内核,那么添加线程将无济于事,因为 CPU 不是问题。事实上,它通常会通过增加开销使情况变得更糟。

我的应用是轻量级的

在这种情况下,CPU 不太可能是您的问题,并且您可以很好地看到超过 1 个 CPU 的加速。您很可能正在加速 RabbitMQ 使用的 CPU。理想情况下,它应该更有效,这应该没什么帮助。恕我直言,更有效的消息传递解决方案不会因多个 CPU 获得太多收益,因为它们不会成为 CPU 的瓶颈。

于 2013-07-09T14:26:27.160 回答
1

一种或另一种方式,您只使用 4 个内核。有很多东西可以阻止您通过加倍线程来使性能加倍,但是从您的 4 线程成功来看,您已经克服了所有这些问题。我猜你的代码中有一个错误来触发 8 个线程,它只启动了 4 个。(即使使用超线程,你也会得到一些改进。即使有每一个可能的问题,你也会得到一些改进.) 否则,我会选择 TJCrowder 和 Stephen C:我不认为你真的有 8 个内核。

我会尝试使用不同数量的线程:3、5、6。看看有什么变化。我想你很快就会发现这个问题。

公平地说,Java:如果您编写线程安全代码并避免瓶颈,它可以很好地处理线程,正如您已经注意到从一个线程到 4 个线程一样。我一直发现开销成本是微不足道的。

于 2013-07-09T14:31:17.423 回答
0

您的应用程序没有线性加速,因此它没有很好的可扩展性。

为了保持增加线程的数量,您需要确保正在处理的数据相应地增长。对于固定数量的数据,增加线程(和/或内核)的数量将在某个时候收益递减,因为创建线程的开销将超过线程的计算时间。

请务必查看以下链接:

古斯塔夫森定律是艾哈迈达定律的一个很好的对立面,所以我强烈建议你理解那篇文章。

于 2013-07-18T03:34:31.120 回答