增加核心数时降低性能

我的mac配备了16个核心。

System.out.println(Runtime.getRuntime().availableProcessors()); //16 

我正在运行下面的代码,以了解使用我的核心的有效性。 线程’CountFileLineThread’只计算文件中的行数(文件夹中有133个文件)

我在这条线上做笔记:

 ExecutorService es = Executors.newFixedThreadPool(NUM_CORES); 

NUM_CORES在1到16之间。

您将从下面的结果中注意到,5个以上内核的性能开始下降。 我不希望6芯及以上的产品“收益递减”(btw,7芯需要超过22分钟,你好?!?!)我的问题是为什么?

在此处输入图像描述

 public class TestCores { public static void main(String args[]) throws Exception { long start = System.currentTimeMillis(); System.out.println("START"); int NUM_CORES = 1; List files = Util.getFiles("/Users/adhg/Desktop/DEST/"); System.out.println("total files: "+files.size()); ExecutorService es = Executors.newFixedThreadPool(NUM_CORES); List<Future> futures = new ArrayList<Future>(); for (File file : files) { Future future = es.submit(new CountFileLineThread(file)); futures.add(future); } Integer total = 0; for (Future future : futures) { Integer result = future.get(); total+=result; System.out.println("result :"+result); } System.out.println("----->"+total); long end = System.currentTimeMillis(); System.out.println("END. "+(end-start)/1000.0); } } 

我添加了这个作为评论,但我会把它作为答案扔在那里。 因为您的测试正在进行文件I / O,所以您可能在第6个线程中遇到了一个问题,即您现在正在执行过多的I / O,从而减慢了所有内容的速度。 如果你真的想看到你拥有的16个内核的好处,你应该重新编写文件读取线程以使用非阻塞I / O.

我的预感是你可能在磁盘I / O上施加了太多的负担,以至于你放慢了一切! 请参阅“活动监视器”中的I / O性能(如果您使用的是OSX)。 在Linux上,使用vmstat命令了解发生了什么。 [如果你看到大量的交换或高速读取/ s和写入/ s那么你去了]


我注意到的一些事情:

CountFileLineThread不在代码中。 请把它放在一起,这样我们就可以看到到底发生了什么。

下一个,

 for (Future future : futures) { Integer result = future.get(); total+=result; System.out.println("result :"+result); } 

在这里,请注意您在第一个Taskfuture.get() )的结果上被阻止。 与此同时,其他结果可能已经可用,但在第一次完成之前您无法看到它们。 请使用CompletionService以按照完成顺序获取结果,以便更好地进行测量。 虽然你想要在结束计时器之前完成所有线程,但这并不重要。

另一点:阻止I / O是关键。 如果任务在I / O,网络等上被阻止,那么你拥有多少核心并不重要。现代处理器具有什么是超线程,如果当前正在执行线程块,它们可以运行等待运行的线程。

因此,例如,如果我有16个内核并且我产生16个线程要求它们读取1 GB文件,那么仅仅通过拥有更多内核我不会获得任何性能改进。 瓶颈是磁盘和内存。

添加处理器会导致各种问题,但大多数问题与处理器之间的同步有关。 文件系统中的任务级锁定等可能会成为一个问题,但更多的问题是核心之间的同步必须只是为了维持缓存一致性,跟踪已更改的页面等等。我不知道如何你拥有的每个芯片有很多核心(大约10年前放弃跟踪这些东西),但通常一旦你开始同步片外性能就会下降。

我将补充一点,JVM可以在这里发挥重要作用。 需要仔细的JVM设计来最小化共享(和经常更新的)高速缓存行的数量,并且需要花费大量精力才能使GC在多核环境中高效工作。