LRU cache, предложения и замечания
От: kaa.python Ниоткуда РСДН профессионально мёртв и завален ватой.
Дата: 14.04.19 02:17
Оценка:
Для собственных нужд накидал LRU-cache, который вроде делает то, что мне нужно и так как мне нужно. Но так как я не часто пишу на C++ именно что библиотеки, а в то же время написание библиотек вообще сильно оттедельный навык, у меня есть некоторые сомнения в том, что все реализовано в соответствии с современными веяниями. Сам кеш тут, минимальный поддерживаемый стандарт C++11. Буду благодарен за дополнения и замечания

  ну и еще вот сюда продублирую
#ifndef CACHEW_LRU_CACHE_HPP
#define CACHEW_LRU_CACHE_HPP

#include <algorithm>
#include <list>
#include <unordered_map>

namespace cachew
{

template <class _Key, class _Tp>
class lru_cache
{
public:
    using key_type   = _Key;
    using value_type = _Tp;

    using kv_pair = std::pair<key_type, value_type>;

    template <class _Storage = std::list<kv_pair>>
    class lru_iterator
    {
    private:
        using it_type        = lru_iterator<_Storage>;
        using parent_it_type = typename _Storage::const_iterator;

        parent_it_type _it;

    public:
        lru_iterator() = default;

        lru_iterator( const it_type &it )
            : _it( it._it )
        {
        }

        explicit lru_iterator( const parent_it_type &it )
            : _it( it )
        {
        }

        bool operator==( const it_type &it ) const
        {
            return it._it == _it;
        }

        bool operator!=( const it_type &it ) const
        {
            return it._it != _it;
        }

        const it_type &operator++()
        {
            ++_it;
            return *this;
        }

        const it_type operator++( int )
        {
            it_type res( *this );
            ++( *this );
            return res;
        }

        const value_type &operator*() const
        {
            return ( _it->second );
        }

        const value_type *operator->() const
        {
            return &( _it->second );
        }
    };

    using lru_list = std::list<std::pair<key_type, value_type>>;
    using lru_map  = std::unordered_map<key_type, typename lru_list::iterator>;

    using iterator = lru_iterator<lru_list>;

    friend bool operator!=( const lru_cache &lhs, const lru_cache &rhs )
    {
        return !( rhs == lhs );
    }

    explicit lru_cache( size_t capacity ) noexcept
        : _capacity( capacity )
    {
    }

    iterator get( const key_type &key )
    {
        auto it = _map.find( key );
        if( it == _map.end() )
        {
            return iterator( _list.end() );
        }
        _list.splice( _list.begin(), _list, it->second );

        return iterator( it->second );
    }

    template <class _PutK, class _PutT>
    void put( _PutK &&key, _PutT &&value )
    {
        auto it = _map.find( key );
        if( it != _map.end() )
        {
            _list.splice( _list.begin(), _list, it->second );
            it->second->second = value;
        }
        if( _map.size() == _capacity )
        {
            auto to_del = _list.back().first;
            _list.pop_back();
            _map.erase( to_del );
        }
        _list.emplace_front( std::forward<key_type>( key ),
                             std::forward<value_type>( value ) );
        _map.emplace( std::forward<key_type>( key ), _list.begin() );
    }

    size_t capacity() const
    {
        return _capacity;
    }

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

    iterator begin() const noexcept
    {
        return iterator( _list.begin() );
    }

    iterator end() const noexcept
    {
        return iterator( _list.end() );
    }

private:
    lru_list _list;
    lru_map  _map;
    size_t   _capacity;
};

} // namespace cachew

#endif // CACHEW_LRU_CACHE_HPP
Отредактировано 14.04.2019 2:18 kaa.python . Предыдущая версия .
c++11 codereview lru-cache
Re: LRU cache, предложения и замечания
От: niXman Ниоткуда https://github.com/niXman
Дата: 14.04.19 07:45
Оценка: 8 (1)
первое — контейнеры заменить на интрузивные.
второе — почему в 'get()' используется 'key_type', а в 'put()' шаблонный параметр '_PutK'?
третье — раз уж в 'put()' у нас в качестве ключа используется шаблонный параметр '_PutK', почему ниже используется 'forward()' с параметром 'key_type'? по поводу 'value_type' вопрос тот же.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Отредактировано 14.04.2019 7:50 niXman . Предыдущая версия . Еще …
Отредактировано 14.04.2019 7:48 niXman . Предыдущая версия .
Отредактировано 14.04.2019 7:48 niXman . Предыдущая версия .
Re[2]: LRU cache, предложения и замечания
От: kaa.python Ниоткуда РСДН профессионально мёртв и завален ватой.
Дата: 14.04.19 08:04
Оценка:
Здравствуйте, niXman, Вы писали:

X>первое — контейнеры заменить на интрузивные.


Не затруднит обрисовать плюсы? Мне хотелось получить поведение схожее с STL контейнерами в данном случае.

X>второе — почему в 'get()' используется 'key_type', а в 'put()' шаблонный параметр '_PutK'?


потому-что perfect forwarding иначе работать не будет

X>третье — раз уж в 'put()' у нас в качестве ключа используется шаблонный параметр '_PutK', почему ниже используется 'forward()' с параметром 'key_type'? по поводу 'value_type' вопрос тот же.


а вот это уже очень хорошее замечание, спасибо!
Re[3]: LRU cache, предложения и замечания
От: niXman Ниоткуда https://github.com/niXman
Дата: 14.04.19 08:24
Оценка:
Здравствуйте, kaa.python, Вы писали:

KP>Не затруднит обрисовать плюсы? Мне хотелось получить поведение схожее с STL контейнерами в данном случае.


нет дополнительных new/delete.



X>>второе — почему в 'get()' используется 'key_type', а в 'put()' шаблонный параметр '_PutK'?

KP>потому-что perfect forwarding иначе работать не будет

я к тому, что сделай и 'get()' с шаблонным пареметром типа ключа.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re: LRU cache, предложения и замечания
От: rg45 СССР  
Дата: 15.04.19 22:50
Оценка: 8 (1)
Здравствуйте, kaa.python, Вы писали:

KP>Для собственных нужд накидал LRU-cache, который вроде делает то, что мне нужно и так как мне нужно. Но так как я не часто пишу на C++ именно что библиотеки, а в то же время написание библиотек вообще сильно оттедельный навык, у меня есть некоторые сомнения в том, что все реализовано в соответствии с современными веяниями. Сам кеш тут, минимальный поддерживаемый стандарт C++11. Буду благодарен за дополнения и замечания


        _list.emplace_front( std::forward<key_type>( key ),
                             std::forward<value_type>( value ) );
        _map.emplace( std::forward<key_type>( key ), _list.begin() );


Сильно вдумчиво не изучал, но глаз зацепился вот за этот фрагмент кода: ты повторно форвардишь key. Это не есть хорошо, поскольку после первого же раза от объекта может остаться один только панцирь. Ну и еще, я бы в подобном случае для реализации итератора использовал бы boost::iterator_adaptor — писанины меньше и результат надежнее.

P.S. Я не очень удачно выразился. Проблема, собственно, не в повторном форвардинге, а в том, что после форвардинга объект еще как-то используется.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 15.04.2019 23:00 rg45 . Предыдущая версия .
Re[2]: LRU cache, предложения и замечания
От: kaa.python Ниоткуда РСДН профессионально мёртв и завален ватой.
Дата: 16.04.19 01:47
Оценка:
Здравствуйте, rg45, Вы писали:

R>Сильно вдумчиво не изучал, но глаз зацепился вот за этот фрагмент кода: ты повторно форвардишь key. Это не есть хорошо, поскольку после первого же раза от объекта может остаться один только панцирь.


Да, спасибо. Это уже было отловлено и поправлено на GitHub, тут забыл исправить

R> Ну и еще, я бы в подобном случае для реализации итератора использовал бы boost::iterator_adaptor — писанины меньше и результат надежнее.


Я знаю про него, но хотелось бы получить реализацию без каких бы то ни было внешних зависимостей.
Re: LRU cache, предложения и замечания
От: ArtDenis Россия  
Дата: 23.04.19 07:04
Оценка: -4
Здравствуйте, kaa.python, Вы писали:

KP>Для собственных нужд накидал LRU-cache, который вроде делает то, что мне нужно и так как мне нужно.


Данная структура не является кэшем. Совсем. Я вижу лишь контейнер с ограниченным размером, который удаляет элементы с самым ранним доступом при помещении новых элементов чтобы не превысить установленный размер. Но это не кэш совсем. Такую структуру можно применять, например, для списка часто используемых файлов, но не для кэширования.

Теперь что такое кэш. Это — промежуточная структура данных для ускорения доступа к чему-либо за счёт более быстрого доступа или локального хранения часто используемых данных. Кэш может активно взаимодействовать с основными данными, для ускорения которых он предназначен, забирая новые порции при чтении, если их нету в кэше, или записывая обратно, если они вытесняются из кэша. Пример этого — кэш файловой системы или кэш данных БД, а также кэши ОЗУ в CPU.

Так что чтобы это превратить в кэш, надо как минимум добавить возможность взаимодействовать с основными данными.
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.