我在解决问题205 of Euler项目时发现了这个问题。问题如下:
彼得有九个四面(金字塔)骰子,每个骰子都有编号为1,2,3,4的面。科林有六面(立方)骰子,每个骰子的号码是1,2,3,4,5,6。 彼得和科林掷骰子比较总数:最高的总胜利。如果总数相等,则结果是平局。 金字塔皮特打败立方科林的概率是多少?给你的答案四舍五入到小数点后的7位,表格0. 0.abcdefg
我用番石榴编写了一个简单的解决方案:
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中运行速度似乎比Java 8快得多。具体来说,Java8在我的机器上计算的是37465 my的解决方案。Java 9是在大约16000 is内完成的,无论我运行的是用Java 8编译的文件还是用Java 9编译的文件。
如果我将streams代码替换为似乎与流前等效的代码:
long solutions = 0;
for (Integer p : peter) {
long count = 0;
for (Integer c : colin) {
if (p > c) {
count++;
}
}
solutions += count;
}它以大约35000毫秒计算解决方案,Java 8和Java 9之间没有可测量的差别。
我在这里错过了什么?为什么流代码在Java9中要快得多,为什么for循环不是呢?
我正在运行Ubuntu16.04LTS 64位.我的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)发布于 2017-10-09 00:44:12
1.为什么流在JDK 9上运行得更快?
Stream.count()实现是JDK 8中的相当愚蠢:它只是遍历整个流,为每个元素添加1L。
这是JDK 9中的固定,尽管bug报告提到了大小流,但是新代码也改进了非大小流。
如果您用Java8风格的实现.count()替换.mapToLong(e -> 1L).sum(),那么即使在JDK 9上,它也会再次慢下来。
2.为什么朴素循环工作慢?
当您将所有代码放入main方法中时,它无法高效编译。此方法只执行一次,在解释器中开始运行,然后,当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;
}在这种情况下,countLargerThan方法将被正常编译,性能将优于JDK 8和JDK 9上的流。
https://stackoverflow.com/questions/46615484
复制相似问题