我写了一个程序来做一些并行计算,比如:
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.");
}
在c5.24xlarge ec2示例上运行此程序,该示例具有96个内核和192gb内存,使用 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>
每隔几秒钟,将结果汇总(对于96个执行的线程) work()
):1844个示例发现一个线程处于“runnable”状态,只有76个发现它正在等待监视器。因此,似乎没有多少时间花在等待锁上(但也许 jstack
有取样偏差吗?或者这种排除锁争用的方法可能还有其他问题。
假设这确实是一个内存问题,那么如何诊断内存争用的来源呢?
更具体地说:
“差异分析”(比较在nthreads=96运行和nthreads=1或nthreads=8运行中花费的时间)有用吗?注意,我并不是在问如何使用评测来加速我的代码,而是在试图了解比较评测结果是否有助于找出内存争用的位置,以及如何使用。
是否有工具可以查看不同变量在每个缓存层中花费的时间,以及触发缓存刷新的因素?
对于哪些对象导致内存争用,我有一些猜测。有什么方法可以检验这些假设吗?
1条答案
按热度按时间dhxwm5r41#
我想看看perf,看看你的cpu在做什么。如果cpu正在争夺内存,它们的ipc应该很低,因为它们将在内存访问上暂停。您可以看看toplev,深入了解时间实际花在哪里,而不会迷失在度量中。
另一个技巧是,可以将它应用于具有固定线程数的同一进程。所以只有1个进程,2个进程,3个进程等等,看看你的系统扩展得有多好。如果进程会受到cpu的限制,我希望性能几乎呈线性增长,直到达到50个内核左右。
ps:确保你的程序运行足够长的时间,因为jit需要时间来预热。所以一定要至少跑几分钟。