Java多线程代码不会完全消耗某些进程上的内核

Java多线程代码不会完全消耗某些进程上的内核,java,multithreading,multicore,Java,Multithreading,Multicore,我有一些密集的处理代码,可以从文件中读取块,处理数据,并以相同的顺序写入输出文件。一些数字:输入文件大约29MB,输出文件大约39MB,有39461个块。 单线程版本需要100%的处理器(对于多核,仅使用一个内核) 再次列出一些数字(每个过程的秒数): 奔腾4(1核)2.8GHz 4302.407 英特尔至强(1核)2.8GHz 3805.281 英特尔E8300(2核)1773.062 英特尔Q6600(4核)2202.231 英特尔i5-4440(4核)1300.127 i7-3632QM(

我有一些密集的处理代码,可以从文件中读取块,处理数据,并以相同的顺序写入输出文件。一些数字:输入文件大约29MB,输出文件大约39MB,有39461个块。 单线程版本需要100%的处理器(对于多核,仅使用一个内核)

再次列出一些数字(每个过程的秒数):

奔腾4(1核)2.8GHz 4302.407

英特尔至强(1核)2.8GHz 3805.281

英特尔E8300(2核)1773.062

英特尔Q6600(4核)2202.231

英特尔i5-4440(4核)1300.127

i7-3632QM(4芯8螺纹)1412.191

有趣的是,在单线程中,评级更好的i7几乎与旧的E8300一样快,而比i5-4440慢

为了利用多核结构,我修改了代码。我启动的线程数量等于内核(线程)的数量-Runtime.getRuntime().AvailableProcessor()。每个线程从文件中读取并选取一个数字(同步块),进行密集处理,然后排队等待其选取的数字可用,以便写入输出文件,执行写入操作并增加显示现在轮到谁写入输出文件(也同步)的数字。 代码工作正常,输出文件生成正确

对于上述处理器(多核),我得到了以下结果:

英特尔E8300(2核)937.766

英特尔Q6600(4核)657.515

英特尔i5-4440(4核)345.244

i7-3632QM(4芯8螺纹)584.346

与单线程版本相比确实有所改进,但是:Task Manager(Windows上的所有系统)让我满意的是,除了i7之外,所有进程/所有内核都100%忙-这里它使用了所有8个线程,但每个线程只使用了约40%,结果反映了这种行为。i7介于旧的q6600和新的(但额定值较低)i5-4440之间,更接近第一款

一些评论:

线程等待写入输出文件的方式是:

        while(ai.intValue() != outSeed.intValue()) {
            Thread.sleep(10);
        }
ai是从输入文件读取时拾取的数字,现在轮到它写入。outSeed由成功写入的线程递增

在Q6600上进行了严格的测试,10毫秒的睡眠时间被证明是最好的。i5也得到了很好的改善。i7不太好,所以我尝试了睡眠(3),睡眠(1),睡眠(0)。对于3ms,i7以529.782的时间运行。睡眠(0)将所有8个线程的忙百分比提高到大约60%,时间为440.897。这是更好的,但这是不够的,因为我预计不到200秒,我认为这是可能的,如果我可以实现一个更繁忙的处理器

同样,结果文件是预期的,该行为是除i7-3632QM之外的大多数进程(100%忙)上预期的。你有什么建议?我从TaskManager尝试了setPriority=realTime,没有效果。操作系统是否可能限制程序的使用? 后者我可能有机会使用六核Xeon,也可以试一试。 谢谢你的阅读

  • 超线程显然不会线性扩展性能。i7有4个核心,而不是8个,并且在这些核心前面只有一点逻辑,这使得上下文切换更快。在没有超线程的情况下,您只能期望比4核系统的性能提高20-30%

  • 您在任务管理器中看到的并不能直接反映单个Java线程的效率,因为线程会在内核之间重新分配。相同的读数可以用少于8个线程来完成,每个线程都以全速运行

  • 仅仅是您有8个线程而不是4个线程这一事实就可能导致一些阻塞问题,因为您无法为所有8个线程提供工作。明确的
    睡眠
    可能会影响这一点

  • 您应该尝试使用依赖于
    Phaser
    的设计替换轮询循环。该类似乎与您的用例完美匹配

  • 您正在编写的代码已经在Java8中通过Streams API提供。我最近写了一篇关于这个主题的文章,解释了如何使用Streams API来并行化任何基于I/O的源代码。你也可以试试那条路

  • 超线程显然不会线性扩展性能。i7有4个核心,而不是8个,并且在这些核心前面只有一点逻辑,这使得上下文切换更快。在没有超线程的情况下,您只能期望比4核系统的性能提高20-30%

  • 您在任务管理器中看到的并不能直接反映单个Java线程的效率,因为线程会在内核之间重新分配。相同的读数可以用少于8个线程来完成,每个线程都以全速运行

  • 仅仅是您有8个线程而不是4个线程这一事实就可能导致一些阻塞问题,因为您无法为所有8个线程提供工作。明确的
    睡眠
    可能会影响这一点

  • 您应该尝试使用依赖于
    Phaser
    的设计替换轮询循环。该类似乎与您的用例完美匹配

  • 您正在编写的代码已经在Java8中通过Streams API提供。我最近写了一篇关于这个主题的文章,解释了如何使用Streams API来并行化任何基于I/O的源代码。你也可以试试那条路


  • 您正在读取和写入文件


    有时您的应用程序会被I/O阻塞,因此不会占用CPU。

    您正在读取和写入文件


    有时您的应用程序会被I/O阻塞,因此不会占用CPU。

    在Marko和Claudio的评论之后,我回到测试:

    -@Claudio I在i7中禁用了HT,并再次运行多核:忙70%/核,结果稍微好一点(睡眠(0)):431.099

    -@Marko:我在启用HT的情况下再次在i7上运行单线程,并观察到java进程占总数的13%,但在8个繁忙的inste中有2个线程