2011-01-19 26 views
5

Le ottimizzazioni eseguite dal compilatore C# o dal JITter possono avere effetti collaterali visibili?C# ottimizzazioni ed effetti collaterali

Un esempio che ho perso.

var x = new Something(); 
A(x); 
B(x); 

Quando si chiama A(x)x è garantito per essere mantenuto in vita fino alla fine del A - perché B utilizza lo stesso parametro. Ma se B è definito come

public void B(Something x) { } 

Poi il B(x) può essere eliminato l'ottimizzatore e quindi potrebbe essere necessaria una chiamata GC.KeepAlive(x) invece.

Può questa ottimizzazione in realtà essere fatto il jitter?

Esistono altre ottimizzazioni che potrebbero avere effetti collaterali visibili, ad eccezione di modifiche dello stack trace?

+2

Perché nel mondo dovresti tenere 'x' vivo se non viene più utilizzato? –

+0

Per un buon articolo sulle ottimizzazioni JIT consulta http://www.codeproject.com/KB/dotnet/JITOptimizations.aspx - il JITer potrebbe ottimizzare le chiamate a metodi vuoti, ma manterrà i metodi stessi – BrokenGlass

+1

@JSBangs: il motivo per cui potrebbe voler mantenere qualcosa di vivo perché la sua distruzione innesca il codice non gestito da eseguire in un luogo indesiderato. Ad esempio, esistono oggetti di archiviazione COM che richiedono archivi "interni" (si pensi che le directory annidate) siano chiuse * prima di * archivi "esterni" che li contengono. Il garbage collector potrebbe determinare che un oggetto gestito che rappresenta uno storage esterno non viene più utilizzato e decide di ripulirlo prima che una memoria interna ancora in vita venga ripulita. In pratica ho dovuto scrivere un codice per affrontare questa situazione ed è un vero dolore. –

risposta

6

Quando si chiama A (x) x è mantenuto in vita fino alla fine di A - perché B utilizza lo stesso parametro.

Questa affermazione è falsa. Supponiamo che il metodo A generi sempre un'eccezione. Il jitter potrebbe sapere che B non sarà mai raggiunto, e quindi x può essere rilasciato immediatamente. Supponiamo che il metodo A entri in un ciclo infinito incondizionato dopo il suo ultimo riferimento a x; di nuovo, il jitter potrebbe sapere che tramite analisi statica, determinare che x non verrà mai più referenziato e pianificarne la pulizia. Non so se il jitter esegue effettivamente queste ottimizzazioni; sembrano poco sicuri, ma sono legali.


Può questa ottimizzazione (vale a dire, a fare pulizia precoce di un riferimento che non viene utilizzato da nessuna parte) in realtà essere fatto dal oscillazioni?

Sì, e in pratica, è fatto. Questo non è un effetto collaterale osservabile.

Ciò è giustificato dalla sezione 3.9 della specifica, che cito per la vostra convenienza:

Se l'oggetto, o parte di esso, non si può accedere da qualsiasi possibile continuazione di esecuzione, oltre alla in esecuzione di distruttori, l'oggetto è considerato non più in uso e diventa idoneo per la distruzione. Il compilatore C# e il garbage collector possono scegliere di analizzare il codice per determinare quali riferimenti a un oggetto possono essere utilizzati in futuro. Ad esempio, se una variabile locale che è in ambito è l'unico riferimento esistente a un oggetto, ma quella variabile locale non viene mai indicata in alcuna possibile continuazione dell'esecuzione dal punto di esecuzione corrente nella procedura, il garbage collector può (ma è non richiesto) trattare l'oggetto come non più in uso.


Può ottimizzazioni fatte dal compilatore C# o il jitter avere effetti collaterali visibili?

tua domanda si risponde nella sezione 3.10 della specifica, che cito qui per comodità:

Esecuzione di un programma C# procede tale che gli effetti collaterali di ogni esecuzione di filo sono conservati a punti di esecuzione critici.

Un lato effetto viene definita come una lettura o scrittura di un campo volatile, una scrittura ad una variabile non volatile, una scrittura a una risorsa esterna , e il lancio di un'eccezione.

L'esecuzione critico punti in cui l'ordine di questi effetti collaterali deve essere conservato sono riferimenti ai campi volatili, istruzioni lock, e creazione di thread e terminazione.

L'ambiente di esecuzione è libero di modifica l'ordine di esecuzione di un programma C# , fatte salve le seguenti vincoli:

dipendenza dai dati è conservato all'interno di un thread di esecuzione. Vale a dire, il valore di ciascuna variabile viene calcolato come se tutte le istruzioni nel thread siano state eseguite nell'ordine originale del programma.

Le regole di ordinazione di inizializzazione sono conservate.

L'ordinamento degli effetti indesiderati viene mantenuto in riferimento alle letture volatili e alle scritture .

Inoltre, l'ambiente esecuzione non deve valutare una parte di espressione se può dedurre che il valore di tale espressione non viene utilizzato e che non necessari effetti collaterali sono prodotte (compresi qualsiasi causato chiamando un metodo o accesso a un campo volatile).

Quando esecuzione del programma viene interrotta da un evento asincrono (come eccezione lanciata da un altro thread), non è garantito che le effetti collaterali osservabili sono visibili in nell'ordine programma originale.

Il secondo penultimo paragrafo è quello che più ti preoccupa; cioè, quali ottimizzazioni è il tempo di esecuzione a cui è consentito eseguire rispetto agli effetti collaterali osservabili? Il runtime è autorizzato a eseguire qualsiasi ottimizzazione che non influisca su un effetto collaterale osservabile.

Si noti che in particolare la dipendenza dei dati viene mantenuta solo all'interno di un thread di esecuzione. La dipendenza dei dati è non garantita per essere conservata se osservata da un altro thread di esecuzione.

Se questo non risponde alla tua domanda, fare una domanda più specifica. In particolare, sarà necessaria una definizione attenta e precisa di "effetto collaterale osservabile" per rispondere alla tua domanda in modo più dettagliato, se non consideri la definizione data sopra come corrispondente alla tua definizione di "effetto collaterale osservabile".

+0

Dato che sto passando l'argomento a 'B', anche se non è usato all'interno di quel metodo, ritengo che' x' sia considerato live almeno fino a quando non viene inserito B. Mi sono sbagliato? – configurator

+0

@configurator Immagino che il JIT possa vedere che il metodo non fa nulla ed elimina completamente il metodo e tutte le chiamate. – Davy8

+0

@configurator: come afferma chiaramente la sezione 3.9, il jitter non ha alcun obbligo di considerare il contenuto di x vivo se x non è mai stato letto da un certo punto. Il jitter non ha alcun obbligo di considerarlo morto. È un'ottimizzazione definita dall'implementazione. –

8

Se la funzione B non utilizza il parametro x, eliminarlo e raccogliere x in anticipo non ha effetti secondari visibili su.

Per essere "effetti collaterali visibili", devono essere visibili per il programma, non per uno strumento esterno come un debugger o visualizzatore di oggetti.

0

Includere B nella tua domanda confonde la questione. Dato questo codice:

var x = new Something(); 
A(x); 

Supponendo che A(x) è gestito il codice, quindi chiamando A(x) mantiene un riferimento alla x, in modo che il garbage collector non può raccogliere fino a dopo xA ritorni. O almeno fino al A non ne ha più bisogno. Le ottimizzazioni fatte dal JITer (bug assenti) non raccoglieranno prematuramente x.

Si dovrebbe definire cosa si intende per "effetti collaterali visibili." Si spera che le ottimizzazioni di JITer abbiano almeno l'effetto collaterale di rendere il codice più piccolo o più veloce. Quelli sono "visibili?" O intendi "indesiderabile?"

0

Eric Lippert ha avviato una grande serie sul refactoring che mi porta a credere che il compilatore C# e JITter si assicurino di non introdurre effetti collaterali. Part 1 e Part 2 sono attualmente online.

Problemi correlati