2009-07-13 8 views
7

Ich habe andere Themen gesehen, die sagen, Java-Reflektionsleistung ist 10-100x langsamer als bei Verwendung von Nichtreflexionsaufrufen.Interpretieren Java Reflektionsleistung: Warum ist es überraschend schnell?

Meine Tests in 1.6 haben gezeigt, dass dies nicht der Fall ist, aber ich fand einige andere interessante Dinge, die ich jemanden brauche, um mir zu erklären.

Ich habe Objekte, die meine Schnittstelle implementieren. Ich habe drei Dinge gemacht 1) mit einem Verweis auf ein Objekt Ich werfe dieses Objekt auf die Schnittstelle und rufen Sie die Methode über die Schnittstelle 2) mit einem Verweis auf das eigentliche Objekt rufen Sie die Methode direkt und 3) rufen Sie die Methode durch Reflexion. Ich sah, dass # 1 Interface Call am schnellsten war, dicht gefolgt von # 3 Reflection, aber ich bemerkte, dass der direkte Methodenaufruf mit Abstand die langsamste war.

Ich verstehe das nicht, ich hätte erwartet, dass der direkte Anruf am schnellsten sein würde, dann die Schnittstelle, dann würde Reflexion viel viel langsamer sein.

Blah und ComplexClass sind in einem anderen Paket als die Hauptklasse und beide haben eine doSomething (int x) -Methode, die die Schnittstelle implementiert und nur die Ganzzahl x ausgibt.

Hier meine Ergebnisse sind (Zeiten in ms ergibt sich sehr ähnlich w/mehrere Studien): direkt Aufruf einer Methode: 107194 ein Verfahren direkt von einem Objekt gecastet in einem Interface Aufruf: 89.594 eine Methode durch Reflexion Aufruf: 90453

Hier ist mein Code:

public class Main 
{ 

    /** 
    * @param args the command line arguments 
    */ 
    public static void main(String[] args) 
    { 
     Blah x = new Blah(); 
     ComplexClass cc = new ComplexClass(); 
     test((Object) x, cc); 
    } 

    public static void test(Object x, ComplexClass cc) 
    { 
     long start, end; 
     long time1, time2, time3 = 0; 
     int numToDo = 1000000; 
     MyInterface interfaceClass = (MyInterface) x; 

     //warming up the cache 
     for (int i = 0; i < numToDo; i++) 
     { 
      cc.doSomething(i); //calls a method directly 
     } 

     start = System.currentTimeMillis(); 
     for (int i = 0; i < numToDo; i++) 
     { 
      cc.doSomething(i); //calls a method directly 
     } 
     end = System.currentTimeMillis(); 
     time1 = end - start; 

     start = System.currentTimeMillis(); 
     for (int i = 0; i < numToDo; i++) 
     { 
      interfaceClass.doSomething(i); //casts an object to an interface then calls the method 
     } 
     end = System.currentTimeMillis(); 
     time2 = end - start; 


     try 
     { 
      Class xClass = x.getClass(); 
      Class[] argTypes = 
      { 
       int.class 
      }; 
      Method m = xClass.getMethod("doSomething", argTypes); 
      Object[] paramList = new Object[1]; 
      start = System.currentTimeMillis(); 
      for (int i = 0; i < numToDo; i++) 
      { 
       paramList[0] = i; 
       m.invoke(x, paramList); //calls via reflection 
      } 
      end = System.currentTimeMillis(); 
      time3 = end - start; 

     } catch (Exception ex) 
     { 
     } 

     System.out.println("calling a method directly: " + time1); 
     System.out.println("calling a method directly from an object cast to an interface: " + time2); 
     System.out.println("calling a method through reflection: " + time3); 
    } 
+0

WARUM CW? es ist eine gültige nicht subjektive Programmierung bezogene Frage? Und warum die Stimmen zu schließen ?! – hhafez

+0

Ich habe den Titel zu einer Frage für diejenigen gemacht, die eine Frage nicht sehen können, es sei denn, sie hat das Zeichen. Aber Community Wiki? –

Antwort

8

Putting alle Tests in das gleiche Programm ist ein microbenchmarking Fehler - es gibt eine gewisse Aufwärm mit Java Leistung verbunden ist. Dies ist der wichtigste Fehler.

Legen Sie Ihre Tests in separaten Programmen. Führen Sie die Tests dann mehrmals durch, damit Sie ein Gefühl dafür bekommen, wann das Aufwärmen beendet ist und statistische Signifikanz.

Auch Sie haben eine große Methode, die Ihre innere Schleife enthält. Hotspot scheint besser damit umzugehen als früher, aber es ist immer noch nicht gut.

Sie sollten feststellen, dass mit -server aufrufen eine virtuelle Methode (auch wenn von einem anderen Klassenlader geladen) in einer engen Schleife wird vollständig weg optimiert. Es macht daher wenig Sinn zu sagen, wie schnell ein direkter Anruf ist, als ein reflektierender Anruf.

+0

Was meinst du, es gibt eine große Methode, die die innere Schleife enthält? Sagen Sie, dass die Testmethode selbst zu groß ist? Warum ist das ein Problem? Was ist -server? – jbu

+0

und ich sehe Ihren Punkt mit dem Problem, dass die einzelnen Tests nicht in ihren eigenen Methoden sind. Obwohl ich das Gefühl habe, werde ich ähnliche Ergebnisse sehen. Werde wahrscheinlich einen separaten Post machen, sobald ich diese Ergebnisse bekomme. – jbu

+1

Sie haben eine große Testmethode, die millionenfach iteriert. -server ist der Go-Faster-Schalter (erhöht aber die Startzeiten und ist nicht unbedingt vorhanden, insbesondere bei der Windows-JRE). Die Tests sollten in ihrem eigenen Prozess sein. Führen Sie das Programm für jeden Test neu aus. –

4

Erstens wurde die Reflexion in den letzten JDKs viel schneller. Zweitens erwarte ich, dass der Hot Spot-Compiler alle diese Aufrufe auf ungefähr den Code optimieren wird. Es kann eine Laufzeitanalyse durchführen, um zu erkennen, dass Sie dieselbe Funktion immer wieder aufrufen, um die Reflektion (und den Aufruf virtueller Funktionen) zu optimieren. Dasselbe gilt für das Beispiel der Benutzeroberfläche.

+0

+1 für die Verbesserung der Reflexion in späteren jdk's. –

0

Mein Test zeigt, dass, wenn Java inline funktioniert, der direkte Aufruf extrem schnell sein kann. In Lined Direct Call ist 200-300 mal schneller als Reflection Call. Getestet auf Ubuntu 12.10, Jdk 1.6.35, CPU Xeon E5-2620.

Java wird jeden Tag intelligenter und intelligenter.

import java.lang.reflect.Method; 

public class Main 
{ 
    static class Test 
    { 
     int i=0; 
     public void set(int value){ 
      this.i = value; 
     } 
    } 

public static void main(String[] args) throws Exception 
{ 
    Test test = new Test(); 
    int max = 10000000; 

    long direct = System.currentTimeMillis(); 
    for(int i=0; i<max; i++){ 
     Integer io = new Integer(i*i); 
     test.set(io); 
    } 
    System.out.println("Direct : " + (System.currentTimeMillis() - direct)); 

    Method method = Test.class.getMethod("set", Integer.TYPE); 
    long reflection = System.currentTimeMillis();   
    for(int i=0; i<max; i++){ 
     Integer io = new Integer(i*i); 
     method.invoke(test, io); 
    } 
    System.out.println("Reflection : " + (System.currentTimeMillis() - reflection)); 

} 
} 
Verwandte Themen