Чтобы попрактиковаться в потоках Java 8, я попытался преобразовать следующий вложенный цикл в потоковый API Java 8. Он вычисляет наибольшую сумму цифр a^b (a,b ‹ 100) и занимает ~0,135 с на моем 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 с?
Я знаю, что микробенчмарки хрупки, и параллелизм стоит того только для больших задач, но для процессора даже 0,1 с — это вечность, верно?
Обновить
Я измеряю с помощью среды Junit 4 в Eclipse Kepler (показывает время, затраченное на выполнение теста).
Мои результаты для a,b‹1000 вместо 100:
- традиционная петля 186s
- последовательный поток 193s
- параллельный поток 55s
Обновление 2 Замена sum+=Integer.valueOf(c+"");
на sum+= c - '0';
(спасибо, Питер!) сократила целые 10 секунд параллельного метода, доведя его до 45 секунд. Не ожидал такого большого влияния на производительность!
Кроме того, уменьшение параллелизма до количества ядер ЦП (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();
sum+=Integer.valueOf(c+"");
наsum+= c - '0';
, так будет намного быстрее. - person Peter Lawrey   schedule 23.02.2014digitSum
потоком, используя методCharSequence.chars()
. Это позволяет избежать выделения массива символов. - person Stuart Marks   schedule 23.02.2014