2011-01-27 25 views
10

Tengo una aplicación de consola que permite a los usuarios especificar variables para procesar. Estas variables vienen en tres sabores: cadena, doble y larga (siendo el doble y el largo los tipos más comúnmente utilizados). El usuario puede especificar cualquier variable que le guste y en cualquier orden para que mi sistema pueda manejar eso. Para este fin, en mi aplicación, los estaba almacenando como objetos y luego los fundí o los deshizo según fue necesario. por ejemplo:En C# /. ¿Hace que un tipo dinámico ocupe menos espacio que el objeto?

public class UnitResponse 
{ 
    public object Value { get; set; } 
} 

Mi entendimiento es que los objetos en cajas ocupan un poco más de memoria (unos 12 bytes) de un tipo de valor estándar.

Mi pregunta es: ¿sería más eficiente usar la palabra clave dinámica para almacenar estos valores? Podría sortear el problema del boxeo/unboxing, y si es más eficiente, ¿cómo afectaría esto el rendimiento?

EDITAR

Para proporcionar un cierto contexto y evitar que el "¿está seguro de que está utilizando suficiente memoria RAM para preocuparse por esto" en mi peor de los casos tengo 420.000.000 puntos de datos que preocuparse (60 variables * 7.000.000 de registros). Esto se suma a muchos otros datos que guardo sobre cada variable (incluidos algunos booleanos, etc.). Entonces, reducir la memoria tiene un GRAN impacto.

+5

¿Ha realizado algún perfil? ¿Es esto un boxeo/unboxing realmente un cuello de botella? ¿El uso de RAM de su programa se está disparando? ¿O estás micro-optimizando? – cdhowie

+1

Si hubiera una manera tan simple de evitar el boxeo como parece pensar, ¿por qué crees que el boxeo existe en primer lugar? – Timwi

+0

@cdhowie: Mi programa consume cantidades copiosas de RAM, en algunos casos de 20-30 GB. La reducción del uso de la memoria es imprescindible. –

Respuesta

19

OK, entonces la pregunta real aquí es "Tengo un gran conjunto de datos que estoy almacenando en la memoria, ¿cómo puedo optimizar su rendimiento en tiempo y espacio de memoria?"

Varios pensamientos:

  • tiene toda la razón para odiar y temer el boxeo. El boxeo tiene un gran costo. Primero, sí, los objetos en caja ocupan más memoria. En segundo lugar, los objetos enmarcados se almacenan en el montón, no en la pila o en los registros. En tercer lugar, son basura recolectada; cada uno de esos objetos tiene que ser interrogado en el momento del GC para ver si contiene una referencia a otro objeto, lo que nunca ocurrirá, y eso es mucho tiempo en el hilo del GC.Es casi seguro que necesitas hacer algo para evitar el boxeo.

Dynamic is not it; es boxeo más una gran cantidad de otros gastos generales. (La dinámica de C# es muy rápida en comparación con otros sistemas de envío dinámico, pero no es rápida ni pequeña en términos absolutos).

Es bruto, pero se puede considerar el uso de una estructura cuyo diseño comparte la memoria entre los diversos campos - como una unión en C. Si lo hace, es muy, muy bruto y no es en absoluto seguro pero puede ayudar en situaciones como estas. Haga una búsqueda web de "StructLayoutAttribute"; encontrarás tutoriales.

  • ¿Long, double or string, really? No puede ser int, float o string? ¿Los datos realmente exceden varios miles de millones de magnitud o tienen una precisión de 15 decimales? ¿No int y flotan hacer el trabajo en el 99% de los casos? Son la mitad del tamaño.

Normalmente no recomiendo usar flotador sobre doble porque es una economía falsa; las personas a menudo economizan de esta manera cuando tienen UN número, como el ahorro de cuatro bytes que marcará la diferencia. La diferencia entre 42 millones de flotadores y 42 millones de dobles es considerable.

  • ¿Hay regularidad en los datos que puede explotar? Por ejemplo, suponga que de sus 42 millones de registros, solo hay 100000 valores reales para, por ejemplo, cada longitud, 100000 valores para cada doble y 100000 valores para cada cadena. En ese caso, crea un almacenamiento indexado de algún tipo para los largos, dobles y cadenas, y luego cada registro obtiene un entero donde los bits bajos son el índice, y los dos bits superiores indican de qué almacenamiento extraerlo. Ahora tiene 42 millones de registros, cada uno con una int, y los valores se almacenan de alguna forma muy compacta en otro lugar.

  • Almacena los booleanos como bits en un byte; escribe propiedades para hacer el cambio de bit para sacarlos. Ahórrese varios bytes de esa manera.

  • Recuerde que la memoria es realmente espacio en disco; La RAM es solo un caché conveniente encima. Si el conjunto de datos va a ser demasiado grande para guardarlo en la memoria RAM, entonces algo va a devolverlo al disco y leerlo más tarde; ese podría ser usted o podría ser el sistema operativo. Es posible que sepa más sobre su localidad de datos que el sistema operativo. Podrías escribir tus datos en el disco de alguna forma convenientemente paginable (como un b-tree) y ser más eficientes para guardar cosas en el disco y solo llevarlas a la memoria cuando las necesites.

+0

¡GUAU! Gracias Eric, hay algunas ideas geniales aquí. Es verdad, puedo salirse con int y flotar en vez de hacerlo a la larga y al doble, tendría que verificar nuestros datos para asegurar que esto sea una posibilidad (tenemos al menos un identificador que requiere un largo pero puede no ser utilizado en los datos). Dado mi espacio limitado, responderé a sus preguntas en diferentes comentarios: –

+0

1. Tenemos cierta regularidad en los datos que podemos explotar, el valor más común con el que trabajamos es un valor codificado donde a menudo hay menos de 100 valores distintos. Podríamos hacer un montón de espacio aquí –

+0

2. Almacenar los booleanos en un byte es una idea genial, que debería ahorrar algo de espacio y ser fácil de acomodar. –

2

No. Dynamic simplemente lo almacenará como un objeto.

Es probable que se trate de una micro optimización que proporcionará poco o ningún beneficio. Si esto realmente se convierte en un problema, existen otros mecanismos que puede usar (genéricos) para acelerar las cosas.

3

dynamic tiene que ver con cómo se realizan operaciones en el objeto, no cómo se almacena el objeto en sí. En este contexto particular, los tipos de valores todavía se guardarán en cajas.

Además, ¿vale realmente todo este esfuerzo 12 bytes por objeto? Seguramente hay un mejor uso para su tiempo que guardar unos pocos kilobytes (si eso) de RAM? ¿Has probado que el uso de RAM por tu programa es realmente un problema?

+0

12 bytes por objeto parece pequeño, pero cuando tiene 420,000,000 de ellos (como lo hago en algunos escenarios), la diferencia se vuelve significativa. Agregue que para cada punto de datos (12 bytes por objeto) necesito mantener varios valores booleanos y algunas referencias, y tiene mucha memoria. En algunas pruebas ya hemos abordado 8 GB de RAM utilizada. –

+0

Ok, solo comprobando. Si este es el caso, entonces debería considerar el uso de genéricos o estructuras de datos fuertemente tipadas en su lugar. – cdhowie

14

Creo que podría estar buscando algo incorrecto aquí. Recuerde lo que hace la dinámica. Es inicia el compilador de nuevo, en proceso, en el tiempo de ejecución. Carga cientos de miles de bytes de código para el compilador, y luego en cada sitio de llamada emite cachés que contienen los resultados del IL recién emitido para cada operación dinámica. Estás gastando unos cientos de miles de bytes para salvar a ocho. Eso parece una mala idea.

Y, por supuesto, no guarda nada. "dinámico" es solo "objeto" con un elegante sombrero. Los objetos "dinámicos" todavía están en caja.

+0

No sabía que los objetos dinámicos estaban enmarcados debajo. Después de leer esto hice algunas pruebas rápidas con un proyecto de muestra y vi que estaban en una caja debajo. ¿Hay algún lugar que indique esto en la documentación? ¡Gracias! –

+2

@Jeffrey: remito a la sección 4.7 de la especificación C# 4, que dice "El tipo dinámico no se puede distinguir del objeto en tiempo de ejecución". –

Cuestiones relacionadas