Threads... Quadcore VS Dualcore

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Threads... Quadcore VS Dualcore

    Hi @all,

    ich möchte eines meiner Programme in mehrere Threads unterteilen, aber ich bin ein Halb-Noob^^

    Mein Programm sieht bis jetzt so aus:

    Thread 1 wird gestartet -->Textausgabe--> Zeitmessung startet --> Prozessor rechnet usw. -->Zeitmessung stoppt -->Textausgabe -->Textausgabe-->Zeitmessung startet -->Prozessor rechnet usw. -->Zeitmessung stoppt -->Textausgabe -->Textausgabe -->Ende

    Das Programm soll aber in 5 Threads geteilt werden.

    Thread 2, 3, 4 und 5 sollen im Grunde hier eingeschoben werden

    Thread 1 wird gestartet -->Textausgabe--> Zeitmessung startet --> Prozessor rechnet usw. -->Zeitmessung stoppt -->Textausgabe -->Textausgabe-->Zeitmessung startet -->[Thread 2, 3, 4, 5]Prozessor rechnet usw.[/Thread 2, 3, 4, 5]-->Zeitmessung stoppt -->Textausgabe -->Textausgabe -->Ende

    geht das?

    Ich hoffe jeder hat es verstanden :)
  • kann mir das vielleicht jemand anhand dieses Programms erklären (ich verstehs nicht so ganz)?

    Quellcode

    1. import java.math.*;
    2. public class Benchmark {
    3. public static void main(String args[]) {
    4. System.out.println();
    5. System.out.println("Test 1 wird ausgefuehrt...");
    6. long zstVorher;
    7. long zstNachher;
    8. long zstVorer;
    9. long zstNacher;
    10. zstVorher = System.currentTimeMillis();
    11. int schritte = 1000000000;
    12. int treffer = 0;
    13. double radius = 1.0;
    14. double x, y, entfernung, pi;
    15. for(int i=0; i < schritte; i++) {
    16. x = Math.random();
    17. y = Math.random();
    18. entfernung = Math.sqrt(x*x + y*y);
    19. if( entfernung <= radius ) {
    20. treffer = treffer + 1;
    21. }
    22. }
    23. pi = 4 * ((double)treffer / (double)schritte);
    24. zstNachher = System.currentTimeMillis();
    25. System.out.println("Test 1 wurde erfolgreich ausgefuehrt. (" + (zstNachher - zstVorher) + " Punkte)");
    26. System.out.println();
    27. //aus diesem Rest Thread 2, 3, 4, 5 machen
    28. System.out.println("Test 2 wird ausgefuehrt...");
    29. zstVorer = System.currentTimeMillis();
    30. int limit = 10000000; //im dritten Thread muss es 10000001 heißen und im 4. 100000002 heißen usw.
    31. long zahl = 2; //Im zweiten Thread kann es so bleiben, im 3. muss es 3 heißen usw.
    32. int zaehler = 0;
    33. long []container = new long[limit/3];
    34. while( zahl< limit) {
    35. boolean primzahl = true;
    36. for (int l_iLoop = 0; (l_iLoop < zaehler); l_iLoop++)
    37. {
    38. if (container[l_iLoop]*container[l_iLoop] > zahl)
    39. break;
    40. if ((zahl % container [l_iLoop]) == 0)
    41. primzahl = false;
    42. }
    43. if (primzahl)
    44. {
    45. container [zaehler] = zahl;
    46. zaehler++; // zaehler erhöhen…
    47. }
    48. zahl = zahl + 1;
    49. };
    50. zstNacher = System.currentTimeMillis();
    51. System.out.println("Test 2 wurde erfogreich ausgefuehrt. (" + (zstNacher - zstVorer) + " Punkte)");
    52. System.out.println();
    53. System.out.println();
    54. System.out.println("Endergebnis: " + ((zstNacher - zstVorer) + (zstNachher - zstVorher)) + "Punkte");
    55. }
    56. }
    Alles anzeigen


    wäre sehr schön, wenn es jemand noch in eigenen Worten formulieren könnte :)
  • Ich habs jetzt mal geschafft (ich hab aber natürlich nicht die ganze Zeit daran gearbeitet^^);
    das Programm hat jetzt eine 8Kernunterstützung. Leider ist es jetzt so,
    dass es bei einem 4Kernprozessor mit 3,175 Ghz (Intel Q8200 übertaktet)
    eine schlechtere Punktzahl als bei einem 2Kernprozessor mit 3,16 Ghz
    (Intel E8500) gibt.

    Kann mir vielleicht jemand helfen?

    Quellcode

    1. import javax.swing.JFrame;
    2. import javax.swing.JLabel;
    3. import javax.swing.JButton;
    4. import javax.swing.JTextArea;
    5. import javax.swing.SwingWorker;
    6. import java.awt.FlowLayout;
    7. import java.awt.event.ActionListener;
    8. import java.awt.event.ActionEvent;
    9. public class Benchmark
    10. {
    11. //Konstanten für die Ausführung des Programms
    12. static final int THREADS = 8; //so viele Threads werden erstellt
    13. static final int PI_SCHRITTE = 38000000; //so oft wird die Schleife im ersten Test durchlaufen
    14. static final int TEST2_SCHRITTE = 100000000; //so oft wird die Schleife im zweiten Test durchlaufen
    15. public static void main (String args[])
    16. {
    17. GUI g = new GUI();
    18. }
    19. }
    20. class GUI extends JFrame
    21. {
    22. JTextArea jta;
    23. public GUI()
    24. {
    25. super("Benchmark");
    26. JLabel jl = new JLabel("<html>Klicken Sie auf 'Start' um die Tests zu starten.");
    27. jta = new JTextArea(10, 20);
    28. jta.setEditable(false);
    29. jta.setLineWrap(true);
    30. jta.setWrapStyleWord(true);
    31. JButton start = new JButton("Start");
    32. setVisible(true);
    33. setSize(300, 275);
    34. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    35. setLayout(new FlowLayout());
    36. getContentPane().add(jl);
    37. getContentPane().add(start);
    38. getContentPane().add(jta);
    39. /*Hier ist der Teil des Programms in dem die Berechnung gestartet wird.
    40. (Mit einem Klick auf den Start-Button)*/
    41. start.addActionListener(new ActionListener()
    42. {
    43. public void actionPerformed(ActionEvent ae)
    44. {
    45. SwingWorker sw = new SwingWorker()
    46. {
    47. protected String doInBackground()
    48. {
    49. long zeitVorTest1; //Zeitmessungsvariablen für die spätere Punktzahl
    50. long zeitNachTest1;
    51. long zeitVorTest2;
    52. long zeitNachTest2;
    53. //Test 1
    54. jta.setText("Test 1 wird ausgeführt...\n");
    55. zeitVorTest1 = System.currentTimeMillis();
    56. ThreadPi thPi[] = new ThreadPi[Benchmark.THREADS];
    57. //Threads erstellen
    58. for (int i = 0; i < Benchmark.THREADS; i++)
    59. {
    60. thPi[i] = new ThreadPi();
    61. }
    62. //Threads starten
    63. for (int i = 0; i < Benchmark.THREADS; i++)
    64. {
    65. thPi[i].start();
    66. }
    67. //auf die Fertigstellung der Threads warten
    68. for (int i = 0; i < Benchmark.THREADS; i++)
    69. {
    70. try
    71. {
    72. thPi[i].join();
    73. }
    74. catch (InterruptedException ie)
    75. {
    76. jta.append("Interruped Exception wurde in Thread " + i + " ausgelöst...\n");
    77. }
    78. }
    79. zeitNachTest1 = System.currentTimeMillis();
    80. jta.append("Test 1 wurde erfolgreich ausgeführt. (" + (zeitNachTest1 - zeitVorTest1) + " Punkte)\n\n");
    81. //Test 2
    82. jta.append("Test 2 wird ausgeführt...\n");
    83. zeitVorTest2 = System.currentTimeMillis();
    84. ThreadTest2 thT2[] = new ThreadTest2[Benchmark.THREADS];
    85. //Threads erstellen
    86. for (int i = 0; i < Benchmark.THREADS; i++)
    87. {
    88. thT2[i] = new ThreadTest2();
    89. }
    90. //Threads starten
    91. for (int i = 0; i < Benchmark.THREADS; i++)
    92. {
    93. thT2[i].start();
    94. }
    95. //auf die Fertigstellung der Threads warten
    96. for (int i = 0; i < Benchmark.THREADS; i++)
    97. {
    98. try
    99. {
    100. thT2[i].join();
    101. }
    102. catch(InterruptedException ie)
    103. {
    104. jta.append("Interrupted Exception wurde in Thread 2 " + i + " ausgelöst...\n");
    105. }
    106. }
    107. zeitNachTest2 = System.currentTimeMillis();
    108. jta.append("Test 2 wurde erfolgreich ausgeführt. (" + (zeitNachTest2 - zeitVorTest2) + " Punkte)\n\n\n");
    109. jta.append("Gesamtpunktzahl: " + ((zeitNachTest1 - zeitVorTest1) + (zeitNachTest2 - zeitVorTest2)) + " Punkte");
    110. return null; //return wegen dem SwingWorker, der einen String erwartet
    111. }
    112. };
    113. sw.execute();
    114. }
    115. });
    116. }
    117. }
    118. class ThreadPi extends Thread //dieser Teil wird im ersten Test acht mal gestartet
    119. {
    120. public void run()
    121. {
    122. final double RADIUS = 1.0;
    123. double x, y, entfernung, pi;
    124. int trefferGes = 0;
    125. for(int i=0; i < Benchmark.PI_SCHRITTE; i++)
    126. {
    127. x = Math.random();
    128. y = Math.random();
    129. entfernung = Math.sqrt(x*x + y*y);
    130. if (entfernung <= RADIUS)
    131. {
    132. trefferGes += 1;
    133. }
    134. }
    135. pi = 4 * ((double)trefferGes / (double)schritte);
    136. }
    137. }
    138. /*
    139. Dieser Teil des Programms hat im Gegensatz zum
    140. ersten Test keinen wirklichen
    141. Nutzen, da nichts hilfreiches ausgerechnet wird
    142. */
    143. class ThreadTest2 extends Thread //dieser Teil wird im zweiten Test acht mal gestartet
    144. {
    145. public void run()
    146. {
    147. int zahl = 2;
    148. int zaehler = 0;
    149. final int SCHLEIFE = 300;
    150. while (zahl < Benchmark.TEST2_SCHRITTE)
    151. {
    152. zaehler = zahl * 3;
    153. zaehler %= zahl;
    154. if (zaehler % 2 == 0)
    155. {
    156. for (int i = 0; i < SCHLEIFE; i++)
    157. {
    158. zaehler -= 1;
    159. }
    160. }
    161. zahl++;
    162. }
    163. }
    164. }
    Alles anzeigen

    Übrigens wäre es auch nicht schlecht, wenn jemand mir sagen könnte, ob
    der Stil annehmbar ist, oder ob ich gegen "Die ungeschrieben Gesetze
    der (Java)Programmierung" verstoße.

    MfG bla
  • Also oberflächlich fällt mir nichts auf, was die Ursache sein könnte.
    Bei parallelem Zeug ist das auch immer nicht ganz einfach. Vielleicht werden die Threads ständig von einer CPU auf die andere verlagert. Das kostet natürlich Zeit.

    Mache dich mal mit den netten (grafischen) tools jconsole und jvisualvm (plugins installieren) vertraut. Sie werden mit jeder halbwegs neuen Java 6-Version mitgeliefert und finden sich im bin-Verzeichnis des JDK. Damit kannst du gut beobachten was in deinem Programm so treibt (z.B. die Thread-Aktivität beobachten). Leider kann man nicht betrachten welcher Thread gerade von welcher CPU behandelt wird.

    Weiterhin solltest du einmal beobachten ob auch alle 4 Prozessoren verwendet werden. Sind es nur 2 oder gar nur einer, dann würde das (anhand der Taktzahl) den Unterschied erklären. Möglicherweise musst du der JVM durch Parameter erst verklickern, dass sie die Ressourcen nutzen soll.
  • Nein, alle Kerne sind zu 100% Prozent ausgelastet.
    Mit 3 Kernen ist es so, dass es schneller als 4 Kerne, aber langsamer als 2 sind. Es scheint also, dass das Programm länger dauert, je mehr Kerne es sind.

    Ich schau mir das mal mit JConsole und JVisualvm die Sache genauer an.

    Edit: Der erste Test dauert mit einem 4Kerner immer so lang, der zweite ist viel schneller.

    Edit2: Was genau soll ich eigentlich mit JConsole machen? Ich habs mir mal angeschaut, weiß aber nicht, was das helfen soll ?(

    MfG bla

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von bla ()

  • Kann man das hin und herschaufeln irgendwie verhindern?

    Edit: Angenommen, dass ist tatsächlich so, warum ist dann nur der erste Test so lang und der zweite nicht? Kann es vielleicht sein, dass z. B. Math.sqrt() bei einem 4Kernprozessor 4mal gleichzeitig aufgerufen wird, aber immer gewartet wird, bis die anderen Ergebnisse ausgerechnet werden?
    Also quasi so:
    Thread 2 will die Wurzel ziehen, es geht aber nicht, weil Thread 1 gerade rechnet? Ist das möglich?

    MfG bla

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von bla ()

  • Mal ein paar Schüsse ins blaue, ich hab keinen Schimmer obs daran liegen könnte oder nicht:

    - Wird der QuadCore vieleicht zu heiße und taktet deswegen zwischendrin runter? (ist ja ganz ordentlich übertaktet)

    - Vieleicht sind die Rechnungen auch Cache-intensiv? der DualCore hat immerhin für beide Kerne zusammen 6MB L2-Cache, der QuadCore für alle 4 kerne gerade mal 4MB

    - Sind die Rechner ansonsten gleich? (vorallem was Chipsatz und Speicher angeht)
  • Danke für die Antwort.
    Der Prozessor ist Primestable, ob er zu heiß ist, weiß ich nicht (der Rechner gehört nicht mir), ich frag mal nach den Temperaturen.
    Die Rechner sind sonst nicht gleich:
    Prozessor: E8500 -------------- Q8200
    Chipsatz: P965 ----------------- P45
    RAM: 4 GB Kingston HyperX 832Mhz DDR2 -----OCZ Reaper 923 Mhz

    Eigentlich macht es das ja nur noch komischer...

    Ob die Rechnungen Cache-intensiv ist, weiß ich nicht...

    MfG bla
  • Sry für den Doppelpost...

    Ich hab mal nach den Temperaturen gefragt, daran liegts nicht. Höchstens 43°C unter 30 minütiger Vollast.
    Ich habs mal bei ein paar Bekannten und Freunden ausprobiert, da ist es genauso: 2Kernprozessor-->schnell, 3/4Kernporzessor-->langsam.

    Es ist ja nur der erste Test langsam. Kann das an Math.sqrt() liegen? Also dass Kern 1 und 2, 3 und 4 gleichzeit die Wurzel ziehen wollen, aber sie nicht gleichzeitig darauf zugreifen können?

    MfG bla
  • bla schrieb:

    Es ist ja nur der erste Test langsam. Kann das an Math.sqrt() liegen? Also dass Kern 1 und 2, 3 und 4 gleichzeit die Wurzel ziehen wollen, aber sie nicht gleichzeitig darauf zugreifen können?
    Nein, das nicht. Das sind 4 echte physische-Prozessorkerne. Ich weiß garnicht ob die eigene sqrt-Einheiten haben oder das simuliert wird, aber auf jedenfall macht das jeder prozessor für sich.
  • Nein, ich meinte das so:
    Math.sqrt() ist doch eine Methode der Klasse Math. Es muss dann ja um die Wurzel zu ziehen, auf diese Methode zugegriffen werden, was aber (laut meiner Theorie) nur geht, wenn ein anderer Prozessorkern nicht gerade darauf zugreift. Bespiel:
    Prozessorkern 1 geift auf die Methode sqrt() zu, was aber auch Kern 2 machen will. Das kann er aber nicht, weil Kern 1 gerade damit arbeitet. Kann das sein?

    MfG bla
  • Ich hab das ganze mal bei mir nachgetestet und komme auch auf sehr seltsame Ergebnisse für Test1 und auf erwartete Ergebnisse für Test 2

    Und ich glaube ich hab das Problem gefunden:
    die Fehlerquelle liegt nicht in der sqrt-Funktion, sondern in der Math.random() Funktion. Zu dieser steht in der Java-Api folgendes:
    When this method is first called, it creates a single new pseudorandom-number generator, exactly as if by the expression

    new java.util.Random

    This new pseudorandom-number generator is used thereafter for all calls to this method and is used nowhere else.

    This method is properly synchronized to allow correct use by more than one thread. However, if many threads need to generate pseudorandom numbers at a great rate, it may reduce contention for each thread to have its own pseudorandom-number generator.
    (vorallem der Fettgedruckte Teil ist interessant)
    Das bedeutet, dass alle Threads immer auf das gleiche Objekt von java.util.Random zugreifen müssen und sie sich wohl gegenseitig dadurch ausbremsen.
    also hab ich einfach mal die 1. Test-Methode folgendermaßen umgebaut:

    Quellcode

    1. class ThreadPi extends Thread //dieser Teil wird im ersten Test acht mal gestartet
    2. {
    3. public void run()
    4. {
    5. final double RADIUS = 1.0;
    6. double x, y, entfernung, pi;
    7. int trefferGes = 0;
    8. java.util.Random rnd = new java.util.Random();
    9. for(int i=0; i < Benchmark.PI_SCHRITTE; i++)
    10. {
    11. x = rnd.nextDouble();
    12. y = rnd.nextDouble();
    13. entfernung = Math.sqrt(x*x + y*y);
    14. if (entfernung <= RADIUS)
    15. {
    16. trefferGes += 1;
    17. }
    18. }
    19. pi = 4 * ((double)trefferGes / (double)Benchmark.PI_SCHRITTE);
    20. }
    21. }
    Alles anzeigen

    hier erstelle ich für jeden Thread ein eigenes Random-Objekt und arbeite dann nurnoch auf diesem.
    Und siehe da: Die Ergebnisse werden wesentlich vorhersehbarer (auf meinem Dualcore braucht er für "THREADS = 1" jetzt genauso lange wie für "THREADS = 2" was vorher ganz und garnicht so war. Und für "Threads = 8" grob 4 mal solange.)
    Hab jetzt keinen Quadvore zum testen, solltest du aber mal so versuchen

    Edit: Was ich auch interessant finde: Die Random Funktion ist hier der größte Rechenzeitfresser und nicht etwa das Wurzelziehen. Daher ist es eher ein Benchmark für die rnd-Funktion :D