当前位置: 首页 > 知识库问答 >
问题:

为什么使用流的代码在Java9中的运行速度比Java8快得多?

柳越
2023-03-14

这是我在解决欧拉项目205题时发现的。问题如下:

彼得有九个四面(金字塔形)骰子,每个骰子的面编号为1、2、3、4。科林有六个六面(立方体)骰子,每个骰子的面编号为1、2、3、4、5、6。

彼得和科林掷骰子比较总数:总数最高的赢。如果总数相等,结果是平局。

金字塔皮特击败立方体科林的概率是多少?将答案四舍五入至小数点后七位,格式为0.abcdefg

我用Guava写了一个简单的解决方案:

import com.google.common.collect.Sets;
import com.google.common.collect.ImmutableSet;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

public class Problem205 {
    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        List<Integer> peter = Sets.cartesianProduct(Collections.nCopies(9, ImmutableSet.of(1, 2, 3, 4)))
                .stream()
                .map(l -> l
                        .stream()
                        .mapToInt(Integer::intValue)
                        .sum())
                .collect(Collectors.toList());
        List<Integer> colin = Sets.cartesianProduct(Collections.nCopies(6, ImmutableSet.of(1, 2, 3, 4, 5, 6)))
                .stream()
                .map(l -> l
                        .stream()
                        .mapToInt(Integer::intValue)
                        .sum())
                .collect(Collectors.toList());

        long startTime2 = System.currentTimeMillis();
        // IMPORTANT BIT HERE! v
        long solutions = peter
                .stream()
                .mapToLong(p -> colin
                        .stream()
                        .filter(c -> p > c)
                        .count())
                .sum();

        // IMPORTANT BIT HERE! ^
        System.out.println("Counting solutions took " + (System.currentTimeMillis() - startTime2) + "ms");

        System.out.println("Solution: " + BigDecimal
                .valueOf(solutions)
                .divide(BigDecimal
                                .valueOf((long) Math.pow(4, 9) * (long) Math.pow(6, 6)),
                        7,
                        RoundingMode.HALF_UP));
        System.out.println("Found in: " + (System.currentTimeMillis() - startTime) + "ms");
    }
}

我所强调的代码,它使用了一个简单的filter()count()sum(),似乎在Java9中运行的速度比Java8快得多。具体来说,Java8在我的机器上以37465ms的速度计算解决方案。Java9在大约16000ms内完成,这与我运行用Java8编译的文件还是用Java9编译的文件是一样的。

如果我将流代码替换为看起来完全相同的预流代码:

long solutions = 0;
for (Integer p : peter) {
    long count = 0;
    for (Integer c : colin) {
        if (p > c) {
            count++;
        }
    }
    solutions += count;
}

它在大约35000ms内计算溶液,Java8和Java9之间没有可测量的差异。

我错过了什么?为什么Java 9中的streams代码要快得多,为什么< code>for循环不是这样?

我运行的是64位Ubuntu 16.04 LTS。我的Java 8版本:

java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)

我的Java 9版本:

java version "9"
Java(TM) SE Runtime Environment (build 9+181)
Java HotSpot(TM) 64-Bit Server VM (build 9+181, mixed mode)

共有1个答案

赫连泰宁
2023-03-14

流。count()实现在JDK8中相当愚蠢:它只是在整个流中迭代,为每个元素添加1L

这在JDK 9中得到了修复。即使错误报告提到了SIZED流,新代码也改进了非大小流。

如果您将. count()替换为Java8风格的实现. mapToLong(e-

当您将所有代码都放在< code>main方法中时,就无法有效地进行JIT编译。这个方法只执行一次,它开始在解释器中运行,然后,当JVM检测到一个热循环时,它从解释模式切换到动态编译模式。这称为堆栈上替换(OSR)。

OSR编译通常不如常规编译方法优化。我已经在前面详细解释过了,看看这个和这个答案。

如果您将内部循环放在单独的方法中,JIT将产生更好的代码:

    long solutions = 0;
    for (Integer p : peter) {
        solutions += countLargerThan(colin, p);
    }

    ...

    private static int countLargerThan(List<Integer> colin, int p) {
        int count = 0;
        for (Integer c : colin) {
            if (p > c) {
                count++;
            }
        }
        return count;
    }

在这种情况下,计数LargerThan方法将正常编译,并且性能将比JDK 8和JDK 9上的流更好。

 类似资料:
  • 问题内容: 我发现这一点的同时解决问题205的项目欧拉。问题如下: 彼得有九个四面(金字塔形)骰子,每个骰子的编号分别为1、2、3、4。科林有六个六边形(立方)骰子,每个骰子的编号为1、2、3、4、5、6。 彼得和科林掷骰子并比较总数:最高的总胜利数。如果总数相等,则结果为平局。 金字塔形皮特击败立方柯林的概率是多少?以0.abcdefg的形式将答案四舍五入到小数点后七个位 我用番石榴写了一个幼稚

  • 我最近用Java写了一个计算密集型算法,然后把它翻译成C++。令我吃惊的是,C++的执行速度要慢得多。我现在已经编写了一个更短的Java测试程序,以及一个相应的C++程序-参见下面。我的原始代码具有大量的数组访问功能,测试代码也是如此。C++的执行时间要长5.5倍(请参阅每个程序末尾的注释)。 以下1st21条评论后的结论... null null Java代码: C++代码:

  • 每个人都说Jave是一种用于大型系统和工程项目的语言,它的运行速度比大多数其他语言快得多。我只是将它与另一个基于代理的建模lanugage(Netlogo)进行了比较,发现Netlog的运行速度是经典狼羊模拟模型中基于Jave的Repast的四倍。两个模型使用相同的参数进行模拟,并实时运行5秒。Netlogo可以模拟8000多个时间步,而Jave Repast只能执行大约2600个时间步。为什么?

  • 我写了一些欧拉问题35的代码: 我想知道为什么这个代码(上图)运行得这么快,当我设置在函数中。这段代码的运行时间约为8秒。最初,我没有设置,我的函数是这样的: 我的初始代码(没有)运行了很长时间,我没有等它完成。我很好奇,为什么这两段代码在运行时间上存在如此大的差异,因为我不相信会有任何重复项,这会使迭代花费如此长的时间,以至于迭代。也许我对set()的想法是错误的。欢迎任何帮助。

  • 考虑到我有2个CPU核心的事实,并行版本不是应该更快吗?有人能给我一个提示为什么并行版本比较慢吗?

  • 问题内容: Java很慢。 这似乎不仅仅是一个“城市传奇”。由于延迟,您不将其用于实时编码,也不将其用于集群/并行计算。有成千上万的基准测试,特别是“ Java vs C#vs C ++”。 http://benchmarksgame.alioth.debian.org/ 根据以上站点,不仅Java性能几乎与C一样好(远远没有C),而且Scala和Clojure(两种在JVM上运行的功能语言)都具