为了练习 Java 8 流,我尝试将以下嵌套循环转换为 Java 8 流 API。它计算 a^b (a,b < 100) 的最大数字总和,并在我的 Core i5 760 上花费约 0.135 秒。
public static int digitSum(BigInteger x)
{
int sum = 0;
for(char c: x.toString().toCharArray()) {sum+=Integer.valueOf(c+"");}
return sum;
}
@Test public void solve()
{
int max = 0;
for(int i=1;i<100;i++)
for(int j=1;j<100;j++)
max = Math.max(max,digitSum(BigInteger.valueOf(i).pow(j)));
System.out.println(max);
}
我的解决方案,由于并行性,我预计会更快,实际上需要 0.25 秒(没有 0.19 秒parallel()
):
int max = IntStream.range(1,100).parallel()
.map(i -> IntStream.range(1, 100)
.map(j->digitSum(BigInteger.valueOf(i).pow(j)))
.max().getAsInt()).max().getAsInt();
我的问题
- 我做了正确的转换还是有更好的方法将嵌套循环转换为流计算?
- 为什么流变体比旧变体慢得多?
- 为什么parallel()语句实际上将时间从0.19s增加到0.25s?
我知道微基准是脆弱的,并行性只对大问题值得,但对于 CPU,即使是 0.1 秒也是永恒的,对吧?
更新
我使用 Eclipse Kepler 中的 Junit 4 框架进行测量(它显示了执行测试所花费的时间)。
我的 a,b<1000 而不是 100 的结果:
- 传统循环 186s
- 顺序流 193s
- 并行流 55s
更新 2
替换sum+=Integer.valueOf(c+"");
为sum+= c - '0';
(感谢彼得!)将并行方法缩短了 10 秒,使其达到 45 秒。没想到性能影响这么大!
此外,将并行度减少到 CPU 内核的数量(在我的情况下为 4 个)并没有起到太大作用,因为它只将时间减少到 44.8 秒(是的,它增加了 a 和 b=0,但我认为这不会影响性能很多):
int max = IntStream.range(0, 3).parallel().
.map(m -> IntStream.range(0,250)
.map(i -> IntStream.range(1, 1000)
.map(j->.digitSum(BigInteger.valueOf(250*m+i).pow(j)))
.max().getAsInt()).max().getAsInt()).max().getAsInt();