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

geo*_*rge 1 java multithreading multicore

我有一些密集的处理代码,它从文件中读取块,处理数据并写入输出文件IN THE SAME ORDER.一些数字:输入文件大约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().availableProcessors().每个线程从文件读取并选取一个数字(synchronized块),进行密集处理,然后排队等待其选择的数字可用以便在输出文件中写入,写入并增加显示其编号的数字现在转到写入输出文件(也同步).代码工作正常,输出文件正确生成.

对于上述处理器(多核)我得到了这个:

英特尔E8300(2核)937.766

英特尔Q6600(4核)657.515

英特尔i5-4440(4核)345.244

i7-3632QM(4核,8线程)584.346

改进确实与单线程版本相比,但是:任务管理器(Windows上的所有系统)让我满意地表示100%忙于所有触发/所有内核除了i7之外 - 这里它使用所有8个线程但每个只有大约40%,并且结果反映了这种行为.i7落在旧的q6600和新的(但评级较低的)i5-4440之间并且更接近第一个.

一些评论:

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

        while(ai.intValue() != outSeed.intValue()) {
            Thread.sleep(10);
        }
Run Code Online (Sandbox Code Playgroud)

ai是从输入文件中读取时选取的数字,现在等待轮到写入.outSeed由成功写入的线程递增.

在Q6600上进行了强烈的测试,10毫秒的睡眠被证明是最好的时间.i5也改进得很好.i7不太好,所以我试着睡觉(3),睡觉(1),睡觉(0).3分钟i7在529.782时间运行.睡眠(0)将忙碌百分比提高到约.所有8个线程和时间的60%是440.897.它更好,但它不够,因为我期望不到200秒,我认为如果我可以实现更繁忙的处理器是可能的.

同样,结果文件是预期的,行为是大多数过程(100%忙)的预期,除了i7-3632QM.你有什么建议?我从TaskManager尝试了setPriority = realTime,没有效果.Op是否有可能.系统限制了proc的使用?后来我可以访问六核Xeon并尝试使用它.谢谢阅读.

Mar*_*nik 5

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

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

  3. 事实上你有8个线程而不是4个线程可能会导致一些阻塞问题,因为你无法用工作提供所有8个线程.明确的sleep可能会影响这一点.

  4. 您应该尝试使用依赖的设计替换轮询循环Phaser.这个类似乎是你的用例的完美匹配.

  5. Java 8中已经使用Streams API提供了您正在编码的内容.我最近写了一个帖子究竟这个问题上,这也解释了如何使用API数据流并行任何I/O基源.您也可以尝试这种途径.