并发程序降级的性能随着线程的增加而增加?

我一直在尝试在四核计算机上实现以下代码,并且Executor服务中100多次迭代中没有线程的平均运行时间如下

1个线程= 78404.95

2个主题= 174995.14

4个线程= 144230.23

但根据我所研究的, 2*(no of cores)线程应该为程序提供最佳结果,这在我的程序中显然不是这样,奇怪地给出了单线程的最佳时间。

代码:

  import java.util.Collections; import java.util.Random; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class TestHashSet { public static void main(String argv[]){ Set S = Collections.newSetFromMap(new ConcurrentHashMap()); S.add(1); S.add(2); S.add(3); S.add(4); S.add(5); long startTime = System.nanoTime(); ExecutorService executor = Executors.newFixedThreadPool(8); int Nb = 0; for(int i = 0;i<10;i++){ User runnable = new User(S); executor.execute(runnable); Nb = Thread.getAllStackTraces().keySet().size(); } executor.shutdown(); try { executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } long endTime = System.nanoTime(); System.out.println(0.001*(endTime-startTime)+" And "+Nb); } } class User implements Runnable{ Set S; User(Set S){ this.S = S; } @Override public void run() { // TODO Auto-generated method stub Set t =Collections.newSetFromMap(new ConcurrentHashMap());; for(int i = 0;i<10;i++){ t.add(i+5); } S.retainAll(t); Set t2 =Collections.newSetFromMap(new ConcurrentHashMap());; for(int i = 0;i<10;i++){ t2.add(i); } S.addAll(t); /* ConcurrentHashSet D = new ConcurrentHashSet(); for(int i=0;i<10;i++){ D.add(i+3); } S.difference(D); */ } } 

更新:如果我将每个线程的查询数量增加到1000,则4线程的性能优于单线程。我认为当我每个线程只使用大约4个查询并且没有增加查询时,开销高于运行时间运行时间是现在大于Overhead.Thanks

但5线程应该提高性能..?

这就是你所谓的“假设”。 但事实上,无法保证添加线程会提高性能。

但根据我研究的2 *(没有核心)线程应该给出最佳结果……

如果你在某处阅读,那么你要么误读它,要么就是完全错误的。

实际情况是,获得最佳性能的线程数量在很大程度上取决于应用程序的性质,也取决于您运行的硬件。


基于对代码的粗略读取,似乎这是测试Java处理multithreading访问和更新共享集( S )的程度的基准。 每个线程在线程限制集上执行某些操作,然后将线程限制集中的所有条目添加或删除到共享集。

问题是addAllretainAll调用可能是并发瓶颈。 基于ConcurrentHashMap的集合将为集合的点访问/更新提供比基于HashMap的更好的并发性能。 但是,addAll和retainAll在其他线程正在操作的相同条目上执行N个此类操作。 鉴于此操作模式的性质,您可能会在ConcurrentHashMap的不同区域内获得重大争用。 这很可能导致一个线程阻塞另一个线程……并且减速。

更新:如果我增加每个线程没有查询4线程的性能优于单线程。我认为开销高于运行时我每个线程只使用大约4个查询而且查询没有增加运行时间现在大于高架。

我假设您的意思是增加哈希映射条目的数量。 考虑到ConcurrentHashMap工作方式,这可能会减少平均争用。 (该类将映射划分为区域,并安排涉及不同区域中的条目的操作产生最小可能的争用开销。通过增加不同条目的数量,可以降低两个同时操作将导致争用的可能性。)


所以回到“2 x no of threads”factoid。

我怀疑你一直在阅读的消息来源实际上并没有说这会给你带来最佳性能。 我怀疑他们真的这么说:

  • “2 x no of threads”是一个很好的起点…… 你需要针对你的应用/问题/硬件进行调整,和/或

  • 对于计算密集型任务,不要超过“2 x no of threads”…因为它不太可能有所帮助。

在您的示例中,争用的主要来源很可能是对共享集/映射的更新……以及确保它们以primefaces方式发生的开销。

您也可以在较低级别获得争用; 即争用内存带宽(RAM读/写)和内存缓存争用。 是否会发生这种情况取决于您运行的硬件的规格……


最后要注意的是,您的基准测试存在缺陷,因为它不允许各种VM预热效果……例如JIT编译。 您的2个线程时间超过 1个线程时间的两倍的事实指向该问题。

您的基准测试还有其他可疑方面:

  • run()方法完成的工作量太小。

  • 该基准似乎不代表现实世界的用例。 在完全虚拟(无意义)算法中测量加速并不能为您提供有关缩放线程数时真实算法可能执行的任何线索。

  • 在4核计算机上运行测试意味着您可能没有足够的数据点来得出具有科学意义的结论……假设基准测试是合理的。


话虽如此,你似乎看到的2到4线程减速对我来说并不意外。