2016-04-20 2 views
0

Ich versuche, eine große Liste in zwei Aufgaben zu teilen und einige Verarbeitung durchzuführen und später zusammenzuführen. Im folgenden Test fand ich heraus, serielle Verarbeitung ist viel schneller als parallel (oder) mache ich etwas falsch. ist unter dem Ergebnis,Java ExecutorService langsamer als seriell bei Verwendung von ArrayList ist es?

sh-4.3$ java -Xmx128M -Xms16M FutureTaskDemo

I =2 
Sequential Result   : 2000000 calculated in 2339 ms 
First Value:2 List Value:2000001       
Initial List First Value:2 List Value:2000001    
I =1000004             
I =4 
1000000 
1000000             
Merging             
Parallel Result   : 2000000 calculated in 3207 ms 
First Value:4 List Value:2000003        

Unten ist der Code,

import java.util.ArrayList; 

import java.util.ArrayList; 
import java.util.Calendar; 
import java.util.List; 
import java.util.concurrent.Callable; 
import java.util.concurrent.ExecutionException; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.FutureTask; 

public class FutureTaskDemo { 

/** 
* Maximum amount of numbers to check 
*/ 
public static final int MAX_NUMBER = 2_000_000; 

public static List<Integer> addOneToListS(List<Integer> list) { 
Integer flag=0; 
for(Integer i = 0;i<list.size();i++) 
{ 
    if(flag==0) System.out.println("I ="+(list.get(i)+2)); 
    list.set(i, list.get(i) +2); 
    flag=1; 
} 

    return list; 
} 

public static List<Integer> addOneToListP(List<Integer> list) 
     throws InterruptedException, ExecutionException { 

    List<Integer> listA = new ArrayList<>(list.subList(0,MAX_NUMBER/2)); 
    List<Integer> listB = new ArrayList<>(list.subList(MAX_NUMBER/2,MAX_NUMBER)); 

    // Prepare to execute and store the Futures 
    int threadNum = 2; 
    ExecutorService executor = Executors.newFixedThreadPool(threadNum); 
    List<FutureTask<List<Integer>>> taskList = new ArrayList<FutureTask<List<Integer>>>(); 

    // Start thread for the first half of the numbers 
    FutureTask<List<Integer>> futureTask_1 = new FutureTask<List<Integer>>(new Callable<List<Integer>>() { 
     @Override 
     public List<Integer> call() { 
      return FutureTaskDemo.addOneToListS(listA); 
     } 
    }); 
    taskList.add(futureTask_1); 
    executor.execute(futureTask_1); 

    // Start thread for the second half of the numbers 
    FutureTask<List<Integer>> futureTask_2 = new FutureTask<List<Integer>>(new Callable<List<Integer>>() { 
     @Override 
     public List<Integer> call() { 
      return FutureTaskDemo.addOneToListS(listB); 
     } 
    }); 
    taskList.add(futureTask_2); 
    executor.execute(futureTask_2); 

    // Wait until all results are available and combine them at the same time 
Integer totalsize = 0; 
    for (int j = 0; j < threadNum; j++) { 
     FutureTask<List<Integer>> futureTask = taskList.get(j); 
     System.out.println(futureTask.get().size()); 
    } 
    executor.shutdown(); 
    System.out.println("Merging"); 
    List<Integer> fullList = new ArrayList<>(); 
    fullList.addAll(futureTask_1.get()); 
    fullList.addAll(futureTask_2.get()); 

    return fullList; 
} 

public static void main(String[] args) throws InterruptedException, ExecutionException { 

    // Sequential execution 
List<Integer> initialList = new ArrayList<>(); 
for(int i=0;i<MAX_NUMBER;i++) 
    initialList.add(i); 

    long timeStart = Calendar.getInstance().getTimeInMillis(); 
    List<Integer> addedList1 = FutureTaskDemo.addOneToListS(initialList); 
    long timeEnd = Calendar.getInstance().getTimeInMillis(); 
    long timeNeeded = timeEnd - timeStart; 
    System.out.println("Sequential Result   : " + addedList1.size()+ " calculated in " + timeNeeded + " ms"); 
    System.out.println("First Value:"+addedList1.get(0)+" List Value:"+addedList1.get(addedList1.size()-1)); 

    System.out.println("Initial List First Value:"+initialList.get(0)+" List Value:"+initialList.get(initialList.size()-1)); 
    timeStart = Calendar.getInstance().getTimeInMillis(); 
    List<Integer> addedList2 = FutureTaskDemo.addOneToListP(initialList); 
    timeEnd = Calendar.getInstance().getTimeInMillis(); 
    timeNeeded = timeEnd - timeStart; 
    System.out.println("Parallel Result   : " + addedList2.size()+ " calculated in " + timeNeeded + " ms"); 
    System.out.println("First Value:"+addedList2.get(0)+" List Value:"+addedList2.get(addedList2.size()-1)); 


} 

}

Antwort

0

Ich würde parallele Ströme verwenden

public static List<Integer> addOneToListP(List<Integer> list) { 
    return list.parallelStream().map(i -> i + 1).collect(Collectors.toList()); 
} 

Das ist viel kürzer, einfacher und einfacher zu verstehen. Ob es schneller geht, hängt davon ab, ob das, was Sie tun, beschleunigt wird, indem Sie mehr CPUs haben, oder der Aufwand, mehrere Threads zu verwenden und Daten zwischen ihnen zu übertragen, ist zu hoch. In Ihrer parallelen Version wird viel von den Daten kopiert.

Ihr größter Overhead ist wahrscheinlich die sehr hohe Rate von Müll, die Sie erstellen. Ich würde sehen, was mit einer Sammlung ohne so viel Müll passiert.

public static int[] addOneToListP(int[] array) { 
    return IntStream.of(array).parallel().map(i -> i + 1).toArray(); 
} 

Dies sollte einen Bruchteil des Speichers verwenden und ein wenig schneller sein, mit oder ohne mehrere CPUs.

Hinweis: Ihr addOneToListS scheint 2 hinzuzufügen, was verwirrend ist.

+0

Vielen Dank Peter für die Beantwortung. In dem obigen Programm ist das Hinzufügen von 2 die einzige Verarbeitung, die ich gerade mache. Versucht mit parallelen Streams, die Ergebnisse waren ähnlich. Primitive Ganzzahl-Arrays arbeiten sehr schnell. Da dies eine Übung iam tut, um zu sehen, ob Parallelität besser ist, wenn ich die MAX_NUMBERS auf 10 Millionen änderte, zeigen Ergebnisse noch serielle Verarbeitung ist besser. Mein ursprüngliches Problem ist, ich habe rund 50k interne HTML-Links in einer Liste und zu scannen und zu finden & Keywords verwendet oder nicht. Die serielle Verarbeitung ist sehr langsam. Also dachte ich daran, Liste 32 zu teilen, da 32 Kerne verfügbar waren. –

+0

@ bobby.dreamer die Kosten der Verschiebung von Daten zwischen Threads ist nicht trivial. Im HTML-Link-Beispiel sind die Kosten für die Weitergabe der URL im Vergleich zu den Kosten der HTTP-Anfrage trivial, sodass Sie eine gute Geschwindigkeit sehen sollten. Stellen Sie sich vor, Sie würden viel Zeit darauf verwenden, auf die Anfragen zu warten. Sie könnten feststellen, dass 32 Kerne 64 Threads verarbeiten können, vor allem Hyper-Threading und vielleicht sogar 100 oder 200 Threads, je nachdem, wie lange sie warten. –

Verwandte Themen