【发布时间】:2020-12-25 19:20:09
【问题描述】:
我写了一个程序来做一些并行计算,像这样:
public void multiThreadedRun(int nThreads) throws InterruptedException {
Thread [] threads = new Thread[nThreads];
// create and start nThreads ...
for (int i = 0; i < nThreads; ++i) {
// ... where each thread runs a task-grabbing loop in this::work.
threads[i] = new Thread(this::work);
threads[i].start();
}
System.out.println("all threads started, waiting for them to finish....");
for(int i = 0; i < nThreads; ++i)
threads[i].join();
System.out.println("all threads finished.");
}
在具有 96 个内核和 192GB 内存的 c5.24xlarge EC2 实例上运行此程序,使用
java -Xms150g -Xmx150g -XX:+UseParallelGC(使用 AWS 的 Corretto jdk),我将上述 multiThreadedRun 方法的执行时间设置为 nThreads 等于 1、8 和 96。运行时间(相当一致):285、63 和 42秒。
因此,与单线程运行相比,8 个线程提供 4.5 倍的加速,而 96 个线程仅提供 6.8 倍的加速。我怎样才能弄清楚是什么限制了加速?
我相信有争议的资源是内存,因为
一个。这是一个不启动任何 I/O 的计算任务,应该很容易适应分配给堆的 150GB(所以我不希望有太多的虚拟内存 I/O)。
b.在 96 个线程上运行时,我每隔几秒调用一次 jstack <pid> 并汇总结果(对于执行 work() 的 96 个线程):1844 个样本找到了处于“RUNNABLE”状态的线程,只有 76 个找到了它等待监视器。因此,似乎没有太多时间花在等待锁上(但也许jstack 存在抽样偏差?或者这种排除锁争用的方法可能存在其他问题?)。
假设这确实是内存问题,如何诊断内存争用的来源?
更具体地说:
-
“差异分析”(比较在 nThreads = 96 运行和 nThreads = 1 或 nThreads = 8 运行中花费的时间)有用吗?请注意,我不是询问如何使用分析来加速我的代码 - 而是我试图了解比较分析结果是否有助于找出内存争用的位置 - 以及如何。
-
是否有工具可以查看不同变量在每个缓存层中花费的时间,以及触发缓存刷新的原因?
-
我猜测哪些对象会导致内存争用。有什么方法可以检验这些假设吗?
【问题讨论】:
-
这段代码在多少核的机器上运行?
-
@dreamcrash 这是一台 96 核机器(或者更准确地说,这是一个应该表现得像 96 核机器的 EC2 实例......)
-
aws.amazon.com/ec2/instance-types/c5(c5.24xlarge 位于中间附近)
-
他们确实有类似的“裸机”实例,我可以尝试,但通常更难获得......
-
感谢您的信息,我添加了标签多线程,以便更多选择可以查看您的问题
标签: java multithreading parallel-processing profiling jstack