2012-09-06 10 views
8

Estoy tratando de entender cuál de las dos soluciones se prefiere desde una perspectiva de rendimiento. Por ejemplo, tengo dos piezas de código:.NET boxeo/unboxing vs rendimiento de conversión

1) Boxeo/Unboxing

int val = 5; 
Session["key"] = val; 
int val2 = (int)Session["key"]; 

2) Casting (IntObj tiene la propiedad int Valor de almacenar int)

IntObj val = new IntObj(5); 
Session["key"] = val; 
int val2 = ((IntObj)Session["key"]).Value; 

Cuál es el diferencia de gestión de memoria entre estos ejemplos? ¿Hay una manera más rápida de realizar tales operaciones?

NOTA:Session es sólo, por ejemplo, puede ser cualquier Dictionary<string, object>

+2

No lo he medido pero diría que los primitivos serían más rápidos. Un objeto tiene sobrecarga. Pero en este caso, creo que la legibilidad es una medida mucho más importante. Los ciclos de CPU son mucho más baratos que los ciclos cerebrales –

Respuesta

7

Parece que lo que realmente está haciendo aquí es comparar el boxeo manual con el boxeo incorporado. El boxeo incorporado ha sido altamente optimizado, así que no esperaría ver una gran diferencia aquí, pero podemos verificarlo. Es importante destacar que tenga en cuenta que ambos tienen el mismo impacto de memoria: un objeto de montón que contiene un campo int, por int en caja/envuelto.

Lo siguiente muestra tiempos bastante idénticos para los dos abordados; Diría, por lo tanto, simplemente boxéelo de forma directa/incorporada.

Nota: ejecútelo en modo de lanzamiento, sin un depurador (idealmente en la línea de comandos). Tenga en cuenta que la primera llamada está ahí para pre-JIT todo.

using System; 
using System.Diagnostics; 
public sealed class IntObj 
{ 
    public readonly int Value; 
    public IntObj(int value) 
    { 
     Value = value; 
    } 
} 
static class Program 
{ 
    static void Main() 
    { 
     Run(1, 0, false); 
     Run(100000, 500, true); 
     Console.ReadKey(); 
    } 
    static void Run(int length, int repeat, bool report) 
    { 
     var data = new object[length]; 

     int chk = 0; 
     var watch = Stopwatch.StartNew(); 
     for (int j = 0; j < repeat; j++) 
     { 
      for (int i = 0; i < data.Length; i++) 
      { 
       data[i] = i; 
       chk += i; 
      } 
     } 
     watch.Stop(); 
     if(report) Console.WriteLine("Box: {0}ms (chk: {1})", watch.ElapsedMilliseconds, chk); 
     chk = 0; 
     watch = Stopwatch.StartNew(); 
     for (int j = 0; j < repeat; j++) 
     { 
      for (int i = 0; i < data.Length; i++) 
      { 
       chk += (int) data[i]; 
      } 
     } 
     watch.Stop(); 
     if (report) Console.WriteLine("Unbox: {0}ms (chk: {1})", watch.ElapsedMilliseconds, chk); 

     chk = 0; 
     watch = Stopwatch.StartNew(); 
     for (int j = 0; j < repeat; j++) 
     { 
      for (int i = 0; i < data.Length; i++) 
      { 
       data[i] = new IntObj(i); 
       chk += i; 
      } 
     } 
     watch.Stop(); 
     if (report) Console.WriteLine("Wrap: {0}ms (chk: {1})", watch.ElapsedMilliseconds, chk); 
     chk = 0; 
     watch = Stopwatch.StartNew(); 
     for (int j = 0; j < repeat; j++) 
     { 
      for (int i = 0; i < data.Length; i++) 
      { 
       chk += ((IntObj)data[i]).Value; 
      } 
     } 
     watch.Stop(); 
     if (report) Console.WriteLine("Unwrap: {0}ms (chk: {1})", watch.ElapsedMilliseconds, chk); 
    } 


} 
+0

¡Gracias! Ahora es fácil ver eso :) –

4

Entonces, ¿qué es más rápido, el boxeo de bricolaje con un IntObj o la incorporada en el boxeo?

Supongo que sería la variedad incorporada. Lo más probable es que ambos compiladores estén optimizados para manejarlo.

¿Existe una forma más "rápida" de realizar tales operaciones?

El enfoque preferido es siempre evitarlo para grandes conjuntos de datos. Para juegos pequeños, simplemente no importa.

+0

Di una sesión, por ejemplo, puede ser un simple diccionario / –

+0

Supuse que la pregunta era sobre IntObj. El contenedor (Session) no es muy relevante. –

0

I clasifican diferentes tipos de instrucciones IL generados por C operador # reparto:

Boxing (caja IL instrucción) y unboxing (unbox IL instrucción) de reparto a través de la jerarquía inhertiance (como dynamic_cast en C++, utiliza castclass instrucción IL para verificar) Casting entre tipos primitivos (como static_cast en C++, hay muchas instrucciones de IL para diferentes tipos de moldes entre tipos primitivos) Llamar operadores de conversión definidos por el usuario (en el nivel IL solo son llamadas al método op_XXX apropiado))

La diferencia es que la fundición asigna memoria adicional a medida que se crea un nuevo tipo de referencia.

+3

Entonces, ¿cómo responde eso la pregunta? (El boxeo también crea una nueva instancia en el montón) –

2

La forma más rápida de hacer algo no es hacerlo en absoluto. Intente reestructurar sus datos para evitar una gran cantidad de boxeo para obtener más seguridad de tipo, legibilidad y posible rendimiento al mismo tiempo.

No es probable que necesite almacenar una gran cantidad de elementos enteros no relacionados (u otro tipo de valor) en el diccionario sin tipo. Por lo general, los valores se organizan en algunos objetos que interactúan, en este caso se almacena un objeto de nivel superior en el diccionario sin tipo y solo se necesita un molde. Para elementos más profundos, utilizaría clases muy tipadas (como Dictionary<string,int>) donde ya se resuelve este problema, ya que no se necesita boxeo.

Si usted siente que en su caso lo que realmente necesita para almacenar gran cantidad de int (u otros elementos de tipo de valor) en la cadena => opbject mapa Comentarios No debe ser muy fácil de realizar mediciones por sí mismo utilizando su conjunto de datos del ADN de sus objetivos para ver si alguna de las versiones tiene beneficios significativos. Si ambos satisfacen sus objetivos (es probable), elija uno que produzca el código más legible (es decir, para mí sería la primera variante).