Hallo Leute,
gestern wollte ich eine Kalkulation auf mehrere Threads durchführen lassen da von 8 Kernen immer nur einer gelaufen ist, die restlichen 7 blieben kalt. Das ganze hat einen bioinformatischen Hintergrund, es geht um die dynamische Berechnung von Stringvergleichen (DNA). Dazu wollte ich den bekannten Needleman-Wunsch Algorithmus auf mehreren Kernen parallel arbeiten lassen (in Form von seperaten, von einander unabhängige(!) Kalkulationen). Irgendwann habe ich jedoch entnervt aufgegeben weil ich keinen bzw. nur einen sehr geringen Gewinn erzielen konnte. Die Gesamtrechenzeit liess sich leider nicht wirklich durch die Anzahl der Threads skalieren.
Irgendwann bin ich dann irgendwie darauf gekommen das große Arrays anscheinend die nebenläufige Berechnung behindern (kann das sein???). Erklären kann ich mir es nicht, RAM ist nämlich genug da (vmargs: -Xmx1024m -Xmx8192m). Ich habe hier mal ein einfaches Beispiel geschrieben das meiner Anwendung relativ ähnlich ist und die Problematik auf einfache Weise veranschaulicht:
Seht Euch bitte mal folgendes Beispiel an und helft mir ggfs. auf die Sprünge:
Ich habe 2 verschiedene Threads gebastelt.
Der erste skaliert quasi linear, also quasi perfekt
:
Hier das zweite Beispiel, das skaliert leider maximal um den Faktor 2 (statt 8 wie bei T1 :-?)
Hier die Mainklasse:
Damit habe ich verschiedene Anzahlen von Threads durchprobiert (von 1-8), die jeweiligen Outputs:
Number of Threads: 1
1 ALL THREADS FINISHED for JOB 1.
1 OVERALL Time: 1014.9110 ms
1 AVG LOOPTIME: 1.0149 ns
2 ALL THREADS FINISHED for JOB 2.
2 OVERALL Time: 977.9630 ms
2 AVG LOOPTIME: 4.8898 ms
Hier brauchen beide ca. 1 Sekunde
-------------------------------------------
Number of Threads: 2
1 ALL THREADS FINISHED for JOB 1.
1 OVERALL Time: 513.0990 ms
1 AVG LOOPTIME: 0.5131 ns
2 ALL THREADS FINISHED for JOB 2.
2 OVERALL Time: 583.1790 ms
2 AVG LOOPTIME: 2.9159 ms
teilt man 2 Threads zu, so brauchen beide Berechnungen nur noch knapp die Hälfte (mein Needleman-Wunsch bekommt hier leider nur ein Plus von ca. maximal 10%)
-------------------------------------------
Number of Threads: 3
1 ALL THREADS FINISHED for JOB 1.
1 OVERALL Time: 343.2470 ms
1 AVG LOOPTIME: 0.3432 ns
2 ALL THREADS FINISHED for JOB 2.
2 OVERALL Time: 524.5810 ms
2 AVG LOOPTIME: 2.6229 ms
T1 skaliert weiterhin linear. T2 lässt spürbar nach.
-------------------------------------------
Number of Threads: 4
1 ALL THREADS FINISHED for JOB 1.
1 OVERALL Time: 256.6200 ms
1 AVG LOOPTIME: 0.2566 ns
2 ALL THREADS FINISHED for JOB 2.
2 OVERALL Time: 479.6200 ms
2 AVG LOOPTIME: 2.3981 ms
-------------------------------------------
Number of Threads: 5
1 ALL THREADS FINISHED for JOB 1.
1 OVERALL Time: 211.0260 ms
1 AVG LOOPTIME: 0.2110 ns
2 ALL THREADS FINISHED for JOB 2.
2 OVERALL Time: 462.9910 ms
2 AVG LOOPTIME: 2.3150 ms
T1 weiterhin linear, T2 stagniert.
-------------------------------------------
Number of Threads: 6
1 ALL THREADS FINISHED for JOB 1.
1 OVERALL Time: 173.1410 ms
1 AVG LOOPTIME: 0.1731 ns
2 ALL THREADS FINISHED for JOB 2.
2 OVERALL Time: 457.7810 ms
2 AVG LOOPTIME: 2.2889 ms
-------------------------------------------
1 ALL THREADS FINISHED for JOB 1.
1 OVERALL Time: 159.6470 ms
1 AVG LOOPTIME: 0.1596 ns
2 ALL THREADS FINISHED for JOB 2.
2 OVERALL Time: 492.2210 ms
2 AVG LOOPTIME: 2.4611 ms
T2 wird ab 7 sogar langsamer (viele Versuche getestet).
-------------------------------------------
Number of Threads: 8
1 ALL THREADS FINISHED for JOB 1.
1 OVERALL Time: 163.3790 ms
1 AVG LOOPTIME: 0.1634 ns
2 ALL THREADS FINISHED for JOB 2.
2 OVERALL Time: 544.1340 ms
2 AVG LOOPTIME: 2.7207 ms
Hier lässt auch T1 im Schnitt ein wenig nach (vermutlich weil ja der 8.te Thread schon vom aufrufenden Thread belegt ist, also ist der 8. eigentlich der 9.?)
-------------------------------------------
mehr als 8 ist eh Unsinn...........
Mach ich irgendwas falsch? Kann man das für dass Beispiel 2 auch irgendwie verbessern? In der Praxis sind meine Arrays ca. 100000 x 250 groß. Ein Stringvergleich dauert ca. 0,5 s. Ich würde sehr gerne 8 verschiedene Vergleiche gleichzeitig machen lassen, da ich insgesamt ca. 4 Mio. dieser Vergleiche durchführen muss. Das sind ca. 23 Tage Rechenzeit bei 1 Thread, im Gegensatz zu 3 Tagen bei 8. Leider skaliert mein Stringvergleich (dessen code ist leider etwas umfangreicher, Bsp. 2 ist ähnlich und hat das Problem auch) nicht mal um den Faktor 2 :-(
Ich würde mich sehr freuen wenn mir das einer erklären und mir irgendwie helfen könnte.
Schonmal vielen Dank!
Thomas
gestern wollte ich eine Kalkulation auf mehrere Threads durchführen lassen da von 8 Kernen immer nur einer gelaufen ist, die restlichen 7 blieben kalt. Das ganze hat einen bioinformatischen Hintergrund, es geht um die dynamische Berechnung von Stringvergleichen (DNA). Dazu wollte ich den bekannten Needleman-Wunsch Algorithmus auf mehreren Kernen parallel arbeiten lassen (in Form von seperaten, von einander unabhängige(!) Kalkulationen). Irgendwann habe ich jedoch entnervt aufgegeben weil ich keinen bzw. nur einen sehr geringen Gewinn erzielen konnte. Die Gesamtrechenzeit liess sich leider nicht wirklich durch die Anzahl der Threads skalieren.
Irgendwann bin ich dann irgendwie darauf gekommen das große Arrays anscheinend die nebenläufige Berechnung behindern (kann das sein???). Erklären kann ich mir es nicht, RAM ist nämlich genug da (vmargs: -Xmx1024m -Xmx8192m). Ich habe hier mal ein einfaches Beispiel geschrieben das meiner Anwendung relativ ähnlich ist und die Problematik auf einfache Weise veranschaulicht:
Seht Euch bitte mal folgendes Beispiel an und helft mir ggfs. auf die Sprünge:
Ich habe 2 verschiedene Threads gebastelt.
Der erste skaliert quasi linear, also quasi perfekt
Code:
// 1. The simple one...
public class TObject1 implements Runnable
{
long loops;
public TObject1(long loops)
{
this.loops = loops;
}
// a lot of hard work...........
public void run()
{
long x = 0;
for (int i = 0; i < loops; i++)
x+=1;
}
}
Hier das zweite Beispiel, das skaliert leider maximal um den Faktor 2 (statt 8 wie bei T1 :-?)
Code:
// 2. The dynamic programming like (simplified)
public class TObject2 implements Runnable
{
long loops;
int dim1;
int dim2;
public TObject2(long loops, int dim1, int dim2)
{
this.loops = loops;
this.dim1 = dim1;
this.dim2 = dim2;
}
// a lot of hard work...........
public void run()
{
for(int i=0; i< loops; i++)
{
int[][] big = new int[dim1][dim2];
// init 1st row and column with 1
for (int k = 0; k < dim1; k++)
big[k][dim2-1] = 1;
for (int n = 0; n < dim2; n++)
big[dim1-1][n] = 1;
// calculate some stupid stuff over the array
for (int k = 1; k < dim1; k++)
{
for (int n = 1; n < dim2; n++)
big[k][n] += big[k-1][n-1];
}
}
}
}
Hier die Mainklasse:
Code:
import java.text.DecimalFormat;
public class maintest {
public static void main(String[] args)
{
try
{
// number of simultaneous threads
int tcount = 1;
System.out.println("Number of Threads: "+tcount);
/*
* TObjects1 - the simple ones
* (runtime scaling approximately linear with number of threads)
*/
long loops = 1000000000L;
Thread[] tarray = new Thread[tcount];
for (int i = 0; i < tcount; i++)
tarray[i] = new Thread(new TObject1(loops/tcount));
// cpu benchmark
long startTime = System.nanoTime();
long stopTime = 0;
long runTime = 0;
for (int i = 0; i < tcount; i++)
tarray[i].start();
for (int i = 0; i < tcount; i++)
tarray[i].join();
// cpu benchmark
stopTime = System.nanoTime();
runTime = stopTime - startTime;
System.out.println();
System.out.println("1 ALL THREADS FINISHED for JOB 1. ");
System.out.println("
1 OVERALL Time: "
+new DecimalFormat("0.0000").format((double)runTime/1000000)+" ms");
System.out.println("
1 AVG LOOPTIME: "
+new DecimalFormat("0.0000").format((double)runTime/(loops))+" ns");
System.out.println();
/*
* TObjects2 - calculates over large array
* (runtime doesn't scale good with number of threads)
*/
loops = 200;
int dim1 = 1000;
int dim2 = 1000;
tarray = new Thread[tcount];
for (int i = 0; i < tcount; i++)
tarray[i] = new Thread(new TObject2(loops/tcount, dim1, dim2));
// cpu benchmark
startTime = System.nanoTime();
for (int i = 0; i < tcount; i++)
tarray[i].start();
for (int i = 0; i < tcount; i++)
tarray[i].join();
// cpu benchmark
stopTime = System.nanoTime();
runTime = stopTime - startTime;
System.out.println();
System.out.println("2 ALL THREADS FINISHED for JOB 2. ");
System.out.println("
2 OVERALL Time: "
+new DecimalFormat("0.0000").format((double)runTime/1000000)+" ms");
System.out.println("
2 AVG LOOPTIME: "
+new DecimalFormat("0.0000").format((double)runTime/1000000/(loops))+" ms"); }
catch (Exception e) {
e.printStackTrace();
}
}
}
Damit habe ich verschiedene Anzahlen von Threads durchprobiert (von 1-8), die jeweiligen Outputs:
Number of Threads: 1
1 ALL THREADS FINISHED for JOB 1.
1 OVERALL Time: 1014.9110 ms
1 AVG LOOPTIME: 1.0149 ns
2 ALL THREADS FINISHED for JOB 2.
2 OVERALL Time: 977.9630 ms
2 AVG LOOPTIME: 4.8898 ms
Hier brauchen beide ca. 1 Sekunde
-------------------------------------------
Number of Threads: 2
1 ALL THREADS FINISHED for JOB 1.
1 OVERALL Time: 513.0990 ms
1 AVG LOOPTIME: 0.5131 ns
2 ALL THREADS FINISHED for JOB 2.
2 OVERALL Time: 583.1790 ms
2 AVG LOOPTIME: 2.9159 ms
teilt man 2 Threads zu, so brauchen beide Berechnungen nur noch knapp die Hälfte (mein Needleman-Wunsch bekommt hier leider nur ein Plus von ca. maximal 10%)
-------------------------------------------
Number of Threads: 3
1 ALL THREADS FINISHED for JOB 1.
1 OVERALL Time: 343.2470 ms
1 AVG LOOPTIME: 0.3432 ns
2 ALL THREADS FINISHED for JOB 2.
2 OVERALL Time: 524.5810 ms
2 AVG LOOPTIME: 2.6229 ms
T1 skaliert weiterhin linear. T2 lässt spürbar nach.
-------------------------------------------
Number of Threads: 4
1 ALL THREADS FINISHED for JOB 1.
1 OVERALL Time: 256.6200 ms
1 AVG LOOPTIME: 0.2566 ns
2 ALL THREADS FINISHED for JOB 2.
2 OVERALL Time: 479.6200 ms
2 AVG LOOPTIME: 2.3981 ms
-------------------------------------------
Number of Threads: 5
1 ALL THREADS FINISHED for JOB 1.
1 OVERALL Time: 211.0260 ms
1 AVG LOOPTIME: 0.2110 ns
2 ALL THREADS FINISHED for JOB 2.
2 OVERALL Time: 462.9910 ms
2 AVG LOOPTIME: 2.3150 ms
T1 weiterhin linear, T2 stagniert.
-------------------------------------------
Number of Threads: 6
1 ALL THREADS FINISHED for JOB 1.
1 OVERALL Time: 173.1410 ms
1 AVG LOOPTIME: 0.1731 ns
2 ALL THREADS FINISHED for JOB 2.
2 OVERALL Time: 457.7810 ms
2 AVG LOOPTIME: 2.2889 ms
-------------------------------------------
1 ALL THREADS FINISHED for JOB 1.
1 OVERALL Time: 159.6470 ms
1 AVG LOOPTIME: 0.1596 ns
2 ALL THREADS FINISHED for JOB 2.
2 OVERALL Time: 492.2210 ms
2 AVG LOOPTIME: 2.4611 ms
T2 wird ab 7 sogar langsamer (viele Versuche getestet).
-------------------------------------------
Number of Threads: 8
1 ALL THREADS FINISHED for JOB 1.
1 OVERALL Time: 163.3790 ms
1 AVG LOOPTIME: 0.1634 ns
2 ALL THREADS FINISHED for JOB 2.
2 OVERALL Time: 544.1340 ms
2 AVG LOOPTIME: 2.7207 ms
Hier lässt auch T1 im Schnitt ein wenig nach (vermutlich weil ja der 8.te Thread schon vom aufrufenden Thread belegt ist, also ist der 8. eigentlich der 9.?)
-------------------------------------------
mehr als 8 ist eh Unsinn...........
Mach ich irgendwas falsch? Kann man das für dass Beispiel 2 auch irgendwie verbessern? In der Praxis sind meine Arrays ca. 100000 x 250 groß. Ein Stringvergleich dauert ca. 0,5 s. Ich würde sehr gerne 8 verschiedene Vergleiche gleichzeitig machen lassen, da ich insgesamt ca. 4 Mio. dieser Vergleiche durchführen muss. Das sind ca. 23 Tage Rechenzeit bei 1 Thread, im Gegensatz zu 3 Tagen bei 8. Leider skaliert mein Stringvergleich (dessen code ist leider etwas umfangreicher, Bsp. 2 ist ähnlich und hat das Problem auch) nicht mal um den Faktor 2 :-(
Ich würde mich sehr freuen wenn mir das einer erklären und mir irgendwie helfen könnte.
Schonmal vielen Dank!
Thomas