Dies ist im Grunde eine Protokollierungs- / Zählanwendung, die die Anzahl der Pakete und den Pakettyp usw. in einem P2P-Chat-Netzwerk zählt. Dies entspricht ungefähr 4-6 Millionen Paketen in einem Zeitraum von 5 Minuten. Und weil ich nur einen "Schnappschuss" dieser Informationen mache, entferne ich nur alle fünf Minuten Pakete, die älter als 5 Minuten sind. Die maximale Anzahl der Artikel in dieser Sammlung liegt bei 10 bis 12 Millionen.
Da ich 300 Verbindungen zu verschiedenen SuperPeers herstellen muss, ist es möglich, dass jedes Paket mindestens 300 Mal versucht, eingefügt zu werden.
Derzeit verwende ich ein Wörterbuch zum Speichern dieser Informationen. Aufgrund der großen Anzahl von Elementen, die ich speichern möchte, treten jedoch Probleme mit dem großen Objekthaufen auf, und die Speichernutzung nimmt im Laufe der Zeit kontinuierlich zu.
Dictionary<ulong, Packet>
public class Packet
{
public ushort RequesterPort;
public bool IsSearch;
public string SearchText;
public bool Flagged;
public byte PacketType;
public DateTime TimeStamp;
}
Ich habe versucht, mysql zu verwenden, aber es war nicht in der Lage, mit der Datenmenge Schritt zu halten, die ich einfügen musste (während ich überprüfte, ob es sich um ein Duplikat handelte), und das während der Verwendung von Transaktionen.
Ich habe mongodb ausprobiert, aber die CPU-Auslastung dafür war verrückt und hat auch nicht gehalten.
Mein Hauptproblem tritt alle 5 Minuten auf, weil ich alle Pakete entferne, die älter als 5 Minuten sind, und einen "Schnappschuss" dieser Daten mache. Da ich LINQ-Abfragen verwende, um die Anzahl der Pakete zu zählen, die einen bestimmten Pakettyp enthalten. Ich rufe auch eine distinct () - Abfrage für die Daten auf, bei der ich 4 Bytes (IP-Adresse) aus dem Schlüssel des Schlüsselpaars entferne und diese mit dem Wert für requestingport im Wert des Schlüsselpaars kombiniere, um eine eindeutige Anzahl von zu erhalten Peers von allen Paketen.
Die Anwendung verwendet derzeit etwa 1,1 GB Arbeitsspeicher, und wenn ein Snapshot aufgerufen wird, kann er die Auslastung verdoppeln.
Nun, das wäre kein Problem, wenn ich eine verrückte Menge an RAM habe, aber die VM, auf der ich das lauffähig habe, ist im Moment auf 2 GB RAM begrenzt.
Gibt es eine einfache Lösung?
quelle
Antworten:
Anstatt ein Wörterbuch zu haben und dieses nach zu alten Einträgen zu durchsuchen; habe 10 Wörterbücher. Erstellen Sie etwa alle 30 Sekunden ein neues "aktuelles" Wörterbuch und verwerfen Sie das älteste Wörterbuch, ohne es zu durchsuchen.
Wenn Sie als Nächstes das älteste Wörterbuch verwerfen, legen Sie alle alten Objekte für später in eine FILO-Warteschlange und ziehen Sie ein altes Objekt aus der FILO-Warteschlange, anstatt mit "new" neue Objekte zu erstellen. Verwenden Sie eine Methode, um das alte zu rekonstruieren Objekt (es sei denn, die Warteschlange alter Objekte ist leer). Dies kann viele Zuordnungen und einen hohen Speicherbereinigungsaufwand vermeiden.
quelle
Der erste Gedanke, der mir einfällt, ist, warum Sie 5 Minuten warten. Könnten Sie die Schnappschüsse häufiger machen und so die große Überlastung reduzieren, die Sie an der 5-Minuten-Grenze sehen?
Zweitens eignet sich LINQ hervorragend für prägnanten Code, aber in Wirklichkeit ist LINQ syntaktischer Zucker in "normalem" C #, und es gibt keine Garantie dafür, dass er den optimalsten Code generiert. Als Übung könnten Sie versuchen, die Hotspots mit LINQ neu zu schreiben. Möglicherweise verbessern Sie die Leistung nicht, aber Sie haben eine klarere Vorstellung davon, was Sie tun, und dies würde die Profilerstellung erleichtern.
Eine andere Sache zu betrachten ist Datenstrukturen. Ich weiß nicht, was Sie mit Ihren Daten machen, aber könnten Sie die von Ihnen gespeicherten Daten auf irgendeine Weise vereinfachen? Könnten Sie eine Zeichenfolge oder ein Byte-Array verwenden und dann relevante Teile aus diesen Elementen nach Bedarf extrahieren? Könnten Sie eine Struktur anstelle einer Klasse verwenden und mit stackalloc sogar etwas Böses anstellen, um Speicher zu reservieren und GC-Läufe zu vermeiden?
quelle
Einfacher Ansatz: Versuchen Sie es im Memcached-Modus .
Der Nachteil ist, dass es speicherbasiert ist und keine Persistenz hat. Wenn eine Instanz inaktiv ist, sind die Daten nicht mehr vorhanden. Wenn Sie Persistenz benötigen, serialisieren Sie die Daten selbst.
Komplexerer Ansatz: Versuchen Sie es mit Redis .
Der Nachteil ist, dass es etwas komplexer ist.
quelle
Sie müssen nicht alle Pakete für die von Ihnen erwähnten Abfragen speichern. Zum Beispiel - Pakettypzähler:
Sie benötigen zwei Arrays:
Das erste Array protokolliert, wie viele Pakete in verschiedenen Typen vorliegen. Das zweite Array protokolliert, wie viele Pakete in jeder Minute hinzugefügt wurden, sodass Sie wissen, wie viele Pakete in jedem Minutenintervall entfernt werden müssen. Ich hoffe, Sie können feststellen, dass das zweite Array als runde FIFO-Warteschlange verwendet wird.
Daher werden für jedes Paket die folgenden Vorgänge ausgeführt:
Die Paketzähler können jederzeit sofort vom Index abgerufen werden, und wir müssen nicht alle Pakete speichern.
quelle
(Ich weiß, dass dies eine alte Frage ist, aber ich bin ihr begegnet, als ich nach einer Lösung für ein ähnliches Problem gesucht habe, bei dem die App beim Durchlauf der zweiten Garbage Collection für einige Sekunden angehalten wurde, um für andere Personen in einer ähnlichen Situation aufzuzeichnen.)
Verwenden Sie eine Struktur anstelle einer Klasse für Ihre Daten (denken Sie jedoch daran, dass diese als Wert mit einer Semantik für das Weitergeben von Kopien behandelt werden). Dies nimmt eine Ebene der Suche in Anspruch, die der GC für jeden Markierungsdurchgang durchführen muss.
Verwenden Sie Arrays (wenn Sie die Größe der gespeicherten Daten kennen) oder List (wenn Sie intern Arrays verwenden). Wenn Sie wirklich den schnellen Direktzugriff benötigen, verwenden Sie ein Wörterbuch mit Array-Indizes. Dies nimmt ein paar weitere Ebenen in Anspruch (oder ein Dutzend oder mehr, wenn Sie ein SortedDictionary verwenden), damit der gc suchen muss.
Je nachdem, was Sie gerade tun, kann das Durchsuchen einer Liste von Strukturen schneller sein als das Nachschlagen des Wörterbuchs (aufgrund der Speicherlokalisierung) - Profils für Ihre bestimmte Anwendung.
Die Kombination von struct & list reduziert sowohl die Speichernutzung als auch die Größe des Garbage Collector Sweeps erheblich.
quelle