2008-09-23 3 views
12

템플릿 기반 캐시의 구현을 아는 사람이 있습니까?객체의 일반 캐시

  • 당신은 (<가> 표준과 동일 ::지도)
  • 당신이 있습니다
  • 동시에 캐시에있을 수있는 개체의 최대 수를 지정 개체를 찾기 위해 키를 사용하여 시설 캐시에없는 객체를 생성하는
  • 개체가 예를 들어 캐시

에서 폐기 될 때 알 수있는 시설이 있습니다

LRU 또는 MRU 캐시처럼 간단 할 수 있습니다.

모든 의견을 환영합니다! 내가 거의 그것이/속도 것이라고 상상할 수있는 응용 프로그램에서 닉

답변

-6

분명히 다시 만들 수 있습니다 저장소 개체에 성능까지 향상 (엉덩이 : 그들은 자동으로 때 캐시 꼭대기를 폐기 할 수 있기 때문에). sw 캐시는 associativism 코드를 통해 메모리를 가져와야합니다. 확실히 메모리 할당과 생성자 실행 (대부분 메모리 초기화)이 느립니다.

페이징 메커니즘을 피하기 위해 (정확하게 성능을 높이려면 btw) 수동 사용자 구성을 제외하고 대부분의 OS는 디스크의 메모리를 "캐싱"합니다 ... "페이징", "높은 비용 캐싱 (caching) "은 아무 것도 버려지기 어렵고 메모리 관리 유닛 (Memory Management Unit)이라고하는 하위 처리 단위 인 HW에 의해 수행됩니다 ...

캐싱 코드는 중복되는 동안 프로세스가 느려질 수 있습니다. 나는 당신의 기준을 충족 생각

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

:

+0

개체의 (다시) 생성이 키 -> 값 조회보다 훨씬 느린 경우 어떻게해야합니까? 모든 생성자가 "주로 메모리 초기화"는 아닙니다. – moswald

+0

나는 왜 downvote를 얻는다 : 나는 응답을 제공하고 있지 않다. 그래서 나는 하나를 얻으려고합니다 : Nowdays MMU는 최근에 사용하지 않은 캐시 된 객체를 포함하는 메모리를 낮은 사용량으로 표시하므로 하드 디스크의 페이지 파일로 전송됩니다. HDD. 따라서 HDD에서 누락 된 캐시 된 객체를 다시 가져 오는 작업, 객체를 다시 작성하기위한 ISO 실행 코드는 아주 까다로운 환경에서만 "올바르게"수행 할 수 있습니다. @ 니콜라스 : 구체적인 상황은 무엇입니까? – jpinto3912

+1

나는 당신이 CPU 캐시와 다른 유형의 데이터 캐시를 혼합한다고 생각하고있다. OP는 CPU가 아닌 데이터 캐시를 찾았습니다. – Dolanor

1

은 필자가지도와 링크 된 목록에서 내장 비교적 간단한 LRU 캐시를 함께 넣어. 어떤 것이 추가되거나 변경되어야합니까?

+0

지도의 성능은 쉽게 끔찍하게 될 수 있습니다. 해시 테이블을 사용하는 것이 좋습니다. 가능한 경우 컴파일 시간을 고정 크기로 만드십시오. 목록을 추가하는 대신 목록을 스캔하십시오. – BitWhistler

+0

@BitWhistler 이것은 해시 테이블 인 기본적으로 해시 테이블 인 std :: unordered_map을 사용합니다. 필자는 컴파일 타임 고정 크기가 좋은 생각이라고 생각하지 않습니다. 크기를 저장하는 오버 헤드가 매우 낮으므로 필요에 따라 크기를 변경할 수 있습니다.목록을 지키고 스캔하는 대신에 무엇을 의미합니까? 목록은 LRU 항목을 삭제할 수 있도록 삽입 순서를 추적합니다. – Straw1239

+0

죄송합니다, 당신 말이 맞아요. 나는 std :: map을 본 것으로 생각했다. 그럼에도 불구하고 모든 것을 미리 할당하는 것은 재 할당하지 않는 이점이 있습니다. 재배치가 가장 큰 비용입니다. 목록에서 같은 아이디어. 당신은이 모든 노드를 떠 다니게 될 것입니다. 엔트리에 나이가 있거나 엔트리에 방해가되지 않는 단일리스트가 있어야합니다. – BitWhistler