2013-03-25 15 views
16

ho misurato il tempo di esecuzione di due modi per calcolare la potenza di 2:C#: Perché una chiamata di funzione è più veloce dell'inlining manuale?

1) linea

result = b * b; 

2) Con una semplice funzione di chiamata

result = Power(b); 

Quando si esegue in modalità Debug, tutto è come previsto: la chiamata di una funzione è notevolmente più costosa rispetto al calcolo in linea (385 ms in linea vs 570 ms chiamata di funzione).

In modalità di rilascio, mi aspetto che il compilatore acceleri notevolmente il tempo di esecuzione della chiamata di funzione poiché il compilatore inline internamente la piccolissima funzione Power(). Ma NON mi aspetto che la chiamata alla funzione sia PIÙ VELOCE rispetto al calcolo manuale in linea.

Incredibilmente questo è il caso: nella versione di rilascio, la prima esecuzione richiede 109 ms e la seconda esecuzione con la chiamata a Power() richiede solo 62 ms.

In che modo una chiamata di funzione può essere più veloce dell'inlining manuale?

Ecco il programma per la riproduzione:

class Program 
{ 
    static void Main(string[] args) 
    { 
     Console.WriteLine("Starting Test"); 

     // 1. Calculating inline without function call 
     Stopwatch sw = Stopwatch.StartNew(); 

     for (double d = 0; d < 100000000; d++) 
     { 
      double res = d * d; 
     } 

     sw.Stop(); 
     Console.WriteLine("Checked: " + sw.ElapsedMilliseconds); 

     // 2. Calulating power with function call 
     Stopwatch sw2 = Stopwatch.StartNew(); 

     for (int d = 0; d < 100000000; d++) 
     { 
      double res = Power(d); 
     } 

     sw2.Stop(); 
     Console.WriteLine("Function: " + sw2.ElapsedMilliseconds); 

     Console.ReadKey(); 
    } 

    static double Power(double d) 
    { 
     return d * d; 
    } 
} 
+0

Hai avviato il programma sotto il debugger (F5)? In quel caso le ottimizzazioni sono state soppresse. – usr

+1

Hai eseguito il file .exe generato o VS nella modalità di rilascio? Inoltre, hai provato a chiamarli in diversi ordini? Ho trovato che questo fa una sottile differenza – DGibbs

+0

Bene, perché usare un int ** come doppio ** più veloce di un semplice doppio? – Vercas

risposta

50

Il test è sbagliato. Nella seconda parte si usa un int d invece di un doppio. Forse spiega la differenza di orario.

+0

Ben avvistato !!! – DGibbs

+0

Il tempo di esecuzione della funzione uno è sempre più lento di quello in linea sul mio computer (non importa int o doppio). – iamsleepy

+0

Assolutamente giusto. Ero cieco L'hai risolto! Grazie! – Knasterbax

22

Come Xavier è stato rilevato correttamente, si sta utilizzando il doppio in un ciclo e l'altro nell'altro. Cambiare entrambi nello stesso tipo renderà i risultati uguali - l'ho provato.

Inoltre: ciò che si sta veramente misurando qui è la durata delle aggiunte e dei confronti. Tu sei non misurando la durata della squadratura di d, perché semplicemente non sta accadendo: in una versione di rilascio, l'ottimizzatore rimuove completamente il corpo del ciclo, perché il risultato non viene utilizzato. Puoi confermare questo commentando il corpo del loop. La durata sarà la stessa.

+0

Inoltre completamente a destra. È stato un mio errore. Ho supervisionato l'int – Knasterbax

3

Daniel Hilgarth ha ragione, il calcolo non si verifica affatto perché il risultato non è utilizzato (che probabilmente non è il caso in modalità Debug). Prova il seguente esempio e otterrai i risultati corretti:

static void Main(string[] args) 
    { 
     Console.WriteLine("Starting Test"); 
     var list = new List<int>(); 
     // 1. Calculating inline without function call 
     Stopwatch sw = Stopwatch.StartNew(); 

     for (int d = 0; d < 100000000; d++) 
     { 
      int res = d * d; 
      list.Add(res); 
     } 

     sw.Stop(); 
     Console.WriteLine("Checked: " + sw.ElapsedMilliseconds); 
     // 2. Calulating power with function call 

     list = new List<int>(); 
     Stopwatch sw2 = Stopwatch.StartNew(); 

     for (int d = 0; d < 100000000; d++) 
     { 
      int res = Power(d); 
      list.Add(res); 
     } 

     sw2.Stop(); 
     Console.WriteLine("Function: " + sw2.ElapsedMilliseconds); 

     Console.ReadKey(); 
    } 
+2

Wont questo codice lancia un 'OverflowException' dato che' res' finirà per essere '' Int32.MaxValue' in quei loop? – Drewman