Ich versuche, die beste Datenstruktur für den Einsatz in einem C++ - Server mit hohem Durchsatz zu finden. Die Datenstruktur wird verwendet, um alles von einigen bis zu mehreren Millionen Objekten zu speichern, und es ist keine Sortierung erforderlich (obwohl ein eindeutiger Sortierschlüssel sehr billig bereitgestellt werden kann).Concurrent Datenstrukturentwurf
Die Anforderungen sind, dass es effiziente Einfügung, idealerweise O (1), mäßig effiziente Entfernung und effiziente Traversierung unterstützen kann. Es muss keine Suchoperation unterstützt werden (außer zum Entfernen).
Der Twist ist, dass es thread sicher in Bezug auf Änderungen sein muss, während andere Threads die Datenstruktur aufzählen. Dies bedeutet, dass ein einfacher Rot-Schwarz-Baum nicht funktioniert, da ein Thread kein Element einfügen kann (und die notwendigen Baumrotationen durchführen kann), ohne dass Cursor, die von anderen Threads gehalten werden, durcheinander gebracht werden.
Es ist nicht akzeptabel, eine Schreib-/Lesesperre zu verwenden und die Schreiboperationen aufzuschieben, bis alle Leser fertig sind, da Lesevorgänge lange dauern können. Es spielt keine Rolle, ob Einfügungen, die passieren, während ein Leser vorhanden ist, für diesen Leser sichtbar sind oder nicht.
Speicherabdruck ist auch sehr wichtig, und klein ist offensichtlich besser!
Welche Vorschläge gibt es?
Antwort auf Kommentare:
Danke für die Antworten.
Nein, Inserts können vorhandene Iteratoren nicht ungültig machen. Iteratoren können die neue Einfügung sehen oder nicht, aber sie müssen alles sehen, was sie gesehen hätten, wenn die Einfügung nicht stattgefunden hätte.
Löschen ist erforderlich, aber aufgrund höherer Regeln kann ich garantieren, dass ein Iterator niemals auf einem Element gestoppt wird, das zum Löschen verfügbar ist.
Das Sperren pro Knoten für einen Cursor hätte einen zu großen Einfluss auf die Leistung. Es kann sein, dass mehrere Threads gleichzeitig lesen, und jede Art von Speicher-Hotspot, die mehrere Threads in einer Sperre verwenden, zerstört die Speicherbandbreite (wie wir es auf die harte Tour herausgefunden haben!). Selbst eine einfache Anzahl von Lesern mit mehreren Threads, die InterlockedIncrement aufrufen, kann nicht sauber skaliert werden.
Ich stimme zu, eine verkettete Liste ist wahrscheinlich der beste Ansatz. Löschvorgänge sind selten, daher ist die Zahlung der Speicherstrafe für die Rückzeiger zur Unterstützung von O (1) delete kostspielig, und wir können diese separat auf Anforderung berechnen, und da Löschvorgänge dazu tendieren, Stapeloperationen zu sein.
Zum Glück erfordert das Einfügen in eine verknüpfte Liste kein Sperren für Leser, solange die Zeiger im eingefügten Knoten aktualisiert werden, bevor der Kopfzeiger geändert wird.
Die Lock-Copy-Unlock-Idee ist interessant. Die Menge der beteiligten Daten ist zu groß, um als Standard für Leser zu dienen, aber sie könnte für Autoren verwendet werden, wenn sie mit Lesern kollidieren. Eine Lese-/Schreibsperre würde die gesamte Struktur schützen, und der Schreibvorgang würde die Datenstruktur klonen, wenn sie mit einem Lesegerät kollidiert. Schreiben sind viel seltener als lesen.
Können Inserts vorhandene Iteratoren ungültig machen? Wenn ja, erleichtert das das Leben. Möchten Sie Löschungen unterstützen? Wenn ja, was ist das erwartete Verhalten, wenn ein Thread ein Element löscht, das von einem anderen gelesen wird? –
Was ist Ihr Ziel? Sperrfreie oder wartefreie Datenstruktur? – user