我有一些密集的处理代码,可以从文件中读取 block ,处理数据并以相同的顺序写入输出文件。一些数字:输入文件大约 29MB,输出文件大约 39MB,有 39461 个 block 。 单线程版本占用 100% 处理器(对于多核,仅使用一个内核)。
又是一些数字(每个过程的秒数):
奔腾 4(1 核)2.8GHz 4302.407
英特尔至强(1 核)2.8GHz 3805.281
Intel E8300(2核)1773.062
Intel Q6600(4核)2202.231
英特尔 i5-4440(4 核)1300.127
i7-3632QM(4核8线程)1412.191
有趣的是,额定值更高的 i7 在单线程中几乎与旧 E8300 一样快,但比 i5-4440 慢。
为了利用多核结构,我修改了代码。我启动的线程数等于核心数(线程)- Runtime.getRuntime().availableProcessors()。每个线程从文件中读取并选择一个数字(同步块(synchronized block)),进行密集处理,然后排队等待其选择的数字可用以便写入输出文件,写入并增加显示其的数字现在轮到写入输出文件(也是同步的)。 代码工作正常,输出文件正确生成。
对于上述处理器(多核),我得到了这个:
Intel E8300(2核)937.766
Intel Q6600(4核)657.515
英特尔 i5-4440(4 核)345.244
i7-3632QM(4核8线程)584.346
与单线程版本相比确实有所改进,但是:令我满意的是,任务管理器(Windows 上的所有系统)在除 i7 之外的所有进程/所有内核上都 100% 忙 - 这里它使用了所有 8 个线程,但每个线程仅使用了大约 40% ,结果反射(reflect)了这种行为。 i7 介于旧的 q6600 和新的(但额定值较低的)i5-4440 之间,更接近第一个。
一些说明:
线程等待写入输出文件的方式是:
while(ai.intValue() != outSeed.intValue()) {
Thread.sleep(10);
}
ai 是从输入文件中读取时选择的数字,现在等待轮到它写入。 outSeed 由成功写入的线程递增。
在 Q6600 上进行了严格测试,证明 10 毫秒的 sleep 时间是最佳时间。 i5也提升的很好。 i7 不太好,所以我尝试了 sleep(3)、sleep(1)、sleep(0)。对于 3 毫秒,i7 运行了 529.782 次。 Sleep(0) 将繁忙百分比提高到大约。所有 8 个线程的 60% 和时间为 440.897。它更好,但还不够,因为我希望少于 200 秒,而且我认为如果我能实现更繁忙的处理器是可能的。
同样,结果文件符合预期,行为符合大多数 proc(100% 忙)的预期,i7-3632QM 除外。你有什么建议?我从 TaskManager 尝试了 setPriority=realTime,没有效果。 Op。 有可能吗?系统限制proc使用? 后来我可能会使用六核 Xeon 并尝试使用它。 感谢阅读。
最佳答案
超线程显然不会线性扩展性能。你的 i7 有 4 个内核,而不是 8 个,并且在这些内核前面只有一些逻辑可以使上下文切换更快。与没有超线程的 4 核系统相比,您的性能预计最多只能提高 20-30%。
您在任务管理器中看到的内容并不直接反射(reflect)各个 Java 线程的效率,因为线程会在内核之间重新分配。相同的读数可以用少于 8 个线程来完成,每个线程都全速运行。
您有 8 个线程而不是 4 个线程这一事实可能会导致一些阻塞问题,因为您无法为所有 8 个线程提供工作。明确的
sleep
可能会影响这一点。您应该尝试用依赖于
Phaser
的设计替换您的轮询循环。该类似乎非常适合您的用例。Java 8 中已经通过 Streams API 提供了您正在编写的代码。我最近写了一个post关于这个主题,它解释了如何使用 Streams API 来并行化任何基于 I/O 的源。你也可以试试这条路。
关于Java 多线程代码在某些进程上没有完全消耗内核,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26039100/