2016-01-21 1 views
1

Несмотря на «разогрев» с помощью повторений и добавленную к алгоритму сложность, parallelSetAll() выглядит здесь медленнее. Я на самом деле не пытаюсь микро-бенчмарк здесь, просто грубо чувствую, что происходит.Попытка сопоставить Java 8 parallelSetAll() vs setAll()

import java.util.*; 
import java.time.*; 

public class SlowParallelSetAll { 
    static final int SIZE = 20_000_000; 
    static long timeIt(Runnable test) { 
    Instant start = Instant.now(); 
    test.run(); 
    long millis = Duration.between(start, Instant.now()).toMillis(); 
    System.out.println(millis); 
    return millis; 
    } 
    public static void main(String[] args) { 
    int reps = 10; 
    long[] la = new long[SIZE]; 
    for(int i = 0; i < reps; i++) 
     timeIt(() -> Arrays.setAll(la, n -> n * n * 11 + n * 7)); 
    System.out.println("###"); 
    for(int i = 0; i < reps; i++) 
     timeIt(() -> Arrays.parallelSetAll(la, n -> n * n * 11 + n * 7)); 
    } 
} 
/* Output: 
38 
37 
35 
34 
35 
34 
35 
34 
34 
35 
### 
52 
42 
43 
44 
46 
46 
44 
44 
43 
43 
*/ 

Алгоритм (лямбда-выражение) должен быть независимым, как она опирается только на значении индекса n, и, следовательно, кажется, что он должен быть легко параллелизуемым.

Перемещение вызовов вокруг, чередуя оба подхода и т. Д., Дает разные результаты, но также кажется, что здесь есть что-то большее, чем просто микро-бенчмаркинг. Во-первых, я ожидал больших временных разниц между нормальной и параллельной версиями. Кроме того, кажется, что можно споткнуться о ситуациях, когда параллельная версия может показаться правильным выбором, но нормальная версия будет более подходящей. В основном я ищу некоторые сведения об этом --- в том числе есть ли простой способ показать, что то, что я вижу, является чисто феноменом микро-бенчмаркинга.

Для чего это стоит, здесь оно переписано для использования System.nanoTime() и просто чередует тесты. Эти результаты кажутся разумными, хотя весь вопрос microbenchmarking обескураживает:

import java.util.*; 
import java.time.*; 
import java.util.concurrent.*; 

public class SlowParallelSetAll { 
    static final int SIZE = 20_000_000; 
    static long timeIt(Runnable test) { 
    long start = System.nanoTime(); 
    test.run(); 
    long delta = System.nanoTime() - start; 
    long millis = TimeUnit.NANOSECONDS.toMillis(delta); 
    System.out.println(millis); 
    return millis; 
    } 
    public static void main(String[] args) { 
    int reps = 10; 
    long[] la = new long[SIZE]; 
    for(int i = 0; i < reps; i++) { 
     timeIt(() -> Arrays.parallelSetAll(la, n -> n * n * 11 + n * 7)); 
     timeIt(() -> Arrays.setAll(la, n -> n * n * 11 + n * 7)); 
    } 
    } 
} 
/* Output: 
41 
74 
41 
73 
41 
67 
40 
67 
40 
67 
41 
67 
41 
67 
40 
67 
40 
67 
40 
67 
*/ 
+0

сколько медленнее? попробуйте изменить порядок вызовов и выполнить вызовы поочередно, а затем отменить этот порядок. Вы часто будете получать разные результаты. – JimmyJames

+0

Какую систему вы выполняете? Например, если у вас есть только один процессор, вы бы ожидали, что параллельный алгоритм будет всегда медленнее, чем серийная версия, поскольку вы платите параллелизм накладные расходы без каких-либо преимуществ. – jacobm

+1

Измерение времени с помощью Instant это, вероятно, не такая хорошая идея. Вы должны использовать нанотим. Или, что еще лучше, используйте надлежащий бенчмаркинг, например, jmh. – assylias

ответ

2

Используя JMH, я получаю следующие результаты (размер длина массива). Счет - это время выполнения за звонок, в микросекундах (меньше = лучше).

Benchmark      (size) Mode Cnt  Score  Error Units 
SO34929316.parallelSetAll   1 avgt 20  0.077 ±  0.003 us/op 
SO34929316.parallelSetAll  1000 avgt 20  9.935 ±  0.478 us/op 
SO34929316.parallelSetAll  100000 avgt 20  56.024 ±  7.008 us/op 
SO34929316.parallelSetAll 1000000 avgt 20  518.495 ± 75.331 us/op 
SO34929316.parallelSetAll 10000000 avgt 20 5640.574 ± 139.324 us/op 
SO34929316.setAll     1 avgt 20  0.016 ±  0.001 us/op 
SO34929316.setAll    1000 avgt 20  1.257 ±  0.023 us/op 
SO34929316.setAll    100000 avgt 20  116.760 ±  3.116 us/op 
SO34929316.setAll   1000000 avgt 20 1168.868 ± 42.153 us/op 
SO34929316.setAll   10000000 avgt 20 12347.399 ± 766.931 us/op 

Как и следовало ожидать, parallelSetAll быстрее для больших массивов и медленнее, для небольших массивов. Однако коэффициент ускорения только ~ x2 в соответствии с тестом (работает на Win 8.1/Xeon 2630 v2/6 ядер (x2)).


Для справки, код:

@State(Scope.Thread) 
@BenchmarkMode(Mode.AverageTime) 
public class SO34929316 { 

    @Param({"1", "1000", "100000", "1000000", "10000000"}) int size; 
    long[] array; 

    @Setup(Level.Iteration) 
    public void setup(){ 
    array = new long[size]; 
    } 

    @Benchmark 
    public void setAll(Blackhole bh) { 
    Arrays.setAll(array, n -> n * n * 11 + n * 7); 
    bh.consume(array); 
    } 

    @Benchmark 
    public void parallelSetAll(Blackhole bh) { 
    Arrays.parallelSetAll(array, n -> n * n * 11 + n * 7); 
    bh.consume(array); 
    } 
} 
+1

При показе параллельных тестов неплохо также сказать, что системы, на которой вы работаете (ОС, чип, ядра). –

+1

@BrianGoetz сделано – assylias

+0

@assylias большое спасибо за это; это определенно помогает моему мышлению в этом вопросе. – user1677663

Смежные вопросы