1
170
可以说,这里同时存在着几个问题。 首先,并行解决问题总是需要执行比按顺序执行更多的实际工作。开销涉及在多个线程之间拆分工作以及连接或合并结果。像将短字符串转换为小写这样的问题足够小,以至于它们有被并行拆分开销淹没的危险。 第二个问题是,对Java程序进行基准测试非常微妙,很容易得到令人困惑的结果。两个常见问题是JIT编译和死代码消除。短期基准测试通常在JIT编译之前或期间完成,因此它们不是在衡量峰值吞吐量,实际上,它们可能是在衡量JIT本身。编译发生的时间有点不确定,因此可能会导致结果也发生很大变化。 对于小型合成基准测试,工作负载通常会计算丢弃的结果。JIT编译器非常擅长检测这一点,并消除不会产生任何地方使用的结果的代码。这种情况可能不会发生,但如果您修补其他合成工作负载,它肯定会发生。当然,如果JIT消除了基准工作负载,那么基准将变得无用。 我强烈建议使用完善的基准测试框架,如 JMH 而不是自己动手滚动。JMH有助于避免常见的基准测试陷阱,包括这些陷阱,而且它的设置和运行非常简单。下面是转换为使用JMH的基准测试:
我使用以下命令运行此命令:
(选项指示五个预热迭代、五个基准迭代和一个分叉JVM。)在运行过程中,JMH会发出大量冗长的消息,我已经省略了这些消息。总结结果如下。
请注意,结果是以每秒操作数为单位的,因此看起来并行运行的速度大约是顺序运行的三倍。但我的机器只有两个核心。Hmmm.每次运行的平均误差实际上大于平均运行时间!沃特?这里有点可疑。 这就引出了第三个问题。更仔细地观察工作负载,我们可以看到它为每个输入分配了一个新的字符串对象,并且它还将结果收集到一个列表中,这涉及到大量的重新分配和复制。我猜这将导致相当数量的垃圾收集。通过在启用GC消息的情况下重新运行基准测试,我们可以看到这一点:
这会产生如下结果:
注意:以开头的行
目前还不清楚该怎么办。这纯粹是一个合成工作负载。与分配和复制相比,它显然只需要很少的CPU时间来完成实际工作。很难说你真正想要衡量的是什么。一种方法是提出一种在某种意义上更“真实”的不同工作负载另一种方法是更改heap和GC参数,以避免在基准测试运行期间使用GC。 |
2
17
在进行基准测试时,您应该注意JIT编译,并且计时行为可能会根据JIT编译的代码路径的数量而改变。如果我在测试程序中添加一个预热阶段,那么并行版本比顺序版本快一点。以下是结果:
下面的代码片段包含我用于此测试的完整源代码。
|
3
10
使用多个线程处理数据有一些初始设置成本,例如初始化线程池。这些成本可能会超过使用这些线程的收益,尤其是在运行时已经很低的情况下。此外,如果存在争用,例如运行的其他线程、后台进程等,则并行处理的性能可能会进一步降低。
这个问题对于并行处理来说并不新鲜。本文根据Java 8提供了一些细节
|
4
2
Java中的流实现在默认情况下是顺序的,除非在并行中明确提到它。当一个流并行执行时,Java运行时将该流划分为多个子流。聚合操作并行地迭代和处理这些子流,然后合并结果。 所以,若开发人员对顺序流有性能影响,可以使用并行流。 请检查性能比较: https://github.com/prathamket/Java-8/blob/master/Performance_Implications.java 您将了解有关性能的总体信息。 |
a a · 为什么在这个可重入锁示例中需要引用计数? 2 年前 |
Grant · goroutines有高空闲唤醒电话 2 年前 |
hoaz · 如何安全地清理并发映射 6 年前 |
Alanpatchi · int基元类型的volatile声明 6 年前 |