Dieses Projekt ist nicht mehr aufrechterhalten !!!
OHC muss sowohl für Rohstoffhardware als auch für große Systeme eine gute Leistung unter Verwendung von ungleichmäßigen Memory-Architekturen bieten.
Noch keine Leistungstestergebnisse verfügbar - Sie können das OHC -Benchmark -Tool ausprobieren. Siehe Anweisungen unten. Ein sehr grundlegender Eindruck auf die Geschwindigkeit ist im Abschnitt _Benchmarking_.
Java 8 VM, die 64bit unterstützen und sun.misc.Unsafe
(Oracle JVMS auf X64 Intel CPUs).
OHC ist für Linux und OSX ausgerichtet. Es sollte unter Windows und anderen Unix -OSS funktionieren.
OHC bietet zwei Implementierungen für verschiedene Cache -Eintragsmerkmale: - Die Implementierung von _linked_ weist für jeden Eintrag einen Off -HEAP -Speicher einzeln zu und eignet sich am besten für mittlere und große Einträge. - Die Implementierung _chunked_ verurteilt für jedes Hash-Segment als Ganzes Off-HEAP-Speicher und ist für kleine Einträge bestimmt.
Die Anzahl der Segmente wird über org.caffinitas.ohc.OHCacheBuilder
konfiguriert, standardmäßig # of cpus * 2
und muss eine Leistung von 2. Einträgen ausmachen. Zugriffe in jedem Segment werden synchronisiert.
Jeder Hash-Map-Eintrag wird einzeln zugewiesen. Die Einträge sind frei (Deallocated), wenn sie nicht mehr von der Off-Heap-Karte selbst oder einer externen Referenz wie org.caffinitas.ohc.DirectValueAccess
oder a org.caffinitas.ohc.CacheSerializer
verwiesen werden.
Das Design dieser Implementierung reduziert die gesperrte Zeit eines Segments auf sehr kurze Zeit. Put/Ersetzen Vorgänge zuerst den Speicher zuordnen, rufen Sie die org.caffinitas.ohc.CacheSerializer
auf, um den Schlüssel und den Wert zu serialisieren, und setzen Sie dann den vollständig vorbereiteten Eintrag in das Segment ein.
Die Räumung wird unter Verwendung eines LRU -Algorithmus durchgeführt. Eine verknüpfte Liste über alle zwischengespeicherten Elemente an Segment wird verwendet, um die ältesten Einträge zu verfolgen.
Speicherallokation außerhalb der HEAP-Implementierung.
Der Zweck dieser Implementierung besteht darin, den Overhead für relativ kleine Cache-Einträge im Vergleich zur verknüpften Implementierung zu verringern, da der Speicher für das gesamte Segment vorab ollloziert ist. Diese Implementierung eignet sich für kleine Einträge mit Fast (DE) -Serialisierungsimplementierungen von org.caffinitas.ohc.CacheSerializer
.
Die Segmentierung ist die gleiche wie in der verknüpften Implementierung. Die Anzahl der Segmente wird über org.caffinitas.ohc.OHCacheBuilder
konfiguriert, standardmäßig # of cpus * 2
und muss eine Leistung von 2. Einträgen ausmachen. Zugriffe in jedem Segment werden synchronisiert.
Jedes Segment ist in mehrere Stücke unterteilt. Jedes Segment ist für einen Teil der Gesamtkapazität (capacity / segmentCount)
verantwortlich. Diese Speichermenge wird während der Initialisierung einmal im Vorfeld zugewiesen und logisch in eine konfigurierbare Anzahl von Stücken unterteilt. Die Größe jedes Stücks wird mithilfe der chunkSize
-Option in org.caffinitas.ohc.OHCacheBuilder
konfiguriert.
Wie bei der verknüpften Implementierung werden Hash -Einträge zuerst in einen temporären Puffer serialisiert, bevor das tatsächliche Put in ein Segment auftritt (Segement -Operationen werden synchronisiert).
Neue Einträge werden in den aktuellen Schreibbad eingebaut. Wenn dieser Teil voll ist, wird der nächste leere Teil zum neuen Schreibbad. Wenn alle Brocken voll sind, wird der am wenigsten verwendete Stück, einschließlich aller Einträge, die es enthält, vertrieben.
Durch die Angabe der Eigenschaften von fixedKeyLength
und fixedValueLength
Builder reduziert die Speicher Fußabdruck um 8 Bytes pro Eintrag.
In dieser Implementierung werden Serialisierung, direkte Zugriff und Get-mit-Laderfunktionen nicht unterstützt.
Geben Sie die chunkSize
in org.caffinitas.ohc.OHCacheBuilder
an, um die Chunked -Implementierung zu aktivieren.
HINWEIS: Die Chunked -Implementierung sollte weiterhin als experimentell angesehen werden.
OHC unterstützt drei Räumungsalgorithmen:
Verwenden Sie die Klasse OHCacheBuilder
, um alle erforderlichen Parameter zu konfigurieren
Im Allgemeinen sollten Sie mit einem großen Hash -Tisch arbeiten. Je größer die Hash-Tabelle ist, desto kürzer die verknüpfte List in jeder Hash-Partition-das bedeutet weniger verknüpfte Linkspaziergänge und eine erhöhte Leistung.
Der Gesamtbetrag des erforderlichen Haufenspeichers ist die Gesamtkapazität plus Hash -Tabelle . Jeder Hash -Bucket (derzeit) benötigt 8 Bytes - daher ist die Formel capacity + segment_count * hash_table_size * 8
.
OHC weist den Speicher außerhalb des HEAP direkt zu, der Javas Off-Hap-Speicherbeschränkung direkt umgeht. Dies bedeutet, dass das gesamte von OHC zugewiesene Speicher nicht in Richtung -XX:maxDirectMemorySize
gezählt wird.
Da insbesondere die verknüpfte Implementierung für jeden einzelnen Eintrag Alloc/Free -Operationen durchführt, können Sie die Speicherfragmentierung erfolgen.
Lassen Sie auch einen Kopfraum, da einige Zuordnungen möglicherweise noch im Flug sind und auch "das andere Zeug" (Betriebssystem, JVM usw.) Speicher benötigen. Es hängt vom Verwendungsmuster ab, wie viel Kopfraum notwendig ist. Beachten Sie, dass die verknüpfte Implementierung Speicher während der Schreibvorgänge _Before_ zu den Segmenten gezählt wird, was ältere Einträge erzeugt. Dies bedeutet: Widmen Sie nicht den verfügbaren Speicher dem OHC.
Wir empfehlen, Jemalloc zu verwenden, um die Fragmentierung niedrig zu halten. Auf UNIX -Betriebssystemen vorladen Jemalloc.
OSX benötigt aus Leistungsgründen normalerweise kein Jemalloc. Stellen Sie außerdem sicher, dass Sie eine aktuelle Version von Jemalloc verwenden - einige Linux -Verteilungen bieten immer noch ziemlich alte Versionen.
Verwenden Sie zum Vorladung export LD_PRELOAD=<path-to-libjemalloc.so
, um Jemalloc unter OSX vorzuladen, um Jemalloc auf Linux zu export DYLD_INSERT_LIBRARIES=<path-to-libjemalloc.so
. Eine Skript -Vorlage für das Vorladen finden Sie im Apache Cassandra -Projekt.
QuickStart:
Ohcache ohcache = ohcacheBuilder.newbuilder () .Keyserializer (yourkeyserializer) .ValueSerializer (Your -ValueSerializer) .bauen();
Dieser QuickStart verwendet die mindestens Standardkonfiguration:
Eine vollständige Liste der Optionen finden Sie in Javadoc of CacheBuilder
.
Schlüssel- und Wertserialisierer müssen die CacheSerializer
-Schnittstelle implementieren. Diese Schnittstelle hat drei Methoden:
int serializedSize(T t)
um die serialisierte Größe des angegebenen Objekts zurückzugebenvoid serialize(Object obj, DataOutput out)
um das angegebene Objekt mit der Datenausgabe zu serialisierenT deserialize(DataInput in)
um ein Objekt aus den Dateneingaben zu dederialisieren Klonen Sie das Git -Repo auf Ihre lokale Maschine. Verwenden Sie entweder den stabilen Master -Zweig oder einen Release -Tag.
git clone https://github.com/snazy/ohc.git
Sie benötigen OpenJDK 11 oder neuer aus der Quelle. Einfach ausführen
mvn clean install
Sie müssen OHC aus der Quelle bauen, da die großen Benchmark -Artefakte nicht auf Maven Central hochgeladen werden.
Führen Sie java -jar ohc-benchmark/target/ohc-benchmark-0.7.1-SNAPSHOT.jar -h
(beim Erstellen aus der Quelle) aus, um einige Hilfeinformationen zu erhalten.
Im Allgemeinen startet das Benchmark -Tool eine Reihe von Threads und führt _get_- und _put_ -Operationen gleichzeitig mit konfigurierbaren Schlüsselverteilungen für _get_ und _put_ operationen aus. Die Wertgrößenverteilung muss auch konfiguriert werden.
Verfügbare Befehlszeilenoptionen:
-Cap <arg> Größe des Cache -D <arg> Benchmark -Dauer in Sekunden -h Hilfe, drucken Sie diesen Befehl aus -lf <arg> Hash -Tabellenlastfaktor -R <arg> Read-Write-Ration (als Double 0..1, die die Chance für eine Lektüre darstellen) -RKD <GIRG> Heißschlüssel Verwenden Sie Verteilung - Standard: Uniform (1..10000) -Sc <arg> Anzahl der Segmente (Anzahl der einzelnen Off-Heap-Maps) -t <arg> Threads für die Ausführung -Vs <arg> Wertgrößen - Standard: Behoben (512) -WKD <GIRG> Heißschlüssel Verwenden Sie Verteilung - Standard: Uniform (1..10000) -Wu <arg> Aufwärmen-<Work-Secs>, <schlafsecs> -Z <arg> Hash -Tabellengröße -cs <arg> Chunk -Größe - Wenn angegeben, wird die Implementierung "Chunked" verwendet -fks <arg> Die Schlüsselgröße in Bytes festgelegt -fvs <arg> Festwertgröße in Bytes -Mes <arg> maximale Einstiegsgröße in Bytes -unl verwenden keine Verriegelung -nur für den Einzel -Thread -Modus zugelassen -HM <arg> Hash -Algorithmus zu verwenden - Murmur3, XX, CRC32 -BH zeigen Bucket Historgram in Statistiken -Kl <arg> Bucket Histogramm aktivieren. Standard: Falsch
Verteilungen für Leseschlüsseln, Schreibschlüssel und Wertgrößen können mit den folgenden Funktionen konfiguriert werden:
Exp (min..max) Eine exponentielle Verteilung über den Bereich [min..max] Extreme (min..max, form) Eine extreme Wertverteilung (Weibull) über den Bereich [min..max] QEXTREME (min..max, Form, Quantas) Ein extremer Wert, der in Quantas aufgeteilt ist, in dem die Wahrscheinlichkeit einer Selektion einheitlich ist Gaußsche (min..max, stdvrng) Eine Gaußsche/Normalverteilung, wobei Mittelwert = (min+max)/2 und Stdev (Mean-Min)/Stdvrng ist Gaußscher (Min..max, Mittelwert, Stdev) Eine Gaußsche/Normalverteilung mit explizit definiertem Mittelwert und Stdev Uniform (min..max) Eine gleichmäßige Verteilung über den Bereich [min, max] Fest (val) eine feste Verteilung, die immer den gleichen Wert zurückgibt Der Vorgehen des Namens mit ~ wird die Verteilung umkehren, z. ~ Exp (1..10) liefert 10 am meisten, anstatt dass es am wenigsten ist, oft, häufig Aliase: extr, qextr, gauß, normal, norm, weibull
(Hinweis: Diese ähneln dem Apache -Cassandra -Stress -Tool - wenn Sie einen kennen, wissen Sie beides;)
Schnelles Beispiel mit einem Lese-/Schreibverhältnis von .9
, ca. 1,5 GB MAX -Kapazität, 16 Threads, die 30 Sekunden lang ausgeführt werden:
Java-Jar OHC-Benchmark/Target/OHC-Benchmark-0.5.1-Snapshot.jar
(Beachten Sie, dass die Version im JAR -Dateinamen unterschiedlich sein könnte.)
Auf einem 2,6 -GHz -Kern -I7 -System (OSX) sind die folgenden Zahlen typisch ausführen, wie der obige Benchmark (.9 Lese-/Schreibverhältnis) ausführt:
Bei der Verwendung einer sehr großen Anzahl von Objekten in einem sehr großen Haufen leiden virtuelle Maschinen unter einem erhöhten GC -Druck, da er im Grunde jedes einzelne Objekt, unabhängig davon, ob er gesammelt werden kann, inspizieren muss und auf alle Speicherseiten zugreifen muss. Ein Cache muss einen heißen Satz von Objekten für den schnellen Zugriff zugänglich halten (z. B. Abläufe von Festplatten oder Netzwerk -Roundtrips). Die einzige Lösung besteht darin, den nativen Speicher zu verwenden - und dort haben Sie die Wahl, entweder einen nativen Code (C/C ++) über JNI oder einen direkten Speicherzugriff zu verwenden.
Natives Code mit C/C ++ über JNI hat den Nachteil, dass Sie für jede Plattform natürlich C/C ++ -Code schreiben müssen. Obwohl die meisten UNIX-OS (Linux, OSX, BSD, Solaris) im Umgang mit Dingen wie Compare-and-Swap- oder POSIX-Bibliotheken ziemlich ähnlich sind, möchten Sie normalerweise auch die andere Plattform (Windows) unterstützen.
Sowohl der native Code als auch der direkte Speicherzugriff haben den Nachteil, dass sie den "Kontext" des JVM "verlassen" müssen. Sie möchten sagen, dass der Zugriff auf Off -Heap -Speicher langsamer ist als der Zugriff auf Daten im Java -Heap und dass jeder JNI -Anruf einige hat " Flucht aus dem JVM -Kontext "Kosten".
Aber aus dem Heap -Speicher ist großartig, wenn Sie sich mit einer großen Menge von mehreren/vielen GB Cache -Speicher befassen müssen, da der DOS den Java -Müllsammler keinen Druck ausübt. Lassen Sie den Java GC ihren Job für die Anwendung erledigen, in der diese Bibliothek ihre Aufgabe für die zwischengespeicherten Daten erledigt.
TL; DR, das den Off-HEAP-Speicher direkt zugute kommt und ByteBuffer.allocateDirect
umgeht. Allocatedirect ist für die GC sehr sanft und wir haben eine explizite Kontrolle über die Speicherzuweisung und, was noch wichtiger ist, frei. Die Aktienimplementierung in Java befreit während einer Müllsammlung den Speicher von Off-HEAP von Full-GCs nacheinander. Darüber hinaus verwendet die Aktienimplementierung eine globale, synchronisierte verknüpfte Liste, um Off-HEAP-Speicherzuweisungen zu verfolgen.
Aus diesem Grund verteilt OHC direkter Speicher und empfiehlt, Jemalloc auf Linux-Systeme zu laden, um die Leistung des Speicherverwaltungsverwalts zu verbessern.
OHC wurde 2014/15 für Apache Cassandra 2.2 und 3.0 entwickelt, um als neues Row-Cache-Backend verwendet zu werden.
Da es keine geeigneten Implementierungen von Cache -Implementierungen von Off -Heap -Cache gab, wurde beschlossen, eine völlig neue zu erstellen - und das ist OHC. Es stellte sich jedoch heraus, dass OHC allein für andere Projekte auch verwendet werden kann - deshalb ist OHC eine separate Bibliothek.
Ein großes Dankeschön muss Benedikt Elliott Smith und Ariel Weisberg von DataStax für ihre sehr nützliche Input an OHC gehen!
Ben Mans, der Autor von Coffein, dem hochkonfigurierbaren On-Heap-Cache mit W-Tiny LFU.
Entwickler: Robert Stupp
Copyright (C) 2014 Robert Stupp, Koeln, Deutschland, Robert-Stupp.de
Lizenziert unter der Apache -Lizenz, Version 2.0 (der "Lizenz"); Sie dürfen diese Datei nur in Übereinstimmung mit der Lizenz verwenden. Sie können eine Kopie der Lizenz bei erhalten
http://www.apache.org/licenses/license-2.0
Sofern nicht nach geltendem Recht oder schriftlich zu vereinbart wird, wird die im Rahmen der Lizenz verteilte Software auf "As is" -Basis ohne Gewährleistung oder Bedingungen jeglicher Art ausdrücklich oder impliziert verteilt. Siehe die Lizenz für die spezifischen Sprachberechtigungen und Einschränkungen im Rahmen der Lizenz.