Sono nuovo di Java 8. Non conosco ancora in modo approfondito l'API, ma ho fatto un piccolo benchmark informale per confrontare le prestazioni della nuova API Streams rispetto alle vecchie vecchie Collezioni.
Il test consiste nel filtrare un elenco di Integer
, e per ogni numero pari, calcolare la radice quadrata e memorizzarla in un risultato List
di Double
.
Ecco il codice:
public static void main(String[] args) {
//Calculating square root of even numbers from 1 to N
int min = 1;
int max = 1000000;
List<Integer> sourceList = new ArrayList<>();
for (int i = min; i < max; i++) {
sourceList.add(i);
}
List<Double> result = new LinkedList<>();
//Collections approach
long t0 = System.nanoTime();
long elapsed = 0;
for (Integer i : sourceList) {
if(i % 2 == 0){
result.add(Math.sqrt(i));
}
}
elapsed = System.nanoTime() - t0;
System.out.printf("Collections: Elapsed time:\t %d ns \t(%f seconds)%n", elapsed, elapsed / Math.pow(10, 9));
//Stream approach
Stream<Integer> stream = sourceList.stream();
t0 = System.nanoTime();
result = stream.filter(i -> i%2 == 0).map(i -> Math.sqrt(i)).collect(Collectors.toList());
elapsed = System.nanoTime() - t0;
System.out.printf("Streams: Elapsed time:\t\t %d ns \t(%f seconds)%n", elapsed, elapsed / Math.pow(10, 9));
//Parallel stream approach
stream = sourceList.stream().parallel();
t0 = System.nanoTime();
result = stream.filter(i -> i%2 == 0).map(i -> Math.sqrt(i)).collect(Collectors.toList());
elapsed = System.nanoTime() - t0;
System.out.printf("Parallel streams: Elapsed time:\t %d ns \t(%f seconds)%n", elapsed, elapsed / Math.pow(10, 9));
}.
E qui ci sono i risultati per una macchina dual core:
Collections: Elapsed time: 94338247 ns (0,094338 seconds)
Streams: Elapsed time: 201112924 ns (0,201113 seconds)
Parallel streams: Elapsed time: 357243629 ns (0,357244 seconds)
Per questo particolare test, i flussi sono circa due volte più lenti delle raccolte e il parallelismo non aiuta (o o lo sto usando nel modo sbagliato?).
Domande:
- Questo test è giusto? Ho fatto qualche errore?
- I flussi sono più lenti delle raccolte? Qualcuno ha fatto un buon punto di riferimento formale su questo?
- Quale approccio dovrei lottare?
Risultati aggiornati.
Ho eseguito il test 1k volte dopo il riscaldamento JVM (iterazioni 1k) come consigliato da @pveentjer:
Collections: Average time: 206884437,000000 ns (0,206884 seconds)
Streams: Average time: 98366725,000000 ns (0,098367 seconds)
Parallel streams: Average time: 167703705,000000 ns (0,167704 seconds)
In questo caso i flussi sono più performanti. Mi chiedo cosa verrebbe osservato in un'app in cui la funzione di filtro viene chiamata solo una o due volte durante il runtime.
toList
deve essere eseguito in parallelo anche se viene raccolto in un elenco non thread-safe, poiché i diversi thread verranno raccolti in elenchi intermedi limitati al thread prima di essere uniti.
IntStream
invece?