2008-09-23 12 views
12

¿Alguien sabe alguna implementación de un caché de objetos con plantillas?Caché genérica de objetos

  • se utiliza una clave para encontrar el objeto (el mismo que en std :: mapa <>)
  • especifica un número máximo de objetos que pueden estar en el caché al mismo tiempo
  • Hay instalaciones para crear un objeto que no se encuentra en la caché
  • Hay instalaciones para saber cuando un objeto se descarta de la caché

Por ejemplo:

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); 

... 

Puede ser tan simple como un caché LRU o MRU.

¡Cualquier sugerencia es bienvenida!

Nic

Respuesta

-6

En una aplicación Me cuesta imaginar que aceleraría/Boost de hasta el rendimiento de los objetos de almacén que al parecer puede ser re-creados (cadera: ya que pueden ser descartados automáticamente, cuando la parte superior de caché). Una memoria caché sw requiere una recuperación de memoria a través del código de asociatividad, seguramente más lenta que la asignación de memoria y el constructor en ejecución (principalmente inicializaciones de memoria).

Con la excepción de la configuración manual del usuario para evitar el mecanismo de paginación (precisamente para aumentar el rendimiento, por cierto), la mayoría de OS "almacena" memoria en caché para usted en el disco ... es "paginación", una forma de "alto costo almacenamiento en caché ", porque nada se descarta, y lo hace HW específico, una unidad de subproceso llamada Unidad de administración de memoria ...

Un código de almacenamiento en caché, en general, estaría ralentizando los procesos mientras era redundante.

+0

¿Qué ocurre si la (re) creación de un objeto es mucho más lenta que una búsqueda de clave-> valor? No todos los constructores son "principalmente inicializaciones de memoria". – moswald

+0

Entiendo por qué el voto a favor: no estoy dando una respuesta. Así que estoy tratando de obtener uno: Hoy en día, la MMU marcará la memoria que contiene los objetos en caché no usados ​​recientemente como de bajo uso, por lo tanto, candidato a enviarse a un archivo de página en el disco duro ... suponiendo que haya un HDD. Por lo tanto, volver a buscar un objeto en caché faltante de HDD, código de ejecución iso para volver a crear el objeto, sería "correcto" en un conjunto de circunstancias muy engorroso. @Nicolas: ¿cuáles son sus circunstancias concretas? – jpinto3912

+1

Estoy pensando en mezclar el caché de la CPU y otro tipo de caché de datos. OP buscó caché de datos, no CPU. – Dolanor

1

he reunido una relativamente simple caché LRU construido a partir de un mapa y una lista enlazada:

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; 
    } 
}; 

que creo que cumple con sus criterios. ¿Se necesita agregar o cambiar algo?

+0

El rendimiento del mapa puede llegar a ser horrible. Sugeriría que uses una tabla hash. Haga que compile el tiempo de tamaño fijo, si puede. En lugar de agregar una lista, escanearla. – BitWhistler

+0

@BitWhistler Esto utiliza una hashtable - por defecto std :: unordered_map que es una tabla hash. No creo que el tamaño fijo en tiempo de compilación sea una buena idea, muy bajo sobrecarga para almacenar el tamaño y esto permite cambiar el tamaño según sea necesario.¿A qué te refieres con que en lugar de mantener una lista, lo escanee? La lista realiza un seguimiento del orden de inserción para que la entrada LRU se pueda eliminar. – Straw1239

+0

lo siento, tienes razón. Creí haber visto std :: map. Aún así, preasignar todo tendrá la ventaja de no reasignarlo. La reasignación es el mayor costo aquí. La misma idea en la lista. Tendría todos estos nodos flotando ... sería mejor tener la edad en las entradas o tener una lista individualmente intrusa en las entradas. – BitWhistler

Cuestiones relacionadas