Ce projet n'est plus maintenu !!!
OHC doit fournir de bonnes performances sur le matériel des produits de base et les grands systèmes à l'aide d'architectures non uniformes.
Aucun résultat de test de performance disponible encore - vous pouvez essayer l'outil OHC-Benchmark. Voir les instructions ci-dessous. Une impression très basique sur la vitesse se trouve dans la section _benchmarking_.
Java 8 VM qui prend en charge 64bit et a sun.misc.Unsafe
(Oracle JVMS sur X64 Intel CPU).
OHC est ciblé pour Linux et OSX. Cela devrait fonctionner sur Windows et autres OSS Unix.
OHC fournit deux implémentations pour différentes caractéristiques d'entrée de cache: - L'implémentation _Linked_ alloue la mémoire hors têtes pour chaque entrée individuellement et fonctionne mieux pour les entrées moyennes et grandes. - L'implémentation _Chunked_ alloue la mémoire hors têtes pour chaque segment de hachage dans son ensemble et est destinée à de petites entrées.
Le nombre de segments est configuré via org.caffinitas.ohc.OHCacheBuilder
, par défaut # of cpus * 2
et doit être une puissance de 2. Les entrées sont distribuées sur les segments en utilisant les bits les plus importants du code de hachage 64 bits. Les accès sur chaque segment sont synchronisés.
Chaque entrée de hash-map est allouée individuellement. Les inscriptions sont libres (traitées), lorsqu'elles ne sont plus référencées par la carte hors trémail elle-même ou toute référence externe comme org.caffinitas.ohc.DirectValueAccess
ou a org.caffinitas.ohc.CacheSerializer
.
La conception de cette implémentation réduit le temps verrouillé d'un segment à un temps très court. Placer / remplacer les opérations allouer la mémoire d'abord, appelez l' org.caffinitas.ohc.CacheSerializer
pour sérialiser la clé et la valeur, puis mettez l'entrée entièrement préparée dans le segment.
L'expulsion est effectuée à l'aide d'un algorithme LRU. Une liste liée via tous les éléments en cache par segment est utilisée pour garder une trace des entrées les plus aînées.
Implémentation de la mémoire de mémoire en morceaux.
Le but de cette implémentation est de réduire les frais généraux pour des entrées de cache relativement petites par rapport à l'implémentation liée, car la mémoire de l'ensemble du segment est pré-allouée. Cette implémentation convient aux petites entrées avec des implémentations de sérialisation rapides (DE) d' org.caffinitas.ohc.CacheSerializer
.
La segmentation est la même que dans l'implémentation liée. Le nombre de segments est configuré via org.caffinitas.ohc.OHCacheBuilder
, par défaut # of cpus * 2
et doit être une puissance de 2. Les entrées sont distribuées sur les segments en utilisant les bits les plus importants du code de hachage 64 bits. Les accès sur chaque segment sont synchronisés.
Chaque segment est divisé en plusieurs morceaux. Chaque segment est responsable d'une partie de la capacité totale (capacity / segmentCount)
. Cette quantité de mémoire est allouée une fois initiale lors de l'initialisation et divisée logiquement en un nombre configurable de morceaux. La taille de chaque morceau est configurée à l'aide de l'option chunkSize
dans org.caffinitas.ohc.OHCacheBuilder
.
Comme la mise en œuvre liée, les entrées de hachage sont d'abord sérialisées dans un tampon temporaire, avant que le segment réel dans un segment ne se produise (les opérations de séglement sont synchronisées).
De nouvelles entrées sont placées dans le morceau d'écriture actuel. Lorsque ce morceau est plein, le prochain morceau vide deviendra le nouveau morceau d'écriture. Lorsque tous les morceaux sont pleins, le morceau le moins récemment utilisé, y compris toutes les entrées qu'il contient, est expulsé.
La spécification des propriétés fixedKeyLength
et fixedValueLength
réduit l'empreinte de la mémoire de 8 octets par entrée.
La sérialisation, l'accès direct et les fonctions GET-with-chargeur ne sont pas pris en charge dans cette implémentation.
Pour activer l'implémentation en morceaux, spécifiez le chunkSize
dans org.caffinitas.ohc.OHCacheBuilder
.
Remarque: La mise en œuvre en morceaux doit toujours être considérée comme expérimentale.
OHC prend en charge trois algorithmes d'expulsion:
Utilisez la classe OHCacheBuilder
pour configurer tout le paramètre nécessaire comme
Généralement, vous devriez travailler avec une grande table de hachage. Plus la table de hachage est grande, plus la liste liée est courte dans chaque partition de hachage - ce qui signifie des promenades en liaison liée moins et des performances accrues.
La quantité totale de mémoire de tas requise est la capacité totale plus la table de hachage . Chaque seau de hachage (actuellement) nécessite 8 octets - de sorte que la formule est capacity + segment_count * hash_table_size * 8
.
OHC alloue la mémoire hors trépail en contournant directement la limitation de la mémoire de la mémoire de Java. Cela signifie que toute mémoire allouée par OHC n'est pas comptée pour -XX:maxDirectMemorySize
.
Étant donné que surtout l'implémentation liée effectue des opérations alloc / gratuites pour chaque entrée individuelle, considérez que la fragmentation de la mémoire peut se produire.
Laissez également une certaine salle de tête, car certaines allocations pourraient toujours être en vol et aussi "les autres trucs" (système d'exploitation, JVM, etc.) ont besoin de mémoire. Cela dépend du modèle d'utilisation de la quantité de chef de la tête nécessaire. Notez que l'implémentation liée alloue la mémoire pendant les opérations d'écriture _BEFOR_ Il est compté pour les segments, ce qui expulsera les entrées plus anciennes. Cela signifie: ne consacrez pas toute la mémoire disponible à OHC.
Nous vous recommandons d'utiliser Jemalloc pour maintenir la fragmentation faible. Sur les systèmes d'exploitation UNIX, préchargement Jemalloc.
OSX ne nécessite généralement pas Jemalloc pour des raisons de performance. Assurez-vous également que vous utilisez une version récente de Jemalloc - certaines distributions Linux fournissent toujours des versions assez anciennes.
Pour préloger le jemalloc sur Linux, utilisez export LD_PRELOAD=<path-to-libjemalloc.so
, pour précharger Jemalloc sur OSX, utilisez export DYLD_INSERT_LIBRARIES=<path-to-libjemalloc.so
. Un modèle de script pour le préchargement peut être trouvé sur le projet Apache Cassandra.
QuickStart:
Ohcache ohcache = ohcachebuilder.newbuilder () .KeySerializer (YourKeySerializer) .ValueSerializer (YourValueSerializer) .construire();
Ce QuickStart utilise la configuration par défaut du moins:
Voir Javadoc de CacheBuilder
pour une liste complète des options.
Les sérialiseurs de clés et de valeur doivent implémenter l'interface CacheSerializer
. Cette interface a trois méthodes:
int serializedSize(T t)
pour retourner la taille sérialisée de l'objet donnévoid serialize(Object obj, DataOutput out)
pour sérialiser l'objet donné à la sortie de donnéesT deserialize(DataInput in)
pour désérialiser un objet à partir de l'entrée de données Clone le repo git de votre machine locale. Utilisez la branche maître stable ou une balise de libération.
git clone https://github.com/snazy/ohc.git
Vous avez besoin d'OpenJDK 11 ou plus récent pour construire à partir de la source. Exécutez simplement
mvn clean install
Vous devez construire OHC à partir de Source car les grands artefacts de référence ne sont pas téléchargés sur Maven Central.
Exécuter java -jar ohc-benchmark/target/ohc-benchmark-0.7.1-SNAPSHOT.jar -h
(lors de la construction de Source) pour obtenir des informations d'aide.
Généralement, l'outil de référence démarre un tas de threads et effectue des opérations _get_ et _put_ simultanément à l'aide de distributions de clés configurables pour les opérations _get_ et _put_. La distribution de la taille de la valeur doit également être configurée.
Options de ligne de commande disponibles:
-Cap <arg> taille du cache -d <arg> Durée de référence en secondes -H aide, imprimez cette commande -lf <arg> Facteur de charge de table de hachage -r <arg> ration en lecture-écriture (comme un double 0..1 représentant la chance de lire) -rkd <gg> Distribution d'utilisation des touches de chaleur - par défaut: uniforme (1..10000) -Sc <arg> Nombre de segments (nombre de MAPS hors heap individuels) -T <arg> threads pour l'exécution -vs <gg> tailles de valeur - par défaut: fixe (512) -wkd <gg> Distribution d'utilisation des touches de chaleur - par défaut: uniforme (1..10000) -Wu <arg> Réchauffement - <Work-Secs>, <Sleep-Secs> -Z Taille de la table de hachage <arg> -cs <arg> taille de morceau - s'il est spécifié, il utilisera l'implémentation "Chunked" -fks <arg> Taille de la clé fixe en octets -fvs <gg> Taille de valeur fixe en octets -MES <RAG> Taille d'entrée max en octets -Unl n'utilisez pas le verrouillage - uniquement approprié pour le mode unique -Hm <arg> algorithme de hachage à utiliser - Murmur3, xx, CRC32 -BH Show Bucket Historgram in Stat -kl <gg> Activer l'histogramme du seau. Par défaut: faux
Les distributions pour les touches de lecture, les clés d'écriture et les tailles de valeur peuvent être configurées à l'aide des fonctions suivantes:
Exp (min..max) une distribution exponentielle sur la plage [min..max] Extreme (min..max, forme) Une distribution de valeur extrême (Weibull) sur la plage [min..max] Qextreme (min..max, forme, quantas) une valeur extrême, divisée en quantas, dans laquelle le risque de sélection est uniforme Gaussien (min..max, stdvrng) une distribution gaussienne / normale, où moyenne = (min + max) / 2, et stdev est (moyen-min) / stdvrng Gaussien (min..max, moyenne, stdev) une distribution gaussienne / normale, avec moyenne explicitement définie et stdev Uniforme (min..max) une distribution uniforme sur la plage [min, max] Fixe (val) une distribution fixe, renvoyant toujours la même valeur Précédant le nom avec ~ inverser la distribution, par exemple ~ exp (1..10) donnera 10 la plupart, au lieu du moins, souvent Alias: Extr, Qextr, Gauss, Normal, Norm, Weibull
(Remarque: ceux-ci sont similaires à l'outil de stress d'Apache Cassandra - si vous en savez un, vous savez les deux;)
Exemple rapide avec un rapport de lecture / écriture de .9
, environ 1,5 Go de capacité maximale, 16 threads qui s'exécute pendant 30 secondes:
java -jar ohc-benchmark / cible / ohc-benchmark-0.5.1-snapshot.jar
(Notez que la version dans le nom du fichier JAR peut différer.)
Sur un système Core i7 (OSX) à 2,6 GHz (OSX), les nombres suivants sont typiques d'exécution de la référence ci-dessus (rapport de lecture / écriture):
Lorsque vous utilisez un très grand nombre d'objets dans un très grand tas, les machines virtuelles souffriront d'une pression GC accrue car elle doit essentiellement inspecter chaque objet s'il peut être collecté et doit accéder à toutes les pages de mémoire. Un cache doit garder un ensemble chaud d'objets accessibles pour un accès rapide (par exemple, omettre le disque ou les aller-retour en réseau). La seule solution consiste à utiliser la mémoire native - et vous vous retrouverez avec le choix pour utiliser un code natif (C / C ++) via JNI ou utiliser un accès à la mémoire directe.
Le code natif utilisant C / C ++ via JNI présente l'inconvénient que vous devez écrire naturellement du code C / C ++ pour chaque plate-forme. Bien que la plupart des OS UNIX (Linux, OSX, BSD, Solaris) soient assez similaires lorsque vous traitez des choses comme les bibliothèques de comparaison et d'échange ou POSIX, vous souhaitez généralement prendre en charge l'autre plate-forme (Windows).
Le code natif et l'accès à la mémoire directe ont l'inconvénient qu'ils doivent "laisser" le contexte JVM "" - veulent dire que l'accès à la mémoire hors tas est plus lent que l'accès aux données du tas Java et que chaque appel JNI en a " Échappez à JVM Context "Coût.
Mais la mémoire du tas est excellente lorsque vous devez faire face à une énorme quantité de plusieurs / plusieurs Go de mémoire de cache car ce DOS ne met pas de pression sur le collecteur de déchets Java. Laissez le Java GC faire son travail pour la demande où cette bibliothèque fait son travail pour les données mises en cache.
TL; DR allouant directement la mémoire hors trébuchement et contournant ByteBuffer.allocateDirect
La mise en œuvre des stocks dans Java libère une mémoire hors têtes lors d'une collection de déchets - également: si aucune mémoire hors têtes n'est disponible, elle déclenche probablement un GC complet, ce qui est problématique si plusieurs threads entrent dans cette situation simultanément car cela signifie beaucoup de lots de GC complet séquentiellement. En outre, l'implémentation des stocks utilise une liste mondiale liée synchronisée pour suivre les allocations de mémoire hors têtes.
C'est pourquoi OHC alloue directement la mémoire hors têtes et recommande de précharger le Jemalloc sur les systèmes Linux pour améliorer les performances de la gestion de la mémoire.
OHC a été développé en 2014/15 pour Apache Cassandra 2.2 et 3.0 pour être utilisé comme nouveau backend à cache Row.
Puisqu'il n'y avait pas de mise en œuvre de cache entièrement complexe appropriée disponible, il a été décidé d'en construire un complètement nouveau - et c'est OHC. Mais il s'est avéré que l'OHC seul pourrait également être utilisable pour d'autres projets - c'est pourquoi OHC est une bibliothèque distincte.
Un grand `` merci '' doit aller à Benedict Elliott Smith et Ariel Weisberg de DataSax pour leur contribution très utile à OHC!
Ben Mans, l'auteur de Caffeine, le cache sur tas très configurable utilisant W-Tiny LFU.
Développeur: Robert Stupp
Copyright (C) 2014 Robert Stupp, Koeln, Allemagne, Robert-Stupp.de
Licencié sous la licence Apache, version 2.0 (la "licence"); Vous ne pouvez pas utiliser ce fichier sauf conforme à la licence. Vous pouvez obtenir une copie de la licence à
http://www.apache.org/licenses/license-2.0
Sauf exiger la loi applicable ou convenu par écrit, les logiciels distribués en vertu de la licence sont distribués sur une base «tel quel», sans garantie ou conditions d'aucune sorte, expresse ou implicite. Voir la licence pour la langue spécifique régissant les autorisations et les limitations sous la licence.