Esto es básicamente una aplicación de registro / conteo que cuenta el número de paquetes y el tipo de paquete, etc. en una red de chat p2p. Esto equivale a unos 4-6 millones de paquetes en un período de 5 minutos. Y debido a que solo tomo una "instantánea" de esta información, solo estoy eliminando paquetes de más de 5 minutos cada cinco minutos. Entonces, la cantidad máxima de artículos que estarán en esta colección es de 10 a 12 millones.
Debido a que necesito hacer 300 conexiones a diferentes superpeers, es una posibilidad que cada paquete intente insertarse al menos 300 veces (lo que probablemente sea la razón por la cual mantener estos datos en la memoria es la única opción razonable).
Actualmente, he estado usando un diccionario para almacenar esta información. Pero debido a la gran cantidad de elementos que estoy tratando de almacenar, me encuentro con problemas con el montón de objetos grandes y la cantidad de uso de memoria crece continuamente con el tiempo.
Dictionary<ulong, Packet>
public class Packet
{
public ushort RequesterPort;
public bool IsSearch;
public string SearchText;
public bool Flagged;
public byte PacketType;
public DateTime TimeStamp;
}
Intenté usar mysql, pero no pude mantener la cantidad de datos que necesito insertar (mientras lo comprobaba para asegurarme de que no era un duplicado), y eso fue mientras usaba las transacciones.
Intenté mongodb, pero el uso de la CPU para eso fue una locura y tampoco se mantuvo.
Mi problema principal surge cada 5 minutos, porque elimino todos los paquetes que tienen más de 5 minutos y tomo una "instantánea" de estos datos. Como estoy usando consultas LINQ para contar el número de paquetes que contienen un determinado tipo de paquete. También estoy llamando a una consulta distinta () en los datos, donde elimino 4 bytes (dirección IP) de la clave del keyvaluepair, y lo combino con el valor del puerto solicitante en el Valor del keyvalupair y lo uso para obtener un número distinto de pares de todos los paquetes.
Actualmente, la aplicación oscila alrededor de 1,1 GB de uso de memoria, y cuando se llama una instantánea, puede llegar a duplicar el uso.
Ahora, esto no sería un problema si tengo una cantidad increíble de ram, pero la vm en la que estoy ejecutando está limitada a 2GB de ram en este momento.
¿Hay alguna solución fácil?
fuente
Respuestas:
En lugar de tener un diccionario y buscar entradas que sean demasiado antiguas en ese diccionario; tener 10 diccionarios. Cada 30 segundos, más o menos, cree un nuevo diccionario "actual" y deseche el diccionario más antiguo sin realizar ninguna búsqueda.
A continuación, cuando descarte el diccionario más antiguo, coloque todos los objetos antiguos en una cola FILO para más adelante y, en lugar de usar "nuevo" para crear nuevos objetos, retire un objeto antiguo de la cola FILO y use un método para reconstruir el antiguo objeto (a menos que la cola de objetos antiguos esté vacía). Esto puede evitar muchas asignaciones y una sobrecarga de recolección de basura.
fuente
El primer pensamiento que me viene a la mente es por qué esperas 5 minutos. ¿Podría hacer las instantáneas más a menudo y así reducir la gran sobrecarga que ve en el límite de 5 minutos?
En segundo lugar, LINQ es ideal para un código conciso, pero en realidad LINQ es azúcar sintáctica en C # "regular" y no hay garantía de que genere el código más óptimo. Como ejercicio, podría intentar reescribir los puntos calientes sin LINQ, es posible que no mejore el rendimiento, pero tendrá una idea más clara de lo que está haciendo y facilitaría el trabajo de creación de perfiles.
Otra cosa a tener en cuenta son las estructuras de datos. No sé qué haces con tus datos, pero ¿podrías simplificar los datos que almacenas de alguna manera? ¿Podría usar una serie de cadenas o bytes y luego extraer partes relevantes de esos elementos según los necesite? ¿Podría usar una estructura en lugar de una clase e incluso hacer algo malo con stackalloc para reservar memoria y evitar ejecuciones de GC?
fuente
Enfoque simple: intente memcached .
La desventaja es que está basado en la memoria y no tiene ninguna persistencia. Si una instancia está inactiva, los datos se han ido. Si necesita persistencia, serialice los datos usted mismo.
Enfoque más complejo: pruebe Redis .
La desventaja es que es un poco más complejo.
fuente
No tiene que almacenar todos los paquetes para las consultas que ha mencionado. Por ejemplo, contador de tipo de paquete:
Necesitas dos matrices:
La primera matriz realiza un seguimiento de cuántos paquetes en diferentes tipos. La segunda matriz realiza un seguimiento de cuántos paquetes más se agregaron en cada minuto, de modo que sepa cuántos paquetes deben eliminarse en cada intervalo de minutos. Espero que sepas que la segunda matriz se usa como una cola FIFO redonda.
Entonces, para cada paquete, se realizan las siguientes operaciones:
En cualquier momento, el contador puede recuperar los contadores de paquetes instantáneamente y no podemos almacenar todos los paquetes.
fuente
(Sé que esta es una pregunta antigua, pero la encontré mientras buscaba una solución a un problema similar en el que el pase de recolección de basura de segunda generación estaba pausando la aplicación durante varios segundos, por lo que grabé para otras personas en una situación similar).
Use una estructura en lugar de una clase para sus datos (pero recuerde que se trata como un valor con semántica de paso por copia). Esto elimina un nivel de búsqueda, el gc tiene que hacer cada pase de marca.
Utilice matrices (si conoce el tamaño de los datos que está almacenando) o Lista, que utiliza matrices internamente. Si realmente necesita el acceso aleatorio rápido, use un diccionario de índices de matriz. Esto elimina otro par de niveles (o una docena o más si está usando un SortedDictionary) para que el gc tenga que buscar.
Dependiendo de lo que esté haciendo, buscar una lista de estructuras puede ser más rápido que la búsqueda del diccionario (debido a la localización de la memoria) - perfil para su aplicación particular.
La combinación de struct & list reduce significativamente el uso de memoria y el tamaño del barrido del recolector de basura.
fuente