2009-07-13 10 views
7

Ho visto altri thread che dicono che le prestazioni di java reflection sono 10-100x più lente rispetto a quando si usano le chiamate senza riflessione.Interpretazione delle prestazioni della riflessione Java: perché è sorprendentemente molto veloce?

I miei test in 1.6 hanno dimostrato che questo non è il caso, ma ho trovato alcune altre cose interessanti che ho bisogno di qualcuno che mi spieghi.

Ho oggetti che implementano la mia interfaccia. Ho fatto tre cose 1) usando un riferimento ad un oggetto I che ho lanciato quell'oggetto all'interfaccia e chiamo il metodo attraverso l'interfaccia 2) usando un riferimento all'oggetto reale, chiamiamo direttamente il metodo e 3) chiamiamo il metodo attraverso il riflesso. Ho visto che la prima chiamata all'interfaccia è stata la più veloce seguita da vicino dal riflesso n. 3, ma ho notato che la chiamata diretta al metodo era la più lenta con un buon margine.

Non lo capisco, mi sarei aspettato che la chiamata diretta fosse più veloce, quindi l'interfaccia, quindi la riflessione sarebbe stata molto più lenta.

Blah e ComplexClass si trovano in un pacchetto diverso dalla classe principale ed entrambi hanno un metodo doSomething (int x) che implementa l'interfaccia e stampa solo il numero intero x.

Qui ci sono i miei risultati (tempi in ms, risultati molto simili w prove multiple /): chiamando un metodo direttamente: 107.194 chiamare un metodo direttamente da un cast oggetto a un'interfaccia: 89594 chiamando un metodo attraverso la riflessione: 90453

Ecco il mio codice:

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

WHY CW? è una domanda relativa alla programmazione non soggettiva valida? E perché i voti per chiudere ?! – hhafez

+0

Ho trasformato il titolo in una domanda per coloro che non possono vedere una domanda a meno che non abbia il marchio. Ma, wiki della comunità ?? –

risposta

8

Mettendo tutti i test nel lo stesso programma è un errore microbenchmarking - c'è un certo riscaldamento associato con le prestazioni di Java. Questo è il fallimento più importante.

Metti i tuoi test in programmi separati. Quindi esegui i test più volte, in modo da avere un'idea di quando il riscaldamento è terminato e la significatività statistica.

Inoltre hai un metodo enorme che contiene il tuo ciclo interno. Hotspot sembra essere più abile nel trattare con questo rispetto a prima, ma non è ancora buono.

Si dovrebbe trovare che con -server chiamare un metodo virtuale (anche se caricato da un caricatore di classi diverse) in un ciclo stretto viene completamente ottimizzato. Quindi non ha molto senso dire quanto è veloce una chiamata diretta rispetto a una chiamata riflessiva.

+0

cosa vuoi dire che c'è un enorme metodo che contiene il ciclo interno? Stai dicendo che il metodo di prova stesso è troppo grande? Perchè questo è un problema? cos'è -server? – jbu

+0

e vedo il tuo punto con il problema che i singoli test non sono nei loro stessi metodi. Anche se ho la sensazione vedrò risultati simili. Farà un post separato probabilmente una volta che otterrò quei risultati. – jbu

+1

Hai un metodo di prova di grandi dimensioni che itera attorno ai loop milioni di volte. -server è l'opzione go più veloce (ma aumenta i tempi di avvio e non è necessariamente presente, in particolare su Windows JRE). I test dovrebbero essere nel loro stesso processo. Esegui il programma fresco per ogni test. –

4

In primo luogo, la riflessione è diventata molto più veloce nei JDK più recenti. Secondo, mi aspetto che il compilatore di Hot Spot ottimizzerà tutte queste chiamate in modo approssimativo. Può eseguire analisi del tempo di esecuzione per rendersi conto che si sta invocando la stessa funzione più e più volte in modo da poter ottimizzare il riflesso (e la chiamata alla funzione virtuale). Stessa cosa con l'esempio dell'interfaccia.

+0

+1 per il miglioramento della riflessione in jdk successivi. –

0

Il mio test mostra che se java è in grado di eseguire la funzione inline, l'invocazione diretta può essere estremamente veloce. La chiamata diretta in linea è 200-300 volte più veloce della chiamata di riflessione. Testato su Ubuntu 12.10, Jdk 1.6.35, CPU Xeon E5-2620.

Java diventa sempre più intelligente e più intelligente ogni giorno.

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)); 

} 
} 
Problemi correlati