Это в основном приложение для регистрации / подсчета, которое подсчитывает количество пакетов, подсчитывает тип пакета и т. Д. В сети чата p2p. Это соответствует примерно 4-6 миллионам пакетов за 5 минут. И поскольку я делаю только «снимок» этой информации, я удаляю только пакеты старше 5 минут каждые пять минут. Таким образом, максимальное количество предметов, которые будут в этой коллекции, составляет от 10 до 12 миллионов.
Поскольку мне нужно сделать 300 подключений к разным супер-суперпользователям, возможно, что каждый пакет пытается быть вставлен как минимум 300 раз (возможно, поэтому хранение этих данных в памяти является единственно разумным вариантом).
В настоящее время я использую словарь для хранения этой информации. Но из-за большого количества элементов, которые я пытаюсь сохранить, у меня возникают проблемы с кучей больших объектов, и объем использования памяти постоянно растет со временем.
Dictionary<ulong, Packet>
public class Packet
{
public ushort RequesterPort;
public bool IsSearch;
public string SearchText;
public bool Flagged;
public byte PacketType;
public DateTime TimeStamp;
}
Я пытался использовать mysql, но он не смог справиться с объемом данных, которые мне нужно вставить (при проверке, чтобы убедиться, что это не дубликат), и это было при использовании транзакций.
Я попробовал mongodb, но использование процессора для этого было безумным и не сохранилось.
Моя основная проблема возникает каждые 5 минут, потому что я удаляю все пакеты старше 5 минут и делаю «снимок» этих данных. Поскольку я использую запросы LINQ для подсчета количества пакетов, содержащих определенный тип пакета. Я также вызываю отдельный запрос () для данных, где я удаляю 4 байта (ip-адрес) из ключа keyvaluepair и объединяю его со значением requesttingport в значении keyvalupair и использую его для получения различного числа peers из всех пакетов.
В настоящее время приложение использует около 1,1 ГБ памяти, и при вызове моментального снимка оно может удвоиться.
Теперь это не будет проблемой, если у меня будет безумное количество оперативной памяти, но виртуальный компьютер, на котором я работаю, ограничен 2 ГБ оперативной памяти.
Есть ли простое решение?
источник
Ответы:
Вместо того, чтобы иметь один словарь и искать в этом словаре слишком старые записи; есть 10 словарей. Каждые 30 секунд или около того создайте новый «текущий» словарь и отбросьте самый старый словарь без поиска вообще.
Затем, когда вы отбрасываете самый старый словарь, поместите все старые объекты в очередь FILO на потом и вместо использования «нового» для создания новых объектов вытащите старый объект из очереди FILO и используйте метод для восстановления старого объект (если очередь старых объектов не пуста). Это может избежать большого количества выделений и больших затрат на сборку мусора.
источник
Первая мысль, которая приходит на ум, это то, почему вы ждете 5 минут. Не могли бы вы сделать снимки чаще и таким образом уменьшить большую перегрузку, которую вы видите на 5-минутной границе?
Во-вторых, LINQ отлично подходит для краткого кода, но на самом деле LINQ является синтаксическим сахаром на «обычном» C #, и нет гарантии, что он сгенерирует наиболее оптимальный код. В качестве упражнения вы можете попытаться переписать «горячие точки» без LINQ, вы не сможете улучшить производительность, но у вас будет более четкое представление о том, что вы делаете, и это облегчит работу по профилированию.
Другая вещь, на которую стоит обратить внимание - это структуры данных. Я не знаю, что вы делаете со своими данными, но не могли бы вы упростить данные, которые вы храните? Не могли бы вы использовать строку или байтовый массив, а затем извлекать соответствующие части из этих элементов по мере необходимости? Не могли бы вы использовать структуру вместо класса и даже сделать что-то плохое с помощью stackalloc, чтобы выделить память и избежать запуска GC?
источник
Простой подход: попробуйте memcached .
Недостатком является то, что он основан на памяти и не имеет постоянства. Если экземпляр не работает, данные исчезли. Если вам нужна настойчивость, сериализуйте данные самостоятельно.
Более сложный подход: попробуйте Redis .
Недостатком является то, что это немного сложнее.
источник
Вам не нужно хранить все пакеты для запросов, которые вы упомянули. Например - счетчик типа упаковки:
Вам нужно два массива:
Первый массив отслеживает, сколько пакетов разных типов. Второй массив отслеживает, сколько дополнительных пакетов было добавлено в каждую минуту, так что вы знаете, сколько пакетов необходимо удалить за каждый минутный интервал. Я надеюсь, что вы можете сказать, что второй массив используется в качестве круглой очереди FIFO.
Таким образом, для каждого пакета выполняются следующие операции:
В любое время счетчики пакетов могут быть мгновенно получены по индексу, и мы не храним все пакеты.
источник
(Я знаю, что это старый вопрос, но я натолкнулся на него, когда искал решение для аналогичной проблемы, когда этап сбора мусора второго поколения приостанавливал работу приложения на несколько секунд, поэтому запись выполнялась для других людей в аналогичной ситуации).
Используйте структуру, а не класс для ваших данных (но помните, что они обрабатываются как значение с семантикой передачи по копии). Это устраняет один уровень поиска, который должен выполнять gc при каждом проходе отметки.
Используйте массивы (если вы знаете размер данных, которые вы храните) или List - который использует массивы внутри. Если вам действительно нужен быстрый произвольный доступ, используйте словарь индексов массива. Это снимает еще пару уровней (или дюжину и более, если вы используете SortedDictionary) для поиска в gc.
В зависимости от того, что вы делаете, поиск списка структур может выполняться быстрее, чем поиск по словарю (из-за локализации памяти) - профиля для вашего конкретного приложения.
Комбинация struct & list значительно уменьшает как использование памяти, так и размер уборщика мусора.
источник