Mise en cache des données de reflection

Quelle est la meilleure façon de mettre en cache des données coûteuses obtenues par reflection? Par exemple, la plupart des sérialiseurs rapides mettent en cache de telles informations, ils n’ont donc pas besoin de réfléchir à chaque fois qu’ils rencontrent le même type. Ils peuvent même générer une méthode dynamic qu’ils recherchent du type.

Avant .net 4

Traditionnellement, j’ai utilisé un dictionnaire statique normal pour cela. Par exemple:

private static ConcurrentDictionary<Type, Action> cache; public static DoSomething(object o) { Action action; if(cache.TryGetValue(o.GetType(), out action)) //Simple lookup, fast! { action(o); } else { // Do reflection to get the action // slow } } 

Cela fuit un peu de mémoire, mais comme cela ne se produit qu’une seule fois par Type et les types ont vécu aussi longtemps que l’ AppDomain je ne le considérais pas comme un problème.

Depuis .net 4

Mais maintenant, .net 4 a introduit les assemblages à collectionner pour la génération de types dynamics . Si j’ai déjà utilisé DoSomething sur un object déclaré dans l’assemblage de collection, cet assemblage ne sera jamais déchargé. Aie.

Alors, quelle est la meilleure façon de mettre en cache les informations par type dans .net 4 qui ne souffrent pas de ce problème? La solution la plus simple que je puisse imaginer est la suivante:

 private static ConcurrentDictionary cache. 

Mais le IEqualityComparer je devrais utiliser avec cela se comporterait très étrangement et violerait probablement aussi le contrat. Je ne suis pas sûr de la rapidité de la recherche non plus.

Une autre idée consiste à utiliser un délai d’expiration. Peut-être la solution la plus simple, mais se sent un peu inélégant.


Dans les cas où le type est fourni en tant que paramètre générique, je peux utiliser une classe générique nestede qui ne devrait pas souffrir de ce problème. Mais son ne fonctionne pas si le type est fourni dans une variable.

 class MyReflection { internal Cache { internal static TData data; } void DoSomething() { DoSomethingWithData(Cache.data); //Obviously simplified, should have similar creation logic to the previous code. } } 

Mise à jour : Une idée que je viens de faire est d’utiliser Type.AssemblyQualifiedName comme clé. Cela devrait identifier ce type de manière unique sans le garder en mémoire. Je pourrais même me permettre d’utiliser l’identité référentielle sur cette chaîne.

Un problème qui rest avec cette solution est que la valeur mise en cache peut également garder une référence au type. Et si j’utilise une référence faible, elle expirera probablement bien avant que l’assemblage ne soit déchargé. Et je ne sais pas à quel point il est peu coûteux d’obtenir une référence normale à partir d’une référence faible. On dirait que je dois faire des tests et des parsings comparatives.

ConcurrentDictionary est incorrect dans ce cas. Supposons que nous essayons de mettre en cache les informations pour le type T, donc WeakReference.Target==typeof(T) . CachedData contiendra probablement la référence pour typeof(T) également. Comme ConcurrentDictionary stocke des éléments dans la collection interne de Node vous aurez une chaîne de références fortes: Instance ConcurrentDictionary -> Instance de Node -> Propriété Value (instance CachedData ) -> typeof(T) . En général, il est impossible d’éviter les memory leaks avec WeakReference dans le cas où les valeurs pourraient avoir des références à leurs clés.

Il était nécessaire d’append un support pour les éphémères afin de rendre ce scénario possible sans memory leaks. Heureusement, .NET 4.0 les supporte et nous avons la classe ConditionalWeakTable . Il semble que les raisons de l’introduire sont proches de votre tâche.

Cette approche résout également les problèmes mentionnés dans votre mise à jour, car la référence à Type continuera d’exister tout au long du chargement de l’assembly.

Vous devriez vérifier la librairie plus rapideflect sur codeplex http://fasterflect.codeplex.com/

Vous pouvez utiliser une reflection normale pour générer dynamicment un nouveau code, puis émettre / comstackr le code, puis mettre en cache la version compilée. Je pense que l’idée de l’assemblée de collection est prometteuse pour éviter la fuite de mémoire sans avoir à charger / décharger depuis un domaine d’application distinct. Cependant, la fuite de mémoire devrait être négligeable, sauf si vous comstackz des centaines de méthodes.

Voici un article sur la compilation dynamic du code à l’exécution: http://introspectingcode.blogspot.com/2011/06/dynamically-comstack-code-at-runtime.html

Vous trouverez ci-dessous une approche similaire à celle des dictionnaires concurrents que j’ai utilisée par le passé pour stocker les objects MethodInfo / PropertyInfo et cela semblait être plus rapide, mais je pense que c’était dans une ancienne version de Silverlight. Je crois que .Net a son propre cache de reflection interne qui le rend inutile.

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.Collections.Concurrent; namespace NetSteps.Common.Reflection { public static class Reflection { private static ConcurrentDictionary> reflectionPropertyCache = new ConcurrentDictionary>(); public static List FindClassProperties(Type objectType) { if (reflectionPropertyCache.ContainsKey(objectType)) return reflectionPropertyCache[objectType].Values.ToList(); var result = objectType.GetProperties().ToDictionary(p => p.Name, p => p); reflectionPropertyCache.TryAdd(objectType, result); return result.Values.ToList(); } } } 

Je pourrais peut-être dire ici l’évidence mais:

Les fournisseurs de cache ne sérialisent-ils généralement pas les données vers une source?

Le processus de désérialisation va sûrement coûter plus cher que de simplement repenser une nouvelle instance?

Ou ai-je manqué quelque chose?

Et il y a tout l’argument autour de la boxe et du temps de déballage … pas sûr que cela compte vraiment.

Modifier:

Qu’en est-il (j’espère que cela explique un peu mieux le problème) …

 Dictionary typecache = new Dictionary(); // finding a type from say a ssortingng that points at a type in an assembly not referrenced // very costly but we can cache that Type myType = GetSomeTypeWithReflection(); typecache.Add("myType", myType); // creating an instance you can use very costly MyThingy thingy = Activator.CreateInstance(typecache["myType"]); 

cherchez-vous à mettre en cache “thingy”?