2010-11-03 12 views
7

Durante una revisión reciente del código, un colega sugirió que, para la clase con 4 propiedades int, la asignación de cada uno a cero en el constructor daría lugar a una penalización de rendimiento.Asignación de campos enteros/propiedades a cero en un constructor

Por ejemplo,

public Example() 
    { 
     this.major = 0; 
     this.minor = 0; 
     this.revision = 0; 
     this.build = 0; 
    } 

Su punto era que esto es redundante, ya que serán puestos a cero por defecto y se están introduciendo sobrecarga mediante la realización esencialmente la misma tarea dos veces. Mi punto era que el impacto en el rendimiento sería insignificante si uno existiera y esto es más legible (hay varios constructores) ya que la intención del estado del objeto después de llamar a este constructor es muy clara.

¿Qué opinas? ¿Hay un aumento en el rendimiento que merezca la pena?

+4

Tenga cuidado con la optimización prematura (posible). – jball

+0

"La optimización prematura es la raíz de todo mal". –

+3

Si esto fuera 'programmers.stackexchange.com' respondería desde el punto de vista de la legibilidad. Claro, no va a afectar el rendimiento, pero agrega ruido al código fuente. Es como, digamos, escribir 'foreach (elemento var en someEnumerable.ToList())'. –

Respuesta

5

No creo que sean la misma operación, y es una diferencia de rendimiento.He aquí una microbenchmark mostrarlo:

using System; 
using System.Diagnostics; 

class With 
{ 
    int x; 

    public With() 
    { 
     x = 0; 
    } 
} 


class Without 
{ 
    int x; 

    public Without() 
    { 
    } 
} 


class Test 
{ 
    static void Main(string[] args) 
    { 
     int iterations = int.Parse(args[0]); 
     Stopwatch sw = Stopwatch.StartNew(); 
     if (args[1] == "with") 
     { 
      for (int i = 0; i < iterations; i++) 
      { 
       new With(); 
      } 
     } 
     else 
     { 
      for (int i = 0; i < iterations; i++) 
      { 
       new Without(); 
      } 
     } 
     sw.Stop(); 
     Console.WriteLine(sw.ElapsedMilliseconds); 
    } 
} 

Resultados:

c:\Users\Jon\Test>test 1000000000 with 
8427 

c:\Users\Jon\Test>test 1000000000 without 
881 

c:\Users\Jon\Test>test 1000000000 with 
7568 

c:\Users\Jon\Test>test 1000000000 without 
819 

Ahora, habría que hacer cambiar el código? Absolutamente no. Escriba primero el código más legible. Si es más legible con la tarea, mantenga la tarea allí. A pesar de que un microbenchmark muestra que tiene un costo, ese sigue siendo un pequeño costo en el contexto de hacer cualquier trabajo real. Aunque la diferencia proporcional es alta, sigue creando instancias de mil millones en 8 segundos en la ruta "lenta". Supongo que en realidad hay algún tipo de optimización para constructores completamente vacíos encadenados directamente al constructor object() completamente vacío. La diferencia entre asignar a dos campos y solo asignar a un campo es mucho más pequeño.

En términos de mientras que el compilador no puede optimizarlo, tenga en cuenta que un constructor base podría modificar el valor por reflexión, o quizás una llamada a un método virtual. El compilador podría potencialmente notar eso, pero parece una optimización extraña.

+0

Gracias Jon, siempre aprecio las pruebas de banco, etc. en una respuesta. –

6

No, no lo hay. El compilador optimizará estas operaciones; la misma tarea no se realizará dos veces. Tu colega está equivocado.

[Editar en base a la entrada de la siempre excelente Jon Skeet]

El compilador debe optimizar las operaciones, pero aparentemente no se optimizan completamente fuera; sin embargo, la ganancia de optimización es completamente insignificante, y el beneficio de tener la asignación sea tan explícita es bueno. Es posible que su colega no esté completamente equivocado, pero se están centrando en una optimización completamente trivial.

+7

¿Qué compilador dice que lo optimizará? El compilador de C# no, por lo que puedo decir. –

+1

No creo que sean la misma operación. Uno es un borrado de memoria, el otro es asignación explícita. No veo ninguna razón para suponer que esto sería eliminado. –

+0

Aquí falta la optimización que hace el JIT/Runtime. Estoy bastante seguro de que esto está optimizado. [Editar:] Podría ser solo algunos tiempos de ejecución para optimizarlo. – Dykam

0

No creo que deba preocuparse por el rendimiento, generalmente hay muchos otros lugares donde se puede optimizar el programa. Por otro lado, no veo ninguna ganancia al especificar estos valores en el constructor, ya que van a establecerse en 0 de todos modos.

3

Debe centrarse en la claridad del código, eso es lo más importante. Si el rendimiento se convierte en un problema, mida el rendimiento y vea cuáles son sus cuellos de botella, y mejore. No vale la pena gastar tanto tiempo preocupándose por el rendimiento cuando la facilidad de comprensión del código es más importante.

4

Mi comprensión es que la memoria de los objetos se borra a cero con un simple y muy rápido borrado de memoria. estas asignaciones explícitas, sin embargo, tomarán IL adicional. De hecho, algunas herramientas detectarán que usted asigna el valor predeterminado (en un inicializador de campo) y desaconsejan su uso.

Así que yo diría: no hagas esto - es potencialmente marginalmente más lento. Pero no por mucho. En resumen, creo que tu amigo está en lo cierto.

Lamentablemente estoy en un dispositivo móvil en este momento, sin las herramientas adecuadas para demostrarlo.

1

puede inicializar como campos directamente:

public int number = 0; 

Y también está claro.

+0

Y también está optimizado por el compilador. – McKay

+1

@McKay: ¿evidencia de esto? –

+1

Por lo que yo sé NO está optimizado por el compilador, pero la penalización de rendimiento es tan baja que ni siquiera vale la pena llamarlo una "penalización". –

1

La pregunta más importante es: ¿hay realmente una ganancia de legibilidad? Si las personas que mantienen el código ya saben que las entradas están asignadas a cero, este es solo un código más que tienen que analizar. Quizás el código sería más limpio sin líneas que no hacen nada.

1

De hecho, usaría la asignación en el constructor, solo por legibilidad, y por marcar la intención 'No me olvidé de inicializar esos'. Confiar en el comportamiento predeterminado tiende a confundir a otros desarrolladores.

Cuestiones relacionadas