2014-04-21 3 views
1

J'essaie de mettre en cache le résultat d'une fonction coûteuse dans un objet MemoryCache.Est-il logique d'utiliser un code de hachage d'objet comme clé de cache mémoire?

Le MemoryCache nécessite une clé qui est une chaîne, donc je me demandais si elle était valide pour effectuer les opérations suivantes:

string key = Char.ConvertFromUtf32(myObject.GetHashCode()); 
if (!_resourceDescriptionCache.Contains(key)) 
{ 
    _resourceDescriptionCache[key] = ExpensiveFunction(myObject); 
} 
return (string)_resourceDescriptionCache[key]; 

On se sent bizarre avec un seul caractère UTF32 comme la clé d'une cache potentiellement importante .

+0

Je reçois une erreur La valeur UTF32 doit être comprise entre 0x000000 et 0x10ffff donc je suppose que je ne peux pas simplement convertir un Int32 en un caractère de cette façon. – Alain

+1

Toutes les valeurs 32 bits ne représentent pas un point de code UTF32 valide. Simple, pas le plus rapide mais décemment efficace serait d'utiliser la représentation hexadécimale du code de hachage. De mémoire, 'myObject.GetHashCode(). ToString (" X ")'. –

+0

Merci à tous, commentaires très utiles – Alain

Répondre

2

Cela dépend.

Il y a beaucoup de cas où l'utilisation GetHashCode() pourrait causer un comportement incorrect:

Un code de hachage est destiné à l'insertion efficace et recherche dans les collections qui sont basées sur une table de hachage. Un code de hachage n'est pas une valeur permanente. Pour cette raison:

  • Ne pas sérialiser les valeurs de code de hachage ou les stocker dans des bases de données.
  • N'utilisez pas le code de hachage comme clé pour extraire un objet d'une collection avec clé.
  • N'envoyez pas de codes de hachage à travers les domaines d'application ou les processus. Dans certains cas, les codes de hachage peuvent être calculés par processus ou par domaine d'application.

http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx

Si le cache de mémoire se produit (ou peut se produire à l'avenir) dans un processus différent ou application domaine que le code qui l'appelle, vous ne parvenez pas à la 3ème condition. Il semble étrange d'utiliser un seul caractère UTF32 comme clé pour un cache potentiellement volumineux.

Si vous mettez en cache suffisamment de choses, le taux de collision sur un hachage 32 bits peut être désagréablement élevé en raison de Birthday Problem. Lors de la mise en cache de dizaines de millions de choses, j'ai utilisé un hachage 64 bits appelé City Hash (créé par Google, open source) avec succès. Vous pouvez également utiliser un guid, bien que la mémoire pour maintenir les clés est deux fois plus grande pour un GUID par rapport à un hachage 64 bits.

+1

C'est un bon point. Je viole la règle # 2 - en utilisant le code de hachage comme une clé pour une collection. Maintenant, je suis coincé avec ne pas savoir comment produire une bonne clé de cache bon marché. Par exemple, sérialiser l'objet serait trop cher. – Alain

+0

Comment ToHashCode() est-il actuellement implémenté? Vraisemblablement, il agit dans une propriété ou une combinaison de propriétés qui devrait être unique (suffisante). Vous pouvez créer votre clé de chaîne en concaténant la représentation sous forme de chaîne de ces propriétés. –

+0

Malheureusement, cette classe traite réellement les types d'objets, donc 'GetHashCode()' est implémenté de la façon dont cet objet l'implémente. Si le type bouscule la méthode 'GetHashCode()' et qu'il y a des collisions, alors les collisions partageront toutes la même valeur de 'ExpensiveFunction (object)' (qui est comme un sérialiseur) - même si elles auraient produit des résultats différents. Je fais de mon mieux pour isoler cet effet en utilisant effectivement 'key = myObject.GetType() + Char.ConvertFromUtf32 (myObject.GetHashCode());' – Alain

-1

Le cache mémoire est soutenu par le dictionnaire C# normal. Ce n'est vraiment pas très différent, sauf le fait qu'il fournit une expiration

Les chances d'une collision sont 2^32, ce qui est la taille d'un nombre entier. Même si vous parvenez à obtenir une collision, le dictionnaire a des mesures de sécurité autour de cela (en utilisant les égaux sur une collision)

Édition: Les collisions de clés ne sont traitées que lorsqu'un dictionnaire reçoit la clé inchangée (ex: Dictionnaire()). Dans ce cas, puisque le MemoryCache utilise des chaînes, il n'y a pas de détection de collision.

+0

'potentiellement grand cache' = taux de collision potentiellement élevé http://en.wikipedia.org/wiki/Birthday_problem –

+2

Les collisions dans un dictionnaire sont résolues en utilisant la méthode' Equals', plus chère. Lorsque vous utilisez le code de hachage comme clé du colloque, les collisions de hachage entraînent un comportement incorrect, et pas seulement des calculs plus coûteux. C'est une énorme différence. – Servy

+0

Dans l'exemple de code fourni, il utilise l'objet.GetHashCode(), qui sera probablement assez unique. Et par potentiellement grand, s'il s'agit de quelques millions de clés, le dictionnaire le gérera encore très bien; il y a environ 4,2 milliards de clés possibles après tout. Le problème d'anniversaire ne s'applique pas, sauf s'il existe un générateur de hachage personnalisé qui ne produit qu'un ensemble de clés limité. – Dan

1

Les codes de hachage peuvent entrer en collision. return 0; est une implémentation valide pour GetHashCode. Plusieurs clés partageront un emplacement de cache qui n'est pas ce que vous désirez ... Vous allez confondre des objets.

Si votre code ne fonctionne pas avec return 0; comme l'implémentation pour GetHashCode votre code est cassé.

Choisissez une meilleure clé de cache.

+0

Que diriez-vous de: 'key = myObject.GetType() + Char.ConvertFromUtf32 (myObject.GetHashCode());', de cette façon les types implémentant correctement 'GetHashCode()' ne sont pas affecté par les types qui ne parviennent pas à le mettre en œuvre intentionnellement? – Alain

+0

Dans certains contextes, 'ExpensiveFunction()' est un sérialiseur qui réfléchit sur l'objet pour donner un aperçu de son contenu dans une sorte de fenêtre "Browser". Il est moins important pour moi que chaque objet ait un «aperçu» correct et plus important que je ne gaspille pas trop de ressources en re-sérialisant chaque objet chaque fois qu'il apparaît dans le navigateur d'objets. – Alain

+1

(Dans le commentaire précédent, je vous ai mal compris.) Néanmoins, vous supposez que myObject.GetHashCode() 'est une clé unique. Si c'est 100% le cas, cela fonctionnera. 'Il est moins important pour moi que chaque objet ait un 'aperçu' correct - votre approche fonctionnera alors. Même une clé de cache avec perte est ok. Je ne comprends pas complètement les remarques de sérialisation, mais vous pouvez peut-être hacher les 32 premiers octets de la représentation sérialisée et l'utiliser comme clé. – usr

Questions connexes