【问题标题】:Java's Lambda Stream Filter Count is Slower than For and Foreach LoopJava 的 Lambda 流过滤器计数比 For 和 Foreach 循环慢
【发布时间】:2014-08-14 10:41:02
【问题描述】:

我对 Java 的新功能 Lambda 非常感兴趣。除了提供简洁明了的代码外,它还通过使用 Stream 而不是创建对象来提高性能。

我创建了一个简单的测试来创建一堆随机数,然后计算其中有多少大于 49。我很惊讶常规的 for 和 foreach 循环提供了更好的性能。

这是我使用的代码:

    long numberOfData = 20000000;

    Random random = new Random();
    IntStream intStream = random.ints(0, 100);
    List<Integer> rand = intStream.limit(numberOfData)
                                  .boxed()
                                  .collect(Collectors.toList());

    // Iterate using "Lambda"
    OffsetTime startL = OffsetTime.now();

    long countL = rand.stream()
                    .filter(x -> x > 49)
                    .count();

    OffsetTime endL = OffsetTime.now();
    Duration durationL = Duration.between(startL, endL);

    System.out.println("[Lambda ] " + countL + " / " + numberOfData 
                     + " in " + durationL.toMillis() + "ms");

    // Iterate using "Foreach"
    int countFE = 0;
    OffsetTime startFE = OffsetTime.now();
    for (int aNumber : rand) {
        if (aNumber > 49) {
            countFE++;
        }
    }
    OffsetTime endFE = OffsetTime.now();
    Duration durationFE = Duration.between(startFE, endFE);
    System.out.println("[Foreach] " + countFE + " / " + numberOfData
                    +  " in " + durationFE.toMillis() + "ms");

    // Iterate using "For"
    int countF = 0;
    int maxLoop = rand.size();
    OffsetTime startF = OffsetTime.now();
    for (int i = 0; i < maxLoop; i++) {
        if (rand.get(i) > 49) {
            countF++;
        }
    }
    OffsetTime endF = OffsetTime.now();
    Duration durationF = Duration.between(startF, endF);
    System.out.println("[For    ] " + countF + " / " + numberOfData
                    + " in " + durationF.toMillis() + "ms");

首次运行结果:

[Lambda ] 10002783 / 20000000 in 325ms
[Foreach] 10002783 / 20000000 in 296ms
[For    ] 10002783 / 20000000 in 195ms

第二次运行结果(以此类推):

[Lambda ] 10000408 / 20000000 in 330ms
[Foreach] 10000408 / 20000000 in 304ms
[For    ] 10000408 / 20000000 in 202ms

注意:我使用的是在 Eclipse Luna 4.4.0 for Windows 上运行的 JDK 1.8.0_11。都是 32 位的。


我的问题是:

  1. 我的测试有问题吗?
  2. Lambda 的流是否只对涉及多个集合的操作带来好处?

【问题讨论】:

  • 任何少于一秒的 Java 基准测试都是无用的。使用 jmh 或卡尺。
  • 并多次运行测试,这样任何热点优化和一次性类加载都不会影响结果。
  • 为什么你决定非并行流比旧的 for 循环更快?理想情况下,它们不会变慢。见stackoverflow.com/questions/22658322/…
  • Lambda 的 count() 方法返回 long 类型的值,但在其他测试中,您使用 int 类型的计数器。如您所知,Java 中 long 是 8 个字节,int 是 4 个字节。为了使测试更精确,将所有类型的计数器设置为 long。
  • 已更改为 long,但 STREAM 仍然慢得多(331 毫秒)。有趣的是,在从 int 移动到 long FOREACH 之后,FOREACH 一点也不慢(65ms),但 FOR 损失了 30%(55ms -> 75ms)。我在一个循环中运行整个基准测试,所以 JVM 预热是无关紧要的。

标签: java performance loops filter lambda


【解决方案1】:

我认为有两个问题: 1.编译IntPredicate耗费大量时间。解决方案在过滤函数中声明为 IntPredicate IntPredicate 谓词=x -> x > 49; long countL = rand.stream() .filter(谓词) 。数数(); 2.下一个问题是count()函数很慢。但我不知道解决方案

【讨论】:

  • 声明谓词 谓词 = x -> x > 49;并且在循环外运行 .filter(predicate) 根本没有帮助。
猜你喜欢
  • 2014-01-21
  • 2013-06-02
  • 2021-09-02
  • 1970-01-01
  • 2022-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多