Cache générique d'objets


12

Est-ce que quelqu'un connaît l'implémentation d'un cache d'objets basé sur un modèle?

  • Vous utilisez une clé pour trouver l'objet (le même que dans std :: carte <>)
  • Vous spécifiez un nombre maximum d'objets qui peuvent être dans le cache en même temps
  • Il y a installations pour créer un objet introuvable dans le cache
  • Il y a des équipements de savoir quand un objet est mis au rebut à partir du cache

Par exemple:

typedef cache<int, MyObj*> MyCache; 
MyCache oCache; 
oCache.SetSize(1); 
oCache.Insert(make_pair(1, new MyObj()); 
oCache.Touch(1); 
MyObj* oldObj = oCache.Delete(1); 

... 

Il peut être aussi simple qu'un cache LRU ou MRU.

Toutes les suggestions sont les bienvenues!

Nic

-6

Dans une application, je peux à peine imaginer accélérerait/stimuler les performances à stocker des objets qui peuvent apparemment être recréés (hanche: car ils peuvent être mis au rebut automatiquement, lorsque le dessus du cache). Un cache sw nécessiterait une récupération de mémoire via le code associativism, sûrement plus lent que l'allocation de mémoire et le fonctionnement du constructeur (principalement des initialisations de mémoire). À l'exception de la configuration manuelle de l'utilisateur pour éviter le mécanisme de pagination (précisément pour booster les performances, btw), la plupart des systèmes d'exploitation «mettent en cache» de la mémoire sur le disque ... c'est «pagination», une forme de «coût élevé». "cache", parce que rien ne sera jeté, et c'est fait par HW spécifique, une unité de sous-traitement appelée Memory Management Unit ...

Un cache-code, dans l'ensemble, ralentirait les processus tout en étant redondant.

  0

Et si la (re) création d'un objet est beaucoup plus lente qu'une recherche de clé -> valeur? Tous les constructeurs ne sont pas "principalement des initialisations de mémoire". 23 sept.. 082008-09-23 22:10:29

  0

Je comprends pourquoi le downvote: Je ne fournis pas de réponse. J'essaie donc d'en obtenir un: De nos jours, le MMU marquera la mémoire contenant les objets en cache non utilisés récemment comme étant peu utilisés, donc candidats à envoyer vers un fichier de page sur le disque dur ... en supposant qu'il y ait un HDD. Donc, ré-extraire un objet en cache manquant de HDD, iso exécutant le code pour recréer l'objet, serait seulement dans un ensemble de circonstances très lourd "juste". @Nicolas: quelles sont vos circonstances concrètes? 22 juin. 102010-06-22 19:55:59

+1

Je pense que vous mélangez le cache du processeur et un autre type de cache de données. OP a cherché le cache de données, pas CPU. 27 févr.. 132013-02-27 10:41:49


1

Ive mis en place un cache LRU relativement simple construit à partir d'une carte et une liste chaînée:

template<typename K, typename V, typename Map = std::unordered_map<K, typename std::list<K>::iterator>> 
class LRUCache 
{ 
    size_t maxSize; 
    Map data; 
    std::list<K> usageOrder; 
    std::function<void(std::pair<K, V>)> onEject = [](std::pair<K, V> x){}; 

    void moveToFront(typename std::list<K>::iterator itr) 
    { 
     if(itr != usageOrder.begin()) 
      usageOrder.splice(usageOrder.begin(), usageOrder, itr); 
    } 


    void trimToSize() 
    { 
     while(data.size() > maxSize) 
     { 
      auto itr = data.find(usageOrder.back()); 

      onEject(std::pair<K, V>(itr->first, *(itr->second))); 
      data.erase(usageOrder.back()); 
      usageOrder.erase(--usageOrder.end()); 
     } 
    } 

public: 
    typedef std::pair<const K, V> value_type; 
    typedef K key_type; 
    typedef V mapped_type; 


    LRUCache(size_t maxEntries) : maxSize(maxEntries) 
    { 
     data.reserve(maxEntries); 
    } 

    size_t size() const 
    { 
     return data.size(); 
    } 

    void insert(const value_type& v) 
    { 
     usageOrder.push_front(v.first); 
     data.insert(typename Map::value_type(v.first, usageOrder.begin())); 

     trimToSize(); 
    } 

    bool contains(const K& k) const 
    { 
     return data.count(k) != 0; 
    } 

    V& at(const K& k) 
    { 
     auto itr = data.at(k); 
     moveToFront(itr); 
     return *itr; 
    } 


    void setMaxEntries(size_t maxEntries) 
    { 
     maxSize = maxEntries; 
     trimToSize(); 
    } 

    void touch(const K& k) 
    { 
     at(k); 
    } 

    template<typename Compute> 
    V& getOrCompute(const K& k) 
    { 
     if(!data.contains(k)) insert(value_type(k, Compute())); 
     return(at(k)); 
    } 

    void setOnEject(decltype(onEject) f) 
    { 
     onEject = f; 
    } 
}; 

qui je crois répond à vos critères. Quelque chose doit être ajouté ou changé?

  0

La performance de la carte peut facilement devenir horrible. Je vous suggère d'utiliser une table de hachage. Faites-le compiler l'heure à une taille fixe, si vous le pouvez. Au lieu d'ajouter une liste, scannez-la. 11 févr.. 162016-02-11 23:47:07

  0

@BitWhistler Ceci utilise une table de hachage par défaut std :: unordered_map qui est une table de hachage. Je ne pense pas que la taille fixe à la compilation soit une bonne idée - des frais généraux très bas pour stocker la taille et cela permet de changer la taille selon les besoins.Que voulez-vous dire par au lieu de garder une liste, le scanner? La liste garde trace de l'ordre d'insertion afin que l'entrée LRU puisse être supprimée. 12 févr.. 162016-02-12 20:57:29

  0

Désolé, vous avez raison. Je pensais avoir vu std :: map. Pourtant, tout préallouer aura l'avantage de ne pas être réalloué. La réallocation est le plus gros coût ici. Même idée sur la liste. Vous auriez tous ces nœuds flottant ... mieux d'avoir l'âge dans les entrées ou d'avoir une liste de liens isolés intrusive dans les entrées. 16 févr.. 162016-02-16 11:07:17


1

Vous pouvez utiliser la bibliothèque Boost.MultiIndex. Il est facile d'implémenter un MRU cache.