for schleife - stark unterschiedliche Berechnungszeit

Framework

Neues Mitglied
Hallo,
ich habe mal aus Spaß ein wenig in Java rumgespielt und dabei ist mir eine eigenartige Sache aufgefallen die ich mir nicht wirklich erklären kann.
Hier erstmal der Code:
Java:
public class TestFloat {
    public static void main(String[] args)
    {
        Vari1();
        System.out.println("-----");
        Vari2();
    }
   
    private static void Vari1()
    {
        System.out.println("### Vari1 ###");
        long lStartTime, lEndTime, lCount = 0;
        float f1 = 0.123456789f, f2 = 0.123456789f, f3 = 0;
        int iEnd2 =  Integer.MAX_VALUE - 1, iLoops = 10;
        long[][] lResult = new long[iLoops][2];
       
       
        for (int i1 = 0; i1 < iLoops; i1++)
        {
            lStartTime = System.currentTimeMillis();
            for (int i2 = 0; i2 <= Integer.MAX_VALUE - 1; i2++)
            {
                f3 = f1 * f2;
                lCount++;
            }
            lEndTime = System.currentTimeMillis();
           
            lResult[i1][0] = lEndTime - lStartTime;
            lResult[i1][1] = lCount;
        }
        for(int i = 0; i < lResult.length; i++)
        {
            System.out.printf("Loop: %d -> %d ms (lCount = %d)\n", i, lResult[i][0], lResult[i][1]);
        }
    }
   
    private static void Vari2()
    {
        System.out.println("### Vari2 ###");
        long lStartTime, lEndTime, lCount = 0;
        float f1 = 0.123456789f, f2 = 0.123456789f, f3 = 0;
        int iEnd2 =  Integer.MAX_VALUE - 1, iLoops = 10;
        long[][] lResult = new long[iLoops][2];
       
       
        for (int i1 = 0; i1 < iLoops; i1++)
        {
            lStartTime = System.currentTimeMillis();
            for (int i2 = 0; i2 <= iEnd2; i2++)
            {
                f3 = f1 * f2;
                lCount++;
            }
            lEndTime = System.currentTimeMillis();
           
            lResult[i1][0] = lEndTime - lStartTime;
            lResult[i1][1] = lCount;
        }
        for(int i = 0; i < lResult.length; i++)
        {
            System.out.printf("Loop: %d -> %d ms (lCount = %d)\n", i, lResult[i][0], lResult[i][1]);
        }
    }
}

Die Console liefert mir folgende Ergebnisse:
Code:
### Vari1 ###
Loop: 0 -> 66 ms (lCount = 2147483647)
Loop: 1 -> 37 ms (lCount = 4294967294)
Loop: 2 -> 47 ms (lCount = 6442450941)
Loop: 3 -> 37 ms (lCount = 8589934588)
Loop: 4 -> 33 ms (lCount = 10737418235)
Loop: 5 -> 38 ms (lCount = 12884901882)
Loop: 6 -> 34 ms (lCount = 15032385529)
Loop: 7 -> 34 ms (lCount = 17179869176)
Loop: 8 -> 34 ms (lCount = 19327352823)
Loop: 9 -> 34 ms (lCount = 21474836470)
-----
### Vari2 ###
Loop: 0 -> 48 ms (lCount = 2147483647)
Loop: 1 -> 540 ms (lCount = 4294967294)
Loop: 2 -> 538 ms (lCount = 6442450941)
Loop: 3 -> 537 ms (lCount = 8589934588)
Loop: 4 -> 539 ms (lCount = 10737418235)
Loop: 5 -> 535 ms (lCount = 12884901882)
Loop: 6 -> 536 ms (lCount = 15032385529)
Loop: 7 -> 536 ms (lCount = 17179869176)
Loop: 8 -> 537 ms (lCount = 19327352823)
Loop: 9 -> 537 ms (lCount = 21474836470)

Die Methode Vari1 und Vari2 unterscheiden sich nur in der Angabe des Zielwertes in der inneren for-Schleife. Bei Vari1 verweise ich direkt auf Integer.MAX_VALUE-1 und in Vari2 übergebe ich eine eigene Integer Variable der der Wert von Integer.MAX_VALUE-1 zugewiesen wurde, was eigentlich nichts am Berechnungsaufwand ändert. ...aber aus irgend einem Grund ist die benötigte Zeit für die Schleifendurchläufe unterschiedlich (s. Consolenausgabe).

--> Hat jemand eine Erklärung dafür?^^

Schonmal vielen Dank im Voraus.

MfG Framework
 

JCODA

Top Contributor
Ich hätte nicht gedacht, dass der Unterschied so gravierend ist, aber nachdem man iEnd2 in der zweiten Methode final macht, laufen beide gleich schnell ab, ich denke es hat deshalb etwas mit dem Lookup auf die Variable zu tun.

Ich vermute
, durch das final-Keyword kann der Wert so optimiert werden, dass er im Cache gehalten wird. Während die nonFinal-Variante immer überprüfen muss, d.h. im RAM nachschauen (oder einer anderen Cache-Ebene), ob sich der Wert geändert hat. Zugriffe im Cache sind sehr viel zeitsparender. (Ein par wenige Maschinenzyklen vs Nanosekunden im Zehnerbereich) http://stackoverflow.com/questions/4087280/approximate-cost-to-access-various-caches-and-main-memory

Aber: Ich bin mir nicht sicher. Ich bin gespannt, welche Antworten hier noch kommen. Ein sehr interessantes Thema!
 

Framework

Neues Mitglied
Dank dir für deine Antwort, hört sich auf jeden Fall plausibel an.
Hast du auch eine Vermutung warum der erste Durchlauf (Loop 0) in Vari2 als einziger "schnell" ist?

Die Unterschiede sind schon der Wahnsinn. Wenn man sowas in seinem Code entdeckt kann man echt viel rausholen. :D
 

JCODA

Top Contributor
Hast du auch eine Vermutung warum der erste Durchlauf (Loop 0) in Vari2 als einziger "schnell" ist?
Leider hab ich hierfür auch keine 100% plausible Erklärung. Ich dachte, dass am Anfang ohne "Optimierungen" ausgeführt wird, und dann der JIT-Compiler einsteigt, aber das erklärt nicht die Zunahme von Laufzeit.

Achtung: Hier ein weiterer Candy: Ändere mal die Zeilen so for (int i2 = 0; i2 < Integer.MAX_VALUE - 1; i2++) { bzw. for (int i2 = 0; i2 < iEnd2; i2++) {
Also mach' mal aus dem <= ein <.
 
X

Xyz1

Gast
Vorsichtig bei solchen Mikrooptimierungen...
Code:
### Vari1 ###
Loop: 0 -> 193 ms (lCount = 2147483647) ==> Compiler optimiert f3 und lCount
Loop: 1 -> 132 ms (lCount = 4294967294) ==> JIT optimiert zusaetzlich
Loop: 2 -> 113 ms (lCount = 6442450941) ==> ...
Loop: 3 -> 113 ms (lCount = 8589934588)
Loop: 4 -> 121 ms (lCount = 10737418235)
Loop: 5 -> 118 ms (lCount = 12884901882)
Loop: 6 -> 116 ms (lCount = 15032385529)
Loop: 7 -> 115 ms (lCount = 17179869176)
Loop: 8 -> 114 ms (lCount = 19327352823)
Loop: 9 -> 119 ms (lCount = 21474836470)
-----
### Vari2 ###
Loop: 0 -> 2043 ms (lCount = 2147483647) ==> JIT optimiert f3 und lCount
Loop: 1 -> 2194 ms (lCount = 4294967294) ==> ...
Loop: 2 -> 2159 ms (lCount = 6442450941)
Loop: 3 -> 2312 ms (lCount = 8589934588)
Loop: 4 -> 2172 ms (lCount = 10737418235)
Loop: 5 -> 2172 ms (lCount = 12884901882)
Loop: 6 -> 2281 ms (lCount = 15032385529)
Loop: 7 -> 2282 ms (lCount = 17179869176)
Loop: 8 -> 2250 ms (lCount = 19327352823)
Loop: 9 -> 2187 ms (lCount = 21474836470)

Ähnliche Ausgabe wie bei dir. Erklärung steht schon dort.

Spaßeshalber schreibst du mal:
Java:
    public static void main(String[] args) {
        Vari1();
        System.out.println("-----");
        Vari2();
    }

    static volatile long lCount = 0;

    private static void Vari1() {
        System.out.println("### Vari1 ###");
        long lStartTime, lEndTime;
        float f1 = 0.123456789f, f2 = 0.123456789f, f3 = 0;
        int iEnd2 = Integer.MAX_VALUE - 1, iLoops = 10;
        long[][] lResult = new long[iLoops][2];

        for (int i1 = 0; i1 < iLoops; i1++) {
            lStartTime = System.currentTimeMillis();
            for (int i2 = 0; i2 <= Integer.MAX_VALUE - 1; i2++) {
                f3 = f1 * f2;
                lCount++;
            }
            lEndTime = System.currentTimeMillis();

            lResult[i1][0] = lEndTime - lStartTime;
            lResult[i1][1] = lCount;
        }
        for (int i = 0; i < lResult.length; i++) {
            System.out.printf("Loop: %d -> %d ms (lCount = %d)\n", i, lResult[i][0], lResult[i][1]);
        }
    }

    private static void Vari2() {
        System.out.println("### Vari2 ###");
        long lStartTime, lEndTime;
        lCount = 0;
        float f1 = 0.123456789f, f2 = 0.123456789f, f3 = 0;
        int iEnd2 = Integer.MAX_VALUE - 1, iLoops = 10;
        long[][] lResult = new long[iLoops][2];

        for (int i1 = 0; i1 < iLoops; i1++) {
            lStartTime = System.currentTimeMillis();
            for (int i2 = 0; i2 <= iEnd2; i2++) {
                f3 = f1 * f2;
                lCount++;
            }
            lEndTime = System.currentTimeMillis();

            lResult[i1][0] = lEndTime - lStartTime;
            lResult[i1][1] = lCount;
        }
        for (int i = 0; i < lResult.length; i++) {
            System.out.printf("Loop: %d -> %d ms (lCount = %d)\n", i, lResult[i][0], lResult[i][1]);
        }
    }

Setzt du lCount, f3, iEnd2 und/oder i2 volatile, DARF NICHTS OPTIMIERT WERDEN:
Code:
### Vari1 ###
Loop: 0 -> 18125 ms (lCount = 2147483647)
Loop: 1 -> 18016 ms (lCount = 4294967294)
Loop: 2 -> 18093 ms (lCount = 6442450941)
Loop: 3 -> 18078 ms (lCount = 8589934588)
Loop: 4 -> 18172 ms (lCount = 10737418235)
Loop: 5 -> 18094 ms (lCount = 12884901882)
Loop: 6 -> 18266 ms (lCount = 15032385529)
Loop: 7 -> 18109 ms (lCount = 17179869176)
Loop: 8 -> 18078 ms (lCount = 19327352823)
Loop: 9 -> 18094 ms (lCount = 21474836470)
-----
### Vari2 ###
Loop: 0 -> 18093 ms (lCount = 2147483647)
Loop: 1 -> 18094 ms (lCount = 4294967294)
Loop: 2 -> 18266 ms (lCount = 6442450941)
Loop: 3 -> 18047 ms (lCount = 8589934588)
Loop: 4 -> 18078 ms (lCount = 10737418235)
Loop: 5 -> 18156 ms (lCount = 12884901882)
Loop: 6 -> 18187 ms (lCount = 15032385529)
Loop: 7 -> 18094 ms (lCount = 17179869176)
Loop: 8 -> 18125 ms (lCount = 19327352823)
Loop: 9 -> 18094 ms (lCount = 21474836470)

Übrigens...: Deine Variablennamen sind scheiße gewählt.
 

JCODA

Top Contributor
@DerWissende
naja, volatile ist ja mehr eine zusätzliche Performancebremse, als "darf nicht optimiert werden".
Du behauptest f3 wird wegoptimiert. Dachte ich zunächst auch, aber ich habe mal ein sysout am ende eingefügt und erhalte gleiche Werte. Warum sollte lCount wegoptimiert werden? Es wird später noch für zuweisung und ausgabe benutzt....

Ich hab nun mal folgendes gemacht:

Java:
public class TestFloat {
    public static void main(String[] args) {    
        Vari1();
        System.out.println("-----");
        Vari2();
        System.out.println("-----");
        Vari3();
        System.out.println("-----");
        Vari4();
        System.out.println("-----");
        Vari5();
    }

    private static void Vari5() {
        System.out.println("### i2 < iEnd2 || i2 == iEnd2 (nonFinal)###");
        long lStartTime, lEndTime, lCount = 0;
        float f1 = 0.123456789f, f2 = 0.123456789f, f3 = 0;
        int iEnd2 = Integer.MAX_VALUE - 1, iLoops = 10;
        long[][] lResult = new long[iLoops][2];

        for (int i1 = 0; i1 < iLoops; i1++) {
            lStartTime = System.currentTimeMillis();
            for (int i2 = 0; i2 < iEnd2 || i2 == iEnd2; i2++) {
                f3 += f1 * f2;
                lCount++;
            }
            lEndTime = System.currentTimeMillis();

            lResult[i1][0] = lEndTime - lStartTime;
            lResult[i1][1] = lCount;
        }
        for (int i = 0; i < lResult.length; i++) {
            System.out.print(lResult[i][0]+ " ms ");
        }
        System.out.println();
        System.out.println(f3);
    }


    private static void Vari1() {
        System.out.println("### i2 <= Integer.MAX_VALUE (final)###");
        long lStartTime, lEndTime, lCount = 0;
        float f1 = 0.123456789f, f2 = 0.123456789f, f3 = 0;
        int iEnd2 = Integer.MAX_VALUE - 1, iLoops = 10;
        long[][] lResult = new long[iLoops][2];

        for (int i1 = 0; i1 < iLoops; i1++) {
            lStartTime = System.currentTimeMillis();
            for (int i2 = 0; i2 <= Integer.MAX_VALUE - 1; i2++) {
                f3 += f1 * f2;
                lCount++;
            }
            lEndTime = System.currentTimeMillis();

            lResult[i1][0] = lEndTime - lStartTime;
            lResult[i1][1] = lCount;
        }
        for (int i = 0; i < lResult.length; i++) {
            System.out.print(lResult[i][0]+ " ms ");
        }
        System.out.println();
        System.out.println(f3);
    }

    private static void Vari2() {
        System.out.println("### i2 <= iEnd2  (nonFinal)###");
        long lStartTime, lEndTime, lCount = 0;
        float f1 = 0.123456789f, f2 = 0.123456789f, f3 = 0;
        int iEnd2 = Integer.MAX_VALUE - 1, iLoops = 10;
        long[][] lResult = new long[iLoops][2];

        for (int i1 = 0; i1 < iLoops; i1++) {
            lStartTime = System.currentTimeMillis();
            for (int i2 = 0; i2 <= iEnd2; i2++) {
                f3 += f1 * f2;
                lCount++;
            }
            lEndTime = System.currentTimeMillis();

            lResult[i1][0] = lEndTime - lStartTime;
            lResult[i1][1] = lCount;
        }
        for (int i = 0; i < lResult.length; i++) {
            System.out.print(lResult[i][0]+ " ms ");
        }
        System.out.println();
        System.out.println(f3);
    }
    private static void Vari3() {
        System.out.println("### i2 <= iEnd2  (final)###");
        long lStartTime, lEndTime, lCount = 0;
        float f1 = 0.123456789f, f2 = 0.123456789f, f3 = 0;
        final int iEnd2 = Integer.MAX_VALUE - 1, iLoops = 10;
        long[][] lResult = new long[iLoops][2];

        for (int i1 = 0; i1 < iLoops; i1++) {
            lStartTime = System.currentTimeMillis();
            for (int i2 = 0; i2 <= iEnd2; i2++) {
                f3 += f1 * f2;
                lCount++;
            }
            lEndTime = System.currentTimeMillis();

            lResult[i1][0] = lEndTime - lStartTime;
            lResult[i1][1] = lCount;
        }
        for (int i = 0; i < lResult.length; i++) {
            System.out.print(lResult[i][0]+ " ms ");
        }
        System.out.println();
        System.out.println(f3);
    }

    private static void Vari4() {
        System.out.println("### i2 < iEnd2 (nonFinal)###");
        long lStartTime, lEndTime, lCount = 0;
        float f1 = 0.123456789f, f2 = 0.123456789f, f3 = 0;
        int iEnd2 = Integer.MAX_VALUE - 1, iLoops = 10;
        long[][] lResult = new long[iLoops][2];

        for (int i1 = 0; i1 < iLoops; i1++) {
            lStartTime = System.currentTimeMillis();
            for (int i2 = 0; i2 < iEnd2; i2++) {
                f3 += f1 * f2;
                lCount++;
            }
            lEndTime = System.currentTimeMillis();

            lResult[i1][0] = lEndTime - lStartTime;
            lResult[i1][1] = lCount;
        }
        for (int i = 0; i < lResult.length; i++) {
            System.out.print(lResult[i][0]+ " ms ");
        }
        System.out.println();
        System.out.println(f3);
    }
}
Hier dürfen ebenfalls die f3 "innerhalb" nicht wegoptimiert werden, weil ich nun f3 += f1 * f2; rechne. Und ich erhalte folgendes Ergebnis:

Code:
### i2 <= Integer.MAX_VALUE (final)###
1807 ms 1798 ms 1781 ms 1787 ms 1777 ms 1783 ms 1784 ms 1781 ms 1782 ms 1782 ms
262144.0
-----
### i2 <= iEnd2  (nonFinal)###
1775 ms 5408 ms 5407 ms 5395 ms 5376 ms 5417 ms 5394 ms 5401 ms 5415 ms 5435 ms
262144.0
-----
### i2 <= iEnd2  (final)###
1780 ms 1772 ms 1783 ms 1843 ms 1828 ms 1840 ms 1805 ms 1810 ms 1809 ms 1839 ms
262144.0
-----
### i2 < iEnd2 (nonFinal)###
1861 ms 1831 ms 1791 ms 1812 ms 1827 ms 1866 ms 1871 ms 1772 ms 1769 ms 1770 ms
262144.0
-----
### i2 < iEnd2 || i2 == iEnd2 (nonFinal)###
1773 ms 1769 ms 1766 ms 1768 ms 1783 ms 1787 ms 1790 ms 1782 ms 1766 ms 1765 ms
262144.0

Das heißt bei mir ist die Variante mit <= bei einer nonFinal Variable am langsamsten. Ein final macht's wieder schneller, oder eben ein ändern auf <. Witzigerweise ist die letzte Variante dann noch schneller(allerdings sagt das in diesem Fall nichts aus, wegen JIT, usw...).

Wenn ich raten müsste, würde ich sagen, bei der Variante mit <= und dem nonFinal läuft der erste Schleifendurchlauf normal durch. Und dann versucht JIT, Hotspot oder irgendetwas zu optimieren und "es klappt nicht".

Achso und ich hatte es auch mal kurz ohne JIT.Compiler mit dem Argument "-Djava.compiler=NONE" versucht. Allerdings dauert mir da selbst die erste Variante zu lang.
Hier die ersten beiden Varianten mit dem Argument:
### i2 <= Integer.MAX_VALUE (final)###
33965 ms 33688 ms 35550 ms 35893 ms 37691 ms 36763 ms 39476 ms 39666 ms 41132 ms 40797 ms
262144.0
-----
### i2 <= iEnd2 (nonFinal)###
34011 ms 34802 ms 33731 ms 36102 ms 36635 ms 31301 ms 30944 ms 31229 ms 31994 ms 34333 ms
262144.0

Übrigens: ... Die Variablennamen sind vollkommen in Ordnung. Das hier ist in keinster Hinsicht Produktivcode und soll nur dieses Phänomen zeigen.
 
Zuletzt bearbeitet:
X

Xyz1

Gast
@DerWissende
naja, volatile ist ja mehr eine zusätzliche Performancebremse, als "darf nicht optimiert werden".
Du behauptest f3 wird wegoptimiert. Dachte ich zunächst auch, aber ich habe mal ein sysout am ende eingefügt und erhalte gleiche Werte. Warum sollte lCount wegoptimiert werden? Es wird später noch für zuweisung und ausgabe benutzt....

Bei volatile dürfen die Variablen nicht in den Cache gelegt werden, dadurch kann und darf nix optimiert werden (da Änderungen "von Außen" jederzeit möglich). Compiler, JIT, Hotspot usw. optimiert auch nicht Variablen weg (unused), s. d. sie nicht mehr zur Verfügung ständen, sondern setzt halt deren Wert einmal und überspringt Schleife/Schleifendurchläufe.

Was im Einzelnen geschieht ==> Bytecode anschauen - und der Rest ist mehr oder weniger schwarze Magie.

Und: Produktivcode hin oder her (oder völlig unnötige/sinnlose Schleifendurchläufe + Variablen), leserliche Variablennamen sehen anders aus.

Wenn es Wärmepumpensteuerung heißt, nenne ich es auch waermepumpensteuerung - und nicht myLrgObjWrmpstrg o. Ä.
 

Meniskusschaden

Top Contributor
Der Bytecode der beiden Methoden sieht bei mir wie folgt aus:
Code:
  // Method descriptor #6 ()V
  // Stack: 7, Locals: 14
  private static void Vari1();
      0  getstatic java.lang.System.out : java.io.PrintStream [19]
      3  ldc <String "### Vari1 ###"> [38]
      5  invokevirtual java.io.PrintStream.println(java.lang.String) : void [27]
      8  lconst_0
      9  lstore 4 [lCount]
     11  ldc <Float 0.12345679> [40]
     13  fstore 6 [f1]
     15  ldc <Float 0.12345679> [40]
     17  fstore 7 [f2]
     19  fconst_0
     20  fstore 8 [f3]
     22  ldc <Integer 2147483646> [41]
     24  istore 9 [iEnd2]
     26  bipush 10
     28  istore 10 [iLoops]
     30  iload 10 [iLoops]
     32  iconst_2
     33  multianewarray long[][] [42]
     37  astore 11 [lResult]
     39  iconst_0
     40  istore 12 [i1]
     42  goto 104
     45  invokestatic java.lang.System.currentTimeMillis() : long [44]
     48  lstore_0 [lStartTime]
     49  iconst_0
     50  istore 13 [i2]
     52  goto 71
     55  fload 6 [f1]
     57  fload 7 [f2]
     59  fmul
     60  fstore 8 [f3]
     62  lload 4 [lCount]
     64  lconst_1
     65  ladd
     66  lstore 4 [lCount]
     68  iinc 13 1 [i2]
     71  iload 13 [i2]
     73  ldc <Integer 2147483646> [41]
     75  if_icmple 55
     78  invokestatic java.lang.System.currentTimeMillis() : long [44]
     81  lstore_2 [lEndTime]
     82  aload 11 [lResult]
     84  iload 12 [i1]
     86  aaload
     87  iconst_0
     88  lload_2 [lEndTime]
     89  lload_0 [lStartTime]
     90  lsub
     91  lastore
     92  aload 11 [lResult]
     94  iload 12 [i1]
     96  aaload
     97  iconst_1
     98  lload 4 [lCount]
    100  lastore
    101  iinc 12 1 [i1]
    104  iload 12 [i1]
    106  iload 10 [iLoops]
    108  if_icmplt 45
    111  iconst_0
    112  istore 12 [i]
    114  goto 167
    117  getstatic java.lang.System.out : java.io.PrintStream [19]
    120  ldc <String "Loop: %d -> %d ms (lCount = %d)\n"> [48]
    122  iconst_3
    123  anewarray java.lang.Object [3]
    126  dup
    127  iconst_0
    128  iload 12 [i]
    130  invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [50]
    133  aastore
    134  dup
    135  iconst_1
    136  aload 11 [lResult]
    138  iload 12 [i]
    140  aaload
    141  iconst_0
    142  laload
    143  invokestatic java.lang.Long.valueOf(long) : java.lang.Long [56]
    146  aastore
    147  dup
    148  iconst_2
    149  aload 11 [lResult]
    151  iload 12 [i]
    153  aaload
    154  iconst_1
    155  laload
    156  invokestatic java.lang.Long.valueOf(long) : java.lang.Long [56]
    159  aastore
    160  invokevirtual java.io.PrintStream.printf(java.lang.String, java.lang.Object[]) : java.io.PrintStream [61]
    163  pop
    164  iinc 12 1 [i]
    167  iload 12 [i]
    169  aload 11 [lResult]
    171  arraylength
    172  if_icmplt 117
    175  return
      Line numbers:
        [pc: 0, line: 13]
        [pc: 8, line: 14]
        [pc: 11, line: 15]
        [pc: 22, line: 16]
        [pc: 30, line: 17]
        [pc: 39, line: 20]
        [pc: 45, line: 22]
        [pc: 49, line: 23]
        [pc: 55, line: 25]
        [pc: 62, line: 26]
        [pc: 68, line: 23]
        [pc: 78, line: 28]
        [pc: 82, line: 30]
        [pc: 92, line: 31]
        [pc: 101, line: 20]
        [pc: 111, line: 33]
        [pc: 117, line: 35]
        [pc: 164, line: 33]
        [pc: 175, line: 37]
      Local variable table:
        [pc: 49, pc: 104] local: lStartTime index: 0 type: long
        [pc: 82, pc: 104] local: lEndTime index: 2 type: long
        [pc: 11, pc: 176] local: lCount index: 4 type: long
        [pc: 15, pc: 176] local: f1 index: 6 type: float
        [pc: 19, pc: 176] local: f2 index: 7 type: float
        [pc: 22, pc: 176] local: f3 index: 8 type: float
        [pc: 26, pc: 176] local: iEnd2 index: 9 type: int
        [pc: 30, pc: 176] local: iLoops index: 10 type: int
        [pc: 39, pc: 176] local: lResult index: 11 type: long[][]
        [pc: 42, pc: 111] local: i1 index: 12 type: int
        [pc: 52, pc: 78] local: i2 index: 13 type: int
        [pc: 114, pc: 175] local: i index: 12 type: int
      Stack map table: number of frames 6
        [pc: 45, full, stack: {}, locals: {_, _, _, _, long, float, float, float, int, int, long[][], int}]
        [pc: 55, full, stack: {}, locals: {long, _, _, long, float, float, float, int, int, long[][], int, int}]
        [pc: 71, same]
        [pc: 104, full, stack: {}, locals: {_, _, _, _, long, float, float, float, int, int, long[][], int}]
        [pc: 117, same]
        [pc: 167, same]
Code:
  // Method descriptor #6 ()V
  // Stack: 7, Locals: 14
  private static void Vari2();
      0  getstatic java.lang.System.out : java.io.PrintStream [19]
      3  ldc <String "### Vari2 ###"> [81]
      5  invokevirtual java.io.PrintStream.println(java.lang.String) : void [27]
      8  lconst_0
      9  lstore 4 [lCount]
     11  ldc <Float 0.12345679> [40]
     13  fstore 6 [f1]
     15  ldc <Float 0.12345679> [40]
     17  fstore 7 [f2]
     19  fconst_0
     20  fstore 8 [f3]
     22  ldc <Integer 2147483646> [41]
     24  istore 9 [iEnd2]
     26  bipush 10
     28  istore 10 [iLoops]
     30  iload 10 [iLoops]
     32  iconst_2
     33  multianewarray long[][] [42]
     37  astore 11 [lResult]
     39  iconst_0
     40  istore 12 [i1]
     42  goto 104
     45  invokestatic java.lang.System.currentTimeMillis() : long [44]
     48  lstore_0 [lStartTime]
     49  iconst_0
     50  istore 13 [i2]
     52  goto 71
     55  fload 6 [f1]
     57  fload 7 [f2]
     59  fmul
     60  fstore 8 [f3]
     62  lload 4 [lCount]
     64  lconst_1
     65  ladd
     66  lstore 4 [lCount]
     68  iinc 13 1 [i2]
     71  iload 13 [i2]
     73  iload 9 [iEnd2]
     75  if_icmple 55
     78  invokestatic java.lang.System.currentTimeMillis() : long [44]
     81  lstore_2 [lEndTime]
     82  aload 11 [lResult]
     84  iload 12 [i1]
     86  aaload
     87  iconst_0
     88  lload_2 [lEndTime]
     89  lload_0 [lStartTime]
     90  lsub
     91  lastore
     92  aload 11 [lResult]
     94  iload 12 [i1]
     96  aaload
     97  iconst_1
     98  lload 4 [lCount]
    100  lastore
    101  iinc 12 1 [i1]
    104  iload 12 [i1]
    106  iload 10 [iLoops]
    108  if_icmplt 45
    111  iconst_0
    112  istore 12 [i]
    114  goto 167
    117  getstatic java.lang.System.out : java.io.PrintStream [19]
    120  ldc <String "Loop: %d -> %d ms (lCount = %d)\n"> [48]
    122  iconst_3
    123  anewarray java.lang.Object [3]
    126  dup
    127  iconst_0
    128  iload 12 [i]
    130  invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [50]
    133  aastore
    134  dup
    135  iconst_1
    136  aload 11 [lResult]
    138  iload 12 [i]
    140  aaload
    141  iconst_0
    142  laload
    143  invokestatic java.lang.Long.valueOf(long) : java.lang.Long [56]
    146  aastore
    147  dup
    148  iconst_2
    149  aload 11 [lResult]
    151  iload 12 [i]
    153  aaload
    154  iconst_1
    155  laload
    156  invokestatic java.lang.Long.valueOf(long) : java.lang.Long [56]
    159  aastore
    160  invokevirtual java.io.PrintStream.printf(java.lang.String, java.lang.Object[]) : java.io.PrintStream [61]
    163  pop
    164  iinc 12 1 [i]
    167  iload 12 [i]
    169  aload 11 [lResult]
    171  arraylength
    172  if_icmplt 117
    175  return
      Line numbers:
        [pc: 0, line: 41]
        [pc: 8, line: 42]
        [pc: 11, line: 43]
        [pc: 22, line: 44]
        [pc: 30, line: 45]
        [pc: 39, line: 48]
        [pc: 45, line: 50]
        [pc: 49, line: 51]
        [pc: 55, line: 53]
        [pc: 62, line: 54]
        [pc: 68, line: 51]
        [pc: 78, line: 56]
        [pc: 82, line: 58]
        [pc: 92, line: 59]
        [pc: 101, line: 48]
        [pc: 111, line: 61]
        [pc: 117, line: 63]
        [pc: 164, line: 61]
        [pc: 175, line: 65]
      Local variable table:
        [pc: 49, pc: 104] local: lStartTime index: 0 type: long
        [pc: 82, pc: 104] local: lEndTime index: 2 type: long
        [pc: 11, pc: 176] local: lCount index: 4 type: long
        [pc: 15, pc: 176] local: f1 index: 6 type: float
        [pc: 19, pc: 176] local: f2 index: 7 type: float
        [pc: 22, pc: 176] local: f3 index: 8 type: float
        [pc: 26, pc: 176] local: iEnd2 index: 9 type: int
        [pc: 30, pc: 176] local: iLoops index: 10 type: int
        [pc: 39, pc: 176] local: lResult index: 11 type: long[][]
        [pc: 42, pc: 111] local: i1 index: 12 type: int
        [pc: 52, pc: 78] local: i2 index: 13 type: int
        [pc: 114, pc: 175] local: i index: 12 type: int
      Stack map table: number of frames 6
        [pc: 45, full, stack: {}, locals: {_, _, _, _, long, float, float, float, int, int, long[][], int}]
        [pc: 55, full, stack: {}, locals: {long, _, _, long, float, float, float, int, int, long[][], int, int}]
        [pc: 71, same]
        [pc: 104, full, stack: {}, locals: {_, _, _, _, long, float, float, float, int, int, long[][], int}]
        [pc: 117, same]
        [pc: 167, same]
Der einzige Unterschied ist Zeile 73:
Code:
// Variante 1:
     73  ldc <Integer 2147483646> [41]

// Variante 2
     73  iload 9 [iEnd2]
Ich denke, das passt gut zu den bisher genannten Erklärungen.

Zur Zunahme der Laufzeit in Variante 2 ab dem zweiten Schleifendurchlauf habe ich die Vermutung, dass sich iEnd2 aufgrund folgender Zeile vor dem ersten Schleifendurchlauf noch im Cache befindet:
Code:
24  istore 9 [iEnd2]
Weiter vermute ich, dass iEnd2 durch die Operationen nach dem ersten Schleifendurchlauf aus dem Cache verdrängt wird, so dass der Zugriff dann langsamer wird. Die Verdrängung müsste meines Erachtens dann innerhalb folgender Zeilen passieren:
Code:
     78  invokestatic java.lang.System.currentTimeMillis() : long [44]
     81  lstore_2 [lEndTime]
     82  aload 11 [lResult]
     84  iload 12 [i1]
     86  aaload
     87  iconst_0
     88  lload_2 [lEndTime]
     89  lload_0 [lStartTime]
     90  lsub
     91  lastore
     92  aload 11 [lResult]
     94  iload 12 [i1]
     96  aaload
     97  iconst_1
     98  lload 4 [lCount]
    100  lastore
Ich habe leider gerade keine Idee, wie man das nachweisen kann, von daher ist der Erklärungsversuch nur eine Spekulation.
 
X

Xyz1

Gast
JIT arbeitet wie eine Blackbox - da lässt sich gar nichts nachweisen. (Es sei denn, man arbeitet bei Oracle oder so.)

Das einzige, was sich zuverlässig sagen lässt, ist, dass Optimierungen es schneller und nicht langsamer machen - deswegen heißen sie ja Optimierungen.

Ich denke, Schleifendurchlauf wird langsamer, sind bei euch Ausnahmeerscheinungen. Bei mir wird nix langsamer (außer er kann die Konstante direkt einsetzen, dann wird's schneller.).
 

Meniskusschaden

Top Contributor
JIT arbeitet wie eine Blackbox - da lässt sich gar nichts nachweisen. (Es sei denn, man arbeitet bei Oracle oder so.)
Eigentlich ging es bei meiner Spekulation darum, dass das Verlangsamungs-Phänomen nicht durch JIT-Compilierung, sondern durch Cache-Effekte verursacht wird. Inzwischen glaube ich aber ohnehin nicht mehr an meine Erklärung. Sie passt nicht zu den Werten.
Das einzige, was sich zuverlässig sagen lässt, ist, dass Optimierungen es schneller und nicht langsamer machen - deswegen heißen sie ja Optimierungen.
Klar, das ist natürlich das Ziel von Optimierungen. Aber damit ist ja noch nicht garantiert, dass Optimierungsversuche niemals kontraproduktiv sind.;)
Ich denke, Schleifendurchlauf wird langsamer, sind bei euch Ausnahmeerscheinungen.
Das glaube ich nicht. Offenbar tritt der Effekt bei @thecain und @DerWissende nicht auf, bei @JCODA geht es aus dem Post nicht eindeutig hervor und bei @Framework und mir tritt es auf. Ich habe es sogar auf drei Rechnern getestet und es war jedesmal zu beobachten. Das ist zu häufig für eine Ausnahmeerscheinung.

Meine Werte:
Code:
### Vari1 ###
Loop: 0 -> 121 ms (lCount = 2147483647)
Loop: 1 -> 42 ms (lCount = 4294967294)
Loop: 2 -> 39 ms (lCount = 6442450941)
Loop: 3 -> 39 ms (lCount = 8589934588)
Loop: 4 -> 40 ms (lCount = 10737418235)
Loop: 5 -> 39 ms (lCount = 12884901882)
Loop: 6 -> 39 ms (lCount = 15032385529)
Loop: 7 -> 39 ms (lCount = 17179869176)
Loop: 8 -> 39 ms (lCount = 19327352823)
Loop: 9 -> 39 ms (lCount = 21474836470)
-----
### Vari2 ###
Loop: 0 -> 121 ms (lCount = 2147483647)
Loop: 1 -> 802 ms (lCount = 4294967294)
Loop: 2 -> 792 ms (lCount = 6442450941)
Loop: 3 -> 793 ms (lCount = 8589934588)
Loop: 4 -> 792 ms (lCount = 10737418235)
Loop: 5 -> 794 ms (lCount = 12884901882)
Loop: 6 -> 797 ms (lCount = 15032385529)
Loop: 7 -> 794 ms (lCount = 17179869176)
Loop: 8 -> 793 ms (lCount = 19327352823)
Loop: 9 -> 791 ms (lCount = 21474836470)
Wenn ich nun der äußeren Schleife ohne größeren Sinn eine Ausgabeanweisung hinzufüge, ändert sich beim ersten Schleifendurchlauf nichts, ab dem zweiten wird es aber noch einmal erheblich langsamer, obwohl sich die zusätzliche Anweisung ausserhalb der Meßpunkte befindet:
Code:
### Vari1 ###
Loop: 0 -> 122 ms (lCount = 2147483647)
Loop: 1 -> 43 ms (lCount = 4294967294)
Loop: 2 -> 39 ms (lCount = 6442450941)
Loop: 3 -> 39 ms (lCount = 8589934588)
Loop: 4 -> 39 ms (lCount = 10737418235)
Loop: 5 -> 40 ms (lCount = 12884901882)
Loop: 6 -> 39 ms (lCount = 15032385529)
Loop: 7 -> 39 ms (lCount = 17179869176)
Loop: 8 -> 39 ms (lCount = 19327352823)
Loop: 9 -> 39 ms (lCount = 21474836470)
-----
### Vari2 ###
Hallo
Hallo
Hallo
Hallo
Hallo
Hallo
Hallo
Hallo
Hallo
Hallo
Loop: 0 -> 122 ms (lCount = 2147483647)
Loop: 1 -> 4011 ms (lCount = 4294967294)
Loop: 2 -> 4031 ms (lCount = 6442450941)
Loop: 3 -> 4033 ms (lCount = 8589934588)
Loop: 4 -> 4028 ms (lCount = 10737418235)
Loop: 5 -> 4019 ms (lCount = 12884901882)
Loop: 6 -> 4032 ms (lCount = 15032385529)
Loop: 7 -> 4019 ms (lCount = 17179869176)
Loop: 8 -> 4027 ms (lCount = 19327352823)
Loop: 9 -> 4036 ms (lCount = 21474836470)
Die Schleife für Variante 2 sieht dabei wie folgt aus:
Java:
        for (int i1 = 0; i1 < iLoops; i1++)
        {
            System.out.println("Hallo");
            lStartTime = System.currentTimeMillis();
            for (int i2 = 0; i2 <= iEnd2; i2++)
            {
                f3 = f1 * f2;
                lCount++;
            }
            lEndTime = System.currentTimeMillis();
            lResult[i1][0] = lEndTime - lStartTime;
            lResult[i1][1] = lCount;
        }
Vielleicht hat es ja auch etwas mit der Verteilung auf mehrere Prozessorkerne zu tun und es bremst sich beim Zugriff auf gemeinsam benötigte Variablen aus.
 

Tobse

Top Contributor
Um zur Verwirrung beizutragen hab ichs bei mir auch laufen lassen (die Original-Version vom TE). Ich sehe nicht, wie manche hier auf < 50ms für die Schleifendurchläufe bei Variante 1 kommen:

Code:
### Vari1 ###
Loop: 0 -> 4847 ms (lCount = 2147483647)
Loop: 1 -> 4857 ms (lCount = 4294967294)
Loop: 2 -> 4812 ms (lCount = 6442450941)
Loop: 3 -> 4824 ms (lCount = 8589934588)
Loop: 4 -> 4811 ms (lCount = 10737418235)
Loop: 5 -> 4839 ms (lCount = 12884901882)
Loop: 6 -> 4863 ms (lCount = 15032385529)
Loop: 7 -> 4832 ms (lCount = 17179869176)
Loop: 8 -> 4814 ms (lCount = 19327352823)
Loop: 9 -> 4842 ms (lCount = 21474836470)
-----
### Vari2 ###
Loop: 0 -> 5096 ms (lCount = 2147483647)
Loop: 1 -> 5095 ms (lCount = 4294967294)
Loop: 2 -> 5251 ms (lCount = 6442450941)
Loop: 3 -> 5403 ms (lCount = 8589934588)
Loop: 4 -> 5229 ms (lCount = 10737418235)
Loop: 5 -> 5185 ms (lCount = 12884901882)
Loop: 6 -> 5255 ms (lCount = 15032385529)
Loop: 7 -> 5213 ms (lCount = 17179869176)
Loop: 8 -> 5170 ms (lCount = 19327352823)
Loop: 9 -> 5207 ms (lCount = 21474836470)

Bei mir ist der Unterschied aber auch marginal (aber trotzdem vorhanden); In Variante 2 wird es über die Zeit auch nur wenig langsamer; kein Krasser Unterschied zwischen dem ersten und letzten Durchlauf.

Hier ist eine SE JRE 1.8u66 auf einem i5-4430 mit DDR3-RAM im Einsatz. (BTW: Während der Ausführung war ein Kern 100% ausgelastet, der Rest hat nix gemacht. Ergo ist es kein Threading-Problem.)
 
X

Xyz1

Gast
Bei mir ist nur 1 physikalischer Kern während der Ausführung voll ausgelastet, die anderen quasi nicht. Also zwei gleiche/identische Schleifendurchläufe, in denen exakt das gleiche passiert, werden langsamer, klingt für mich merkwürdig. Ein paar Schwankungen bei der Ausführung neben einem Benutzersystem (Prozesse Threads Programme Dienste Hintergrund und was sonst noch so spukt) sind natürlich immer gegeben...
 

Flown

Administrator
Mitarbeiter
Erstmal alle hier Diskutierenden: http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java

Dann einfach mal JMH anwerfen, Code auf das Wesentliche reduzieren:
Java:
import java.util.concurrent.TimeUnit;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;

@Fork(3)
@BenchmarkMode(Mode.AverageTime)
@Measurement(iterations = 10, timeUnit = TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
@Threads(1)
@Warmup(iterations = 5, timeUnit = TimeUnit.NANOSECONDS)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class MyBenchmark {
  
  @Benchmark
  public float vari1() {
    float f1 = 0.123456789f, f2 = 0.123456789f, f3 = 0;
    
    for (int i = 0; i <= Integer.MAX_VALUE - 1; i++) {
      f3 = f1 * f2;
    }
    return f3;
  }
  
  @Benchmark
  public float vari2() {
    float f1 = 0.123456789f, f2 = 0.123456789f, f3 = 0;
    int end = Integer.MAX_VALUE - 1;
    
    for (int i = 0; i <= end; i++) {
      f3 = f1 * f2;
    }
    return f3;
  }
}
Ergebnisse anzeigen lassen:
Code:
# Run complete. Total time: 00:01:31

Benchmark          Mode  Cnt  Score   Error  Units
MyBenchmark.vari1  avgt   30  2,289 ± 0,005  ns/op
MyBenchmark.vari2  avgt   30  2,292 ± 0,006  ns/op
=> Staunen!
 
X

Xyz1

Gast
Es geht nicht darum, einen korrekten Mikrotest zu schreiben, @Flown . Sondern es wurde vom TO ein Quellcode gegeben, den wir erklären sollen - nicht zerpflücken oder sonst was mit machen. Alle Beteiligten habn sich auch daran gehalten. In dem Punkt bin ich also anderer Meinung als du. Und wie du siehst, setzt er In the Wild nicht die Variable als Konstante ein. Nur in deinem speziellen Fall ist das so.
 

Meniskusschaden

Top Contributor
Ich sehe das ähnlich wie @DerWissende. Der Benchmark ist zwar interessant, mich interessiert inzwischen aber mehr, wie sich die seltsamen Werte erklären. Da hilft der Benchmark (zumindest mir) nicht weiter. Da der Effekt bisher nur dann aufgetreten ist, wenn mehrere Kerne aktiv waren, vermute ich, dass jemand (JRE? OS? Prozessor?) versucht, zu parallelisieren und dass das nicht gut funktioniert. Im Moment verstehe ich aber weder, ob die Vermutung überhaupt zutrifft, noch wer hier parallelisiert und auch nicht warum es kontraproduktiv ist.
 

Flown

Administrator
Mitarbeiter
Also händisch Benchmarktest schreiben, hast du jede Menge Noise drinnen (wie man auch sieht - GC, JIT, deoptimizations, etc.). Man kann nie genau feststellen, was gerade passiert, ist auch Sache von HotSpot und der VM. Wenn man hier diskutieren will, sollte man sich mal einlesen in JVM Optimierungen, JIT-Compiling.

Was ich absolut nicht verstehe ist, wie man schlichtweg falsche Benchmarks diskutiert, da offensichtlich nicht der Code gemessen wird (was ich auch bewiesen habe), sondern eine oben genannten "Optimierung" oder VM Funktionalität - wo man eben nur raten kann.
 

Meniskusschaden

Top Contributor
Was ich absolut nicht verstehe ist, wie man schlichtweg falsche Benchmarks diskutiert, da offensichtlich nicht der Code gemessen wird (was ich auch bewiesen habe), sondern eine oben genannten "Optimierung" oder VM Funktionalität - wo man eben nur raten kann.
Weil es einfach ein interessanter Effekt ist, der einem nicht jeden Tag über den Weg läuft. Wann soll man es denn sonst diskutieren, wenn nicht bei so einer Gelegenheit? Das bleibt doch auch nach deinen Benchmarks interessant. Mag ja sein, dass man da "nur raten kann". Aber ich glaube nicht, dass das so offensichtlich ist, dass man es als diskussionsunwürdig abtun sollte.

Im übrigen wirft dein Benchmark eine neue Frage auf: Was nützt der Benchmark überhaupt, wenn er die beiden Varianten als gleichwertig ausweist, obwohl Variante2 in der Praxis dramatisch langsamer als Variante 1 ist. Bei mir sind es für einen kompletten Durchlauf 476ms für Variante 1 und 7242 für Variante 2. Ich habe versucht, einiges zu variieren, bekomme aber immer ähnliche Ergebnisse. Ich würde im Moment eindeutig Variante 1 vorziehen, aber dein Benchmark zeigt das nicht.
 

Meniskusschaden

Top Contributor
Und wie du siehst, setzt er In the Wild nicht die Variable als Konstante ein. Nur in deinem speziellen Fall ist das so.
Ich bin nicht sicher, ob ich verstehe was du meinst, aber das Pendant zur einzig unterschiedlichen Bytecode-Zeile im Code des TE ist auch im Bytecode der Tests von @Flown unterschiedlich. Das war die Zeile, die in Variante 1 Integer.MAX_VALUE-1 enthält und in Variante2 auf die Variablentabelle zugreift. Insofern glaube ich schon, dass er den Code in dieser Hinsicht auf das Wesentliche reduziert hat.
 

Flown

Administrator
Mitarbeiter
Weil es einfach ein interessanter Effekt ist, der einem nicht jeden Tag über den Weg läuft.
Er läuft bei jedem Code, den man schreibt, über den Weg, nur man sieht ihn nicht - wer misst schon ständig seinen Code? -, das sollte jedem klar sein. Bzw. jetzt sollte es jedem klar sein, dass die JVM nicht nur einfach stupide Code ausführt.

Was nützt der Benchmark überhaupt, wenn er die beiden Varianten als gleichwertig ausweist
Das es nicht am geschriebenen Code liegt und somit wirft sich für mich die Frage auf, warum man kleine Veränderungen am Programm vornimmt, wenn sie und so Äquivalent sind.

Wenn man genau wissen will, was hier "schief" läuft, dann sollte man sich mit JVM Tuning beschäftigen und eine Funktionalität nach dem anderen Ausschalten - falls möglich. Wenn das dann nicht zum gewünschten Ziel führt, dann liegt es schlichtweg am OS. Da kann dann noch viel, viel, viel, .... mehr passieren. Vom Caching/Paging bishin zur ALU Optimierung. Das beste Beispiel, was mir so in Erinnerung schwebt ist und zu diesem Thema passt: http://stackoverflow.com/questions/...process-a-sorted-array-than-an-unsorted-array

Versteht mich nicht falsch, dieses Thema ist absolut diskutabel. Es ist nur so, dass Optimierungen - sei es von der JVM/OS - nicht deterministisch sind und jederzeit auftreten können (was eventuell auch JVM Profiling tools messen könnten?). Es macht schon einen Unterschied, wenn das OS 32Bit oder 64Bit hat.
Darum sollte man die "Optimierung" die zu diesen Zeiten führt identifizieren und extrahieren. Dazu sind eben ein großes Wissen an Optimierungsstrategien - allgemein Compilertechniken - nötig und wenn es denn dann nicht am Compiler (generell JVM) liegt, dann auch noch ein großes Wissen an Rechnerarchitekturen. Dann sollte man auch noch wissen wie man diese Optimierungen by-passen kann, damit auch sichergestellt ist, dass es auch an dieser "einen Sache" liegt.

Das erste ist schon mal ausgeschlossen worden, dass es am Code liegt, den Rest obliegt den Interessenten und auch den Könnern.
 
X

Xyz1

Gast
Nebenbei bemerkt: Bei multithreaded tritt der Effekt nicht mehr auf. Hier zum Nachmessen:

Variante 1:
Java:
    public static void main(String[] args) throws InterruptedException {
        final AtomicLong count = new AtomicLong();
        int anzahlThreads = 3;
        ExecutorService es = Executors.newFixedThreadPool(anzahlThreads);
        long t1 = System.currentTimeMillis();
        for (int i = 0; i < anzahlThreads; i++) {
            es.execute(new Runnable() {
                @Override
                public void run() {
                    float f1 = 0.123456789f, f2 = 0.123456789f, f3 = 0;
                    for (int j = 0; j < Integer.MAX_VALUE / 8; j++) {
                        f3 = f1 * f2;
                        count.incrementAndGet();
                    }
                }
            });
        }
        es.shutdown();
        es.awaitTermination(60, TimeUnit.SECONDS);
        long t2 = System.currentTimeMillis();
        System.out.println(t2 - t1);
        System.out.println(count.get());
        System.out.println((long) Integer.MAX_VALUE / 8 * anzahlThreads);

        es = Executors.newFixedThreadPool(anzahlThreads);
        t1 = System.currentTimeMillis();
        for (int i = 0; i < anzahlThreads; i++) {
            es.execute(new Runnable() {
                @Override
                public void run() {
                    float f1 = 0.123456789f, f2 = 0.123456789f, f3 = 0;
                    int to = Integer.MAX_VALUE / 8;
                    for (int j = 0; j < to; j++) {
                        f3 = f1 * f2;
                        count.incrementAndGet();
                    }
                }
            });
        }
        es.shutdown();
        es.awaitTermination(60, TimeUnit.SECONDS);
        t2 = System.currentTimeMillis();
        System.out.println(t2 - t1);
        System.out.println(count.get());
        System.out.println((long) Integer.MAX_VALUE / 8 * anzahlThreads * 2);
    }

Variante 2:
Java:
    public static void main(String[] args) throws InterruptedException {
        final AtomicLong count = new AtomicLong();
        int anzahlThreads = 3;
        ExecutorService es = Executors.newFixedThreadPool(anzahlThreads);
        long t1 = System.currentTimeMillis();
        for (int i = 0; i < anzahlThreads; i++) {
            es.execute(new Runnable() {
                float f1 = 0.123456789f, f2 = 0.123456789f, f3 = 0;
                @Override
                public void run() {
                    for (int j = 0; j < Integer.MAX_VALUE / 8; j++) {
                        f3 = f1 * f2;
                        count.incrementAndGet();
                    }
                }
            });
        }
        es.shutdown();
        es.awaitTermination(60, TimeUnit.SECONDS);
        long t2 = System.currentTimeMillis();
        System.out.println(t2 - t1);
        System.out.println(count.get());
        System.out.println((long) Integer.MAX_VALUE / 8 * anzahlThreads);

        es = Executors.newFixedThreadPool(anzahlThreads);
        t1 = System.currentTimeMillis();
        for (int i = 0; i < anzahlThreads; i++) {
            es.execute(new Runnable() {
                float f1 = 0.123456789f, f2 = 0.123456789f, f3 = 0;
                int to = Integer.MAX_VALUE / 8;
                @Override
                public void run() {
                    for (int j = 0; j < to; j++) {
                        f3 = f1 * f2;
                        count.incrementAndGet();
                    }
                }
            });
        }
        es.shutdown();
        es.awaitTermination(60, TimeUnit.SECONDS);
        t2 = System.currentTimeMillis();
        System.out.println(t2 - t1);
        System.out.println(count.get());
        System.out.println((long) Integer.MAX_VALUE / 8 * anzahlThreads * 2);
    }

Imports:
Java:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

Ausgabe:
Code:
26313
805306365
805306365
26094
1610612730
1610612730

(Variante 2 ist diesmal sogar "marginal" schneller.)

Das ist aber auch verständlich, da wegen der gemeinsam genutzten Ressource nicht optimiert werden kann/darf.

@Flown kann ja wieder einen korrekten Benchmark schreiben. ;)
 

Tobse

Top Contributor
Na dann würde ich mal sagen: Wer weiss, dass er nichts weiss, weiss mehr als der, der nicht weiss, dass er nichts weiss. Wir wissen nicht, was der Grund ist und damit wissen wir mehr als Vorher.

Auch wenn ich mit den Benchmarks von @Flown ebenfalls nicht 100% einverstanden bin, muss ich ihm beipflichten, dass wir nur äußerst schwer alle Variablen und Details werden ausmerzen können. Schlussendlich sind hier wahrscheinlich so viele verschiedene Java-Versionen (sowohl Compiler als auch Runtime) im Einsatz wie User in diesem Thread.
 
X

Xyz1

Gast
Das wird wohl ein Endlostheater... eh... sorry, ich meine -Thread. Und deine Sprüche kannst du dir sparen, @Tobse .

Und richtig heißt es, wer weiß, dass er NICHT weiß,... der ist dumm.

Jetzt gebe ich euch schon 4 multithreaded Varianten und dann sowas.

Selbst wenn wir uns auf ein JDK/JRE einigen, spielt immernoch das Benutzersystem und 1000 weitere Faktoren mit rein, selbst bei einer virtuellen Maschine, auf der zufällig Java lauffähig wäre. Solange du nicht bei Oracle oder wer-weiß-wo arbeitest, kannst du nicht sagen, diese und jene Laufzeitoptimierung ist der Grund für dieses und jenes. Und die vielen Compiler- und Laufzeitoptimierungen lassen sich auch nicht sukzessive Ein- und Ausschalten: http://stackoverflow.com/questions/4997513/javac-optimization-flags .

Optimization in Java is mostly done by the JIT compiler at runtime. Hence there is no point trying to instruct it to optimize a certain way at compile time (when it is creating only bytecode anyway). The JIT will almost surely make better decisions on the spot, knowing the exact environment and observing the actual patterns of execution of specific parts of your code.

There are some specific compiler options affecting performance, but these are for tuning the JVM (including the garbage collector) rather than optimizing the code itself.
 

Kababär

Top Contributor
Aus "theoretische Informatik" weiß ich, dass Konstanten auf realen Rechnern nicht in konstanter Zeit berechenbar sind.
Da wir dort allerdings ein universelles Kostenmodell für unser Berechnungsmodell gewählt haben, kann ich keine Laufzeitanalyse vornehmen, da es nicht sehr realistisch ist.
Was mir aber hier aus dem Thread ersichtlich ist:
Wer seinen Code nicht optimal schreibt (blockt eine Methode die andere? Haben beide Methoden unabhängig gleich viel Ressourcen? Laufen beide Methoden unter den selben Bedingungen?), kann nicht erwarten, dass die zu vergleichenden Methoden in sinnvoller Beziehung zueinander stehen.
Der Benchmark-Test von Flown zeigt, dass beide Methoden in etwa gleich gut sind.
Wieso hatte der TE also so große Unterschiede, obwohl der Benchmark etwas ganz anderes zeigte?
Weil sein Code nicht optimal war bzw. die Bedingungen nicht erfüllt waren, um eine wirkliche Laufzeitanalyse durchzuführen.
Erstellt man die notwendigen Bedingungen wie DerWissende es getan hat, so geht der Benchmark-Test und die Laufzeitanalyse Hand in Hand.

Ich finde es gut, dass Flown einen Benchmark-Test macht. Das zeigt schließlich das Ergebnis, das man zu erwarten hat (hier: beide Methoden in etwa gleich schnell).
Ergibt meine Laufzeitanalyse was komplett anderes (hier: die riesigen Unterschiede der Laufzeiten vom TE), muss etwas am Code optimiert werden.
Über die Gründe können wir nur rätseln (Threads, Blocking in der Hardware, Neuzuordnung von Speicher, nicht konstante Geschwindigkeiten beim Rechnen von großen Konstanten, etc).
Wichtig ist doch nur, dass wir Abhängigkeiten erkennen sollten und versuchen sollten, diese aufzulösen, wann immer es sinnvoll ist, auch wenn das mehr Arbeit benötigt.
 
X

Xyz1

Gast
So etwas ähnliches meinte ich mit: In the Wild, unter realen Bedingungen, nicht aus dem Kontext nehmen, nicht reduzieren, nicht vereinfachen usw. wie @Kababär es sagte.

Editierung:
Aber wir haben ein Phänomen, das sich nur schwer ohne umfangreiches Internes Wissen erklären/begründen/argumentieren lässt.
 

Kababär

Top Contributor
Meine Argumentation zielt aber eher darauf, dass man solche Vergleiche kontextfrei durchführen sollte.

Wenn ein Bauarbeiter an der Außenfassade eines Hauses arbeitet und nach Feierabend einfach nachhause geht und das Gerümpel überall liegen lässt und nun verglichen wird, wie lange ein 2ter Bauarbeiter braucht für die gleiche Arbeit, unter der Voraussetzung dass beide Arbeiter gleich schnell arbeiten, wird man feststellen, dass Arbeiter 1 schneller ist, da Arbeiter 2 erstmal das Gerümpel wegräumen muss, um zu arbeiten (TE-Test).
Also löse ich den Kontext auf indem ich zur gemessenen Aufgabenstellung hinzufüge, dass beide Arbeiter auch aufräumen (Benchmark-Test <=> DerWissende-Test). So ist es erst real, wobei ich denke, dass Kontext ein falsch gewähltes Wort ist.
 
X

Xyz1

Gast
Das lässt sich einfach umgehen, indem einmal Arbeiter 1 anfängt und dann Arbeiter 2, und einmal Arbeiter 2 anfängt und dann Arbeiter 1. Aber ich bin gar kein Bauarbeiter - du etwa? Ich weiß nicht, was auf dem Bau alles für Utensilien befindlich sind. Aber es war ja auch nur eine Metapher. Ich befürchte, um in der Metapher zu bleiben, dass so wenig aufzuräumen ist und so wenig aufgeräumt wird, dass das Phänomen das gleiche wäre, finge man mit Variante 2 zuerst an.

Aber das ist jetzt alles nur etwas Blabla. Wie gesagt, von Bauarbeiter weiß ich gar nicht.
 

Meniskusschaden

Top Contributor
Ich befürchte, um in der Metapher zu bleiben, dass so wenig aufzuräumen ist und so wenig aufgeräumt wird, dass das Phänomen das gleiche wäre, finge man mit Variante 2 zuerst an.
Das und viele andere Variationen habe ich getestet und es war immer Variante 2 deutlich schlechter.

Ich glaube nicht, dass die Metapher von @Kababär hier zutrifft. Der Unterschied ist zu groß, zu einseitig und zu reproduzierbar, als dass man es mit allgemeinen Effekten abtun könnte. Zugegebenermassen waren einige unserer Tests nicht unbedingt professionell, aber aus ihnen kann man immerhin eine klare Handlungsempfehlung ableiten, nämlich Variante 1 zu nutzen oder Variante 2 zu verbessern, etwa gemäß dem Vorschlag von @JCODA.

Wenn Benchmark-Ergebnisse und Praxisbeobachtungen nicht übereinstimmen, sollte man nach Erklärungen suchen. Wenn man sie findet (Arbeiter haben nicht aufgeräumt;)) ist es gut. Wenn man sie nicht findet, halte ich es aber für angebracht, auch zu hinterfragen, ob der Benchmark vielleicht gar nicht das Wesentliche gemessen hat.

Ich habe inzwischen auch ein wenig mit JMH experimentiert und gelegentlich auch damit schlechtere Ergebnisse für Variante2 erzielt. Das ließ sich aber nicht reproduzieren. Fast immer waren aber beide Varianten gleich schnell, so dass ich die Ausreisser eher darauf zurückführe, dass da vielleicht gerade noch irgend etwas Anderes auf meinem Rechner aktiv geworden ist.

Mit meinem Wissensstand werde ich wohl nicht mehr viel heraus finden, so dass ich in dieser Sache wahrscheinlicht nicht mehr weiter forsche. Solange ich nichts besseres höre, gebe ich mich mit folgender Spekulation als Erklärung zufrieden:
Bei Variante 1 ist der erste Schleifendurchlauf langsam, alle folgenden sind deutlich schneller. Das ist das, was man als normales Verhalten erwartet, denn beim ersten Durchlauf ist der JIT-Compiler noch im Spiel.
Bei Variante 2 ist der erste Schleifendurchlauf genauso schnell wie bei Variante 1. Das wird also die normale Zeit sein, die man für den ersten Schleifendurchlauf mit JIT braucht. Der Algorithmenunterschied fällt nicht in's Gewicht, was zu den Benchmarks passt. Beim ersten Durchlauf wird noch nicht parallelisiert, etwa weil JIT noch beteiligt ist, oder weil das (vermeintliche) Optimierungspotential erst beim zweiten Durchlauf erkannt wird. Dann wird parallelisiert, aber aufgrund der gemeinsam genutzten Variablen ergeben sich Wartezeiten. Das passt auch dazu, dass diejenigen, die nur auf einem Kern Aktivität hatten, 100% Auslastung beobachtet haben und bei mehreren aktiven Kernen (zumindest bei mir war es so) die Last deutlich niedriger, aber dennoch signifikant war.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
M While-Schleife mit Wartezeit Java Basics - Anfänger-Themen 15
T Ich brauche eine Schleife die eine beliebige Zahl so lange durch 10 teilt bis zur Null Java Basics - Anfänger-Themen 5
DrahtEck Schleife soll wieder da anfangen wo ich es möchte ! Java Basics - Anfänger-Themen 17
Finn_lol Fehlermeldung bei Schleife mit Array Java Basics - Anfänger-Themen 4
Ranger229 Endless loop in while Schleife Java Basics - Anfänger-Themen 3
MaZ Quadrat Schleife(Pyramide) Java Basics - Anfänger-Themen 9
M Datentypen While-Schleife eine Java Methode erstellen Java Basics - Anfänger-Themen 3
P Wie kann diese Schleife beenden Java Basics - Anfänger-Themen 1
T float soll durch schleife die größte mögliche Zahl herausfinden, Ausgabe ist aber "Infinity" Java Basics - Anfänger-Themen 1
T Variable in Schleife deklarieren, Speicherplatz, Garbage Collector Java Basics - Anfänger-Themen 10
Ostkreuz While Schleife neustarten Java Basics - Anfänger-Themen 20
S Verschachtelte for-Schleife Java Basics - Anfänger-Themen 2
M Problem bei verschachtelter for-Schleife bei zweidimensionalen Arrays Java Basics - Anfänger-Themen 3
laxla123 Verschachtelte If-Else Schleife Java Basics - Anfänger-Themen 21
S Erste Schritte do-while Schleife Münzwurf Java Basics - Anfänger-Themen 1
S while Schleife Taschenrechner Java Basics - Anfänger-Themen 1
P Best Practice While loop schleife Java Basics - Anfänger-Themen 5
ohneInformatik; For Schleife. Was macht dieser Code?? Java Basics - Anfänger-Themen 5
I For Schleife Summe berechnen Java Basics - Anfänger-Themen 13
A Erste Schritte Aufgabe mit while Schleife Java Basics - Anfänger-Themen 11
R do while Schleife Verständnisfrage Java Basics - Anfänger-Themen 2
Say Fehlenden Code finden in einer while-Schleife? Java Basics - Anfänger-Themen 11
N Warum Springt iterator nur in der Schleife weiter Java Basics - Anfänger-Themen 9
J for Schleife kleinste Zufallszahl finden Java Basics - Anfänger-Themen 25
A Return in While Schleife Java Basics - Anfänger-Themen 6
M Erste Schritte While Schleife / Ausgabe von buchstabe & ASCII Wert Java Basics - Anfänger-Themen 4
J do..while Schleife Java Basics - Anfänger-Themen 14
J Java To String Methode, Array mit For-Schleife Java Basics - Anfänger-Themen 2
S Textausgabe in einer For-Schleife Java Basics - Anfänger-Themen 12
B Automatisierte Ausgabe (Schleife, If-Abfrage?) Java Basics - Anfänger-Themen 24
C 2D Array Ausgabe mit for-Schleife i,j Java Basics - Anfänger-Themen 4
T Mit jedem Wert in der for-Schleife weiter arbeiten Java Basics - Anfänger-Themen 3
berserkerdq2 Warum muss man manchmal in der RUnmethode sleep in eine schleife tun? Java Basics - Anfänger-Themen 9
F for-Schleife halbiert Durchläufe ungewollt Java Basics - Anfänger-Themen 6
ravenz Schleife mit for über String Array „zahlen“und prüfen ob Wert „a“ oder „b“ oder „c“ entspricht (mittels || ) Java Basics - Anfänger-Themen 4
Bugs Bunny Fehlerhafte Berechnung beim erneuten Durchlaufen der Schleife Java Basics - Anfänger-Themen 5
J Methoden iterator for-schleife (hasNext() ) Java Basics - Anfänger-Themen 7
S Was macht ++ ohne Schleife? Java Basics - Anfänger-Themen 4
LFB In einer For-Schleife alles in einer Zeile ausgeben Java Basics - Anfänger-Themen 14
Neuling47 for schleife Java Basics - Anfänger-Themen 6
M Variable in einer Schleife initialisieren Java Basics - Anfänger-Themen 46
B Zuweisungen und Methodenaufrufe in Bedingung der while Schleife? Java Basics - Anfänger-Themen 2
JavaBeginner22 Würfeln bis 6 while Schleife Java Basics - Anfänger-Themen 13
D EinMalEins mithilfe einer for-Schleife und Array Java Basics - Anfänger-Themen 1
xanxk Problem For-Schleife mit Charakter Java Basics - Anfänger-Themen 2
W Schleife und einmal variable++ zu viel Java Basics - Anfänger-Themen 20
sgtcoopa Array übergeben Schleife Java Basics - Anfänger-Themen 0
T Mäxchenspiel mit Schleife Java Basics - Anfänger-Themen 3
D try/catch-Block bei for-Schleife Java Basics - Anfänger-Themen 14
D Hilfe bei einer Aufgabe mit for-Schleife Java Basics - Anfänger-Themen 6
J Schleife Problem Java Basics - Anfänger-Themen 2
X Hilfe beim Übertragen in eine For-Schleife Java Basics - Anfänger-Themen 1
L while Schleife mit 2 Bedingung endet nicht Java Basics - Anfänger-Themen 3
stormyark 4 Bit in einer for-schleife funktioniert nicht Java Basics - Anfänger-Themen 3
M ArrayList mit einer Schleife befüllen Java Basics - Anfänger-Themen 2
K Schleife berechnet kein Ergebnis (Vererbung) Java Basics - Anfänger-Themen 6
S Sentinel-Schleife Java Basics - Anfänger-Themen 0
D Array mit while-schleife Java Basics - Anfänger-Themen 12
Kiki01 Wie würde eine geeignete Schleife aussehen, die die relative Häufigkeit für jeden Charakter in einem Text bestimmt? Java Basics - Anfänger-Themen 3
P9cman Vokal Zähler mit switch case und for-Schleife Java Basics - Anfänger-Themen 4
B do while Schleife Java Basics - Anfänger-Themen 3
I Fehler bei for-Schleife Java Basics - Anfänger-Themen 6
S Mit for-Schleife ein 2D JLabel-Array mit veränderbaren Icons erstellen Java Basics - Anfänger-Themen 3
D Codeverständis For-Schleife Java Basics - Anfänger-Themen 4
SergioCK Do while Schleife wiederholen Java Basics - Anfänger-Themen 14
M For-Schleife Java Basics - Anfänger-Themen 20
el_pato DialogFenster wird nicht in Schleife geöffnet? Java Basics - Anfänger-Themen 30
J if-Schleife innerhalb einer if-Schleife wird in der Konsole nicht gelesen Java Basics - Anfänger-Themen 4
EinNickname9 Denkfehler bei einfacher Schleife Java Basics - Anfänger-Themen 83
paulen1 Methoden Unerwünschte Ausgabe bei System.out.print in For-Schleife Java Basics - Anfänger-Themen 8
S Array mit for-Schleife besetzen Java Basics - Anfänger-Themen 7
CptK For-Schleife in Thread nach jedem Durchlauf pausieren Java Basics - Anfänger-Themen 35
M for schleife ohne geschweifte Klammer Java Basics - Anfänger-Themen 15
H For-Schleife bis Index von Eingabe laufen lassen Java Basics - Anfänger-Themen 24
Informatikf Methoden While Schleife Java Basics - Anfänger-Themen 3
U geschachtelte if-Schleife Java Basics - Anfänger-Themen 15
M While Schleife? Java Basics - Anfänger-Themen 4
Poppigescorn Quersumme Berechnen mit einer While Schleife Java Basics - Anfänger-Themen 13
I Potenz berechnen mit for-Schleife Java Basics - Anfänger-Themen 3
J Koordinaten per Schleife ausgeben Java Basics - Anfänger-Themen 6
S Schleife funktioniert nicht Java Basics - Anfänger-Themen 2
M For Schleife/ArrayList Java Basics - Anfänger-Themen 12
OZAN86 Methoden for schleife Java Basics - Anfänger-Themen 3
G --i versus i++ in for-Schleife Java Basics - Anfänger-Themen 11
OZAN86 For Schleife von 1-50 die Zahlen werden durch ein Komma getrennt Java Basics - Anfänger-Themen 10
M Wie kann ich Werte die in einer While Schleife sind weiter genutzt werden? Java Basics - Anfänger-Themen 7
T for-each-Schleife, verschiedene Datentypen Java Basics - Anfänger-Themen 1
T Methode um Array mit for-each-Schleife auszulesen Java Basics - Anfänger-Themen 7
Jana01 Schleife Java Basics - Anfänger-Themen 12
H Kann eine while-Schleife ein Programm blockieren? Java Basics - Anfänger-Themen 8
D For Schleife Java Basics - Anfänger-Themen 8
D Doppelte For Schleife / Array Java Basics - Anfänger-Themen 3
TimoN11 Array -> Schleife wieder von vorne durchlaufen lassen Java Basics - Anfänger-Themen 1
O Methode in while-Schleife aufrufen geht nur beim ersten Mal Java Basics - Anfänger-Themen 2
T Variable in for Schleife ansprechen ohne Array ? Java Basics - Anfänger-Themen 25
MiMa log4j als separate Dateien in Schleife? Java Basics - Anfänger-Themen 6
A Wie schaffe ich das eine while Schleife addiert danach subtrahirt? Java Basics - Anfänger-Themen 1
HoT verschiedene ArrayLists mit ähnlichem Namen in for-Schleife aufrufen Java Basics - Anfänger-Themen 3
U Problem mit dem initialisieren meines Strings in einer Schleife Java Basics - Anfänger-Themen 5
Khaled-Abo Ziffern unterscheiden mittels einer For-Schleife Java Basics - Anfänger-Themen 6

Ähnliche Java Themen

Neue Themen


Oben