# Collection Multithreading?



## Cheefrocker (9. Okt 2007)

hallo zusammen!° 


Ich habe ein Collection. Momentan gehe ich hin und gehe jedes Element der collection durch und ruf die Methode Print von diesem Objekt auf. Das Ganze läuft in 1 Thread ab. 

Wie kann ich das jetzt beschleunigen? Die Anwendung kann beispielsweise auf 1,2 oder 4 CPUs laufen. 


Da die serielle Variante zu langsam ist wollte ich fragen wie ich diese jetzt paralelisieren kann?


----------



## tuxedo (9. Okt 2007)

Das Durchlaufen sollte doch schnell gehen. Wenn aber der Print-Aufruf zu viel Zeit braucht, kannst du diesen ja in einen Thread auslagern und während print noch ausgeführt wird in der Collection schon zum nächsten Eintrag springen.

- Alex


----------



## Cheefrocker (9. Okt 2007)

das problem ist, es macht nur sinn pro CPU 1 thread zu starten. bei 4 CPUS haben wir 4 nebenläufige Threads..  

ich weiss nicht wie icheine sperre einbaue....


----------



## tuxedo (9. Okt 2007)

Java weiß AFAIk nicht wieviele Prozessoren oder CPU-Kerne das System hat. Ergo hast du auch nicht die möglichkeit das zu beschränken. Außerdem wüsste ich nicht wieso du nur exakt einen Thread pro CPU haben willst. Sobald du eine Swing-GUI hast hast du eh mehr als einen Thread.

- Alex


----------



## Cheefrocker (9. Okt 2007)

also bringt das mir nichts ? ja die gui läuft, aber die verarbeitung benötigt in dem Fall keine Gui. Anzahl an CPUS würde ich wie folgt ermitteln:

java.lang.Runtime.getRuntime().availableProcessors()


----------



## tuxedo (9. Okt 2007)

Okay, wieder was dazugelernt. Aber effektiv bringen tut's dir nix wenn du pro Kern/CPU einen Thread nimmst. Was hindert dich daran einfach 10 oder mehr Threads drauf los zu lassen? Woher hast du deine "seltsame" Eingebung 1-Thread-pro-CPU? Hast du nicht mal dran gedacht dass dein Betriebssystem schon etliche Threads und Prozesse laufen hat? 

- Alex


----------



## -frank (9. Okt 2007)

hmm, probier doch einfach mal aus, ob es performancevorteile bringt. ich weiß nicht, ob sich deine collection während der print-aufgabe ändert bzw. wie es mit der reihenfolge aussieht, aber generell kannst du ja ganz einfach so viele threads erstellen wie prozessoren vorhanden sind und dann, wenn du zb 4 zur verfügung hast, 4 threads machen, die alle nur jedes 4. element printen. oder zb thread 1 von index 1-25 printen lassen, thread 2 von 26-50, etc.


----------



## -frank (9. Okt 2007)

alex0801 hat gesagt.:
			
		

> Okay, wieder was dazugelernt. Aber effektiv bringen tut's dir nix wenn du pro Kern/CPU einen Thread nimmst. Was hindert dich daran einfach 10 oder mehr Threads drauf los zu lassen? Woher hast du deine "seltsame" Eingebung 1-Thread-pro-CPU? Hast du nicht mal dran gedacht dass dein Betriebssystem schon etliche Threads und Prozesse laufen hat?
> 
> - Alex



naja, für das thread management entsteht ein gewisser overhead. auf einem 1 kern prozessor wirds wohl etwas langsamer werden, wenn er zb 100 threads erzeugt anstatt eines einzelnen. also für mich klingt das logisch. (bin aber kein experte, also wenn ihr welche seid, vergesst es  )


----------



## tuxedo (9. Okt 2007)

Klar, der Overhead entsteht. Aber dennoch macht es Sinn für solche Sachen mehr als 1 Thread pro CPU zu verwenden. Und auch sollte klar sein dass man nicht beliebig viele Threads einsetzen sollte. Weil zu viel des guten war noch nie gut (da kommt dann der Overhead richtig zum tragen da der Dispatcher nur noch mit switchen beschäftigt ist). 

Und noch ne kleine hypiothetische Frage am Rande: Wenn es so tragisch wäre mit dem Overhead und den Threads pro CPU: Wieso haben wir dann die ganzen vergangenen Jahre als wir Single-CPU Maschinen benutzt haben mit einem Multi-Threading/Tasking Betriebssystem gearbeitet?

- Alex


----------



## -frank (9. Okt 2007)

alex0801 hat gesagt.:
			
		

> Aber dennoch macht es Sinn für solche Sachen mehr als 1 Thread pro CPU zu verwenden.



warum denn?


----------



## -frank (9. Okt 2007)

alex0801 hat gesagt.:
			
		

> Und noch ne kleine hypiothetische Frage am Rande: Wenn es so tragisch wäre mit dem Overhead und den Threads pro CPU: Wieso haben wir dann die ganzen vergangenen Jahre als wir Single-CPU Maschinen benutzt haben mit einem Multi-Threading/Tasking Betriebssystem gearbeitet?



ist das nicht was anderes? ich meine, wir brauchen ja garnicht auf OS ebene zu gehen: selbst wenn ich nur will, dass irgendein wert alle paar sekunden überprüft wird, machts doch schon sinn, einen eigenen thread diese arbeit erledigen zu lassen. oder wie du ja selbst gesagt hast: wenns ne GUI gibt, hat man sowieso immer mehrere threads.
aber hier gings ja rein um die performance. wie kann es durch mehrere threads schneller werden?


----------



## tuxedo (9. Okt 2007)

Angenommen wir haben Thread A und Thread B

Beide solle so schnell wie möglich abgearbeitet werden.

Thread A hat Codezeilen die z.B. auf irgendwelche Ressourcen warten. Beispielsweise Netzwerk, Filesystem, ...

Thread B will nur was auf der Console ausgeben.

B braucht also (im normalfall) weniger Zeit als A.

Lässt man sie jetzt hintereinander laufen, addieren sich die Laufzeiten. Laufzeit von A + Laufzeit von B.

Lässt man beide gleichzeitig laufen so kommt man bei Laufzeit von A plus x raus, wobei x eine sehr kleine Zahl ist und für's switchen im Dispatcher und noch n bisschen Kleinkram steht.

Trotz dass die CPU immer nur eine Sache gleichzig tun kann, ist hier das Programm in der poarallelen verarbeitung mit Threads schneller weil der rest des Programms nicht von der "blockierenden Codezeile" in A ausgebremst wird. Die CPU hätte da eh nix zu tun, außer auf die Ressource zu warten. Und die Zeit kann man für andere Sachen, unter anderem B, nutzen.

Deshalb macht es Sinn trotz einer Single-CPU/Core Maschine mehrere Threads zu verwenden ...

- Alex


----------



## byte (9. Okt 2007)

-frank hat gesagt.:
			
		

> alex0801 hat gesagt.:
> 
> 
> 
> ...


Weil es immer wieder Operationen gibt, wo die CPU warten muss (z.B. bei IO). In diesem Fall kannst Du auch auf einer CPU mit Multithreading Performance gut machen. Der Overhead durch den Scheduler fällt vergleichsweise gering aus.
Man sollte aber nicht zu viele Threads erzeugen und diese lieber bei Bedarf wiederverwenden (Stichwort: ThreadPools im Concurrency Framework).


----------



## tuxedo (9. Okt 2007)

-frank hat gesagt.:
			
		

> wie kann es durch mehrere threads schneller werden?



Die CPU wird dadruch nicht schneller, aber ich kann Wartezeiten der CPU für andere Sachen ausnutzen. Und Wartezeiten hat man immer irgendwo. Manchmal mehr, manchmal weniger.

- Alex


----------



## -frank (9. Okt 2007)

alex0801 hat gesagt.:
			
		

> -frank hat gesagt.:
> 
> 
> 
> ...



alles klar. bei IO verstehe ich es. wie siehts aus wenn ich nur "intern" in meiner applikation arbeite --> also keine zugriffe auf das UserInterface, Eingabegeräte, Festplatten, etc. ich habe zb einfach nur einen Thread der irgendwelche zahlen ausrechnet. bringts dann trotzdem was?


----------



## tuxedo (9. Okt 2007)

geht ja nicht nur um das IO was einem sofort einfällt. Programme laufen hauptsächlich im RAM und auch da gibts zugriffszeiten. Okay, moderner Speicher hat da kleinste Nanosekunden. Aber der RAM wird ja auch noch vom OS/JVM verwaltet etc... Und das ist noch nicht das Ende vom Lied...

Hattest du Eingangs nicht irgendwas von "print" geschrieben? Auch das Ausdrucken auf der Console ist ein IO. Wenn du nur 10 Elemente in der Collection hast, dann wird's nicht viel bringen. Wenn du jetzt aber 100, 1000 oder noch mehr drin hast bringts sicher was. 

Ob's jetzt 10%, 35.24% oder 56.234% schneller läuft kann dir heir keiner sagen. Das musst du ausprobieren.

- Alex


----------



## Guest (9. Okt 2007)

Die erset Frage, die sich stellt, ist, wo und warum geht die Zeit verloren?
Was macht die Print-Methode? Console-Ausgabe?

Wenn ja, dann kannst du an dieser Stelle mit der Optimierung ansetzen, indem du
die Ausgabe in einen Puffer (SrtingBuilder/StringBuffer) umleitest und z.B. alle
100 Aufrufe von Print den Puffer ausgibst. Console-Ausgabe ist immer sehr langsam.

Es macht keinen Sinn etwas zu optimieren, solange man nicht die Ursache genauer
lokalisiert hat.


----------



## Cheefrocker (10. Okt 2007)

Anonymous hat gesagt.:
			
		

> Die erset Frage, die sich stellt, ist, wo und warum geht die Zeit verloren?
> Was macht die Print-Methode? Console-Ausgabe?
> 
> Wenn ja, dann kannst du an dieser Stelle mit der Optimierung ansetzen, indem du
> ...



Die Print-Methode erstellt aus einem Frame(Inhalts eines JeditorPane) ein Image. Dieses Image wird dann in Tiff umgewandelt. 

Detailierter:
Es wird in eine HTML-Seite in JeditorPane reingeladen. Der Inhalt des Jeditorpane wird dann als tiff abgespeichert. Dabei verwende ich Graphics2D




Die Print-Methode wird von 1-20000 mal oder mehr aufgerufen. 


11000 Dateien erstellen dauert ca 10 Stunden.


----------



## Cheefrocker (10. Okt 2007)

Die meiste Zeit geht beim umwandeln drauf.


----------



## Cheefrocker (10. Okt 2007)

Hier bissel Code:



```
private void saveTiff(Graphics2D g) {
        /* Ab hier Tiff speichern */
        //dummyJob.cancel();
        BufferedImage bi = new BufferedImage(g.getClipBounds().width,
                g.getClipBounds().height, BufferedImage.TYPE_BYTE_BINARY);
        bi.getGraphics().fillRect(0,0, g.getClipBounds().width , g.getClipBounds().height);
        Graphics2D g2 = (Graphics2D)bi.createGraphics().create(0, 0,
                g.getClipBounds().width , g.getClipBounds().height);
        /* Das Bild zeichnen */
        this.paint(g2);
        /* Das Bild auf DIN A4 größe skalieren */
        Image img = bi.getScaledInstance(2480, 3508, bi.SCALE_FAST);
        BufferedImage buf2 = new BufferedImage( 2480, 3508, BufferedImage.TYPE_BYTE_BINARY);
        /* Das skalierte Bild in eine neues Grafikobjekt zeichnen */
        buf2.createGraphics().drawImage( img, 0 ,0, this);
        SimpleReadImage sr = new SimpleReadImage();
        try {
            sr.writeCompressedImage(saveToFile, sr.COMPRESS_CCITT4, buf2);
        } catch ( Exception e) {
            System.out.println(e);
        }
    }
```


----------



## tuxedo (10. Okt 2007)

DAS würde ich mit Threads auslagern, nachdem ich es weitestgehend optimiert habe ...

- Alex


----------



## Cheefrocker (10. Okt 2007)

ja das ist ja leider mein Problem! Wie lagere ich das in mehrere Threads aus, sodass sich das vielleicht je nach Anzahl der Prozesorren scalieren lässt...

Zur Zeit hab ich 1 Thread. und 1Forschleife die nach einander Savetiff aufruft.


----------



## tuxedo (10. Okt 2007)

Naja, in der For-Schleife erstellst du nen Thread der im inneren die SafeTiff Methode aufruft und startest ihn ... Fertig.

Vielleicht hilfts wenn du dich ein wenig in das Thema einliest:

http://www.galileocomputing.de/open...09_001.htm#mj544b7c1df0f1e2c82ac44e2e42d49034

- Alex


----------



## Cheefrocker (10. Okt 2007)

Ich nutze noch Java 5. Aber es macht doch kein Sinn 11000 Threads oder mehr direkt nacheinander zu starten oder? Wäre nett wenn einer mir diese Frage beantworten kann.


----------



## maki (10. Okt 2007)

> Die Print-Methode wird von 1-20000 mal oder mehr aufgerufen.
> 
> 
> 11000 Dateien erstellen dauert ca 10 Stunden.


Teile die Collection in so viele kleinere Collections auf, wie es Prozessoren gibt.
Lass für jede Collection einen Thread laufen, dass garantiert zwar nicht, das wirklich jeder Thread einen eigenen Prozessor bekommt, aber zumindest hast du dann nicht mehr Threads als Prozessoren.

Anosnten wäre das eine Aufgabe für einen Batch die im Hintergrund läuft.


----------



## byte (10. Okt 2007)

Benutzt Du Java 5+ ? Dann ist der Fall prädestiniert für Executor/ ExecutorService aus dem Java Concurrency Framework. Obige Methode packst Du in ein Runnable. Dann holst Du Dir über Executors einen ThreadPool. Wenn Du pro CPU einen Thread haben willst, kannst Du z.B. Executors.newFixedThreadPool(anzahl) nehmen. Dem ExecutorService übergibst Du dann die Runnables zur Ausführung. Du brauchst nicht manuell Threads zu erzeugen. Der Service verwaltet alles selber.


----------



## tuxedo (10. Okt 2007)

Cheefrocker hat gesagt.:
			
		

> Aber es macht doch kein Sinn 11000 Threads oder mehr direkt nacheinander zu starten oder?



Hat ja auch niemand behauptet... Zumal wir das glaube ich schonmal hatten von wegen "mit zu vielen Threads ist der dispatcher nur noch am switchen"..

byto's Vorschlag klingt doch super ...


- Alex


----------



## Cheefrocker (10. Okt 2007)

werde ich so ausprobieren! Besten dank @byto und @alex


----------



## Cheefrocker (10. Okt 2007)

byto hat gesagt.:
			
		

> Benutzt Du Java 5+ ? Dann ist der Fall prädestiniert für Executor/ ExecutorService aus dem Java Concurrency Framework. Obige Methode packst Du in ein Runnable. Dann holst Du Dir über Executors einen ThreadPool. Wenn Du pro CPU einen Thread haben willst, kannst Du z.B. Executors.newFixedThreadPool(anzahl) nehmen. Dem ExecutorService übergibst Du dann die Runnables zur Ausführung. Du brauchst nicht manuell Threads zu erzeugen. Der Service verwaltet alles selber.



Noch eine Frage: ich habe  Methode in eine Runnable reinpacke. Wie baue ich die Schleife jetzt darein damit er immer andere Graphics2D Objekte laden kann?


----------



## byte (10. Okt 2007)

Schreib Dir doch ne Klasse implements Runnable, der Du ein G2D im Konstruktor übergibst. Dann erzeugst Du nach und nach je Grafik ein Runnable und führst es per ExecutorService#submit nebenläufig aus.


----------



## Cheefrocker (10. Okt 2007)

```
public void Multithread(Collection input)
    {
           
            Object[] t = input.toArray();  
            
            ExecutorService executor = Executors.newCachedThreadPool();
            Executors.newFixedThreadPool(2);
            
            for(int i=0;i<t.length;i++)
            {
                test2 n = new test2(t[i],new File("C:\\Dokumente und Einstellungen\\xv\\Desktop\\Neuer Ordner (2)\\"));
                executor.execute(n);
            }
            
               
    }
```


meinste in Etwa so? 


test2 ist dann meine Klasse wo saveTiff aufgerufen wird?


----------



## byte (10. Okt 2007)

Executor#submit(Runnable)


----------



## tfa (10. Okt 2007)

Cheefrocker hat gesagt.:
			
		

> ```
> ExecutorService executor = Executors.newCachedThreadPool();
> Executors.newFixedThreadPool(2);
> ```



Warum das newCachedThreadPool()? So sollte es sein:

```
ExecutorService executor =  Executors.newFixedThreadPool(2);
```


----------



## Cheefrocker (10. Okt 2007)

So hats geklappt! vielen dank


----------

