Domanda

Ho scoperto di recente che per alcuni tipi di calcoli finanziari che il seguente modello è molto più facile da seguire e prova soprattutto in situazioni in cui potremmo aver bisogno di ottenere numeri da varie fasi del calcolo.

public class nonsensical_calculator
{ 

   ...

    double _rate;
    int _term;
    int _days;

    double monthlyRate { get { return _rate / 12; }}

    public double days { get { return (1 - i); }}
    double ar   { get { return (1+ days) /(monthlyRate  * days)
    double bleh { get { return Math.Pow(ar - days, _term)
    public double raar { get { return bleh * ar/2 * ar / days; }}
    ....
}

Ovviamente questo si traduce spesso in più chiamate allo stesso di accesso all'interno di una data formula. Ero curioso di vedere se il compilatore è abbastanza intelligente per ottimizzare via questi ripetuti appelli senza alcun cambiamento intervenire nello stato, o se questo stile sta causando un calo di prestazioni decenti.

suggerimenti Approfondimenti sono sempre apprezzati

È stato utile?

Soluzione

Da quello che so, il compilatore C # non ottimizzare questo, perché non può essere certi di effetti collaterali (ad esempio, cosa succede se si è accessCount++ nel getter?) Date un'occhiata qui a un eccellente da Eric Lippert

Da questa risposta:

  

Il compilatore C # non fa mai questo tipo di ottimizzazione; come noto, così facendo avrebbe richiesto che il peer compilatore nel codice chiamato e verificare che il risultato si calcola non cambia per tutta la durata del codice del chiamato. Il compilatore C # non lo fa.

     

La potenza compilatore JIT. Non c'è ragione per cui non poteva. Ha tutto il codice seduto proprio lì. E 'completamente gratuito per inline il getter di proprietà, e se il jitter determina che il getter proprietà inline restituisce un valore che può essere memorizzato nella cache in un registro e ri-utilizzato, allora è libero di farlo. (Se non si vuole fare così perché il valore potrebbe essere modificato su un altro thread allora hai già una condizione di competizione bug;. Correggere il bug prima di preoccuparsi di prestazioni)

Solo una nota, visto che Eric sulla squadra compilatore C #, ho fiducia la sua risposta:)

Altri suggerimenti

A pochi pensieri casuali.

In primo luogo, come altri hanno notato, il compilatore C # non fa questo tipo di ottimizzazione, anche se il jitter è libero di farlo.

In secondo luogo, il modo migliore per rispondere a una domanda di prestazioni è di provare e vedere. La classe cronometro è tuo amico. Provate un miliardo di volte in entrambe le direzioni e vedere quale è più veloce; allora saprete.

In terzo luogo, ovviamente non ha senso di spendere tempo a ottimizzare qualcosa che è già abbastanza veloce. Prima di spendere un sacco di tempo di benchmarking, trascorrere qualche ora di profilazione e alla ricerca di punti caldi. Questo è improbabile che sia uno.

E quarto, un'altra risposta suggerito memorizzare i risultati intermedi in una variabile locale. Si noti che così facendo può in alcune situazioni rendere le cose molto più velocemente, e in altri, può rendere più lento. A volte è più veloce per ricalcolare il risultato inutilmente rispetto per memorizzarlo e guardare di nuovo quando ne hai bisogno.

Come può essere? architetture di chip con un piccolo numero di registri - Sto guardando voi, X 86 - richiedono il jitter di essere molto prudente circa che i locali arrivare ad essere in registri e che arrivare a essere accessi Stack. Incoraggiare il jitter di mettere qualcosa che viene utilizzato di rado in un registro a volte significa costringere qualcos'altro fuori di tale registro, cosa che avrebbe fornito maggiori benefici dall'essere in un registro che il vostro valore di rado utilizzato.

In breve: non tentare di indovinare il jitter dal comoda poltrona; il comportamento del codice di mondo reale può essere profondamente controintuitivo. Prendere decisioni prestazioni basati su misurazioni empiriche realistici.

A destra, il compilatore C # non fa ottimizzazioni come questo. Ma il compilatore JIT fa certamente. Tutti i getter hai postato sono abbastanza piccoli per ottenere inline, con un conseguente accesso diretto al campo.

Un esempio:

static void Main(string[] args) {
  var calc = new nonsensical_calculator(42);
  double rate = calc.monthlyRate;
  Console.WriteLine(rate);
}

Genera:

00000000  push        ebp                          ; setup stack frame
00000001  mov         ebp,esp 
00000003  sub         esp,8 
00000006  mov         ecx,349DFCh                  ; eax = new nonsensical_calculator
0000000b  call        FFC50AD4 
00000010  fld         dword ptr ds:[006E1590h]     ; st0 = 42
00000016  fstp        qword ptr [eax+4]            ; _rate = st0
00000019  fld         qword ptr [eax+4]            ; st0 = _rate
0000001c  fdiv        dword ptr ds:[006E1598h]     ; st0 = st0 / 12
00000022  fstp        qword ptr [ebp-8]            ; rate = st0
      Console.WriteLine(rate);
// etc..

Si noti come sia la chiamata al costruttore e il getter di proprietà sono scomparsi, sono inline in Main (). Il codice accede direttamente sul campo _rate. Anche la variabile calc è andato, si svolge il riferimento nel registro EAX.

L'istruzione a indirizzo 19 mostra che più lavoro potrebbe essere fatto sul ottimizzatore. Tempo permettendo.

Per mettere una rotazione leggermente diversa su questo, ritengono che le proprietà sono davvero wrapper proprio dietro metodi una volta che il codice viene compilato a IL. Quindi, se, invece di questo:

public class nonsensical_calculator
{
    double bleh
    {
        get { return Math.Pow(ar - days, _term); }
    }
    // etc.
}

Hai avuto questo:

public class nonsensical_calculator
{
    double GetBleh()
    {
        return Math.Pow(ar - days, _term);
    }
}

Ti aspetti che il compilatore di ottimizzare la chiamata di metodo per voi?

Io non sono un esperto in jitter ma dubito che anche il jitter sarà "cache" questo; avrebbe dovuto tenere traccia di tutti i tipi di stato e di invalidare la voce quando uno dei campi dipendenti cambiano, e terrificante come il jitter NET è, io non credo che sia così intelligente. Può inline il metodo, ma che di solito non farà una grande differenza riguarda le prestazioni.

In conclusione, non si basano sul compilatore o jitter per fare queste ottimizzazioni per voi. Inoltre, si potrebbe considerare seguendo le linee guida di progettazione comune di non mettere calcoli costosi getter di proprietà, perché appare al chiamante di essere a buon mercato, anche se potrebbe non essere.

Se avete bisogno di prestazioni, quindi precompute questi valori ogni volta che i campi dipendenti cambiano. O, meglio ancora, profilo il codice utilizzando uno strumento come EQATEC (gratuito) o ANTS e vedere se dove il costo delle prestazioni è davvero . Ottimizzazione senza profiling è come sparare con una benda sugli occhi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top