【问题标题】:What really is to “warm up” threads on multithreading processing?在多线程处理中真正“热身”线程是什么?
【发布时间】:2015-03-25 16:27:41
【问题描述】:

我正在处理 Java 中的多线程,正如有人向我指出的那样,我注意到线程会升温,也就是说,它们会随着重复执行而变得更快。我想了解为什么会发生这种情况,以及它是否与 Java 本身有关,或者它是否是每个多线程程序的常见行为。

示例代码(由 Peter Lawrey 编写)如下:

for (int i = 0; i < 20; i++) {
    ExecutorService es = Executors.newFixedThreadPool(1);
    final double[] d = new double[4 * 1024];
    Arrays.fill(d, 1);
    final double[] d2 = new double[4 * 1024];
    es.submit(new Runnable() {
    @Override
    public void run() {
        // nothing.
    }
    }).get();
    long start = System.nanoTime();
    es.submit(new Runnable() {
    @Override
    public void run() {
        synchronized (d) {
            System.arraycopy(d, 0, d2, 0, d.length);
        }
    }
    });
    es.shutdown();
    es.awaitTermination(10, TimeUnit.SECONDS);
    // get a the values in d2.
    for (double x : d2) ;
    long time = System.nanoTime() - start;
    System.out.printf("Time to pass %,d doubles to another thread and back was %,d ns.%n", d.length, time);
}

结果:

Time to pass 4,096 doubles to another thread and back was 1,098,045 ns.
Time to pass 4,096 doubles to another thread and back was 171,949 ns.
 ... deleted ...
Time to pass 4,096 doubles to another thread and back was 50,566 ns.
Time to pass 4,096 doubles to another thread and back was 49,937 ns.

即它变得更快并稳定在 50 ns 左右。这是为什么呢?

如果我运行此代码(20 次重复),然后执行其他操作(假设对先前结果进行后处理并为另一轮多线程做准备),然后在同一 ThreadPool 上执行相同的 Runnable 再重复 20 次,反正已经预热了?

在我的程序中,我只在一个线程中执行Runnable(实际上我拥有的每个处理核心一个,它是一个 CPU 密集型程序),然后交替执行其他一些串行处理多次。随着程序的进行,它似乎并没有变得更快。也许我可以找到一种方法来加热它……

【问题讨论】:

    标签: java multithreading warm-up


    【解决方案1】:

    与 JVM 相比,预热的不是线程。

    JVM 具有所谓的 JIT(即时)编译功能。当程序运行时,它会分析程序中发生的事情并动态优化它。它通过获取 JVM 运行的字节码并将其转换为运行速度更快的本机代码来实现这一点。它可以以最适合您当前情况的方式执行此操作,因为它通过分析实际运行时行为来执行此操作。这可以(不总是)导致很大的优化。甚至比一些在没有这些知识的情况下编译为本机代码的程序更是如此。

    您可以在http://en.wikipedia.org/wiki/Just-in-time_compilation阅读更多内容

    当代码加载到 CPU 缓存中时,您可以在任何程序上获得类似的效果,但我相信这将是一个较小的差异。

    【讨论】:

    • 感谢您的解释,@rfeak。但是你认为编译器能优化我的程序吗? (请阅读我添加到问题中的最后一段)
    • JIT 编译器只能做这么多,而且只会影响 CPU 时间。如果您的串行过程涉及任何类型的 IO,编译器几乎无能为力。我建议分析你的程序,看看时间花在了哪里,然后如果你需要更高的性能,就解决那里最大的瓶颈。
    • 其实它没有任何IO。我给出初始条件,程序自行运行。我解决了一些特殊的微分方程。
    • @usoouindio - 如果这只是 CPU 限制,JIT 编译器应该显示一些好处。不过,我坚持我的建议。如果您需要更快的速度,请使用分析器找到瓶颈并对其进行攻击。
    【解决方案2】:

    我看到线程执行最终可以更快的唯一原因是:

    • 内存管理器可以重用已分配的对象空间(例如,让堆分配填满可用内存,直到达到最大内存 - Xmx 属性)

    • 工作集在硬件缓存中可用

    • 重复操作可能会创建操作,编译器可以更轻松地重新排序以优化执行

    【讨论】:

    • 这些原因与是Java还是其他语言无关?
    • 是和不是。许多编程语言确实重用对象空间进行重新分配,而JIT(即时编译)优化主要针对 JVM/.NET 语言。硬件缓存在所有平台上都是通用的。
    猜你喜欢
    • 2021-11-02
    • 2011-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多