2011-01-19 19 views
5

¿Las optimizaciones realizadas por el compilador C# o JITter pueden tener efectos secundarios visibles?C# optimizations and side effects

Un ejemplo que he pensado.

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

Al llamar A(x)x se garantiza que sea mantenido con vida hasta el final de A - porque B utiliza el mismo parámetro. Pero si B se define como

public void B(Something x) { } 

Entonces el B(x) pueden ser eliminados por el optimizador y luego podría ser necesaria una llamada GC.KeepAlive(x) lugar.

¿Puede esta optimización realmente ser realizada por JITter?

¿Hay otras optimizaciones que puedan tener efectos secundarios visibles, excepto cambios en la traza de pila?

+2

¿Por qué en el mundo necesitarías mantener 'x' vivo si ya no se usa? –

+0

Para obtener un buen artículo sobre optimizaciones de JIT, consulte http://www.codeproject.com/KB/dotnet/JITOptimizations.aspx: el JITer podría optimizar las llamadas a métodos vacíos, pero mantendrá los métodos en sí – BrokenGlass

+1

@JSBangs: la razón por la que Es posible que desee mantener algo vivo porque su destrucción provoca que el código no administrado se ejecute en un lugar no deseado. Por ejemplo, hay objetos de almacenamiento COM que requieren almacenes "internos" (piense en directorios anidados) para que se cierren * antes de * almacenes "externos" que los contengan. El recolector de basura podría determinar que un objeto administrado que representa un almacenamiento externo ya no se usa, y decide limpiarlo antes de que se limpie un almacenamiento interno que todavía está vivo. En la práctica, tuve que escribir un código para manejar esta situación y es un verdadero dolor. –

Respuesta

6

Cuando se garantiza que la llamada A (x) x se mantiene viva hasta el final de A - porque B usa el mismo parámetro.

Esta afirmación es falsa. Supongamos que el método A siempre arroja una excepción. El jitter podría saber que nunca se alcanzará B y, por lo tanto, x puede liberarse inmediatamente. Supongamos que el método A entra en un ciclo infinito incondicional después de su última referencia a x; de nuevo, el jitter podría saber que a través del análisis estático, determine que nunca se volverá a hacer referencia a x y programe su limpieza. No sé si el jitter realmente realiza esta optimización; parecen dudosos, pero son legales.


¿Puede esta optimización (es decir, haciendo la limpieza inicial de una referencia que no se utiliza en cualquier lugar) en realidad ser hecho por la fluctuación de fase?

Sí, y en la práctica, ya está hecho. Eso no es un efecto secundario observable.

Esto se justifica por la sección 3.9 de la especificación, que cito para su conveniencia:

Si el objeto, o cualquier parte de ella, no se puede acceder por cualquier posible continuación de la ejecución, que no sea el ejecución de destructores, se considera que el objeto ya no está en uso, y se convierte en elegible para la destrucción. El compilador de C# y el recolector de basura pueden elegir analizar el código para determinar qué referencias a un objeto se pueden usar en el futuro. Por ejemplo, si una variable local que está en el alcance es la única referencia existente a un objeto, pero esa variable local nunca se menciona en ninguna posible continuación de ejecución desde el punto de ejecución actual en el procedimiento, el recolector de basura puede (pero es no es obligatorio) tratar el objeto como si ya no estuviera en uso.


Pueden optimizaciones realizadas por el compilador de C# o la fluctuación de tener efectos secundarios visibles?

Su pregunta se responde en la sección 3.10 de la especificación, que cito aquí para su conveniencia:

ejecución de un programa en C# procede tal que los efectos secundarios de cada ejecución de hilo se conservan en puntos críticos de ejecución.

Un lado efecto se define como una lectura o escritura de un campo volátil, una escritura a una variable no volátil, una escritura a un recurso externo , y el lanzamiento de una excepción.

La crítica de ejecución puntos en los que se debe preservar el orden de estos efectos secundarios son referencias a campos volátiles, estados de bloqueo, y la creación del hilo y terminación.

El entorno de ejecución es libre de cambio el orden de ejecución de un programa en C# , sujeto a las limitaciones siguientes:

dependencia de datos es conservado dentro de un hilo de ejecución. Es decir, el valor de cada variable se calcula como si todas las sentencias del subproceso se ejecutaran en el orden de programa original.

Las reglas de ordenamiento de inicialización son preservadas.

El orden de los efectos secundarios se conserva con respecto a las lecturas volátiles y escribe.

Además, el entorno ejecución no tiene por qué evaluar parte de una expresión si puede deducir que el valor de esa expresión no se utiliza y que no se producen efectos secundarios necesarios (incluyendo cualquier causados ​​por llamar a un método o accediendo a un campo volátil).

Cuando la ejecución del programa se interrumpe por un evento asíncrono (tal como un excepción lanzada por otro hilo), no se garantiza que los efectos secundarios observables son visibles en el orden original del programa.

El penúltimo párrafo es Creo que es el que más le preocupa; es decir, ¿qué optimizaciones puede realizar el tiempo de ejecución con respecto a los efectos secundarios observables? El tiempo de ejecución está permitido realizar cualquier optimización que no afecte a un efecto secundario observable.

Tenga en cuenta que, en particular, la dependencia de datos solo se conserva dentro de un hilo de ejecución. La dependencia de datos es no, se garantiza que se conservará cuando se observe desde otro hilo de ejecución.

Si eso no responde a su pregunta, haga una pregunta más específica. En particular, será necesaria una definición cuidadosa y precisa de "efecto secundario observable" para responder a su pregunta con más detalle, si no considera que la definición dada arriba coincide con su definición de "efecto secundario observable".

+0

Dado que paso el argumento a 'B', aunque no se usa dentro de ese método, pensé que' x' se consideraría en vivo al menos hasta que se ingrese B. ¿Estaba equivocado? – configurator

+0

@configurator Supongo que el JIT puede ver que el método no hace nada y eliminar el método y todas las llamadas a él por completo. – Davy8

+0

@configurator: como se establece claramente en la sección 3.9, el jitter no tiene la obligación de considerar el contenido de x vivo si x no se puede leer desde un cierto punto. El jitter no tiene la obligación de considerarlo muerto, tampoco. Es una optimización definida por la implementación. –

8

Si su función B no utiliza el parámetro x, eliminarlo y recopilar x early no tiene ningún efecto secundario visible.

Para ser "efectos secundarios visibles", deben estar visibles en el programa, no en una herramienta externa como un depurador o un visor de objetos.

0

Incluir B en su pregunta solo confunde el asunto. Teniendo en cuenta este código:

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

Suponiendo que A(x) se gestiona código, a continuación, llamar A(x) mantiene una referencia a x, por lo que el recolector de basura no puede cobrar x hasta después A devoluciones. O al menos hasta que A ya no lo necesite. Las optimizaciones realizadas por el JITer (errores ausentes) no recogerán prematuramente x.

Debe definir lo que quiere decir con "efectos secundarios visibles". Uno esperaría que las optimizaciones de JITer al menos tengan el efecto secundario de hacer que su código sea más pequeño o más rápido. ¿Son esos "visibles"? ¿O quieres decir "indeseable"?

0

Eric Lippert ha comenzado una gran serie sobre refactorización que me lleva a pensar que el compilador C# y JITter se aseguran de no introducir efectos secundarios. Part 1 y Part 2 se encuentran actualmente en línea.