2011-03-08 8 views
5

He escrito algunos códigos de prueba para comparar el rendimiento del uso de acceso directo a la propiedad o la reflexión o reflexión mediante el uso de delegados. Pero los resultados que obtengo son desconcertantes, ya que muestran que la reflexión no es mucho más lenta (~ 4%) que el acceso directo a la propiedad, lo cual no creo que sea cierto. ¿Podría alguien decirme si estoy haciendo algo mal aquí?La prueba de reflexión no muestra los números esperados


de 5000 artículos que estoy consiguiendo los siguientes resultados

  • acceso directo: 32.2609 segundos
  • reflexión: 33.623 segundos reflexión de
  • utilizando delegados: 31.7981 segundos

Código:

private static Random random = new Random((int)DateTime.Now.Ticks); 
Private Dictionary<string, Delegate> delegateList = new Dictionary<string, Delegate>(); 
private List<ReflectClass1> dataList = new List<ReflectClass1>(); 

    private void TestMethod2<T>() 
    { 
     foreach (var propertyInfo in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)) 
     { 
      if (propertyInfo.PropertyType.BaseType != typeof(ValueType)) 
      { 
       Func<T, object> getPropDelegate = 
        (Func<T, object>) Delegate.CreateDelegate(typeof (Func<T, object>), null, propertyInfo.GetGetMethod()); 
       delegateList.Add(propertyInfo.Name, getPropDelegate); 
      } 
      //else 
      //{ 
      // Type propertyType = propertyInfo.PropertyType.GetType(); 
      // delegateList.Add(propertyInfo.Name, 
      //      Delegate.CreateDelegate(typeof(Func<T, TResult>), null, propertyInfo.GetGetMethod())); 
      //} 
     } 
    } 
    //http:_//stackoverflow.com/questions/1122483/c-random-string-generator  
    private string RandomString(int size) 
    { 
     StringBuilder builder = new StringBuilder(); 
     char ch; 
     for (int i = 0; i < size; i++) 
     { 
      ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65))); 
      builder.Append(ch); 
     } 

     return builder.ToString(); 
    } 

    private void SetUpReflectObjList() 
    { 
     for (int i = 0; i < 5000 ; i++) 
     { 
      ReflectClass1 reflectClass1 = new ReflectClass1(); 
      reflectClass1.Prop1 = RandomString(15); 
      reflectClass1.Prop2 = RandomString(10); 
      reflectClass1.Prop3 = RandomString(10); 
      reflectClass1.Prop4 = RandomString(10); 
      reflectClass1.Prop5 = RandomString(10); 
      reflectClass1.Prop6 = RandomString(10); 
      reflectClass1.Prop7 = RandomString(10); 
      reflectClass1.Prop8 = RandomString(10); 
      reflectClass1.Prop9 = RandomString(10); 
      reflectClass1.Prop10 = RandomString(10); 
      dataList.Add(reflectClass1); 
     } 
    } 

    private void UseDelegateList() 
    { 
     Debug.WriteLine(string.Format(" Begin delegate performance test. item count = {0} start time: {1}",dataList.Count, DateTime.Now.ToLongTimeString())); 
     for (int i = 0; i < dataList.Count; i++) 
     { 
      foreach (PropertyInfo propertyInfo in typeof(ReflectClass1).GetProperties()) 
      { 
       if (delegateList.ContainsKey(propertyInfo.Name)) 
       { 
        Func<ReflectClass1, object> getPropDelegate = (Func<ReflectClass1, object>) delegateList[propertyInfo.Name]; 
        Debug.Write(string.Format(" By delegates Object: {0} Property: {1} Value: {2}", i, propertyInfo.Name, getPropDelegate(dataList[i]))); 
       } 
      } 
     } 
     Debug.WriteLine(""); 
     Debug.WriteLine(string.Format(" End delegate performance test. item count = {0} end time: {1}", dataList.Count, DateTime.Now.ToLongTimeString())); 
    } 

    private void UseDirectReflection() 
    { 
     Debug.WriteLine(string.Format(" Begin direct reflection performance test. item count = {0} start time: {1}", dataList.Count, DateTime.Now.ToLongTimeString())); 
     for (int i = 0; i < dataList.Count; i++) 
     { 
      foreach (PropertyInfo propertyInfo in typeof(ReflectClass1).GetProperties()) 
      { 
       if (propertyInfo == null) continue; 
       { 
        Debug.Write(string.Format(" By reflection Object: {0} Property: {1} Value: {2}", i, propertyInfo.Name, propertyInfo.GetValue(dataList[i], null))); 
       } 
      } 
     } 
     Debug.WriteLine(""); 
     Debug.WriteLine(string.Format(" End direct reflection performance test. item count = {0} end time: {1}", dataList.Count, DateTime.Now.ToLongTimeString())); 
    } 

    private void DirectOutputTest() 
    { 
     Debug.WriteLine(string.Format(" Begin direct output benchmark. item count = {0} start time: {1}", dataList.Count, DateTime.Now.ToLongTimeString())); 
     for (int i = 0; i < dataList.Count; i++) 
     { 

      Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop1", dataList[i].Prop1)); 
      Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop2", dataList[i].Prop2)); 
      Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop3", dataList[i].Prop3)); 
      Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop4", dataList[i].Prop4)); 
      Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop5", dataList[i].Prop5)); 
      Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop6", dataList[i].Prop6)); 
      Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop7", dataList[i].Prop7)); 
      Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop8", dataList[i].Prop8)); 
      Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop9", dataList[i].Prop9)); 
      Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop10", dataList[i].Prop10)); 
     } 
     Debug.WriteLine(""); 
     Debug.WriteLine(string.Format(" End direct output benchmark. item count = {0} end time: {1}", dataList.Count, DateTime.Now.ToLongTimeString())); 
    } 
+6

Su punto de referencia es defectuoso. Está evaluando el lento 'string.Format' y las llamadas' Debug.Write' aún más lentas (lento en este contexto significa lento en comparación con el acceso directo a la propiedad y en comparación con la reflexión). –

Respuesta

3

Reescribí su prueba sólo acceder a la propiedad en el bucle interno y no utilizar una lista de las cosas y tiene los siguientes resultados:

Depuración:

  • Fin prueba de rendimiento delegado. iteraciones = 1,000,000 de tiempo de finalización: 0.278
  • Prueba de rendimiento de reflexión directa final. iteraciones = 1,000,000 de tiempo de finalización: 5.622
  • Final de referencia de salida directa. tiempo de iteraciones = 1.000.000 final: 0,045

lanzamiento:

  • Fin prueba de rendimiento delegado. iteraciones = 1,000,000 de tiempo de finalización: 0.194
  • Prueba de rendimiento de reflexión directa final. iteraciones = 1,000,000 hora de finalización: 5,523
  • Final de referencia de salida directa. iteraciones = 1,000,000 hora de finalización: 0,003

Por supuesto, si no tiene acceso a la propiedad millones de veces, ¿el impacto en el rendimiento realmente importa para su aplicación?

class ReflectClass1 
{ 
    public ReflectClass1(Random rand) 
    { 
     Prop1 = rand.Next(); 
     Prop2 = rand.Next(); 
     Prop3 = rand.Next(); 
     Prop4 = rand.Next(); 
     Prop5 = rand.Next(); 
     Prop6 = rand.Next(); 
     Prop7 = rand.Next(); 
     Prop8 = rand.Next(); 
     Prop9 = rand.Next(); 
     Prop10 = rand.Next(); 
    } 

    public int Prop1 {get;set;} 
    public int Prop2 { get; set; } 
    public int Prop3 { get; set; } 
    public int Prop4 { get; set; } 
    public int Prop5 { get; set; } 
    public int Prop6 { get; set; } 
    public int Prop7 { get; set; } 
    public int Prop8 { get; set; } 
    public int Prop9 { get; set; } 
    public int Prop10 { get; set; } 
} 

class Program 
{ 
    private static Random random = new Random((int)DateTime.Now.Ticks); 
    private List<Func<ReflectClass1, int>> delegateList = new List<Func<ReflectClass1, int>>(); 
    private static int Iterations = 1000000; 

    private void UseDelegateList() 
    { 
     //setup delegateList 
     foreach (var propertyInfo in typeof(ReflectClass1).GetProperties(BindingFlags.Public | BindingFlags.Instance)) 
     { 
      Func<ReflectClass1, int> getPropDelegate = (Func<ReflectClass1, int>)Delegate.CreateDelegate(typeof(Func<ReflectClass1, int>), null, propertyInfo.GetGetMethod()); 
      delegateList.Add(getPropDelegate); 
     } 

     Stopwatch sw = Stopwatch.StartNew(); 

     ReflectClass1 testClass = new ReflectClass1(random); 
     int total = 0; 
     for (int i = 0; i < Iterations; i++) 
     { 
      foreach (var getProp in delegateList) 
      { 
       total += getProp(testClass); 
      }   
     } 

     Console.WriteLine(string.Format(" End delegate performance test. iterations = {0:N0} end time: {1:0.000}", Iterations, sw.ElapsedMilliseconds/1000.0)); 
     Console.WriteLine(total); 
    } 

    private void UseDirectReflection() 
    { 
     Stopwatch sw = Stopwatch.StartNew(); 

     int total = 0; 
     ReflectClass1 testClass = new ReflectClass1(random); 
     for (int i = 0; i < Iterations; i++) 
     { 
      foreach (PropertyInfo propertyInfo in typeof(ReflectClass1).GetProperties()) 
      { 
       if (propertyInfo == null) 
        continue; 

       total += (int)propertyInfo.GetValue(testClass, null); 
      } 
     } 

     Console.WriteLine(string.Format(" End direct reflection performance test. iterations = {0:N0} end time: {1:0.000}", Iterations, sw.ElapsedMilliseconds/1000.0)); 
     Console.WriteLine(total); 
    } 

    private void DirectOutputTest() 
    { 
     Stopwatch sw = Stopwatch.StartNew(); 

     int total = 0; 
     ReflectClass1 testClass = new ReflectClass1(random); 
     for (int i = 0; i < Iterations; i++) 
     { 
      total += testClass.Prop1; 
      total += testClass.Prop2; 
      total += testClass.Prop3; 
      total += testClass.Prop4; 
      total += testClass.Prop5; 
      total += testClass.Prop6; 
      total += testClass.Prop7; 
      total += testClass.Prop8; 
      total += testClass.Prop9; 
      total += testClass.Prop10; 
     } 


     Console.WriteLine(string.Format(" End direct output benchmark. iterations = {0:N0} end time: {1:0.000}", Iterations, sw.ElapsedMilliseconds/1000.0)); 
     Console.WriteLine(total); 
    } 

    static void Main(string[] args) 
    { 
     var test = new Program(); 

     test.UseDelegateList(); 
     test.UseDirectReflection(); 
     test.DirectOutputTest(); 
    } 
} 
3

Es cierto. La reflexión suele ser lenta en términos relativistas, pero no suele ser lenta en el día a día, preocupándose por los términos. Aquí hay un excelente artículo sobre las partes lentas de la reflexión y las partes no tan lentas: Reflection Article

Creo que preocuparse por la reflexión cae en la categoría de optimización prematura. Si la mejor manera de expresar su diseño es usar la reflexión, úsela. De lo contrario, no. Además, tendría cuidado de ejecutar el código en un bucle que ejecuta solo ese código y tratando de obtener algunos números significativos. Terminará micro-optimizando algo que realmente no tuvo impacto en su aplicación.

4

2 cosas:

  • Rendimiento de la reflexión ha mejorado mucho en los nuevos tiempos de ejecución, ya que es una gran potencia de los lenguajes .NET y por tanto se ha prestado atención a la diferencia entre estática y dinámica actuación. Supongo que está ejecutando esto en Framework v3.5 o 4.0; Si tuviera que ejecutar este código en Framework v2.0, probablemente tendría un peor rendimiento.

  • La mayoría de lo que hace no es un uso muy "pesado" de la reflexión. La invocación de propiedad dinámica es un poco pesada, pero la mayoría de lo que hace es solo obtener información. Los verdaderos "pesados" son la invocación de métodos dinámicos y la creación de instancias dinámicas.

Supongamos que ha realizado la siguiente prueba. Muy simple, y lo único diferente es la instanciación estática y la invocación vs reflexivo:

public class ReflectionTest 
    { 
     public int Method1(){return 1;} 
     public int Method2() { return 2; } 
     public int Method3() { return 3; } 
     public int Method4() { return 4; } 
     public int Method5() { return 5; } 
     public int Method6() { return 6; } 
    } 

    [Test] 
    public void TestStatic() 
    { 
     for (var i = 1; i <= 100000; i++) 
     { 
      var reflectTest = new ReflectionTest(); 
      reflectTest.Method1(); 
      reflectTest.Method2(); 
      reflectTest.Method3(); 
      reflectTest.Method4(); 
      reflectTest.Method5(); 
      reflectTest.Method6(); 
     } 
    } 

    [Test] 
    public void TestReflection() 
    { 
     var fullName = typeof (ReflectionTest).FullName; 
     for (var i = 1; i <= 100000; i++) 
     { 
      var type = Assembly.GetExecutingAssembly().GetType(fullName, true, true); 
      var reflectTest = Activator.CreateInstance(type); 
      for (var j = 1; j <= 6; j++) 
       type.GetMethod("Method" + j.ToString()).Invoke(reflectTest, null); 
     } 
    } 

Si desea asegurarse de que la prueba fue completamente justo, que podría quitar el interior de bucle y llamar GetMethod 6 veces con cadenas literales "Método1", "Método2", etc.

La prueba de reflexión no solo invoca dinámicamente los métodos, sino que busca el manifiesto para encontrar y crear una instancia de un objeto Type, luego crea una instancia dinámica del objeto real del tipo, en el que los métodos son dinámicamente llamados. Apostaría que si ejecutabas ambas pruebas, la segunda funcionaría mucho peor. Además, explore el paso de parámetros a estos métodos; Primero tiene que encontrar la sobrecarga correcta, luego la invocación reflexiva toma una matriz Object [], que encapsula y desempaqueta cualquier parámetro de tipo de valor de los métodos, reduciendo aún más el algoritmo reflectivo.

En resumen, la reflexión tendrá un peor rendimiento que un algoritmo estático; SIN EMBARGO, se han logrado grandes avances para mejorar su rendimiento, por lo que a partir de .NET 4.0, un algoritmo dinámico escrito inteligentemente no es una gran pérdida en comparación con un algoritmo estático equivalente, lo que hace que la reflexión sea mucho más viable cuando sea necesario.

EDIT: Después de ejecutar las 2 pruebas anteriores una al lado de la otra, hay una gran diferencia relativa: algoritmo estático 0.07s para 100k iteraciones, reflejando la friolera de 2.12s. La creación/invocación reflectante tarda 30 veces más que la estática. Sin embargo, la diferencia tomó 100.000 iteraciones para ser significativa; las instrucciones Debug.WriteLine en mi versión original de esta prueba fueron, de lejos, la parte más lenta de cualquiera de las pruebas.

Cuestiones relacionadas