2012-02-02 10 views
7

Así que en C#, podría tener el siguiente código:¿Por qué C# vincula las variables locales por adelantado?

void DoSomething() 
{ 
    //some code. 
    int x = 5; 
    //some more code. 
} 

Tan pronto como entras HacerAlgo, el CLR establece un espacio para int x. ¿Por qué no espera hasta que alcanza la línea con int x = 5 en él? Especialmente porque a pesar de que x está vinculado, no le permite realmente usarlo hasta que se llegue a esa línea de todos modos.

+8

¿Por qué el ajuste del tamaño de la pila sería incrementalmente mejor que reservarlo todo por adelantado? – ildjarn

+0

presumiblemente, lleva tiempo mezclar la pila ... si tengo muchas variables locales, ¿por qué esperar cuando no tengo que hacerlo (todavía)? – GWLlosa

+2

Correcto, lleva un tiempo confundir la pila, por lo que hacerlo todo por adelantado en lugar de una y otra vez cada vez que una nueva variable ingresa al alcance es exactamente lo que se hace. – ildjarn

Respuesta

13

Tan pronto como ingrese DoSomething, el CLR configura el espacio para int x. ¿Por qué no espera hasta que alcanza la línea con int x = 5 en él?

La pregunta no responde porque toda la pregunta se basa en una premisa incorrecta. El espacio de almacenamiento para la variable local puede ser:

  • asignaron cuando se introduce primero el método
  • asignado cuando el control llega a la declaración
  • asignado cuando el control llega a la inicialización (suponiendo que la inicialización y la declaración son diferentes)
  • asignado en circunstancias especiales; si, por ejemplo, el local es un local cerrado de una lambda, o en un bloque iterador, o en un bloque asíncrono, cómo y cuándo se asigna el almacenamiento local puede volverse complicado
  • elided enteramente; si el local nunca se usa, puede que no se asigne en primer lugar.

El compilador de C# y el compilador JIT sin duda asegurarse de que el almacenamiento local se asigna de una manera que es correcta, y tratará de asegurarse de que es eficiente. La forma en que elijan hacerlo depende de la situación exacta. Podría ser más eficiente asignar el espacio por adelantado, y podría ser más eficiente asignarlo solo mientras la variable esté en uso; el jitter tiene una gran latitud al elegir el tiempo de vida de una variable local. Se permite que las variables locales vivan más largas y más cortas de lo que su alcance implicaría si el jitter puede hacerlo sin violar la corrección del programa.

Dado que la premisa de la pregunta es incorrecta, no hay respuesta a la pregunta. Haz una mejor pregunta.

+0

La respuesta es [mu] (http://bit.ly/s4jx85). – jason

+1

10 upvotes y una aceptación por una respuesta que afirme que no hay respuesta a la pregunta. Esta respuesta parece contradecirse por existir. –

+2

@IgbyLargeman: La sugerencia que obtienes cuando pasas el voto arriba es "esta respuesta es útil", no "esta respuesta responde a la pregunta que se hizo". Si señalar que una pregunta se basa en una premisa incorrecta es * útil *, entonces no hay contradicción allí. –

10

Como se puede saber, hay varios pasos de C# código al código nativo aquellos que son:

  • Compilar desde C# para IL (código de bytes)
  • JITting de bytecode a código nativo

C# no tiene ningún control de la hora en que se asigna la memoria, lo que se llama vinculante, todo depende de JIT. Al sacar esto del camino, veamos qué es en el control de C#. El código de bytes producido por C# debe cumplir el estándar CLR ECMA. Si vamos a la sección 12.1.6.1 de la Partición 1, veremos que la norma define que la casa de la variable local es en el encabezado del método. Como las firmas de métodos generalmente aparecen en el comienzo de un método en una lista, se obtiene una impresión (falsa) de que están vinculados por adelantado, lo que de hecho puede ser o no ser el caso.

Sin embargo, si está mirando el código nativo compilado, el resultado puede variar de una plataforma a otra. Históricamente, la asignación de espacio en la pila de la CPU para una variable local se realiza mediante una sola instrucción de la CPU de cambiar el puntero de la pila. Si desea hacerlo variable por variable, tendrá muchas instrucciones, una por variable, que es menos eficiente. Esta es la razón por la que, al menos en x86, verá que el espacio en la pila de la CPU se asigna por adelantado.

1

Su pregunta parece basado en algunas suposiciones:

  • El costo de instalación siempre va a ser alta
  • instalación sólo va a pasar un pequeño número de veces
  • la referencia/tipo de valor en el nivel CLR siempre tiene un mapeo uno a uno con la variable en el nivel C#

Esto puede ser cierto para su código, pero puede no ser cierto para la mayoría del código que existe.

Las suposiciones también parecen ignorar la presencia de capas subyacentes del proceso que compilan/interpretan esto hasta el código de máquina.

En resumen, el código que escribe en C# es una abstracción que se basa en IL, que es otra abstracción que se basa en el CLR, que es otra abstracción, etc.

Por lo que vale, tengo serias dudas de que esta decisión tenga un impacto significativo en el rendimiento de su aplicación ... pero tal vez el estilo de Eric Lippert (http://blogs.msdn.com/b/ericlippert/) puede compartir un análisis más profundo.

3

qué debería hacer el compilador si encuentra algo similar a:

for (int i = 0; i < 1000; i++) 
{ 
    int j = .... //should the compiler set up space when it reaches this line? 1000 times? 
} 

Además Realmente creo que el coste de creación de espacio de los locales no es un factor. Si llega a ser, entonces probablemente estés lidiando con demasiados locales en un solo método y es mejor refacturar tu código.

Cuestiones relacionadas