Windows 上多线程 Java 应用程序的 CPU 使用率过低
2022-09-02 22:39:53
我正在开发一个Java应用程序,用于解决一类数值优化问题 - 更精确的大规模线性规划问题。单个问题可以拆分为可以并行解决的更小的子问题。由于子问题比 CPU 内核多,因此我使用 ExecutorService 并将每个子问题定义为提交到 ExecutorService 的可调用项。求解子问题需要调用本机库 - 在本例中为线性规划求解器。
问题
我可以在Unix和Windows系统上运行该应用程序,这些系统具有多达44个物理内核和多达256g的内存,但是Windows上的计算时间比Linux上的计算时间高出一个数量级。Windows 不仅需要更多的内存,而且随着时间的推移,CPU 利用率从开始时的 25% 下降到几个小时后的 5%。以下是Windows中任务管理器的屏幕截图:
观察
- 整个问题的大型实例的求解时间从几小时到几天不等,消耗高达32g的内存(在Unix上)。子问题的求解时间在 ms 范围内。
- 在只需要几分钟就能解决的小问题上,我不会遇到这个问题。
- Linux开箱即用地使用这两个套接字,而Windows要求我在BIOS中显式激活内存交错,以便应用程序利用两个内核。不过,无论我是否这样做,对整体CPU利用率的恶化都没有影响。
- 当我查看 VisualVM 中的线程时,所有池线程都在运行,没有一个正在等待,否则。
- 根据VisualVM,90%的CPU时间花在本机函数调用上(解决一个小的线性程序)
- 垃圾回收不是问题,因为应用程序不会创建和取消引用大量对象。此外,大多数内存似乎是在堆外分配的。对于最大的实例,Linux上4g的堆就足够了,Windows上的8g堆就足够了。
我尝试了什么
- 各种JVM参数,高XMS,高元空间,UseNUMA标志,其他GC。
- 不同的 JVM(热点 8、9、10、11)。
- 不同线性规划求解器(CLP,Xpress,Cplex,Gurobi)的不同本机库。
问题
- 是什么推动了大量使用本机调用的大型多线程 Java 应用程序的 Linux 和 Windows 之间的性能差异?
- 在实现中,我是否可以更改任何可以帮助Windows的东西,例如,我是否应该避免使用接收数千个可调用的ExecutorService并执行其他操作?