繁体   English   中英

为什么这个多线程并行程序执行接近顺序时间?

[英]Why does this multi threaded parallel program executes close to sequential time?

在下面的程序中,我安排了少量线程,每个线程执行一个受 CPU 限制的长时间运行的单线程计算,该计算不使用任何锁并使 Apple M1 的核心饱和。 我使用的线程少于物理内核。

该程序在CONCURRENCY=8时在 26 秒内执行 8 个线程,在CONCURRENCY=1时在 4 秒内执行 1 个线程。 我不明白这个巨大的差距。

机器上没有其他东西在运行。 温度低,插电。

我可以确认我看到 8 个内核在使用中,我看到这个 Java 进程的 CPU 占用率为790%

这里的计算 function 只是一个虚拟计算,不是我使用的实际计算,而是重现问题的东西。

我希望程序将花费大致相同的时间,无论我使用多少线程(例如,只要CONCURRENCY <= 8 ,并且我在 Apple M1 上有 10 个内核)。

然而,更多的线程需要更多的时间,并且它非常接近相同的性能(时间),就像我在一个内核上一个接一个地顺序执行计算 8 次一样。

事实上,我当然有不同的计算,这只是关于这个线程池使用的模式以及可能出错的地方。

将计算固定在核心上对此没有太大帮助。

i7 和 Apple M1 上的结果类型相同。

到目前为止,我只在 MBP 笔记本电脑上进行了测试。 在 Apple M1 上,由于即使在 1 个线程和 2 个线程之间也存在巨大差距,我认为热节流或功率节流不能发挥重要作用。


class ProblemWithParallelismTest {
    private static final int CONCURRENCY = 8;

    @SneakyThrows
    @Test
    void evaluate() {
        var latch = new CountDownLatch(CONCURRENCY);
        var executorService = newFixedThreadPool(CONCURRENCY);
        for (int m = 0; m < CONCURRENCY; m++) {
            executorService.submit(() -> {
                computation();
                latch.countDown();
            });
        }
        latch.await();
    }

    private static void computation() {
        long n = (long) Math.pow(10, 7);
        var sum = 0;
        for (long i = 0; i < n; i++) {
            sum += LongStream.range(1, 9).boxed().limit(n).map(l -> new BigDecimal(String.valueOf(l))).distinct().count();
        }
    }
}

看起来,当我将计算固定到内核时,上面的内容确实如预期的那样在线程中水平扩展,并且时间从 1 个内核到 8 个内核是相同的。

这不会发生在原始计算上,但会发生在这个计算上。 我无法分享原始计算。

所以这证明节流不是原因。

原始计算确实占用了 790% 的 CPU(8 核),并且没有锁定问题。

在原始计算中,使用固定和所有,仍然

    1 thread  => 5s  physical time
    2 threads => 10s physical time
    4 threads => 34s physical time
    8 threads => 83s physical time

这是水平扩展的程序(从 1 个线程执行到 8 个线程的时间相同),带有虚拟计算:

final class PinnedThreadsParallelismTest {
    private static final int CONCURRENCY = 8;

    public static void main(String[] args) throws InterruptedException {
        var latch = new CountDownLatch(CONCURRENCY);
        var executorService = newFixedThreadPool(CONCURRENCY);
        for (int m = 0; m < CONCURRENCY; m++) {
            executorService.submit(() -> {
                try (var lock = AffinityLock.acquireLock()) {
                    if (lock.cpuId() > 0) {
                        System.out.println("locked on " + lock.cpuId());
                        computation();
                        latch.countDown();
                        System.out.println("countDown");
                    } else {
                        latch.countDown();
                    }
                }
            });
        }
        latch.await();
        System.exit(0);
    }

    private static void computation() {
        long n = (long) Math.pow(10, 7);
        var sum = 0;
        for (long i = 0; i < n; i++) {
            sum += LongStream.range(1, 9).boxed().limit(n).map(l -> new BigDecimal(String.valueOf(l))).distinct().count();
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM