Чтобы тренировать потоки Java 8, я попытался преобразовать следующий вложенный цикл в API потока Java 8. Он вычисляет наибольшую сумму суммы a ^ b (a, b < 100) и принимает ~ 0.135s на моем Core i5 760.
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,19 до 0,25 с?
Я знаю, что microbenchmarks хрупкие, а parallelism стоит только для больших проблем, но для CPU, даже 0.1s - это вечность, правильно?
Обновление
Я измеряю фреймворком Junit 4 в Eclipse Kepler (он показывает время, затраченное на выполнение теста).
Мои результаты для a, b < 1000 вместо 100:
- традиционный цикл 186s
- последовательный поток 193s
- параллельный поток 55s
Обновление 2
Заменив sum+=Integer.valueOf(c+"");
на sum+= c - '0';
(спасибо Peter!), Сбрил 10 целых секунд параллельного метода, доведя его до 45 секунд. Не ожидал такого большого эффекта!
Кроме того, сокращение числа parallelism до количества ядер процессора (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();