Este é basicamente um aplicativo de registro / contagem que conta o número de pacotes e o tipo de pacote etc. em uma rede de bate-papo p2p. Isso equivale a cerca de 4-6 milhões de pacotes em um período de 5 minutos. E como eu tiro apenas um "instantâneo" dessas informações, apenas removo pacotes com mais de 5 minutos a cada cinco minutos. Portanto, o máximo de itens que estarão nesta coleção é de 10 a 12 milhões.
Como preciso fazer 300 conexões com diferentes superpeers, é possível que cada pacote esteja tentando ser inserido pelo menos 300 vezes (e é provavelmente por isso que manter esses dados na memória é a única opção razoável).
Atualmente, estou usando um dicionário para armazenar essas informações. Mas, devido à grande quantidade de itens que estou tentando armazenar, encontro problemas com a pilha de objetos grandes e a quantidade de uso de memória aumenta continuamente ao longo do tempo.
Dictionary<ulong, Packet>
public class Packet
{
public ushort RequesterPort;
public bool IsSearch;
public string SearchText;
public bool Flagged;
public byte PacketType;
public DateTime TimeStamp;
}
Eu tentei usar o mysql, mas ele não foi capaz de acompanhar a quantidade de dados que eu preciso inserir (enquanto verificava para garantir que não era uma duplicata), e isso enquanto usava transações.
Eu tentei o mongodb, mas o uso da CPU era insano e não o mantinha.
Meu problema principal surge a cada 5 minutos, porque removo todos os pacotes com mais de 5 minutos e tiro um instantâneo desses dados. Como eu estou usando consultas LINQ para contar o número de pacotes que contêm um determinado tipo de pacote. Também estou chamando uma consulta distinta () nos dados, em que retiro 4 bytes (endereço IP) da chave do keyvaluepair e a combino com o valor requestingport no valor do keyvalupair e o uso para obter um número distinto de pares de todos os pacotes.
Atualmente, o aplicativo mantém em torno de 1,1 GB de uso de memória e, quando um instantâneo é chamado, pode chegar ao ponto de duplicar o uso.
Agora, isso não seria um problema se eu tiver uma quantidade insana de RAM, mas a vm em que estou executando esta limitada a 2 GB de RAM no momento.
Existe alguma solução fácil?
fonte
Respostas:
Em vez de ter um dicionário e pesquisar entradas muito antigas nesse dicionário; tem 10 dicionários. A cada 30 segundos, crie um novo dicionário "atual" e descarte o dicionário mais antigo sem fazer nenhuma pesquisa.
Em seguida, ao descartar o dicionário mais antigo, coloque todos os objetos antigos em uma fila FILO para mais tarde e, em vez de usar "novo" para criar novos objetos, retire um objeto antigo da fila FILO e use um método para reconstruir o antigo objeto (a menos que a fila de objetos antigos esteja vazia). Isso pode evitar muitas alocações e muita sobrecarga de coleta de lixo.
fonte
O primeiro pensamento que vem à mente é por que você espera 5 minutos. Você poderia tirar fotos com mais frequência e, assim, reduzir a grande sobrecarga que você vê no limite de 5 minutos?
Em segundo lugar, o LINQ é ótimo para código conciso, mas, na realidade, o LINQ é o açúcar sintático no C # "regular" e não há garantia de que ele gerará o código mais ideal. Como exercício, você pode tentar reescrever os pontos de acesso sem o LINQ, talvez não melhore o desempenho, mas terá uma idéia mais clara do que está fazendo e isso facilitaria o trabalho de criação de perfil.
Outra coisa a olhar é estruturas de dados. Não sei o que você faz com seus dados, mas você poderia simplificar os dados armazenados de alguma forma? Você poderia usar uma matriz de cadeia de caracteres ou bytes e extrair partes relevantes desses itens conforme necessário? Você poderia usar uma struct em vez de uma classe e até fazer algo errado com o stackalloc para reservar a memória e evitar execuções de GC?
fonte
Abordagem simples: tente memcached .
A desvantagem é que é baseado em memória e não tem nenhuma persistência. Se uma instância estiver inativa, os dados desaparecerão. Se você precisar de persistência, serialize os dados você mesmo.
Abordagem mais complexa: tente Redis .
A desvantagem é que é um pouco mais complexo.
fonte
Você não precisa armazenar todos os pacotes para as consultas que você mencionou. Por exemplo - contador de tipo de pacote:
Você precisa de duas matrizes:
A primeira matriz controla quantos pacotes em tipos diferentes. A segunda matriz controla quantos pacotes foram adicionados a cada minuto, de modo que você saiba quantos pacotes precisam ser removidos a cada intervalo de minutos. Espero que você possa dizer que o segundo array é usado como uma fila FIFO redonda.
Portanto, para cada pacote, são executadas as seguintes operações:
A qualquer momento, os contadores de pacotes podem ser recuperados pelo índice instantaneamente e não armazenamos todos os pacotes.
fonte
(Eu sei que essa é uma pergunta antiga, mas eu a encontrei enquanto procurava uma solução para um problema semelhante em que a segunda passagem de coleta de lixo gen estava pausando o aplicativo por vários segundos, gravando para outras pessoas em situação semelhante).
Use uma estrutura em vez de uma classe para seus dados (mas lembre-se de que eles são tratados como um valor com semântica de passagem por cópia). Isso remove um nível de pesquisa que o gc precisa fazer a cada passagem de marca.
Use matrizes (se você souber o tamanho dos dados que está armazenando) ou Lista - que usa matrizes internamente. Se você realmente precisa do acesso aleatório rápido, use um dicionário de índices de matriz. Isso remove outros níveis (ou uma dúzia ou mais, se você estiver usando um SortedDictionary) para que o gc precise pesquisar.
Dependendo do que você está fazendo, a pesquisa em uma lista de estruturas pode ser mais rápida que a pesquisa no dicionário (devido à localização da memória) - perfil para seu aplicativo específico.
A combinação de estrutura e lista reduz significativamente o uso da memória e o tamanho da varredura do coletor de lixo.
fonte