2012-03-23 15 views
55

Tenga en cuenta que esta pregunta está relacionada únicamente con el rendimiento. Vamos a omitir las pautas de diseño, la filosofía, la compatibilidad, la portabilidad y todo lo que no esté relacionado con el rendimiento puro. Gracias.Campo vs Propiedad. Optimización del rendimiento

Ahora a la pregunta. Siempre asumí que debido a que C# getters/setters son realmente métodos disfrazados, entonces leer el campo público debe ser más rápido que llamar a un getter.

Para asegurarse de que hice una prueba (el código a continuación). Sin embargo, esta prueba solo produce los resultados esperados (es decir, los campos son más rápidos que getters al 34%) si lo ejecuta desde Visual Studio.

Una vez que se ejecuta desde la línea de comandos se muestra más o menos el mismo tiempo ...

La única explicación podría ser es que el CLR hace una optimización adicional (corrígeme si estoy equivocado aquí).

No creo que en una aplicación real donde esas propiedades se usen de una manera mucho más sofisticada se optimicen de la misma manera.

Por favor, ayúdame a demostrar o refutar la idea de que en las propiedades de la vida real son más lentos que los campos.

La pregunta es: ¿cómo debo modificar las clases de prueba para hacer que el CLR cambie el comportamiento para que el campo público supere a los getters? O muéstreme que cualquier propiedad sin lógica interna funcionará igual que un campo (al menos en el captador)

EDITAR: Estoy hablando solamente de la versión de lanzamiento x64.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Diagnostics; 
using System.Runtime.InteropServices; 

namespace PropertyVsField 
{ 
    class Program 
    { 
     static int LEN = 20000000; 
     static void Main(string[] args) 
     { 
      List<A> a = new List<A>(LEN); 
      List<B> b = new List<B>(LEN); 

      Random r = new Random(DateTime.Now.Millisecond); 

      for (int i = 0; i < LEN; i++) 
      { 
       double p = r.NextDouble(); 
       a.Add(new A() { P = p }); 
       b.Add(new B() { P = p }); 
      } 

      Stopwatch sw = new Stopwatch(); 

      double d = 0.0; 

      sw.Restart(); 
      for (int i = 0; i < LEN; i++) 
      { 
       d += a[i].P; 
      } 

      sw.Stop(); 

      Console.WriteLine("auto getter. {0}. {1}.", sw.ElapsedTicks, d); 

      sw.Restart(); 
      for (int i = 0; i < LEN; i++) 
      { 
       d += b[i].P; 
      } 

      sw.Stop(); 

      Console.WriteLine("  field. {0}. {1}.", sw.ElapsedTicks, d); 

      Console.ReadLine(); 
     } 
    } 

    class A 
    { 
     public double P { get; set; } 
    } 
    class B 
    { 
     public double P; 
    } 
} 
+9

Bobb, Microsoft sabe muy bien que por mucho que pregonen la filosofía/filosofía del diseño, todos usarían campos públicos en lugar de propiedades una vez que se den cuenta de que es un 34% más rápido (aunque es una micro optimización en el 99,9% de los casos, la gente lo haría de todos modos). Así que Eric Lipperts del mundo ha creado un compilador y optimizador muy bueno que se da cuenta de que su propiedad automática es un campo público disfrazado y lo optimiza en consecuencia. – dasblinkenlight

+4

Es posible que desee intentar hacer B una estructura en lugar de una clase si está utilizando solo un contenedor sin métodos. También debe tener en cuenta si está realizando la función Iniciar depuración o Iniciar sin depuración al iniciarlo en Visual Studio. El uso de Iniciar depuración enlaza muchas cosas para permitirle hacer pasos, ver valores, etc., y así puede tener un impacto de rendimiento significativo por sí mismo. – JamieSee

+0

@dasblinkenlight - buen punto ... pero parece que las cosas buenas vienen de CLR, no compilador de C# ... Si fuera compilador, entonces también lo sería en VS. ... ¿Me falta algo aquí? –

Respuesta

44

Como otros ya han mencionado, los captadores son con línea.

Si se quiere evitar procesos en línea, usted tiene que

  • reemplazar las propiedades automáticas con las manual:

    class A 
    { 
        private double p; 
        public double P 
        { 
         get { return p; } 
         set { p = value; } 
        } 
    } 
    
  • y decirle al compilador que no inline del captador (o ambas cosas, si se siente como él):

     [MethodImpl(MethodImplOptions.NoInlining)] 
         get { return p; } 
    

Tenga en cuenta que el primer cambio no hace una diferencia en el rendimiento, mientras que el segundo cambio muestra una clara sobrecarga llamada al método: propiedades

Manual:

auto getter. 519005. 10000971,0237547. 
     field. 514235. 20001942,0475098. 

n procesos en línea del captador:

auto getter. 785997. 10000476,0385552. 
     field. 531552. 20000952,077111. 
+4

Solo me preguntaba si necesita usar una propiedad manual o si seguiría aplicando el atributo al getter automático de propiedades. – Lukazoid

+0

¿Hay alguna razón por la cual alguna vez quiera evitar la inclusión? –

5

La única explicación podría ser es que el CLR hace una optimización adicional (correrct conmigo si estoy equivocado aquí).

Sí, se llama inline. Se realiza en el compilador (nivel de código de máquina, es decir, JIT). Como el getter/setter es trivial (es decir, un código muy simple), las llamadas al método se destruyen y el getter/setter se escribe en el código circundante.

Esto no ocurre en el modo de depuración para admitir la depuración (es decir, la capacidad de establecer un punto de interrupción en un getter o setter).

En el estudio visual no hay forma de hacerlo en el depurador. Compile versión, ejecute sin depurador adjunto y obtendrá la optimización completa.

No creo que en una aplicación real donde esas propiedades se usen de una forma mucho más sofisticada se optimicen de la misma manera.

El mundo está lleno de ilusiones que son incorrectas. Se optimizarán ya que todavía son triviales (es decir, código simple, por lo que están en línea).

+0

gracias ... pero deje caer los comentarios sobre la depuración. No soy tan estúpido tratando de comparar compilaciones de depuración para el rendimiento. ¡Salud! –

+0

Son muy válidas porque cuando desees cosas más complicadas encontrarás un montón de pequeños comportamientos diferentes. El GC para examplke deos NO limpia muchas cosas rápido, también. Mantiene las referencias mucho más tiempo. – TomTom

11

El JIT alineará cualquier método (no solo un captador) que sus métricas internas determinen que será más rápido en línea. Dado que una propiedad estándar es return _Property;, estará en línea en todos los casos.

La razón por la que está viendo un comportamiento diferente es que en el modo de depuración con un depurador conectado, el JIT tiene una desventaja significativa, para garantizar que cualquier ubicación de pila coincida con lo que cabría esperar del código.

También se está olvidando la regla de rendimiento número uno, probar el pensamiento de beats. Por ejemplo, aunque la ordenación rápida es asintóticamente más rápida que la ordenación por inserción, la clasificación por inserción es realmente más rápida para entradas extremadamente pequeñas.

+2

Genial ... bien dicho ... "la prueba supera al pensamiento". Gracias. –

20

Eche un vistazo al artículo del blog Properties vs Fields – Why Does it Matter? (Jonathan Aneja) de uno de los miembros del equipo de VB en MSDN. Él describe la propiedad frente al argumento de campos y también explica las propiedades triviales de la siguiente manera:

Un argumento que he oído para el uso de campos sobre propiedades es que “campos son más rápidos”, pero para las propiedades triviales que en realidad no es verdadera , ya que el compilador Just-In-Time (JIT) de CLR alineará el acceso a la propiedad y generará código que es tan eficiente como acceder directamente a un campo .

+0

Hola @Bobb, no dudo que puedas leer. También me gusta más la respuesta de Heinzi. Me di cuenta de que esta era una pequeña referencia útil y haría una buena contribución al tema en general ya que la explicación proviene de una fuente primaria. – JamieSee

+0

gracias amigo ... era viernes broma no se preocupe. Marqué a Heinzi porque vi su respuesta unos minutos antes que la tuya. pero me gusta el tuyo también, ¡salud! :-) –

2

Cabe señalar que es posible ver el rendimiento "real" en Visual Studio.

  1. Compilar en modo Release con Optimisations habilitado.
  2. Vaya a Depuración -> Opciones y configuraciones, y desmarque "Suprimir la optimización de JIT en la carga del módulo (solo administrado)".
  3. De manera opcional, desmarque "Habilitar solo mi código", de lo contrario, es posible que no pueda ingresar el código.

Ahora, el conjunto de jits será el mismo incluso con el depurador conectado, lo que le permite intervenir en el desmontaje optimizado si lo desea. Esto es esencial para entender cómo CLR optimiza el código.

+0

buen punto. sin embargo es muy elaborado. es más fácil ejecutarlo desde el explorador (especialmente porque VS tiene la carpeta abierta en el comando Explorador de archivos;)) –

+0

Pero luego, si quiere depurar y ver si hay algo en línea o no, deberá adjuntar el depurador como un paso separado Al desmarcar estas dos opciones, puede presionar F5 para depurar su compilación y paso optimizados en el código ensamblado generado. – Asik

Cuestiones relacionadas