PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [Java] Problem mit Multithreading bei Verwendung von double


minos5000
2011-01-19, 09:18:57
Hi,

ich habe ein Java Programm das verschiedene Berechnung ausführt und damit die Hardware gut ausgelastet wird wollte ich das ganze multithreadingfähig machen.
Nun habe ich dabei eine eigenartige Beobachtung gemacht, wenn ich für die Berechnungen double Datentypen verwende läuft alles in nur einem Thread ab, obwohl natürlich mehrere gestartet wurden. Erst wenn ich auf sämtliche Zahlen auf BigInteger umstelle werden auch tatsächlich alle Kerne meines Rechners voll ausgelastet.

Hat jemand eine Erklärung für dieses Phänomen?

universaL
2011-01-19, 09:53:17
ohne den code zu sehen schwer zu sagen, aber der datentyp söllte keinen einfluss auf die anzahl der threads haben. ich rechne für eine simulation an der uni auch mit mehreren threads, zumeist mit doubles und das funktioniert wunderbar mit 4 threads.

Monger
2011-01-19, 10:41:25
Ein BigInteger ist ein Objekt, ein double ist ein elementarer Datentyp. Vielleicht hat es damit irgendwas zu tun? Vielleicht Autoboxing Probleme?!

Ohne den dazugehörigen Code lässt sich das natürlich nicht beantworten.

minos5000
2011-01-19, 12:03:40
Ich hab mal versucht die double-Werte durch Double-Objekte zu ersetzen, aber geholfen hat es nicht.

Die Klassen sehe etwa so aus:

public class Problem {

public Double start(int start, int end) {
Double summand = new Double("0");
for (int i = start; i < end; i++) {
summand+=function(i);
}
return summand;
}

private Double function(int i) {
Double dLeastMultiple = new Double(i);
String sLeastMultiple = dLeastMultiple.toString();
int factor = 0;
while (containsOtherNumbers(sLeastMultiple)) {
factor++;
dLeastMultiple = new Double(i*factor);
sLeastMultiple = dLeastMultiple.toString();
}
return dLeastMultiple;
}

private boolean containsOtherNumbers(String s) {
if (s.contains("3"))
return true;
else if (s.contains("4"))
return true;
else if (s.contains("5"))
return true;
else if (s.contains("6"))
return true;
else if (s.contains("7"))
return true;
else if (s.contains("8"))
return true;
else if (s.contains("9"))
return true;
else
return false;
}
}

public class Runner implements Runnable {

private int start, end;
private Problem p;
private Double result = new Double("0");

public Runner(int start, int end) {
this.start = start;
this.end = end;
p = new Problem();
}

@Override
public void run() {
Problem p = new Problem();
result = p.start(start, end);
}

public Double getResult() {
return result;
}

}

...
public static void main(String[] args) {
Runner r1 = new Runner(1, 24);
Runner r2 = new Runner(25, 50);
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
...
}
...


So wie es jetzt da steht läuft alles in einem Thread. Ersetzt man alle Doubles durch BigInts verteilt es sich auf mehrere...

Senior Sanchez
2011-01-19, 12:51:24
Der Code so wie er da steht lässt sich zwar nicht kompilieren, aber nach der Korrektur der Fehler läuft das hier einwandfrei mit zwei Threads auf einem MacBook Pro mit Core 2 Duo.

Mal ganz dumm gefragt: wie bestimmt du eigentlich ob das Multithreaded läuft? Per Taskmanager? Soll nicht doof klingen, aber kannst du den richtig bedienen? ;)

Welche Java-Version und welches Betriebssystem nutzt du?

minos5000
2011-01-19, 14:44:40
Angeschaut hab ich mir nur die Auslastung der CPU-Kerne und wie gesagt, wenn ich mit Double arbeite, wird nur ein Kern ausgelastet...

Aber im Eclipse sieht man im Debugmodus auch die Anzahl der Threads:

BigInteger:
http://img816.imageshack.us/img816/875/bigint.png

Double:
http://img151.imageshack.us/img151/8281/doublea.png

Und Javaversion ist bei mir 1.6.x unter WinXP.

Aber ich werd das zur Sicherheit auch noch an einem anderen Rechner testen.

Senior Sanchez
2011-01-19, 15:04:25
Das ist in der Tat seltsam! Sonst die Quelltexte, abgesehen vom anderen Datentyp, wirklich absolut identisch?
Probier es wirklich mal auf einer anderen Maschine aus, vielleicht läuft es da multithreaded.

Gast
2011-01-19, 16:01:40
Hmm ich würde da mal ein paar Debugausgaben machen, ob die einzelnen Threads gestartet werden, dann noch mal am Ende der Threads in im Mainthread...

Coda
2011-01-19, 17:51:43
Ich bin mir ziemlich sicher, dass der Thread mit double so schnell fertig ist, dass er sich schon wieder beendet hat bevor der zweite start-Aufruf kommt.

Typische Race-Condition. Nimm mal viel mehr Zahlen.

Tesseract
2011-01-19, 19:07:25
Ich bin mir ziemlich sicher, dass der Thread mit double so schnell fertig ist, dass er sich schon wieder beendet hat bevor der zweite start-Aufruf kommt.

Typische Race-Condition. Nimm mal viel mehr Zahlen.

wo wir gleich zum nächsten punkt kommen: ich weiß zwar nicht was dein eigentlicher code macht, aber wenn er so ähnlich wie der hier abläuft ist die threadverwaltung wahrscheinlich noch um größenordnungen aufwändiger als der eigentliche code. sprich: durch threading wird er deutlich langsamer.

Senior Sanchez
2011-01-19, 20:41:26
Ich bin mir ziemlich sicher, dass der Thread mit double so schnell fertig ist, dass er sich schon wieder beendet hat bevor der zweite start-Aufruf kommt.

Typische Race-Condition. Nimm mal viel mehr Zahlen.

BigInteger sollte aber eigentlich nicht so viel mehr Aufwand erfordern. Warum eigentlich BigInteger fällt mir gerade auf? BigDecimal wäre da eher das passende Gegenstück.

Grundsätzlich könntest du aber recht haben. Für die Tests habe ich auch erstmal die Zahlen ein bisschen erhöht, damit ich direkt die Threads in der Aktivitätsanzeige sehe.

minos5000
2011-01-19, 21:58:18
Code hatte recht, es lag daran, dass die Zahlen zu klein waren. Mit größeren Zahlen laufen dann auch tatsächlich die Threads parallel.
Etwas verwundert bin ich schon, dass BigInteger dann im Vergleich derart langsam ist...

Coda
2011-01-19, 22:26:06
BigInteger sollte aber eigentlich nicht so viel mehr Aufwand erfordern.
Du täuscht dich. Das ist mindestens Faktor 10 langsamer, aber tendenziell eher Richtung 100x-1000x je nach Operation und Größe der Zahlen.

Senior Sanchez
2011-01-19, 22:53:30
Du täuscht dich. Das ist mindestens Faktor 10 langsamer, aber tendenziell eher Richtung 100x-1000x je nach Operation und Größe der Zahlen.

Hm, hast scheinbar Recht, wie minos ja jetzt bestätigt hat.

Das der Unterschied so extrem ist (vor allem bei diesen kleinen Zahlen) hätte ich aber wirkllch nicht gedacht. Wieder etwas gelernt. :)

Der_Donnervogel
2011-01-19, 23:46:59
Der Overhead ist sehr groß. Bei double können die Berechnungen direkt in Hardware laufen, während bei BigInteger alles in Software nachgebildet werden muss. Ich glaube das ganze wird klarer wenn man sich mal den Quellcode anschaut. Hier z.B. wie Add von BigInteger bei Java implementiert ist:

public BigInteger add(BigInteger val) {
int[] resultMag;
if (val.signum == 0)
return this;
if (signum == 0)
return val;
if (val.signum == signum)
return new BigInteger(add(mag, val.mag), signum);

int cmp = intArrayCmp(mag, val.mag);
if (cmp==0)
return ZERO;
resultMag = (cmp>0 ? subtract(mag, val.mag)
: subtract(val.mag, mag));
resultMag = trustedStripLeadingZeroInts(resultMag);

return new BigInteger(resultMag, cmp*signum);
}


Das sind auch bei kleinen Zahlen eine ganze Menge Operationen im Vergleich zu einem skalaren Typ.

Coda
2011-01-20, 00:36:28
Hm, hast scheinbar Recht
Nicht nur scheinbar ;)

Senior Sanchez
2011-01-20, 11:36:34
Nicht nur scheinbar ;)

Hehe, okay, streichen wir das scheinbar. :D