2011-10-20 8 views
8

Ho letto diversi articoli e domande/risposte che concludono che la pratica migliore è lasciare che il compilatore JIT esegua tutte le ottimizzazioni per le chiamate di funzione in linea. Ha senso.Il compilatore JIT ottimizza le dichiarazioni di variabili non necessarie (in linea)?

E le dichiarazioni di variabili in linea? Il compilatore ottimizza anche questi?

Cioè, sarà questo:

 Dim h = (a + b + c)/2  'Half-Perimeter 

     If maxEdgeLength/(Math.Sqrt(h * (h - a) * (h - b) * (h - c))/h) <= MaximumTriangleAspectRatio Then 
      'Do stuff here. 
     End If 

avere prestazioni migliori di questo:

 Dim perimeter = a + b + c 'Perimeter 
     Dim h = perimeter/2  'Half-Perimeter 

     Dim area = Math.Sqrt(h * (h - a) * (h - b) * (h - c)) 'Heron's forumula. 
     Dim inradius = area/h 
     Dim aspectRatio = maxEdgeLength/inradius 

     If aspectRatio <= MaximumTriangleAspectRatio Then 
      'Do stuff here. 
     End If 

Naturalmente io preferisco il secondo perché è più facile da leggere e correggere, ma non possono permettersi il degrado delle prestazioni, se esiste.

Nota: ho già identificato questo codice come collo di bottiglia: non c'è bisogno di risposte sull'ottimizzazione prematura. :-)

+6

Non puoi permetterti 20 byte extra di RAM?È molto improbabile che ciò provochi le prestazioni della tua app, specialmente se questo codice viene eseguito più e più volte ogni volta sono gli stessi 20 byte. –

+0

Non sarebbe il compilatore (non il JIT!) Sarebbe "responsabile" per questo tipo di ottimizzazione durante la traduzione in MSIL? (E se, quindi, in che misura il compilatore MS C# si ottimizza.) In ogni caso * lo confrontiamo in un ambiente relativistico * in quanto è l'unico modo per "sapere per certo" che è più veloce e in che misura. –

+4

Sei l'unica persona qui che può rispondere alla domanda. Hai scritto il codice in entrambi i modi. Eseguilo in entrambe le direzioni, misura i tempi e poi saprai quale è più veloce. Ciò che il jitter fa o non fa è irrilevante; sapere cosa fa il jitter in nessun modo risponde alla domanda "quale è più veloce?" –

risposta

16

Variabili temporanee che hanno nomi o no è un non-problema.

Ma è possibile ottimizzare in modo significativo questa disuguaglianza.

Il codice era:

If maxEdgeLength/(Math.Sqrt(h * (h - a) * (h - b) * (h - c))/h) <= MaximumTriangleAspectRatio Then 

moltiplicare entrambi i lati per la radice quadrata, eliminando divisione (disuguaglianza è conservata, perché radice quadrata non può restituire un numero negativo):

If maxEdgeLength <= (Math.Sqrt(h * (h - a) * (h - b) * (h - c))/h) * MaximumTriangleAspectRatio Then 

Ora, quadrato entrambi i lati eliminano quella radice quadrata costosa:

If maxEdgeLength * maxEdgeLength <= h * (h - a) * (h - b) * (h - c)/h/h * MaximumTriangleAspectRatio * MaximumTriangleAspectRatio Then 

Annulla e m in più da h.

If maxEdgeLength * maxEdgeLength * h <= (h - a) * (h - b) * (h - c) * MaximumTriangleAspectRatio * MaximumTriangleAspectRatio Then 

Questo sarà molto più veloce. Se questo calcolo viene ripetuto, prendere in considerazione la memorizzazione nella cache dei risultati di parte di questa espressione per ulteriori miglioramenti.

Utilizzare i commenti per spiegare la formula. Per eliminare una chiamata Math.Sqrt in una parola bottleneck, vale la pena scrivere l'espressione in un formato non proprio semplice.

+1

Invece di (solo) usare i commenti, rifatta il calcolo in una funzione autonoma - è troppo lungo che si verifichi in un codice del genere. Spiega * che * funziona come necessario (in particolare, spiega la trasformazione fatta sopra ...). –

+6

+1 per identificare che è possibile trovare un'ottimizzazione non nel codice o nella compilazione, ma nell'algoritmo stesso. –

5

Tra l'altro, solo per fare l'avvocato del diavolo, anche ho voluto farlo notare:

JIT inlining della funzione di guarda la lunghezza in byte del MSIL, e non la complessità del calcolo. L'aggiunta di variabili locali (e l'attesa del JIT per la loro registrazione) potrebbe aumentare la dimensione MSIL della funzione in modo sufficiente a rendere l'intera funzione non candidata all'integrazione.

Non è probabile che ciò comporti una grande differenza rispetto all'uso non necessario di Math.Sqrt, ma è possibile. Come diceva Eric Lippert, ne saprai di più misurando effettivamente. Tuttavia, tale misura è valida solo per una particolare esecuzione del programma e non generalizza a diversi processori o versioni future del runtime .NET (inclusi i service pack) che spesso modificano il comportamento JIT. Quindi vuoi un approccio analitico ed empirico combinato all'ottimizzazione.

+1

Sei un eccellente punto Ben; tranne che in casi banali non esiste in realtà alcuna cosa come "ottimizzazione". Ogni cosiddetto "ottimizzazione" è in realtà un compromesso che noi * speriamo * sia migliore, ma potrebbe non esserlo. Puoi negoziare più registri usando questo calcolo per meno registri disponibili su quel calcolo, e spera che tu abbia scelto quello che doveva essere più veloce. Oppure scambiate più memoria con meno tempo, e sperate che in tal modo non manchi più tardi una cache. E così via. –

+0

@EricLippert: Sì, l'ottimizzazione assoluta comporta dei compromessi. Ma questo è oltre i punti che sto facendo. Quando si utilizza la compilazione JIT, si è soggetti a algoritmi di ottimizzatore modificati, che apportano diversi compromessi in base allo stesso input MSIL. Anche con i compilatori nativi AOT tradizionali, la stessa sequenza di istruzioni può avere prestazioni diverse su processori diversi con caratteristiche CPI diverse. –

Problemi correlati