# Performance Methodenaufruf - if Abfrage



## MQue (2. Dez 2009)

Hab gerade versucht herauszufinden, was performanter ist, vor dem Methodenaufruf eine Abfrage zu machen oder die Methode einfach aufzurufen und in der Methode zu validieren. 
Zweitere Variante scheint ca. um das Doppelte schneller zu sein als die erstere Variante.
Kann man das irgendwie interpretieren? 
Beste Grüße,



```
package commandseparator;

public class Main {

    public Main() {
        String test = null;
        int erg = 2;
        long start1 = System.currentTimeMillis();
        System.out.println("");
        for (int i = 0; i < 1000000000; i++) {
            erg *= 2;
            testMethode(test);
            }
        long end1 = System.currentTimeMillis();

        int erg2 = 2;
        long start2 = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            erg2 *= 2;
            if(test != null)
                testMethode(test);
            }
        long end2 = System.currentTimeMillis();
        System.out.println("Ergebnis: " + (end1-start1) + "; " + (end2-start2));
        }

    private void testMethode(String test) {
        if(test == null)
            return;
        }

    public static void main(String[] args) {
        new Main();
        }
}
```


----------



## Der Müde Joe (2. Dez 2009)

>Kann man das irgendwie interpretieren? 

HotSpot ? Wikipedia
Just-in-time-Kompilierung ? Wikipedia
Dynamische Optimierung ? Wikipedia


----------



## maki (2. Dez 2009)

> Hab gerade versucht herauszufinden, was performanter ist,


Micro-Benchmarks sind unzuverlässig und ihre Eregebnisse stimmen eigentlich nie, deswegen sind sie sinnlos.


----------



## Marco13 (2. Dez 2009)

Ergebnis: 0; 0
 

Interessanterweise wollte ich kürzlich mal einen Thread zu genau diesem Thema aufmachen. Dabei wäre es aber nicht um die Frage der Performance gegangen, sondern eher um die "philosophische" Frage, was denn "schöner" oder "besser" ist.
Es gibt da unterschiedliche Pros und Contras - vermutlich einer der vielen Punkte, wo man von Fall zu Fall abwägen muss...


----------



## Marco13 (2. Dez 2009)

Aber noch so als Nachtrag: Es KANN schon Fälle geben, wo Microbenchmarks Sinn machen. Allerdings immer mit einem gewissen Vorbehalt, und nur, wenn man sie sinnvoll modellieren kann. In diesem Beispiel ist das praktisch nicht möglich.


----------



## musiKk (2. Dez 2009)

Bei solchen kleinen Stücken weiß man auch nie genau, was der Compiler jetzt wie optimiert. Und selbst wenn man ein halbwegs aussagekräftiges Ergebnis hat, kann es sein, dass das in Produktivumgebungen (schließlich ist das hier nur ein sinnfreies Trivialbeispiel) aufgrund geänderter Parameter wieder völlig anders aussieht.



Marco13 hat gesagt.:


> Dabei wäre es aber nicht um die Frage der Performance gegangen, sondern eher um die "philosophische" Frage, was denn "schöner" oder "besser" ist.



Die Frage ist da sicher interessanter. Prinzipiell ist das nur Definitionssache. Es gibt genug Methoden, die kein [c]null[/c] als Parameter akzeptieren und eine NPE (oder vielleicht auch IllegalArgumentException) werfen. Andererseits kann es auch sinnvoll sein, [c]null[/c] zu übergeben und erst später zu prüfen. Das hängt alles vom genauen Einsatz ab. Solange man dann aber konsequent bleibt und richtig dokumentiert, ist ja schonmal viel gewonnen.


----------



## Marco13 (2. Dez 2009)

Ja (auch auf die Gefahr hin, dass das jetzt als Thread-Hijacking angesehen wird) es ging da quasi um die "Kompetenzen" - WER entscheidet, ob in einer Methode etwas gemacht wird (oder ob eine Methode aufgerufen wird). Die Grenzen sind da fließend...

```
if (node != null) process(node);
// oder
if (node != null && node.getContent() != null) process(node);
// oder
if (node != null && node.getContent() != null && node.getContent().isValid()) process(node);

// Und dann einfach
void process(Node node)
{
    processValidNodeContent(node.getContent());
}
```
oder

```
process(node);

// Und dann 
void process(Node node)
{
    if (node != null && node.getContent() != null && node.getContent().isValid()) 
    {
        processValidNodeContent(node.getContent());
    }
}
```

Das (selbstverständlich) wichtigste ist, dass das Verhalten dokumentiert ist:

```
/** Processes the content of the node. The node and its contents must be
 *   non-null, and the content must be valid, otherwise the world will explode...
 *   ...
 */
void process(Node node)
```
vs.

```
/** Processes the content of the node if the node and its contents are 
 *   non-null, and the content is valid, otherwise does nothing...
 *   ...
 */
void process(Node node)
```

_Eigentlich_ hängt die Entscheidung auch von der gewünschten Art der Anwendung ab - sofern man die vorher schon kennt. Natürlich wäre es schöner, wenn man nicht

```
if (node0 != null && node0.getContent() != null && node0.getContent().isValid()) process(node0);
if (node1 != null && node1.getContent() != null && node1.getContent().isValid()) process(node1);
if (node2 != null && node2.getContent() != null && node2.getContent().isValid()) process(node2);
```
machen müßte, sondern einfach

```
process(node0);
process(node1);
process(node2);
```
zumal man in der "process"-Methode _eigentlich_ sowieso die Eingaben auf Gültigkeit prüfen sollte, aber es gibt eben Fälle, wo _das_ dann wieder unschön wäre, und durch den "Vertrag" (die Doku, die besagt, wie die Methode aufgerufen werden darf) eigentlich schon abgedeckt sein sollte...


----------



## fastjack (3. Dez 2009)

Interessant wäre auch mal, was der Compiler aus deinem Beispiel macht (javap.exe), wird wohl aber eins zu eins umgesetzt und nicht weiter optimiert. Ich denke das der Test vor dem Methodenaufruf tatsächlich schneller ist, als im Methodenaufruf, da hier einiges an Overhead seitens der VM wegfallen wird. Wenn die VM eine Methode aufruft, werden verschiedene Dinge erledigt: Methoden-Stackframe erstellen, Variablen kopieren etc.


----------



## Empire Phoenix (3. Dez 2009)

ich denke das der Unterschied ob während des Testens die Maus bewegt wird oder nicht stärker ausfällt als der methodenaufruf^^


----------



## fastjack (3. Dez 2009)

Nur bei Windows :applaus:


----------



## Empire Phoenix (3. Dez 2009)

Nur weil keine GUI da ist heist das nicht das interupts nicht verarbeitet werden ....


----------



## KrokoDiehl (3. Dez 2009)

Aber man kann wohl generell sagen, dass es teurer ist, eine Methode aufzurufen (wie _fastjack _schon erwähnte wegen Stacks und Variablen etc.), als sie nicht aufzurufen 
Ob es natürlich deswegen sinnvoll ist, alle Prüfungen auszulagern, würde ich nicht sagen. Hier sollte man sich eher an saubere Implementierung und Kontext halten als an die paar Millisekunden zu denken, die man sich theoretisch sparen könnte.


----------



## FArt (3. Dez 2009)

Wie der müde Joe schon gezeigt hat, kommt es einfach auch darauf an, wie oft die Methode aufgerufen wird. Irgendwann kommt der HotSpotCompiler auf den Trichter, das z.B. durch Inlining zu optimieren, und schon sieht die Sache anders aus, vermutlich besser ;-)


----------



## FArt (3. Dez 2009)

Nachtrag: es ist immer gut sich bei solchen Fragen mal den Binärcode der .class Datei anzusehen (nicht den decompilierten Javacode). Da sieht man dann tatsächlich wie was vom normalen Compiler schon optimiert wird.


----------



## MQue (3. Dez 2009)

Hab mal die Klasse compiliert und dann mit jad wieder decompiliert, und da kam das dabei raus,
Eingentlich nichts aufregendes ausser das in der for-Schleife der iterier- Wert in hex dargestellt wird und das die Code converntion nicht so ganz passt (eigene Zeile für {).


```
package commandseparator;

import java.io.PrintStream;

public class Main
{

    public Main()
    {
        String test = null;
        int erg = 2;
        long start1 = System.currentTimeMillis();
        System.out.println("");
        for(int i = 0; i < 0x3b9aca00; i++)
        {
            erg *= 2;
            testMethode(test);
        }

        long end1 = System.currentTimeMillis();
        int erg2 = 2;
        long start2 = System.currentTimeMillis();
        for(int i = 0; i < 0x3b9aca00; i++)
        {
            erg2 *= 2;
            if(test != null)
                testMethode(test);
        }

        long end2 = System.currentTimeMillis();
        System.out.println((new StringBuilder()).append("Ergebnis: ").append(end1 - start1).append("; ").append(end2 - start2).toString());
    }

    private void testMethode(String test)
    {
        if(test == null)
            return;
        else
            return;
    }

    public static void main(String args[])
    {
        new Main();
    }
}
```


----------



## FArt (3. Dez 2009)

fart hat gesagt.:


> ... (nicht den decompilierten javacode)...



[edit]
JDC Tech Tips: August 29, 2000


;-)


----------



## MQue (3. Dez 2009)

>>[ (nicht den decompilierten javacode)

Habs schon gelesen, wollte aber trotzdem mal schaun was der Decompiler daraus macht.
Beste Grüße,

Das wäre mal mein Bytecode, von dem ich nur Bruchteile verstehe (soweit es C- ähnlich ist wi  goto usw.)

```
Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   aconst_null
   5:   astore_1
   6:   iconst_2
   7:   istore_2
   8:   invokestatic    #2; //Method java/lang/System.currentTimeMillis:()J
   11:  lstore_3
   12:  getstatic       #3; //Field java/lang/System.out:Ljava/io/PrintStream;
   15:  ldc     #4; //String
   17:  invokevirtual   #5; //Method java/io/PrintStream.println:(Ljava/lang/Str
ing;)V
   20:  iconst_0
   21:  istore  5
   23:  iload   5
   25:  ldc     #6; //int 1000000000
   27:  if_icmpge       45
   30:  iload_2
   31:  iconst_2
   32:  imul
   33:  istore_2
   34:  aload_0
   35:  aload_1
   36:  invokespecial   #7; //Method testMethode:(Ljava/lang/String;)V
   39:  iinc    5, 1
   42:  goto    23
   45:  invokestatic    #2; //Method java/lang/System.currentTimeMillis:()J
   48:  lstore  5
   50:  iconst_2
   51:  istore  7
   53:  invokestatic    #2; //Method java/lang/System.currentTimeMillis:()J
   56:  lstore  8
   58:  iconst_0
   59:  istore  10
   61:  iload   10
   63:  ldc     #6; //int 1000000000
   65:  if_icmpge       89
   68:  iload   7
   70:  iconst_2
   71:  imul
   72:  istore  7
   74:  aload_1
   75:  ifnull  83
   78:  aload_0
   79:  aload_1
   80:  invokespecial   #7; //Method testMethode:(Ljava/lang/String;)V
   83:  iinc    10, 1
   86:  goto    61
   89:  invokestatic    #2; //Method java/lang/System.currentTimeMillis:()J
   92:  lstore  10
   94:  getstatic       #3; //Field java/lang/System.out:Ljava/io/PrintStream;
   97:  new     #8; //class java/lang/StringBuilder
   100: dup
   101: invokespecial   #9; //Method java/lang/StringBuilder."<init>":()V
   104: ldc     #10; //String Ergebnis:
   106: invokevirtual   #11; //Method java/lang/StringBuilder.append:(Ljava/lang
/String;)Ljava/lang/StringBuilder;
   109: lload   5
   111: lload_3
   112: lsub
   113: invokevirtual   #12; //Method java/lang/StringBuilder.append:(J)Ljava/la
ng/StringBuilder;
   116: ldc     #13; //String ;
   118: invokevirtual   #11; //Method java/lang/StringBuilder.append:(Ljava/lang
/String;)Ljava/lang/StringBuilder;
   121: lload   10
   123: lload   8
   125: lsub
   126: invokevirtual   #12; //Method java/lang/StringBuilder.append:(J)Ljava/la
ng/StringBuilder;
   129: invokevirtual   #14; //Method java/lang/StringBuilder.toString:()Ljava/l
ang/String;
   132: invokevirtual   #5; //Method java/io/PrintStream.println:(Ljava/lang/Str
ing;)V
   135: return

public static void main(java.lang.String[]);
  Code:
   0:   new     #15; //class commandseparator/Main
   3:   dup
   4:   invokespecial   #16; //Method "<init>":()V
   7:   pop
   8:   return

}
```


----------



## fastjack (3. Dez 2009)

Daran erkennt man auch eigentlich nur, das der null-Check durchgeführt wird bevor die Methode aufgerufen wird, bei Zeile 75. Bei ersterer Variante wird die Methode gleich aufgerufen. Also analog zur eigentlichen Programmierung und den vorherigen Erkenntnissen.


----------



## Marco13 (3. Dez 2009)

Das ganze jetzt nochmal mit einem Debug-Build vom JDK und dem -XX:+PrintOptoAssembly - Parameter
Deep dive into assembly code from Java | Java.net


----------



## FArt (4. Dez 2009)

Marco13 hat gesagt.:


> Das ganze jetzt nochmal mit einem Debug-Build vom JDK und dem -XX:+PrintOptoAssembly - Parameter
> Deep dive into assembly code from Java | Java.net



Na Gott sei dank eine richtige Programmiersprache. Assembler hat doch noch am meisten Spaß gemacht. Da konnte man noch alles selber bestimmen ;-)


----------

