Общий кеш объектов

Кто-нибудь знает какую-нибудь реализацию шаблонного кеша объектов?

  • Вы используете ключ для поиска объекта (так же, как в std::map‹>)
  • Вы указываете максимальное количество объектов, которые могут находиться в кэше одновременно
  • Есть средства для создания объекта, не найденного в кеше.
  • Есть возможность узнать, когда объект удаляется из кеша.

Например :

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

...

Это может быть так же просто, как кэш LRU или MRU.

Любые предложения приветствуются!

Ник


person Nicolas    schedule 23.09.2008    source источник


Ответы (3)


Вы можете использовать библиотеку Boost.MultiIndex. Легко реализовать кеш MRU.

person david.jugon    schedule 07.04.2016

Я собрал относительно простой кэш LRU, построенный из карты и связанного списка:

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

Что, я считаю, соответствует вашим критериям. Что-то нужно добавить или изменить?

person Straw1239    schedule 03.02.2016
comment
Производительность карты может легко стать ужасной. Я предлагаю вам использовать хеш-таблицу. Сделайте время компиляции фиксированным размером, если можете. Вместо добавления списка отсканируйте его. - person BitWhistler; 12.02.2016
comment
@BitWhistler При этом используется хеш-таблица — по умолчанию std::unordered_map, которая является хеш-таблицей. Я не думаю, что фиксированный размер во время компиляции - хорошая идея - очень низкие накладные расходы для хранения размера, и это позволяет изменять размер по мере необходимости. Что вы имеете в виду, говоря, что вместо того, чтобы вести список, отсканируйте его? Список отслеживает порядок вставки, так что запись LRU может быть удалена. - person Straw1239; 12.02.2016
comment
извините, вы правы. Мне показалось, что я видел std::map. Тем не менее, предварительное выделение всего будет иметь то преимущество, что не будет перераспределения. Перераспределение - самая большая стоимость здесь. Та же идея в списке. У вас были бы все эти узлы, плавающие вокруг ... лучше иметь возраст в записях или иметь односвязный список, навязчивый в записях. - person BitWhistler; 16.02.2016

В приложении я с трудом могу представить, что это ускорит / повысит производительность для хранения объектов, которые, по-видимому, могут быть воссозданы (бедро: поскольку они могут быть автоматически отброшены, когда кеш заканчивается). Кэш sw потребует извлечения памяти с помощью кода ассоциативности, что, безусловно, медленнее, чем выделение памяти и запуск конструктора (в основном инициализация памяти).

За исключением ручной настройки пользователя, чтобы избежать механизма подкачки (именно для повышения производительности, кстати), большинство ОС «кэширует» память для вас на диске ... это «пейджинг», форма «кэширования высокой стоимости», потому что ничего не выбрасывается, и это делается специальным аппаратным обеспечением, подпроцессором, называемым блоком управления памятью...

Кэширующий код, по большому счету, будет замедлять процессы, будучи избыточным.

person jpinto3912    schedule 23.09.2008
comment
Что, если (повторное) создание объекта происходит намного медленнее, чем поиск по ключу и значению? Не каждый конструктор в основном инициализирует память. - person moswald; 24.09.2008
comment
Я понимаю, почему отрицательный голос: я не даю ответа. Итак, я пытаюсь получить один: теперь MMU будет помечать память, содержащую недавно использованные кэшированные объекты, как малоиспользуемую, следовательно, кандидата на отправку в файл подкачки на жестком диске ... при условии, что есть жесткий диск. Таким образом, повторное извлечение отсутствующего кэшированного объекта с жесткого диска, запуск кода iso для повторного создания объекта было бы правильным только в очень громоздком стечении обстоятельств. @Nicolas: каковы ваши конкретные обстоятельства? - person jpinto3912; 22.06.2010
comment
Я думаю, вы смешиваете кеш процессора и другой тип кеша данных. ОП искал кеш данных, а не ЦП. - person Dolanor; 27.02.2013