Skip to content

Commit

Permalink
Fixed a problem with SimpleLRUCache that meant cache wasn't being use…
Browse files Browse the repository at this point in the history
…d correctly
  • Loading branch information
alanw authored and ustramooner committed Nov 16, 2010
1 parent 3a46357 commit c47f614
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 80 deletions.
5 changes: 3 additions & 2 deletions include/Lucene.h
Expand Up @@ -17,6 +17,7 @@
#include <vector> #include <vector>
#include <map> #include <map>
#include <set> #include <set>
#include <list>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp> #include <boost/make_shared.hpp>
#include <boost/weak_ptr.hpp> #include <boost/weak_ptr.hpp>
Expand Down Expand Up @@ -212,8 +213,8 @@ namespace Lucene
typedef Map< String, IndexReaderPtr > MapStringIndexReader; typedef Map< String, IndexReaderPtr > MapStringIndexReader;
typedef Map< TermPtr, NumPtr, luceneCompare<TermPtr> > MapTermNum; typedef Map< TermPtr, NumPtr, luceneCompare<TermPtr> > MapTermNum;


template <typename KEY, typename VALUE> class SimpleLRUCache; template < class KEY, class VALUE, class HASH = boost::hash<KEY>, class EQUAL = std::equal_to<KEY> > class SimpleLRUCache;
typedef SimpleLRUCache<TermPtr, TermInfoPtr> TermInfoCache; typedef SimpleLRUCache< TermPtr, TermInfoPtr, luceneHash<TermPtr>, luceneEquals<TermPtr> > TermInfoCache;
typedef boost::shared_ptr<TermInfoCache> TermInfoCachePtr; typedef boost::shared_ptr<TermInfoCache> TermInfoCachePtr;
} }


Expand Down
80 changes: 38 additions & 42 deletions include/SimpleLRUCache.h
Expand Up @@ -7,6 +7,7 @@
#ifndef SIMPLELRUCACHE_H #ifndef SIMPLELRUCACHE_H
#define SIMPLELRUCACHE_H #define SIMPLELRUCACHE_H


#include "LuceneObject.h"
#include <boost/multi_index_container.hpp> #include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp> #include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/identity.hpp> #include <boost/multi_index/identity.hpp>
Expand All @@ -16,21 +17,17 @@
namespace Lucene namespace Lucene
{ {
/// General purpose LRU cache map. /// General purpose LRU cache map.
/// Accessing an entry will keep the entry cached. {@link #get(const KEY&, VALUE&)} and /// Accessing an entry will keep the entry cached. {@link #get(const KEY&)} and
/// {@link #put(const KEY&, const VALUE&)} results in an access to the corresponding entry. /// {@link #put(const KEY&, const VALUE&)} results in an access to the corresponding entry.
template <typename KEY, typename VALUE> template <class KEY, class VALUE, class HASH, class EQUAL>
class SimpleLRUCache class SimpleLRUCache : public LuceneObject
{ {
public: public:
typedef std::pair<KEY, VALUE> cache_item; typedef std::pair<KEY, VALUE> key_value;
typedef boost::multi_index::multi_index_container< cache_item, boost::multi_index::indexed_by< typedef std::list< key_value > key_list;
boost::multi_index::hashed_unique< boost::multi_index::member<cache_item, KEY, &cache_item::first> >, typedef typename key_list::const_iterator const_iterator;
boost::multi_index::sequenced<> >, Allocator<cache_item> > cache_items; typedef boost::unordered_map< KEY, typename key_list::iterator, HASH, EQUAL, Allocator< std::pair<KEY, typename key_list::iterator> > > map_type;
typedef typename cache_items::iterator iterator; typedef typename map_type::const_iterator map_iterator;
typedef typename cache_items::template nth_index<0>::type hash_index;
typedef typename hash_index::iterator hash_index_iterator;
typedef typename cache_items::template nth_index<1>::type sequenced_index;
typedef typename sequenced_index::iterator sequenced_index_iterator;


SimpleLRUCache(int32_t cacheSize) SimpleLRUCache(int32_t cacheSize)
{ {
Expand All @@ -42,58 +39,57 @@ namespace Lucene
} }


protected: protected:
cache_items cacheItems;
int32_t cacheSize; int32_t cacheSize;
key_list cacheList;
map_type cacheMap;


public: public:
int32_t size() void put(const KEY& key, const VALUE& value)
{ {
return cacheItems.size(); cacheList.push_front(std::make_pair(key, value));
} cacheMap[key] = cacheList.begin();


bool get(const KEY& key, VALUE& value) if ((int32_t)cacheList.size() > cacheSize)
{ {
hash_index& hashIndex = cacheItems.template get<0>(); cacheMap.erase(cacheList.back().first);
hash_index_iterator it1 = hashIndex.find(key); cacheList.pop_back();
}
}


if (it1 == hashIndex.end()) VALUE get(const KEY& key)
return false; {
value = it1->second; map_iterator find = cacheMap.find(key);
if (find == cacheMap.end())
return VALUE();


sequenced_index& sequencedIndex = cacheItems.template get<1>(); VALUE value(find->second->second);
sequencedIndex.relocate(sequencedIndex.end(), cacheItems.template project<1>(it1)); cacheList.erase(find->second);
cacheList.push_front(std::make_pair(key, value));
cacheMap[key] = cacheList.begin();


return true; return value;
} }


void put(const KEY& key, const VALUE& value) bool contains(const KEY& key) const
{
if (cacheSize > 0 && (int32_t)cacheItems.size() >= cacheSize)
{ {
sequenced_index& sequencedIndex = cacheItems.template get<1>(); return (cacheMap.find(key) != cacheMap.end());
sequencedIndex.erase(sequencedIndex.begin());
} }


hash_index& hashIndex = cacheItems.template get<0>(); int32_t size() const
hash_index_iterator it = hashIndex.find(key); {

return cacheList.size();
if (it == hashIndex.end())
cacheItems.insert(std::make_pair(key, value));
else
hashIndex.replace(it, std::make_pair(key, value));
} }


iterator begin() const_iterator begin() const
{ {
return cacheItems.begin(); return cacheList.begin();
} }


iterator end() const_iterator end() const
{ {
return cacheItems.end(); return cacheList.end();
} }
}; };
}; };



#endif #endif
3 changes: 2 additions & 1 deletion src/core/index/TermInfosReader.cpp
Expand Up @@ -157,7 +157,8 @@ namespace Lucene
{ {
cache = resources->termInfoCache; cache = resources->termInfoCache;
// check the cache first if the term was recently looked up // check the cache first if the term was recently looked up
if (cache->get(term, ti)) ti = cache->get(term);
if (ti)
return ti; return ti;
} }


Expand Down
99 changes: 64 additions & 35 deletions src/test/util/SimpleLRUCacheTest.cpp
Expand Up @@ -7,36 +7,38 @@
#include "TestInc.h" #include "TestInc.h"
#include "LuceneTestFixture.h" #include "LuceneTestFixture.h"
#include "SimpleLRUCache.h" #include "SimpleLRUCache.h"
#include "Term.h"


using namespace Lucene; using namespace Lucene;


typedef SimpleLRUCache<int32_t, String> TestLRUCache; typedef SimpleLRUCache<int32_t, String> TestLRUSimpleCache;
typedef SimpleLRUCache< TermPtr, int32_t, luceneHash<TermPtr>, luceneEquals<TermPtr> > TestLRUTermCache;


BOOST_FIXTURE_TEST_SUITE(SimpleLRUCacheTest, LuceneTestFixture) BOOST_FIXTURE_TEST_SUITE(SimpleLRUCacheTest, LuceneTestFixture)


BOOST_AUTO_TEST_CASE(testCachePut) BOOST_AUTO_TEST_CASE(testCachePut)
{ {
TestLRUCache testCache(5); TestLRUSimpleCache testCache(5);


testCache.put(1, L"test 1"); testCache.put(1, L"test 1");
testCache.put(2, L"test 2"); testCache.put(2, L"test 2");
testCache.put(3, L"test 3"); testCache.put(3, L"test 3");
testCache.put(4, L"test 4"); testCache.put(4, L"test 4");
testCache.put(5, L"test 5"); testCache.put(5, L"test 5");
testCache.put(6, L"test 6"); // should pop off "1" testCache.put(6, L"test 6"); // this should pop off "1" because size = 5


BOOST_CHECK_EQUAL(testCache.size(), 5); BOOST_CHECK_EQUAL(testCache.size(), 5);


int32_t expectedKey = 2; int32_t expectedKey = 6;


// lru = 2, 3, 4, 5, 6 // lru = 6, 5, 4, 3, 2
for (TestLRUCache::iterator cache = testCache.begin(); cache != testCache.end(); ++cache) for (TestLRUSimpleCache::const_iterator cache = testCache.begin(); cache != testCache.end(); ++cache)
BOOST_CHECK_EQUAL(cache->first, expectedKey++); BOOST_CHECK_EQUAL(cache->first, expectedKey--);
} }


BOOST_AUTO_TEST_CASE(testCacheGet) BOOST_AUTO_TEST_CASE(testCacheGet)
{ {
TestLRUCache testCache(5); TestLRUSimpleCache testCache(5);


testCache.put(1, L"test 1"); testCache.put(1, L"test 1");
testCache.put(2, L"test 2"); testCache.put(2, L"test 2");
Expand All @@ -45,32 +47,27 @@ BOOST_AUTO_TEST_CASE(testCacheGet)
testCache.put(5, L"test 5"); testCache.put(5, L"test 5");


BOOST_CHECK_EQUAL(testCache.size(), 5); BOOST_CHECK_EQUAL(testCache.size(), 5);

BOOST_CHECK_EQUAL(testCache.get(2), L"test 2");
String val; BOOST_CHECK_EQUAL(testCache.get(3), L"test 3");
BOOST_CHECK(testCache.get(2, val));
BOOST_CHECK_EQUAL(val, L"test 2");

BOOST_CHECK(testCache.get(3, val));
BOOST_CHECK_EQUAL(val, L"test 3");
} }


BOOST_AUTO_TEST_CASE(testCacheGetNotExists) BOOST_AUTO_TEST_CASE(testCacheExists)
{ {
TestLRUCache testCache(5); TestLRUSimpleCache testCache(5);


testCache.put(1, L"test 1"); testCache.put(1, L"test 1");
testCache.put(2, L"test 2"); testCache.put(2, L"test 2");
testCache.put(3, L"test 3"); testCache.put(3, L"test 3");
testCache.put(4, L"test 4"); testCache.put(4, L"test 4");
testCache.put(5, L"test 5"); testCache.put(5, L"test 5");


String val; BOOST_CHECK(testCache.contains(1));
BOOST_CHECK(!testCache.get(7, val)); // doesn't exist BOOST_CHECK(!testCache.contains(7));
} }


BOOST_AUTO_TEST_CASE(testCachePutGet) BOOST_AUTO_TEST_CASE(testCachePutGet)
{ {
TestLRUCache testCache(5); TestLRUSimpleCache testCache(5);


testCache.put(1, L"test 1"); testCache.put(1, L"test 1");
testCache.put(2, L"test 2"); testCache.put(2, L"test 2");
Expand All @@ -80,56 +77,88 @@ BOOST_AUTO_TEST_CASE(testCachePutGet)


BOOST_CHECK_EQUAL(testCache.size(), 5); BOOST_CHECK_EQUAL(testCache.size(), 5);


String val; BOOST_CHECK_EQUAL(testCache.get(2), L"test 2");
BOOST_CHECK(testCache.get(2, val)); BOOST_CHECK_EQUAL(testCache.get(3), L"test 3");
BOOST_CHECK(testCache.get(3, val));


testCache.put(6, L"test 6"); testCache.put(6, L"test 6");
testCache.put(7, L"test 7"); testCache.put(7, L"test 7");
testCache.put(8, L"test 8"); testCache.put(8, L"test 8");


std::vector<int32_t> expectedLRU; Collection<int32_t> expectedLRU = Collection<int32_t>::newInstance();
for (TestLRUCache::iterator cache = testCache.begin(); cache != testCache.end(); ++cache) for (TestLRUSimpleCache::const_iterator cache = testCache.begin(); cache != testCache.end(); ++cache)
expectedLRU.push_back(cache->first); expectedLRU.add(cache->first);


BOOST_CHECK_EQUAL(expectedLRU.size(), 5); BOOST_CHECK_EQUAL(expectedLRU.size(), 5);


// lru = 5, 2, 3, 6, 7 // lru = 8, 7, 6, 3, 2
BOOST_CHECK_EQUAL(expectedLRU[0], 2); BOOST_CHECK_EQUAL(expectedLRU[0], 8);
BOOST_CHECK_EQUAL(expectedLRU[1], 3); BOOST_CHECK_EQUAL(expectedLRU[1], 7);
BOOST_CHECK_EQUAL(expectedLRU[2], 6); BOOST_CHECK_EQUAL(expectedLRU[2], 6);
BOOST_CHECK_EQUAL(expectedLRU[3], 7); BOOST_CHECK_EQUAL(expectedLRU[3], 3);
BOOST_CHECK_EQUAL(expectedLRU[4], 8); BOOST_CHECK_EQUAL(expectedLRU[4], 2);
} }


BOOST_AUTO_TEST_CASE(testRandomAccess) BOOST_AUTO_TEST_CASE(testRandomAccess)
{ {
const int32_t n = 100; const int32_t n = 100;
TestLRUCache cache(n); TestLRUSimpleCache cache(n);
String value = L"test"; String value = L"test";


for (int32_t i = 0; i < n; ++i) for (int32_t i = 0; i < n; ++i)
cache.put(i, value); cache.put(i, value);


// access every 2nd item in cache // access every 2nd item in cache
for (int32_t i = 0; i < n; i += 2) for (int32_t i = 0; i < n; i += 2)
BOOST_CHECK(cache.get(i, value)); BOOST_CHECK_NE(cache.get(i), L"");


// add n/2 elements to cache, the ones that weren't touched in the previous loop should now be thrown away // add n/2 elements to cache, the ones that weren't touched in the previous loop should now be thrown away
for (int32_t i = n; i < n + (n / 2); ++i) for (int32_t i = n; i < n + (n / 2); ++i)
cache.put(i, value); cache.put(i, value);


// access every 4th item in cache // access every 4th item in cache
for (int32_t i = 0; i < n; i += 4) for (int32_t i = 0; i < n; i += 4)
BOOST_CHECK(cache.get(i, value)); BOOST_CHECK_NE(cache.get(i), L"");


// add 3/4n elements to cache, the ones that weren't touched in the previous loops should now be thrown away // add 3/4n elements to cache, the ones that weren't touched in the previous loops should now be thrown away
for (int32_t i = n; i < n + (n * 3 / 4); ++i) for (int32_t i = n; i < n + (n * 3 / 4); ++i)
cache.put(i, value); cache.put(i, value);


// access every 4th item in cache // access every 4th item in cache
for (int32_t i = 0; i < n; i += 4) for (int32_t i = 0; i < n; i += 4)
BOOST_CHECK(cache.get(i, value)); BOOST_CHECK_NE(cache.get(i), L"");
}

BOOST_AUTO_TEST_CASE(testTermCache)
{
TestLRUTermCache testCache(5);

testCache.put(newLucene<Term>(L"field1", L"text1"), 1);
testCache.put(newLucene<Term>(L"field2", L"text2"), 2);
testCache.put(newLucene<Term>(L"field3", L"text3"), 3);
testCache.put(newLucene<Term>(L"field4", L"text4"), 4);
testCache.put(newLucene<Term>(L"field5", L"text5"), 5);

BOOST_CHECK_EQUAL(testCache.size(), 5);

BOOST_CHECK_EQUAL(testCache.get(newLucene<Term>(L"field2", L"text2")), 2);
BOOST_CHECK_EQUAL(testCache.get(newLucene<Term>(L"field3", L"text3")), 3);

testCache.put(newLucene<Term>(L"field6", L"text6"), 6);
testCache.put(newLucene<Term>(L"field7", L"text7"), 7);
testCache.put(newLucene<Term>(L"field8", L"text8"), 8);

Collection<TermPtr> expectedLRU = Collection<TermPtr>::newInstance();
for (TestLRUTermCache::const_iterator cache = testCache.begin(); cache != testCache.end(); ++cache)
expectedLRU.add(cache->first);

BOOST_CHECK_EQUAL(expectedLRU.size(), 5);

// lru = field8, field7, field6, field3, field2
BOOST_CHECK(expectedLRU[0]->equals(newLucene<Term>(L"field8", L"text8")));
BOOST_CHECK(expectedLRU[1]->equals(newLucene<Term>(L"field7", L"text7")));
BOOST_CHECK(expectedLRU[2]->equals(newLucene<Term>(L"field6", L"text6")));
BOOST_CHECK(expectedLRU[3]->equals(newLucene<Term>(L"field3", L"text3")));
BOOST_CHECK(expectedLRU[4]->equals(newLucene<Term>(L"field2", L"text2")));
} }


BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

0 comments on commit c47f614

Please sign in to comment.