From c057fb882bcacb494955ac8e7be6fe2c42d5fa9e Mon Sep 17 00:00:00 2001 From: Davi Reis Date: Wed, 7 Mar 2012 03:10:29 -0500 Subject: [PATCH 01/16] Iterator game. --- cxxmph/bm_map.cc | 2 -- cxxmph/mph_map.h | 61 +++++++++++++++++++++++++++++++++--------------- 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/cxxmph/bm_map.cc b/cxxmph/bm_map.cc index 8334604..5c0f7a4 100644 --- a/cxxmph/bm_map.cc +++ b/cxxmph/bm_map.cc @@ -87,14 +87,12 @@ using namespace cxxmph; int main(int argc, char** argv) { srandom(4); - /* Benchmark::Register(new BM_CreateUrls>("URLS100k")); Benchmark::Register(new BM_CreateUrls>("URLS100k")); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0)); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0)); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0.9)); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0.9)); -*/ Benchmark::Register(new BM_SearchUint64>); Benchmark::Register(new BM_SearchUint64>); Benchmark::RunAll(); diff --git a/cxxmph/mph_map.h b/cxxmph/mph_map.h index 7541c45..6a09d21 100644 --- a/cxxmph/mph_map.h +++ b/cxxmph/mph_map.h @@ -41,8 +41,18 @@ class mph_map { typedef typename std::vector::const_reference const_reference; typedef typename std::vector::size_type size_type; typedef typename std::vector::difference_type difference_type; - typedef typename std::vector::iterator iterator; - typedef typename std::vector::const_iterator const_iterator; + + template + struct indirect_iterator : public typename slack_type::iterator { + indirect_iterator(T* v, iterator it) : iterator(it), v_(v) { } + const typename iterator::value_type::first_type& operator*() const { + return v->begin() + (this->iterator::operator*())->second; + } + }; + + + typedef indirect_iterator, slack_type>::iterator iterator; + typedef indirect_iterator, slack_type>::const_iterator const_iterator; // For making macros simpler. typedef void void_type; @@ -69,7 +79,7 @@ class mph_map { data_type& operator[](const key_type &k); const data_type& operator[](const key_type &k) const; - size_type bucket_count() const { return size(); } + size_type bucket_count() const { return index_.perfect_hash_size() + slack_.bucket_count(); } // FIXME: not sure if this has the semantics I want void rehash(size_type nbuckets /*ignored*/) { pack(); } @@ -80,7 +90,7 @@ class mph_map { template struct iterator_first : public iterator { iterator_first(iterator it) : iterator(it) { } - const typename iterator::value_type::first_type& operator*() const { + const typename iterator::value_type::first_type& operator*() const { return this->iterator::operator*().first; } }; @@ -90,6 +100,11 @@ class mph_map { return iterator_first(it); } + template + indirect_iterator make_indirect_iterator(T* v, iterator it) { + return indirect_iterator(v, it); + } + void pack(); std::vector values_; SimpleMPHIndex::hash_function> index_; @@ -113,31 +128,39 @@ MPH_MAP_TMPL_SPEC MPH_MAP_CLASS_SPEC::~mph_map() { MPH_MAP_METHOD_DECL(insert_return_type, insert)(const value_type& x) { iterator it = find(x.first); if (it != end()) return make_pair(it, false); + should_pack = false; + if (values_.capacity() == values_.size() && values_.size() > 256) { + should_pack = true; + } values_.push_back(x); slack_.insert(make_pair(x.first, values_.size() - 1)); - if (slack_.size() == index_.size() || - (slack_.size() >= 256 && index_.size() == 0)) { - pack(); - } + if (should_pack) pack(); it = find(x.first); return make_pair(it, true); } MPH_MAP_METHOD_DECL(void_type, pack)() { if (values_.empty()) return; - slack_type().swap(slack_); bool success = index_.Reset( - make_iterator_first(values_.begin()), - make_iterator_first(values_.end())); + make_iterator_first(slack_.begin())), + make_iterator_first(slack_.end()))); assert(success); - std::vector new_values(values_.size()); + std::vector new_values(index_.size()); for (const_iterator it = values_.begin(), end = values_.end(); it != end; ++it) { - size_type id = index_.index(it->first); + size_type id = index_.index((*it)->first); assert(id < new_values.size()); new_values[id] = *it; } values_.swap(new_values); + std::vector new_values_pointer( + index_.perfect_hash_size());; + for (size_type i = 0; i < values_.size(); ++i) { + size_type id = index_.perfect_hash(values_[i].first); + assert(id < new_values_pointer.size()); + new_values_pointer[id] = i; + } + values_pointer_.swap(new_values_pointer); } MPH_MAP_METHOD_DECL(iterator, begin)() { return values_.begin(); } @@ -169,25 +192,25 @@ MPH_MAP_METHOD_DECL(const_iterator, find)(const key_type& k) const { if (it != slack_.end()) return values_.begin() + it->second; } if (__builtin_expect(index_.size() == 0, 0)) return end(); - const_iterator it = values_.begin() + index_.index(k); + const_iterator it = values_.begin() + values_pointer_[index_.perfect_hash(k)]; if (__builtin_expect(equal_(k, it->first), 1)) return it; return end(); } MPH_MAP_METHOD_DECL(iterator, find)(const key_type& k) { - if (!slack_.empty()) { + if (__builtin_expect(!slack_.empty(), 0)) { typename slack_type::const_iterator it = slack_.find(k); if (it != slack_.end()) return values_.begin() + it->second; } - if (index_.size() == 0) return end(); - iterator it = values_.begin() + index_.index(k); - if (equal_(it->first, k)) return it; + if (__builtin_expect(index_.size() == 0, 0)) return end(); + iterator it = values_.begin() + values_pointer_[index_.perfect_hash(k)]; + if (__builtin_expect(equal_(k, it->first), 1)) return it; return end(); } MPH_MAP_METHOD_DECL(my_int32_t, index)(const key_type& k) const { if (index_.size() == 0) return -1; - return index_.index(k); + return index_.perfect_hash(k); } MPH_MAP_METHOD_DECL(data_type&, operator[])(const key_type& k) { From 238e384367e635d2fdaf61904446348086351d75 Mon Sep 17 00:00:00 2001 From: Davi Reis Date: Sun, 11 Mar 2012 23:21:18 -0300 Subject: [PATCH 02/16] Compiles, still need to fix size tracking. --- cxxmph/Makefile.am | 7 ++- cxxmph/bm_index.cc | 4 +- cxxmph/bm_map.cc | 3 +- cxxmph/cxxmph.cc | 4 +- cxxmph/hollow_iterator.h | 69 +++++++++++++++++++++++++ cxxmph/hollow_iterator_test.cc | 35 +++++++++++++ cxxmph/mph_index.h | 11 ++-- cxxmph/mph_index_test.cc | 2 +- cxxmph/mph_map.h | 92 +++++++++++++++++----------------- 9 files changed, 167 insertions(+), 60 deletions(-) create mode 100644 cxxmph/hollow_iterator.h create mode 100644 cxxmph/hollow_iterator_test.cc diff --git a/cxxmph/Makefile.am b/cxxmph/Makefile.am index 2e57a18..cec2073 100644 --- a/cxxmph/Makefile.am +++ b/cxxmph/Makefile.am @@ -1,12 +1,12 @@ TESTS = $(check_PROGRAMS) -check_PROGRAMS = mph_map_test mph_index_test trigraph_test +check_PROGRAMS = hollow_iterator_test mph_map_test mph_index_test trigraph_test noinst_PROGRAMS = bm_index bm_map bin_PROGRAMS = cxxmph lib_LTLIBRARIES = libcxxmph.la libcxxmph_la_SOURCES = MurmurHash2.h trigragh.h trigraph.cc mph_index.h mph_index.cc seeded_hash.h stringpiece.h benchmark.h benchmark.cc libcxxmph_la_LDFLAGS = -version-info 0:0:0 cxxmph_includedir = $(includedir)/cxxmph/ -cxxmph_include_HEADERS = mph_map.h mph_index.h MurmurHash2.h trigraph.h seeded_hash.h stringpiece.h +cxxmph_include_HEADERS = mph_map.h mph_index.h MurmurHash2.h trigraph.h seeded_hash.h stringpiece.h hollow_iterator.h mph_map_test_LDADD = libcxxmph.la mph_map_test_SOURCES = mph_map_test.cc @@ -25,3 +25,6 @@ bm_map_SOURCES = bm_common.cc bm_map.cc cxxmph_LDADD = libcxxmph.la cxxmph_SOURCES = cxxmph.cc + +hollow_iterator_test_SOURCES = hollow_iterator_test.cc + diff --git a/cxxmph/bm_index.cc b/cxxmph/bm_index.cc index 924231c..d1cbc00 100644 --- a/cxxmph/bm_index.cc +++ b/cxxmph/bm_index.cc @@ -21,7 +21,7 @@ class BM_MPHIndexCreate : public UrlsBenchmark { protected: virtual void Run() { SimpleMPHIndex index; - index.Reset(urls_.begin(), urls_.end()); + index.Reset(urls_.begin(), urls_.end(), urls_.size()); } }; @@ -53,7 +53,7 @@ class BM_MPHIndexSearch : public SearchUrlsBenchmark { protected: virtual bool SetUp () { if (!SearchUrlsBenchmark::SetUp()) return false; - index_.Reset(urls_.begin(), urls_.end()); + index_.Reset(urls_.begin(), urls_.end(), urls_.size()); return true; } SimpleMPHIndex index_; diff --git a/cxxmph/bm_map.cc b/cxxmph/bm_map.cc index 5c0f7a4..e381976 100644 --- a/cxxmph/bm_map.cc +++ b/cxxmph/bm_map.cc @@ -13,7 +13,8 @@ namespace cxxmph { template const T* myfind(const MapType& mymap, const T& k) { auto it = mymap.find(k); - if (it == mymap.end()) return NULL; + auto end = mymap.end(); + if (it == end) return NULL; return &it->second; } diff --git a/cxxmph/cxxmph.cc b/cxxmph/cxxmph.cc index 68bb23e..e9bffd0 100644 --- a/cxxmph/cxxmph.cc +++ b/cxxmph/cxxmph.cc @@ -63,8 +63,8 @@ int main(int argc, char** argv) { for (int i = 0; i < keys.size(); ++i) table[keys[i]] = keys[i]; mph_map::const_iterator it = table.begin(); mph_map::const_iterator end = table.end(); - for (; it != end; ++it) { - cout << (it - table.begin()) << ": " << it->first + for (int i = 0; it != end; ++it, ++i) { + cout << i << ": " << it->first <<" -> " << it->second << endl; } } diff --git a/cxxmph/hollow_iterator.h b/cxxmph/hollow_iterator.h new file mode 100644 index 0000000..bbb34bf --- /dev/null +++ b/cxxmph/hollow_iterator.h @@ -0,0 +1,69 @@ +#ifndef __CXXMPH_HOLLOW_ITERATOR_H__ +#define __CXXMPH_HOLLOW_ITERATOR_H__ + +#include + +namespace cxxmph { + +template +struct hollow_iterator_base + : public std::iterator { + typedef presence_type presence; + typedef container_type container; + typedef iterator_type iterator; + typedef hollow_iterator_base& self_reference; + typedef typename iterator::reference reference; + typedef typename iterator::pointer pointer; + + hollow_iterator_base(container* c, presence* p, iterator it) + : c_(c), p_(p), it_(it) { find_present(); } + self_reference operator++() { + ++it_; find_present(); + } + reference operator*() { return *it_; } + pointer operator->() { return &(*it_); } + + // TODO find syntax to make this less permissible at compile time + template + bool operator==(const T& rhs) { return rhs.it_ == this->it_; } + template + bool operator!=(const T& rhs) { return rhs.it_ != this->it_; } + + public: // TODO find syntax to make this friend of const iterator + void find_present() { + while (it_ != c_->end() && !((*p_)[it_-c_->begin()])) ++it_; + } + container* c_; + presence* p_; + iterator it_; +}; + +template +struct hollow_iterator : public hollow_iterator_base< + container_type, std::vector, typename container_type::iterator> { + typedef hollow_iterator_base< + container_type, std::vector, typename container_type::iterator> parent_class; + hollow_iterator(typename parent_class::container* c, + typename parent_class::presence* p, + typename parent_class::iterator it) + : parent_class(c, p, it) { } +}; + +template +struct hollow_const_iterator : public hollow_iterator_base< + const container_type, const std::vector, typename container_type::const_iterator> { + typedef hollow_iterator_base< + const container_type, const std::vector, typename container_type::const_iterator> parent_class; + typedef hollow_const_iterator self_type; + typedef hollow_iterator non_const_type; + hollow_const_iterator(non_const_type rhs) : parent_class(rhs.c_, rhs.p_, typename container_type::const_iterator(rhs.it_)) { } + hollow_const_iterator(const typename parent_class::container* c, + const typename parent_class::presence* p, + typename parent_class::iterator it) + : parent_class(c, p, it) { } +}; + +} // namespace cxxmph + +#endif // __CXXMPH_HOLLOW_ITERATOR_H__ diff --git a/cxxmph/hollow_iterator_test.cc b/cxxmph/hollow_iterator_test.cc new file mode 100644 index 0000000..201b748 --- /dev/null +++ b/cxxmph/hollow_iterator_test.cc @@ -0,0 +1,35 @@ +#include +#include +#include + +#include "hollow_iterator.h" + +using std::vector; +using cxxmph::hollow_iterator; +using cxxmph::hollow_const_iterator; + +int main(int argc, char** argv) { + vector v; + vector p; + for (int i = 0; i < 100; ++i) { + v.push_back(i); + p.push_back(i % 2 == 0); + } + auto begin = hollow_iterator>(&v, &p, v.begin()); + auto end = hollow_iterator>(&v, &p, v.end()); + for (auto it = begin; it != end; ++it) { + if (((*it) % 2) != 0) exit(-1); + } + hollow_const_iterator> const_begin(begin); + hollow_const_iterator> const_end(end); + for (auto it = const_begin; it != const_end; ++it) { + if (((*it) % 2) != 0) exit(-1); + } + vector::iterator vit1 = v.begin(); + vector::const_iterator vit2 = v.begin(); + if (vit1 != vit2) exit(-1); + auto it1 = hollow_iterator>(&v, &p, v.begin()); + auto it2 = hollow_const_iterator>(&v, &p, v.begin()); + if (it1 != it2) exit(-1); +} + diff --git a/cxxmph/mph_index.h b/cxxmph/mph_index.h index d2e4a01..ad5bc6e 100644 --- a/cxxmph/mph_index.h +++ b/cxxmph/mph_index.h @@ -48,7 +48,7 @@ class MPHIndex { ~MPHIndex(); template - bool Reset(ForwardIterator begin, ForwardIterator end); + bool Reset(ForwardIterator begin, ForwardIterator end, uint32_t size); template // must agree with Reset // Get a unique identifier for k, in the range [0;size()). If x wasn't part // of the input in the last Reset call, returns a random value. @@ -120,12 +120,13 @@ class MPHIndex { // Template method needs to go in the header file. template -bool MPHIndex::Reset(ForwardIterator begin, ForwardIterator end) { +bool MPHIndex::Reset( + ForwardIterator begin, ForwardIterator end, uint32_t size) { if (end == begin) { clear(); return true; } - m_ = end - begin; + m_ = size; r_ = static_cast(ceil((c_*m_)/3)); if ((r_ % 2) == 0) r_ += 1; n_ = 3*r_; @@ -204,8 +205,8 @@ template >::hash class SimpleMPHIndex : public MPHIndex { public: template - bool Reset(ForwardIterator begin, ForwardIterator end) { - return MPHIndex::Reset(begin, end); + bool Reset(ForwardIterator begin, ForwardIterator end, uint32_t size) { + return MPHIndex::Reset(begin, end, size); } uint32_t index(const Key& key) const { return MPHIndex::index(key); } uint32_t perfect_hash(const Key& key) const { return MPHIndex::perfect_hash(key); } diff --git a/cxxmph/mph_index_test.cc b/cxxmph/mph_index_test.cc index 7a7d036..70e01bc 100644 --- a/cxxmph/mph_index_test.cc +++ b/cxxmph/mph_index_test.cc @@ -24,7 +24,7 @@ int main(int argc, char** argv) { keys.push_back("algume"); SimpleMPHIndex mph_index; - if (!mph_index.Reset(keys.begin(), keys.end())) { exit(-1); } + if (!mph_index.Reset(keys.begin(), keys.end(), keys.size())) { exit(-1); } vector ids; for (vector::size_type i = 0; i < keys.size(); ++i) { ids.push_back(mph_index.index(keys[i])); diff --git a/cxxmph/mph_map.h b/cxxmph/mph_map.h index 6a09d21..7687ba5 100644 --- a/cxxmph/mph_map.h +++ b/cxxmph/mph_map.h @@ -14,6 +14,7 @@ #include "MurmurHash2.h" #include "mph_index.h" +#include "hollow_iterator.h" namespace cxxmph { @@ -42,17 +43,8 @@ class mph_map { typedef typename std::vector::size_type size_type; typedef typename std::vector::difference_type difference_type; - template - struct indirect_iterator : public typename slack_type::iterator { - indirect_iterator(T* v, iterator it) : iterator(it), v_(v) { } - const typename iterator::value_type::first_type& operator*() const { - return v->begin() + (this->iterator::operator*())->second; - } - }; - - - typedef indirect_iterator, slack_type>::iterator iterator; - typedef indirect_iterator, slack_type>::const_iterator const_iterator; + typedef hollow_iterator> iterator; + typedef hollow_const_iterator> const_iterator; // For making macros simpler. typedef void void_type; @@ -90,7 +82,7 @@ class mph_map { template struct iterator_first : public iterator { iterator_first(iterator it) : iterator(it) { } - const typename iterator::value_type::first_type& operator*() const { + const typename iterator::value_type::first_type& operator*() { return this->iterator::operator*().first; } }; @@ -100,25 +92,29 @@ class mph_map { return iterator_first(it); } - template - indirect_iterator make_indirect_iterator(T* v, iterator it) { - return indirect_iterator(v, it); + iterator make_iterator(typename std::vector::iterator it) { + return hollow_iterator>(&values_, &present_, it); + } + const_iterator make_iterator(typename std::vector::const_iterator it) const { + return hollow_const_iterator>(&values_, &present_, it); } void pack(); std::vector values_; + std::vector present_; SimpleMPHIndex::hash_function> index_; // TODO(davi) optimize slack to no hold a copy of the key typedef unordered_map slack_type; slack_type slack_; + size_type size_; }; MPH_MAP_TMPL_SPEC bool operator==(const MPH_MAP_CLASS_SPEC& lhs, const MPH_MAP_CLASS_SPEC& rhs) { - return lhs.values_ == rhs.values_; + return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); } -MPH_MAP_TMPL_SPEC MPH_MAP_CLASS_SPEC::mph_map() { +MPH_MAP_TMPL_SPEC MPH_MAP_CLASS_SPEC::mph_map() : size_(0) { pack(); } @@ -126,13 +122,15 @@ MPH_MAP_TMPL_SPEC MPH_MAP_CLASS_SPEC::~mph_map() { } MPH_MAP_METHOD_DECL(insert_return_type, insert)(const value_type& x) { - iterator it = find(x.first); - if (it != end()) return make_pair(it, false); - should_pack = false; + auto it = find(x.first); + auto it_end = end(); + if (it != it_end) return make_pair(it, false); + bool should_pack = false; if (values_.capacity() == values_.size() && values_.size() > 256) { should_pack = true; } values_.push_back(x); + present_.push_back(true); slack_.insert(make_pair(x.first, values_.size() - 1)); if (should_pack) pack(); it = find(x.first); @@ -142,43 +140,39 @@ MPH_MAP_METHOD_DECL(insert_return_type, insert)(const value_type& x) { MPH_MAP_METHOD_DECL(void_type, pack)() { if (values_.empty()) return; bool success = index_.Reset( - make_iterator_first(slack_.begin())), - make_iterator_first(slack_.end()))); + make_iterator_first(begin()), + make_iterator_first(end()), size_); assert(success); std::vector new_values(index_.size()); - for (const_iterator it = values_.begin(), end = values_.end(); - it != end; ++it) { - size_type id = index_.index((*it)->first); + std::vector new_present(index_.size(), false); + for (iterator it(begin()), it_end(end()); it != it_end; ++it) { + size_type id = index_.index(it->first); assert(id < new_values.size()); new_values[id] = *it; + new_present[id] = true; } values_.swap(new_values); - std::vector new_values_pointer( - index_.perfect_hash_size());; - for (size_type i = 0; i < values_.size(); ++i) { - size_type id = index_.perfect_hash(values_[i].first); - assert(id < new_values_pointer.size()); - new_values_pointer[id] = i; - } - values_pointer_.swap(new_values_pointer); + present_.swap(new_present); + slack_type().swap(slack_); } -MPH_MAP_METHOD_DECL(iterator, begin)() { return values_.begin(); } -MPH_MAP_METHOD_DECL(iterator, end)() { return values_.end(); } -MPH_MAP_METHOD_DECL(const_iterator, begin)() const { return values_.begin(); } -MPH_MAP_METHOD_DECL(const_iterator, end)() const { return values_.end(); } -MPH_MAP_METHOD_DECL(bool_type, empty)() const { return values_.empty(); } -MPH_MAP_METHOD_DECL(size_type, size)() const { return values_.size(); } +MPH_MAP_METHOD_DECL(iterator, begin)() { return make_iterator(values_.begin()); } +MPH_MAP_METHOD_DECL(iterator, end)() { return make_iterator(values_.end()); } +MPH_MAP_METHOD_DECL(const_iterator, begin)() const { return make_iterator(values_.begin()); } +MPH_MAP_METHOD_DECL(const_iterator, end)() const { return make_iterator(values_.end()); } +MPH_MAP_METHOD_DECL(bool_type, empty)() const { return size_ == 0; } +MPH_MAP_METHOD_DECL(size_type, size)() const { return size_; } MPH_MAP_METHOD_DECL(void_type, clear)() { values_.clear(); + present_.clear(); slack_.clear(); index_.clear(); } MPH_MAP_METHOD_DECL(void_type, erase)(iterator pos) { - values_.erase(pos); - pack(); + present_[pos - begin] = false; + *pos = value_type(); } MPH_MAP_METHOD_DECL(void_type, erase)(const key_type& k) { iterator it = find(k); @@ -188,22 +182,26 @@ MPH_MAP_METHOD_DECL(void_type, erase)(const key_type& k) { MPH_MAP_METHOD_DECL(const_iterator, find)(const key_type& k) const { if (__builtin_expect(!slack_.empty(), 0)) { - typename slack_type::const_iterator it = slack_.find(k); - if (it != slack_.end()) return values_.begin() + it->second; + auto it = slack_.find(k); + if (it != slack_.end()) return make_iterator(values_.begin() + it->second); } if (__builtin_expect(index_.size() == 0, 0)) return end(); - const_iterator it = values_.begin() + values_pointer_[index_.perfect_hash(k)]; + auto id = index_.perfect_hash(k); + if (!present_[id]) return end(); + auto it = make_iterator(values_.begin() + id); if (__builtin_expect(equal_(k, it->first), 1)) return it; return end(); } MPH_MAP_METHOD_DECL(iterator, find)(const key_type& k) { if (__builtin_expect(!slack_.empty(), 0)) { - typename slack_type::const_iterator it = slack_.find(k); - if (it != slack_.end()) return values_.begin() + it->second; + auto it = slack_.find(k); + if (it != slack_.end()) return make_iterator(values_.begin() + it->second); } if (__builtin_expect(index_.size() == 0, 0)) return end(); - iterator it = values_.begin() + values_pointer_[index_.perfect_hash(k)]; + auto id = index_.perfect_hash(k); + if (!present_[id]) return end(); + auto it = make_iterator(values_.begin() + id); if (__builtin_expect(equal_(k, it->first), 1)) return it; return end(); } From 09c1af7771811dd17f5c50f979b099d4c96eb1de Mon Sep 17 00:00:00 2001 From: Davi Reis Date: Mon, 12 Mar 2012 00:17:08 -0300 Subject: [PATCH 03/16] Perfect hash working, but it is slower. --- cxxmph/hollow_iterator.h | 4 +++- cxxmph/hollow_iterator_test.cc | 3 +++ cxxmph/mph_map.h | 26 +++++++++++++++++++------- cxxmph/mph_map_test.cc | 31 +++++++++++++++++-------------- 4 files changed, 42 insertions(+), 22 deletions(-) diff --git a/cxxmph/hollow_iterator.h b/cxxmph/hollow_iterator.h index bbb34bf..c650d21 100644 --- a/cxxmph/hollow_iterator.h +++ b/cxxmph/hollow_iterator.h @@ -17,7 +17,7 @@ struct hollow_iterator_base typedef typename iterator::pointer pointer; hollow_iterator_base(container* c, presence* p, iterator it) - : c_(c), p_(p), it_(it) { find_present(); } + : c_(c), p_(p), it_(it) { if (c_) find_present(); } self_reference operator++() { ++it_; find_present(); } @@ -44,6 +44,7 @@ struct hollow_iterator : public hollow_iterator_base< container_type, std::vector, typename container_type::iterator> { typedef hollow_iterator_base< container_type, std::vector, typename container_type::iterator> parent_class; + hollow_iterator() : parent_class(NULL, NULL, typename container_type::iterator()) { } hollow_iterator(typename parent_class::container* c, typename parent_class::presence* p, typename parent_class::iterator it) @@ -58,6 +59,7 @@ struct hollow_const_iterator : public hollow_iterator_base< typedef hollow_const_iterator self_type; typedef hollow_iterator non_const_type; hollow_const_iterator(non_const_type rhs) : parent_class(rhs.c_, rhs.p_, typename container_type::const_iterator(rhs.it_)) { } + hollow_const_iterator() : parent_class(NULL, NULL, typename container_type::iterator()) { } hollow_const_iterator(const typename parent_class::container* c, const typename parent_class::presence* p, typename parent_class::iterator it) diff --git a/cxxmph/hollow_iterator_test.cc b/cxxmph/hollow_iterator_test.cc index 201b748..07963ae 100644 --- a/cxxmph/hollow_iterator_test.cc +++ b/cxxmph/hollow_iterator_test.cc @@ -31,5 +31,8 @@ int main(int argc, char** argv) { auto it1 = hollow_iterator>(&v, &p, v.begin()); auto it2 = hollow_const_iterator>(&v, &p, v.begin()); if (it1 != it2) exit(-1); + + hollow_iterator> default_constructed; + default_constructed = hollow_iterator>(&v, &p, v.begin()); } diff --git a/cxxmph/mph_map.h b/cxxmph/mph_map.h index 7687ba5..ac77a06 100644 --- a/cxxmph/mph_map.h +++ b/cxxmph/mph_map.h @@ -7,6 +7,7 @@ // and should not be used if performance is a concern. In fact, you should only // use it for educational purposes. +#include #include #include #include @@ -71,9 +72,8 @@ class mph_map { data_type& operator[](const key_type &k); const data_type& operator[](const key_type &k) const; - size_type bucket_count() const { return index_.perfect_hash_size() + slack_.bucket_count(); } - // FIXME: not sure if this has the semantics I want - void rehash(size_type nbuckets /*ignored*/) { pack(); } + size_type bucket_count() const { return index_.size() + slack_.bucket_count(); } + void rehash(size_type nbuckets /*ignored*/); protected: // mimicking STL implementation EqualKey equal_; @@ -131,6 +131,7 @@ MPH_MAP_METHOD_DECL(insert_return_type, insert)(const value_type& x) { } values_.push_back(x); present_.push_back(true); + ++size_; slack_.insert(make_pair(x.first, values_.size() - 1)); if (should_pack) pack(); it = find(x.first); @@ -143,10 +144,12 @@ MPH_MAP_METHOD_DECL(void_type, pack)() { make_iterator_first(begin()), make_iterator_first(end()), size_); assert(success); - std::vector new_values(index_.size()); - std::vector new_present(index_.size(), false); - for (iterator it(begin()), it_end(end()); it != it_end; ++it) { - size_type id = index_.index(it->first); + std::vector new_values(index_.perfect_hash_size()); + new_values.reserve(new_values.size() * 2); + std::vector new_present(index_.perfect_hash_size(), false); + new_present.reserve(new_present.size() * 2); + for (iterator it = begin(), it_end = end(); it != it_end; ++it) { + size_type id = index_.perfect_hash(it->first); assert(id < new_values.size()); new_values[id] = *it; new_present[id] = true; @@ -168,11 +171,13 @@ MPH_MAP_METHOD_DECL(void_type, clear)() { present_.clear(); slack_.clear(); index_.clear(); + size_ = 0; } MPH_MAP_METHOD_DECL(void_type, erase)(iterator pos) { present_[pos - begin] = false; *pos = value_type(); + --size_; } MPH_MAP_METHOD_DECL(void_type, erase)(const key_type& k) { iterator it = find(k); @@ -214,6 +219,13 @@ MPH_MAP_METHOD_DECL(my_int32_t, index)(const key_type& k) const { MPH_MAP_METHOD_DECL(data_type&, operator[])(const key_type& k) { return insert(make_pair(k, data_type())).first->second; } +MPH_MAP_METHOD_DECL(void_type, rehash)(size_type nbuckets) { + pack(); + vector(values_.begin(), values_.end()).swap(values_); + vector(present_.begin(), present_.end()).swap(present_); + slack_type().swap(slack_); +} + } // namespace cxxmph diff --git a/cxxmph/mph_map_test.cc b/cxxmph/mph_map_test.cc index 579e0ca..11bfbc9 100644 --- a/cxxmph/mph_map_test.cc +++ b/cxxmph/mph_map_test.cc @@ -11,21 +11,25 @@ using cxxmph::mph_map; int main(int argc, char** argv) { mph_map b; - for (int i = 0; i < 100*1000; ++i) { + int32_t num_keys = 1000*10; + for (int i = 0; i < num_keys; ++i) { b.insert(make_pair(i, i)); } - for (int i = 0; i < 1000*1000; ++i) { - b.find(i); + for (int i = 0; i < num_keys; ++i) { + auto it = b.find(i); + if (it->first != it->second || it->first != i) { + std::cerr << "Found " << it->first << " looking for " << i << std::endl; + exit(-1); + } } - /* mph_map h; h.insert(std::make_pair("-1",-1)); mph_map::const_iterator it; for (it = h.begin(); it != h.end(); ++it) { - std::cerr << it->first << " -> " << it->second << std::endl; + if (it->second != -1) exit(-1); } - std::cerr << "Search -1 gives " << h.find("-1")->second << std::endl; - for (int i = 0; i < 100; ++i) { + int32_t num_valid = 100; + for (int i = 0; i < num_valid; ++i) { char buf[10]; snprintf(buf, 10, "%d", i); h.insert(std::make_pair(buf, i)); @@ -34,18 +38,17 @@ int main(int argc, char** argv) { for (int i = 1000; i > 0; --i) { char buf[10]; snprintf(buf, 10, "%d", i - 1); - h.find(buf); - std::cerr << "Search " << i - 1 << " gives " << h.find(buf)->second << std::endl; + auto it = h.find(buf); + if (i < num_valid && it->second != i - 1) exit(-1); } } for (int j = 0; j < 100; ++j) { for (int i = 1000; i > 0; --i) { char buf[10]; - snprintf(buf, 10, "%d", i*100 - 1); - h.find(buf); - std::cerr << "Search " << i*100 - 1 << " gives " << h.find(buf)->second << std::endl; + int key = i*100 - 1; + snprintf(buf, 10, "%d", key); + auto it = h.find(buf); + if (key < num_valid && it->second != key) exit(-1); } } - */ - } From 9dcf0450f00fd5bbf12ab39f38565babb15546de Mon Sep 17 00:00:00 2001 From: Davi Reis Date: Mon, 12 Mar 2012 01:43:06 -0300 Subject: [PATCH 04/16] Added Murmur3 support. Not necessarily faster. --- cxxmph/Makefile.am | 4 +- cxxmph/MurmurHash2.h | 74 ---------------------------------- cxxmph/bm_common.h | 6 ++- cxxmph/bm_index.cc | 2 +- cxxmph/bm_map.cc | 4 +- cxxmph/mph_index.h | 5 ++- cxxmph/mph_index_test.cc | 3 +- cxxmph/mph_map.h | 3 +- cxxmph/seeded_hash.h | 85 ++++++++++++++++++++++++++++------------ 9 files changed, 76 insertions(+), 110 deletions(-) delete mode 100644 cxxmph/MurmurHash2.h diff --git a/cxxmph/Makefile.am b/cxxmph/Makefile.am index cec2073..0de662f 100644 --- a/cxxmph/Makefile.am +++ b/cxxmph/Makefile.am @@ -3,10 +3,10 @@ check_PROGRAMS = hollow_iterator_test mph_map_test mph_index_test trigraph_test noinst_PROGRAMS = bm_index bm_map bin_PROGRAMS = cxxmph lib_LTLIBRARIES = libcxxmph.la -libcxxmph_la_SOURCES = MurmurHash2.h trigragh.h trigraph.cc mph_index.h mph_index.cc seeded_hash.h stringpiece.h benchmark.h benchmark.cc +libcxxmph_la_SOURCES = MurmurHash3.h MurmurHash3.cpp trigragh.h trigraph.cc mph_index.h mph_index.cc seeded_hash.h stringpiece.h benchmark.h benchmark.cc libcxxmph_la_LDFLAGS = -version-info 0:0:0 cxxmph_includedir = $(includedir)/cxxmph/ -cxxmph_include_HEADERS = mph_map.h mph_index.h MurmurHash2.h trigraph.h seeded_hash.h stringpiece.h hollow_iterator.h +cxxmph_include_HEADERS = mph_map.h mph_index.h MurmurHash3.h trigraph.h seeded_hash.h stringpiece.h hollow_iterator.h mph_map_test_LDADD = libcxxmph.la mph_map_test_SOURCES = mph_map_test.cc diff --git a/cxxmph/MurmurHash2.h b/cxxmph/MurmurHash2.h deleted file mode 100644 index 0d318a3..0000000 --- a/cxxmph/MurmurHash2.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef __CXXMPH_MURMUR_HASH2__ -#define __CXXMPH_MURMUR_HASH2__ - -//----------------------------------------------------------------------------- -// MurmurHash2, by Austin Appleby - -// Note - This code makes a few assumptions about how your machine behaves - - -// 1. We can read a 4-byte value from any address without crashing -// 2. sizeof(int) == 4 - -// And it has a few limitations - - -// 1. It will not work incrementally. -// 2. It will not produce the same results on little-endian and big-endian -// machines. - -namespace cxxmph { - -inline // not measured, for making compilation easier only -unsigned int MurmurHash2 ( const void * key, int len, unsigned int seed ) -{ - // 'm' and 'r' are mixing constants generated offline. - // They're not really 'magic', they just happen to work well. - - const unsigned int m = 0x5bd1e995; - const int r = 24; - - // Initialize the hash to a 'random' value - - unsigned int h = seed ^ len; - - // Mix 4 bytes at a time into the hash - - const unsigned char * data = (const unsigned char *)key; - - while(len >= 4) - { - unsigned int k = *(unsigned int *)data; - - k *= m; - k ^= k >> r; - k *= m; - - h *= m; - h ^= k; - - data += 4; - len -= 4; - } - - // Handle the last few bytes of the input array - - switch(len) - { - case 3: h ^= data[2] << 16; - case 2: h ^= data[1] << 8; - case 1: h ^= data[0]; - h *= m; - }; - - // Do a few final mixes of the hash to ensure the last few - // bytes are well-incorporated. - - h ^= h >> 13; - h *= m; - h ^= h >> 15; - - return h; -} - -} // namespace cxxmph - -#endif // __CXXMPH_MURMUR_HASH2__ diff --git a/cxxmph/bm_common.h b/cxxmph/bm_common.h index aaf12b9..eed12df 100644 --- a/cxxmph/bm_common.h +++ b/cxxmph/bm_common.h @@ -6,14 +6,16 @@ #include #include #include // std::hash -#include "MurmurHash2.h" +#include "MurmurHash3.h" #include "benchmark.h" namespace std { template <> struct hash { uint32_t operator()(const cxxmph::StringPiece& k) const { - return cxxmph::MurmurHash2(k.data(), k.length(), 1); + uint32_t out; + MurmurHash3_x86_32(k.data(), k.length(), 1, &out); + return out; } }; } // namespace std diff --git a/cxxmph/bm_index.cc b/cxxmph/bm_index.cc index d1cbc00..9345a11 100644 --- a/cxxmph/bm_index.cc +++ b/cxxmph/bm_index.cc @@ -47,7 +47,7 @@ class BM_MPHIndexSearch : public SearchUrlsBenchmark { for (auto it = random_.begin(); it != random_.end(); ++it) { auto idx = index_.index(*it); // Collision check to be fair with STL - if (strcmp(urls_[idx].c_str(), it->data()) != 0) idx = -1; + // if (strcmp(urls_[idx].c_str(), it->data()) != 0) idx = -1; } } protected: diff --git a/cxxmph/bm_map.cc b/cxxmph/bm_map.cc index e381976..a90b7b2 100644 --- a/cxxmph/bm_map.cc +++ b/cxxmph/bm_map.cc @@ -91,9 +91,9 @@ int main(int argc, char** argv) { Benchmark::Register(new BM_CreateUrls>("URLS100k")); Benchmark::Register(new BM_CreateUrls>("URLS100k")); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0)); - Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0)); + Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0)); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0.9)); - Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0.9)); + Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0.9)); Benchmark::Register(new BM_SearchUint64>); Benchmark::Register(new BM_SearchUint64>); Benchmark::RunAll(); diff --git a/cxxmph/mph_index.h b/cxxmph/mph_index.h index ad5bc6e..7b54250 100644 --- a/cxxmph/mph_index.h +++ b/cxxmph/mph_index.h @@ -158,7 +158,8 @@ bool MPHIndex::Mapping( std::vector* edges, std::vector* queue) { TriGraph graph(n_, m_); for (ForwardIterator it = begin; it != end; ++it) { - uint32_t h[3]; + uint32_t h[4]; + // SeededHashFcn().hash64(*it, hash_seed_[0], reinterpret_cast(&h)); for (int i = 0; i < 3; ++i) h[i] = SeededHashFcn()(*it, hash_seed_[i]); uint32_t v0 = h[0] % r_; uint32_t v1 = h[1] % r_ + r_; @@ -201,7 +202,7 @@ uint32_t MPHIndex::index(const Key& key) const { // Simple wrapper around MPHIndex to simplify calling code. Please refer to the // MPHIndex class for documentation. -template >::hash_function> +template >::hash_function> class SimpleMPHIndex : public MPHIndex { public: template diff --git a/cxxmph/mph_index_test.cc b/cxxmph/mph_index_test.cc index 70e01bc..b4101df 100644 --- a/cxxmph/mph_index_test.cc +++ b/cxxmph/mph_index_test.cc @@ -7,7 +7,7 @@ using std::string; using std::vector; -using cxxmph::SimpleMPHIndex; +using namespace cxxmph; int main(int argc, char** argv) { @@ -38,4 +38,3 @@ int main(int argc, char** argv) { SimpleMPHIndex other_mph_index; other_mph_index.deserialize(serialized); } - diff --git a/cxxmph/mph_map.h b/cxxmph/mph_map.h index ac77a06..fa264c8 100644 --- a/cxxmph/mph_map.h +++ b/cxxmph/mph_map.h @@ -6,6 +6,8 @@ // This class is about 20% to 100% slower than unordered_map (or ext/hash_map) // and should not be used if performance is a concern. In fact, you should only // use it for educational purposes. +// +// See http://www.strchr.com/crc32_popcnt and new Murmur3 function to try to beat stl #include #include @@ -13,7 +15,6 @@ #include #include // for std::pair -#include "MurmurHash2.h" #include "mph_index.h" #include "hollow_iterator.h" diff --git a/cxxmph/seeded_hash.h b/cxxmph/seeded_hash.h index d079a57..e204d36 100644 --- a/cxxmph/seeded_hash.h +++ b/cxxmph/seeded_hash.h @@ -6,9 +6,12 @@ #include #include // for std::hash -#include "MurmurHash2.h" +#include "MurmurHash3.h" #include "stringpiece.h" +// From murmur, only used naively to extend 32 bits functions to 64 bits. +uint32_t fmix ( uint32_t h ); + namespace cxxmph { template @@ -17,72 +20,106 @@ struct seeded_hash_function { uint32_t operator()(const Key& k, uint32_t seed) const { return HashFcn()(k) ^ seed; } + template + void hash64(const Key& k, uint32_t seed, uint32_t* out) const { + for (int i = 0; i < 4; ++i) { + out[i] = HashFcn()(k) ^ seed; + seed = fmix(seed); + } + } }; -struct Murmur2 { +struct Murmur3 { template uint32_t operator()(const Key& k) const { - return MurmurHash2(reinterpret_cast(&k), sizeof(Key), 1 /* seed */); + uint32_t out; + MurmurHash3_x86_32(reinterpret_cast(&k), sizeof(Key), 1 /* seed */, &out); + return out; + } + template + void hash64(const Key& k, uint32_t* out) const { + MurmurHash3_x64_128(reinterpret_cast(&k), sizeof(Key), 1 /* seed */, out); } }; -struct Murmur2StringPiece { + +struct Murmur3StringPiece { template uint32_t operator()(const Key& k) const { StringPiece s(k); - return MurmurHash2(s.data(), s.length(), 1 /* seed */); + uint32_t out; + MurmurHash3_x86_32(s.data(), s.length(), 1 /* seed */, &out); + return out; + } + template + void hash64(const Key& k, uint32_t* out) const { + StringPiece s(k); + MurmurHash3_x64_128(s.data(), s.length(), 1 /* seed */, out); } }; template <> -struct seeded_hash_function { +struct seeded_hash_function { template uint32_t operator()(const Key& k, uint32_t seed) const { - return MurmurHash2(reinterpret_cast(&k), sizeof(Key), seed); + uint32_t out; + MurmurHash3_x86_32(reinterpret_cast(&k), sizeof(Key), seed, &out); + return out; + } + template + void hash64(const Key& k, uint32_t seed, uint32_t* out) const { + MurmurHash3_x64_128(reinterpret_cast(&k), sizeof(Key), seed, out); } }; template <> -struct seeded_hash_function { +struct seeded_hash_function { template uint32_t operator()(const Key& k, uint32_t seed) const { StringPiece s(k); - return MurmurHash2(s.data(), s.length(), seed); + uint32_t out; + MurmurHash3_x86_32(s.data(), s.length(), seed, &out); + return out; + } + template + void hash64(const Key& k, uint32_t seed, uint32_t* out) const { + StringPiece s(k); + MurmurHash3_x64_128(s.data(), s.length(), seed, out); } }; template struct seeded_hash { typedef seeded_hash_function hash_function; }; -// Use Murmur2 instead for all types defined in std::hash, plus +// Use Murmur3 instead for all types defined in std::hash, plus // std::string which is commonly extended. template <> struct seeded_hash > -{ typedef seeded_hash_function hash_function; }; +{ typedef seeded_hash_function hash_function; }; template <> struct seeded_hash > -{ typedef seeded_hash_function hash_function; }; +{ typedef seeded_hash_function hash_function; }; template <> struct seeded_hash > -{ typedef seeded_hash_function hash_function; }; +{ typedef seeded_hash_function hash_function; }; template <> struct seeded_hash > -{ typedef seeded_hash_function hash_function; }; +{ typedef seeded_hash_function hash_function; }; template <> struct seeded_hash > -{ typedef seeded_hash_function hash_function; }; +{ typedef seeded_hash_function hash_function; }; template <> struct seeded_hash > -{ typedef seeded_hash_function hash_function; }; +{ typedef seeded_hash_function hash_function; }; template <> struct seeded_hash > -{ typedef seeded_hash_function hash_function; }; +{ typedef seeded_hash_function hash_function; }; template <> struct seeded_hash > -{ typedef seeded_hash_function hash_function; }; +{ typedef seeded_hash_function hash_function; }; template <> struct seeded_hash > -{ typedef seeded_hash_function hash_function; }; +{ typedef seeded_hash_function hash_function; }; template <> struct seeded_hash > -{ typedef seeded_hash_function hash_function; }; +{ typedef seeded_hash_function hash_function; }; template <> struct seeded_hash > -{ typedef seeded_hash_function hash_function; }; +{ typedef seeded_hash_function hash_function; }; template <> struct seeded_hash > -{ typedef seeded_hash_function hash_function; }; +{ typedef seeded_hash_function hash_function; }; template <> struct seeded_hash > -{ typedef seeded_hash_function hash_function; }; +{ typedef seeded_hash_function hash_function; }; template <> struct seeded_hash > -{ typedef seeded_hash_function hash_function; }; +{ typedef seeded_hash_function hash_function; }; } // namespace cxxmph From 7b8b3e583476abbd6d33eed0f2f547f2c60225bb Mon Sep 17 00:00:00 2001 From: Davi Reis Date: Tue, 13 Mar 2012 19:31:35 -0300 Subject: [PATCH 05/16] Use hash64. --- cxxmph/mph_index.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cxxmph/mph_index.h b/cxxmph/mph_index.h index 7b54250..f2741ea 100644 --- a/cxxmph/mph_index.h +++ b/cxxmph/mph_index.h @@ -159,8 +159,8 @@ bool MPHIndex::Mapping( TriGraph graph(n_, m_); for (ForwardIterator it = begin; it != end; ++it) { uint32_t h[4]; - // SeededHashFcn().hash64(*it, hash_seed_[0], reinterpret_cast(&h)); - for (int i = 0; i < 3; ++i) h[i] = SeededHashFcn()(*it, hash_seed_[i]); + SeededHashFcn().hash64(*it, hash_seed_[0], reinterpret_cast(&h)); + // for (int i = 0; i < 3; ++i) h[i] = SeededHashFcn()(*it, hash_seed_[i]); uint32_t v0 = h[0] % r_; uint32_t v1 = h[1] % r_ + r_; uint32_t v2 = h[2] % r_ + (r_ << 1); @@ -176,8 +176,9 @@ bool MPHIndex::Mapping( template uint32_t MPHIndex::perfect_hash(const Key& key) const { - uint32_t h[3]; - for (int i = 0; i < 3; ++i) h[i] = SeededHashFcn()(key, hash_seed_[i]); + uint32_t h[4]; + SeededHashFcn().hash64(key, hash_seed_[0], reinterpret_cast(&h)); + // for (int i = 0; i < 3; ++i) h[i] = SeededHashFcn()(key, hash_seed_[i]); assert(r_); h[0] = h[0] % r_; h[1] = h[1] % r_ + r_; From aa5fa26b49d324b8dfe4c6e46362a7e8640eabcc Mon Sep 17 00:00:00 2001 From: Davi Reis Date: Tue, 13 Mar 2012 20:25:06 -0300 Subject: [PATCH 06/16] Strange optimizations for 64 bit integers. --- cxxmph/bm_map.cc | 2 ++ cxxmph/seeded_hash.h | 34 +++++++++++++++++++++++++++++++--- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/cxxmph/bm_map.cc b/cxxmph/bm_map.cc index a90b7b2..8195217 100644 --- a/cxxmph/bm_map.cc +++ b/cxxmph/bm_map.cc @@ -88,12 +88,14 @@ using namespace cxxmph; int main(int argc, char** argv) { srandom(4); + /* Benchmark::Register(new BM_CreateUrls>("URLS100k")); Benchmark::Register(new BM_CreateUrls>("URLS100k")); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0)); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0)); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0.9)); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0.9)); + */ Benchmark::Register(new BM_SearchUint64>); Benchmark::Register(new BM_SearchUint64>); Benchmark::RunAll(); diff --git a/cxxmph/seeded_hash.h b/cxxmph/seeded_hash.h index e204d36..f0bab05 100644 --- a/cxxmph/seeded_hash.h +++ b/cxxmph/seeded_hash.h @@ -9,8 +9,9 @@ #include "MurmurHash3.h" #include "stringpiece.h" -// From murmur, only used naively to extend 32 bits functions to 64 bits. +// From murmur, only used naively to extend 32 bits functions to 128 bits. uint32_t fmix ( uint32_t h ); +uint64_t fmix ( uint64_t h ); namespace cxxmph { @@ -57,6 +58,19 @@ struct Murmur3StringPiece { } }; +struct Murmur3Fmix64bitsType { + template + uint32_t operator()(const Key& k) const { + return fmix(*reinterpret_cast(&k)); + } + template + void hash64(const Key& k, uint32_t* out) const { + uint64_t h = fmix(*reinterpret_cast(&k)); + *reinterpret_cast(out) = h; + *reinterpret_cast(out + 2) = h; + } +}; + template <> struct seeded_hash_function { template @@ -87,6 +101,20 @@ struct seeded_hash_function { } }; +template <> +struct seeded_hash_function { + template + uint32_t operator()(const Key& k, uint32_t seed) const { + return fmix(k + seed); + } + template + void hash64(const Key& k, uint32_t seed, uint32_t* out) const { + *reinterpret_cast(out) = fmix(k ^ seed); + *(out + 2) = fmix(*out); + } +}; + + template struct seeded_hash { typedef seeded_hash_function hash_function; }; // Use Murmur3 instead for all types defined in std::hash, plus @@ -117,9 +145,9 @@ template <> struct seeded_hash > template <> struct seeded_hash > { typedef seeded_hash_function hash_function; }; template <> struct seeded_hash > -{ typedef seeded_hash_function hash_function; }; +{ typedef seeded_hash_function hash_function; }; template <> struct seeded_hash > -{ typedef seeded_hash_function hash_function; }; +{ typedef seeded_hash_function hash_function; }; } // namespace cxxmph From 86797b6402c37c9285cdcc4f588320bd9a9a90f5 Mon Sep 17 00:00:00 2001 From: Davi Reis Date: Wed, 14 Mar 2012 01:29:13 -0300 Subject: [PATCH 07/16] Finally beat STL. Trying improvement around cuckoo hashing idea. --- cxxmph/bm_map.cc | 7 +++--- cxxmph/mph_index.h | 43 +++++++++++++++++++++++++++++++---- cxxmph/mph_map.h | 54 +++++++++++++++++++++++++++++++++----------- cxxmph/seeded_hash.h | 9 ++++---- 4 files changed, 87 insertions(+), 26 deletions(-) diff --git a/cxxmph/bm_map.cc b/cxxmph/bm_map.cc index 8195217..0a0b225 100644 --- a/cxxmph/bm_map.cc +++ b/cxxmph/bm_map.cc @@ -49,6 +49,7 @@ class BM_SearchUrls : public SearchUrlsBenchmark { mymap_[*it] = *it; } mymap_.rehash(mymap_.bucket_count()); + fprintf(stderr, "Occupation: %f\n", static_cast(mymap_.size())/mymap_.bucket_count()); return true; } MapType mymap_; @@ -57,7 +58,7 @@ class BM_SearchUrls : public SearchUrlsBenchmark { template class BM_SearchUint64 : public SearchUint64Benchmark { public: - BM_SearchUint64() : SearchUint64Benchmark(10000, 10*1000*1000) { } + BM_SearchUint64() : SearchUint64Benchmark(100000, 10*1000*1000) { } virtual bool SetUp() { if (!SearchUint64Benchmark::SetUp()) return false; for (int i = 0; i < values_.size(); ++i) { @@ -88,15 +89,13 @@ using namespace cxxmph; int main(int argc, char** argv) { srandom(4); - /* Benchmark::Register(new BM_CreateUrls>("URLS100k")); Benchmark::Register(new BM_CreateUrls>("URLS100k")); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0)); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0)); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0.9)); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0.9)); - */ - Benchmark::Register(new BM_SearchUint64>); Benchmark::Register(new BM_SearchUint64>); + Benchmark::Register(new BM_SearchUint64>); Benchmark::RunAll(); } diff --git a/cxxmph/mph_index.h b/cxxmph/mph_index.h index f2741ea..45390a4 100644 --- a/cxxmph/mph_index.h +++ b/cxxmph/mph_index.h @@ -25,6 +25,7 @@ #include #include +#include #include #include // for std::hash #include @@ -63,6 +64,12 @@ class MPHIndex { template // must agree with Reset uint32_t minimal_perfect_hash(const Key& x) const; + // Crazy functions. Ignore. + template // must agree with Reset + uint32_t cuckoo_hash(const Key& x, const uint32_t* h, uint8_t nest) const; + template // must agree with Reset + void hash_vector(const Key& x, uint32_t* h) const; + // Serialization for mmap usage - not tested well, ping me if you care. // Serialized tables are not guaranteed to work across versions or different // endianness (although they could easily be made to be). @@ -94,6 +101,8 @@ class MPHIndex { // Partition vertex count, derived from c parameter. uint32_t r_; + uint32_t nest_displacement_[3]; // derived from r_ + // The array containing the minimal perfect hash function graph. Do not use // c++ vector to make mmap based backing easier. const uint8_t* g_; @@ -118,6 +127,16 @@ class MPHIndex { }; +template +T nexthigher(T k) { + if (k == 0) + return 1; + k--; + for (int i=1; i> i; + return k+1; +} + // Template method needs to go in the header file. template bool MPHIndex::Reset( @@ -129,6 +148,13 @@ bool MPHIndex::Reset( m_ = size; r_ = static_cast(ceil((c_*m_)/3)); if ((r_ % 2) == 0) r_ += 1; + nest_displacement_[0] = 0; + nest_displacement_[1] = r_; + nest_displacement_[2] = (r_ << 1); + // This can be used to speed mods, but increases occupation too much. + // Needs to try http://gmplib.org/manual/Integer-Exponentiation.html instead + // r_ = nexthigher(r_); + n_ = 3*r_; k_ = 1U << b_; @@ -174,15 +200,24 @@ bool MPHIndex::Mapping( return false; } +template +uint32_t MPHIndex::cuckoo_hash(const Key& key, const uint32_t* h, uint8_t nest) const { + return (h[nest] % r_) + nest_displacement_[nest]; +} + +template +void MPHIndex::hash_vector(const Key& key, uint32_t* h) const { + SeededHashFcn().hash64(key, hash_seed_[0], reinterpret_cast(&h)); +} + template uint32_t MPHIndex::perfect_hash(const Key& key) const { uint32_t h[4]; SeededHashFcn().hash64(key, hash_seed_[0], reinterpret_cast(&h)); // for (int i = 0; i < 3; ++i) h[i] = SeededHashFcn()(key, hash_seed_[i]); - assert(r_); - h[0] = h[0] % r_; - h[1] = h[1] % r_ + r_; - h[2] = h[2] % r_ + (r_ << 1); + h[0] = (h[0] % r_) + nest_displacement_[0]; + h[1] = (h[1] % r_) + nest_displacement_[1]; + h[2] = (h[2] % r_) + nest_displacement_[2]; assert(g_size_); // cerr << "g_.size() " << g_size_ << " h0 >> 2 " << (h[0] >> 2) << endl; assert((h[0] >> 2) >(&values_, &present_, it); } + static void set_2bit_value(uint8_t *d, uint32_t i, uint8_t v) { + d[(i >> 2)] &= ((v << ((i & 3) << 1)) | valuemask[i & 3]); + } + static uint32_t get_2bit_value(const uint8_t* d, uint32_t i) { + return (d[(i >> 2)] >> (((i & 3) << 1)) & 3); + } + void pack(); std::vector values_; std::vector present_; + const uint8_t* nests_; SimpleMPHIndex::hash_function> index_; // TODO(davi) optimize slack to no hold a copy of the key typedef unordered_map slack_type; @@ -187,28 +195,48 @@ MPH_MAP_METHOD_DECL(void_type, erase)(const key_type& k) { } MPH_MAP_METHOD_DECL(const_iterator, find)(const key_type& k) const { - if (__builtin_expect(!slack_.empty(), 0)) { - auto it = slack_.find(k); - if (it != slack_.end()) return make_iterator(values_.begin() + it->second); + uint32_t h[4]; + auto nest = nests_[index_.hash_vector(k, reinterpret_cast(&h))]; + if (nest != kNestCollision) { + auto vit = values_.begin() + h[nest]; + if (equal_(k, vit->first)) return make_iterator(vit); } - if (__builtin_expect(index_.size() == 0, 0)) return end(); + return slow_find(k); +} + +MPH_MAP_METHOD_DECL(const_iterator, slow_find)(const key_type& k) const { auto id = index_.perfect_hash(k); if (!present_[id]) return end(); - auto it = make_iterator(values_.begin() + id); - if (__builtin_expect(equal_(k, it->first), 1)) return it; + auto vit = values_.begin() + id; + if (equal_(k, vit->first)) return make_iterator(vit); + + if (__builtin_expect(!slack_.empty(), 0)) { + auto sit = slack_.find(k); + if (it != slack_.end()) return make_iterator(values_.begin() + sit->second); + } return end(); } MPH_MAP_METHOD_DECL(iterator, find)(const key_type& k) { - if (__builtin_expect(!slack_.empty(), 0)) { - auto it = slack_.find(k); - if (it != slack_.end()) return make_iterator(values_.begin() + it->second); + uint32_t h[4]; + auto nest = nests_[index_.hash_vector(k, reinterpret_cast(&h))]; + if (nest != kNestCollision) { + auto vit = values_.begin() + h[nest]; + if (equal_(k, vit->first)) return make_iterator(vit); } - if (__builtin_expect(index_.size() == 0, 0)) return end(); + return slow_find(k); +} + +MPH_MAP_METHOD_DECL(iterator, slow_find)(const key_type& k) { auto id = index_.perfect_hash(k); if (!present_[id]) return end(); - auto it = make_iterator(values_.begin() + id); - if (__builtin_expect(equal_(k, it->first), 1)) return it; + auto vit = values_.begin() + id; + if (equal_(k, vit->first)) return make_iterator(vit); + + if (__builtin_expect(!slack_.empty(), 0)) { + auto sit = slack_.find(k); + if (it != slack_.end()) return make_iterator(values_.begin() + sit->second); + } return end(); } diff --git a/cxxmph/seeded_hash.h b/cxxmph/seeded_hash.h index f0bab05..69cb0ac 100644 --- a/cxxmph/seeded_hash.h +++ b/cxxmph/seeded_hash.h @@ -65,9 +65,8 @@ struct Murmur3Fmix64bitsType { } template void hash64(const Key& k, uint32_t* out) const { - uint64_t h = fmix(*reinterpret_cast(&k)); - *reinterpret_cast(out) = h; - *reinterpret_cast(out + 2) = h; + *reinterpret_cast(out) = fmix(k); + *(out + 2) = fmix(*out); } }; @@ -145,9 +144,9 @@ template <> struct seeded_hash > template <> struct seeded_hash > { typedef seeded_hash_function hash_function; }; template <> struct seeded_hash > -{ typedef seeded_hash_function hash_function; }; +{ typedef seeded_hash_function hash_function; }; template <> struct seeded_hash > -{ typedef seeded_hash_function hash_function; }; +{ typedef seeded_hash_function hash_function; }; } // namespace cxxmph From a4d96e6cb26fead353d2b8afc110c154291b10c7 Mon Sep 17 00:00:00 2001 From: Davi Reis Date: Wed, 14 Mar 2012 04:51:55 -0300 Subject: [PATCH 08/16] Tests pass, but it segfaults at the benchmark. Need further investigation, but the core for the cuckoo stuff is already there. --- cxxmph/Makefile.am | 2 +- cxxmph/bm_map.cc | 2 ++ cxxmph/mph_bits.cc | 4 +++ cxxmph/mph_bits.h | 18 ++++++++++ cxxmph/mph_index.h | 54 ++++++++++++++--------------- cxxmph/mph_map.h | 77 ++++++++++++++++++++++++++++++------------ cxxmph/mph_map_test.cc | 4 +++ 7 files changed, 111 insertions(+), 50 deletions(-) create mode 100644 cxxmph/mph_bits.cc create mode 100644 cxxmph/mph_bits.h diff --git a/cxxmph/Makefile.am b/cxxmph/Makefile.am index 0de662f..22c0bb2 100644 --- a/cxxmph/Makefile.am +++ b/cxxmph/Makefile.am @@ -3,7 +3,7 @@ check_PROGRAMS = hollow_iterator_test mph_map_test mph_index_test trigraph_test noinst_PROGRAMS = bm_index bm_map bin_PROGRAMS = cxxmph lib_LTLIBRARIES = libcxxmph.la -libcxxmph_la_SOURCES = MurmurHash3.h MurmurHash3.cpp trigragh.h trigraph.cc mph_index.h mph_index.cc seeded_hash.h stringpiece.h benchmark.h benchmark.cc +libcxxmph_la_SOURCES = MurmurHash3.h MurmurHash3.cpp trigragh.h trigraph.cc mph_index.h mph_index.cc seeded_hash.h stringpiece.h benchmark.h benchmark.cc mph_bits.h mph_bits.cc libcxxmph_la_LDFLAGS = -version-info 0:0:0 cxxmph_includedir = $(includedir)/cxxmph/ cxxmph_include_HEADERS = mph_map.h mph_index.h MurmurHash3.h trigraph.h seeded_hash.h stringpiece.h hollow_iterator.h diff --git a/cxxmph/bm_map.cc b/cxxmph/bm_map.cc index 0a0b225..44e3fe7 100644 --- a/cxxmph/bm_map.cc +++ b/cxxmph/bm_map.cc @@ -89,10 +89,12 @@ using namespace cxxmph; int main(int argc, char** argv) { srandom(4); + /* Benchmark::Register(new BM_CreateUrls>("URLS100k")); Benchmark::Register(new BM_CreateUrls>("URLS100k")); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0)); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0)); + */ Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0.9)); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0.9)); Benchmark::Register(new BM_SearchUint64>); diff --git a/cxxmph/mph_bits.cc b/cxxmph/mph_bits.cc new file mode 100644 index 0000000..9fb97bd --- /dev/null +++ b/cxxmph/mph_bits.cc @@ -0,0 +1,4 @@ +#include "mph_bits.h" + +namespace cxxmph { +} diff --git a/cxxmph/mph_bits.h b/cxxmph/mph_bits.h new file mode 100644 index 0000000..03f7c08 --- /dev/null +++ b/cxxmph/mph_bits.h @@ -0,0 +1,18 @@ +#ifndef __CXXMPH_MPH_BITS_H__ +#define __CXXMPH_MPH_BITS_H__ + +#include // for uint32_t and friends + +namespace cxxmph { + +static const uint8_t valuemask[] = { 0xfc, 0xf3, 0xcf, 0x3f}; +static void set_2bit_value(uint8_t *d, uint32_t i, uint8_t v) { + d[(i >> 2)] &= ((v << ((i & 3) << 1)) | valuemask[i & 3]); +} +static uint32_t get_2bit_value(const uint8_t* d, uint32_t i) { + return (d[(i >> 2)] >> (((i & 3) << 1)) & 3); +} + +} // namespace cxxmph + +#endif diff --git a/cxxmph/mph_index.h b/cxxmph/mph_index.h index 45390a4..ad1d7f6 100644 --- a/cxxmph/mph_index.h +++ b/cxxmph/mph_index.h @@ -36,6 +36,7 @@ using std::cerr; using std::endl; #include "seeded_hash.h" +#include "mph_bits.h" #include "trigraph.h" namespace cxxmph { @@ -43,7 +44,7 @@ namespace cxxmph { class MPHIndex { public: MPHIndex(double c = 1.23, uint8_t b = 7) : - c_(c), b_(b), m_(0), n_(0), k_(0), r_(0), + c_(c), b_(b), m_(0), n_(0), k_(0), r_(1), g_(NULL), g_size_(0), ranktable_(NULL), ranktable_size_(0), deserialized_(false) { } ~MPHIndex(); @@ -65,8 +66,12 @@ class MPHIndex { uint32_t minimal_perfect_hash(const Key& x) const; // Crazy functions. Ignore. + template // must agree with Reset + uint32_t cuckoo_hash(const uint32_t* h, uint8_t nest) const; template // must agree with Reset - uint32_t cuckoo_hash(const Key& x, const uint32_t* h, uint8_t nest) const; + uint8_t cuckoo_nest(const Key& x, const uint32_t* h) const; + template // must agree with Reset + uint32_t cuckoo_nest_index(const Key& x, uint32_t* h) const; template // must agree with Reset void hash_vector(const Key& x, uint32_t* h) const; @@ -117,26 +122,8 @@ class MPHIndex { bool deserialized_; static const uint8_t valuemask[]; - static void set_2bit_value(uint8_t *d, uint32_t i, uint8_t v) { - d[(i >> 2)] &= ((v << ((i & 3) << 1)) | valuemask[i & 3]); - } - static uint32_t get_2bit_value(const uint8_t* d, uint32_t i) { - return (d[(i >> 2)] >> (((i & 3) << 1)) & 3); - } - - }; -template -T nexthigher(T k) { - if (k == 0) - return 1; - k--; - for (int i=1; i> i; - return k+1; -} - // Template method needs to go in the header file. template bool MPHIndex::Reset( @@ -153,7 +140,7 @@ bool MPHIndex::Reset( nest_displacement_[2] = (r_ << 1); // This can be used to speed mods, but increases occupation too much. // Needs to try http://gmplib.org/manual/Integer-Exponentiation.html instead - // r_ = nexthigher(r_); + // r_ = nextpoweroftwo(r_); n_ = 3*r_; k_ = 1U << b_; @@ -200,30 +187,40 @@ bool MPHIndex::Mapping( return false; } -template -uint32_t MPHIndex::cuckoo_hash(const Key& key, const uint32_t* h, uint8_t nest) const { +template +uint32_t MPHIndex::cuckoo_hash(const uint32_t* h, uint8_t nest) const { return (h[nest] % r_) + nest_displacement_[nest]; } template void MPHIndex::hash_vector(const Key& key, uint32_t* h) const { - SeededHashFcn().hash64(key, hash_seed_[0], reinterpret_cast(&h)); + SeededHashFcn().hash64(key, hash_seed_[0], h); +} + +template +uint8_t MPHIndex::cuckoo_nest(const Key& key, const uint32_t* h) const { + uint32_t x[4]; + x[0] = (h[0] % r_) + nest_displacement_[0]; + x[1] = (h[1] % r_) + nest_displacement_[1]; + x[2] = (h[2] % r_) + nest_displacement_[2]; + return (get_2bit_value(g_, x[0]) + get_2bit_value(g_, x[1]) + get_2bit_value(g_, x[2])) % 3; } template uint32_t MPHIndex::perfect_hash(const Key& key) const { uint32_t h[4]; - SeededHashFcn().hash64(key, hash_seed_[0], reinterpret_cast(&h)); + SeededHashFcn().hash64(key, hash_seed_[0], h); // for (int i = 0; i < 3; ++i) h[i] = SeededHashFcn()(key, hash_seed_[i]); h[0] = (h[0] % r_) + nest_displacement_[0]; h[1] = (h[1] % r_) + nest_displacement_[1]; h[2] = (h[2] % r_) + nest_displacement_[2]; - assert(g_size_); + if (!g_size_) return 0; // cerr << "g_.size() " << g_size_ << " h0 >> 2 " << (h[0] >> 2) << endl; assert((h[0] >> 2) > 2) > 2) @@ -248,6 +245,9 @@ class SimpleMPHIndex : public MPHIndex { uint32_t index(const Key& key) const { return MPHIndex::index(key); } uint32_t perfect_hash(const Key& key) const { return MPHIndex::perfect_hash(key); } uint32_t minimal_perfect_hash(const Key& key) const { return MPHIndex::minimal_perfect_hash(key); } + uint8_t cuckoo_nest(const Key& key, const uint32_t* h) const { return MPHIndex::cuckoo_nest(key, h); } + uint32_t cuckoo_hash(const uint32_t* h, uint8_t nest) const { return MPHIndex::cuckoo_hash(h, nest); } + void hash_vector(const Key& key, uint32_t* h) const { MPHIndex::hash_vector(key, h); } }; } // namespace cxxmph diff --git a/cxxmph/mph_map.h b/cxxmph/mph_map.h index 66822ad..25fecab 100644 --- a/cxxmph/mph_map.h +++ b/cxxmph/mph_map.h @@ -9,8 +9,9 @@ // // See http://www.strchr.com/crc32_popcnt and new Murmur3 function to try to beat stl -#include #include +#include +#include #include #include #include // for std::pair @@ -100,17 +101,19 @@ class mph_map { return hollow_const_iterator>(&values_, &present_, it); } - static void set_2bit_value(uint8_t *d, uint32_t i, uint8_t v) { - d[(i >> 2)] &= ((v << ((i & 3) << 1)) | valuemask[i & 3]); - } - static uint32_t get_2bit_value(const uint8_t* d, uint32_t i) { - return (d[(i >> 2)] >> (((i & 3) << 1)) & 3); + iterator slow_find(const key_type& k); + const_iterator slow_find(const key_type& k) const; + static const uint8_t kNestCollision = 3; // biggest 2 bit value + uint32_t nest_index(const key_type& k, uint32_t* h) const { + index_.hash_vector(k, h); + // Use a pivot to prevent branch in the fast path + return h[3] % (index_.perfect_hash_size() + 1); } void pack(); std::vector values_; std::vector present_; - const uint8_t* nests_; + std::vector nests_; SimpleMPHIndex::hash_function> index_; // TODO(davi) optimize slack to no hold a copy of the key typedef unordered_map slack_type; @@ -124,6 +127,7 @@ bool operator==(const MPH_MAP_CLASS_SPEC& lhs, const MPH_MAP_CLASS_SPEC& rhs) { } MPH_MAP_TMPL_SPEC MPH_MAP_CLASS_SPEC::mph_map() : size_(0) { + clear(); pack(); } @@ -140,6 +144,10 @@ MPH_MAP_METHOD_DECL(insert_return_type, insert)(const value_type& x) { } values_.push_back(x); present_.push_back(true); + nests_.resize(ceil(values_.size() / 2.0), std::numeric_limits::max()); + uint32_t h[4]; + auto index = nest_index(x.first, h); + set_2bit_value(&(nests_[0]), index, kNestCollision); ++size_; slack_.insert(make_pair(x.first, values_.size() - 1)); if (should_pack) pack(); @@ -157,14 +165,28 @@ MPH_MAP_METHOD_DECL(void_type, pack)() { new_values.reserve(new_values.size() * 2); std::vector new_present(index_.perfect_hash_size(), false); new_present.reserve(new_present.size() * 2); + std::vector new_nests(ceil(index_.perfect_hash_size() / 2.0), std::numeric_limits::max()); + new_nests.reserve(new_nests.size() * 2); + vector used_nests(new_nests.size() * 2); for (iterator it = begin(), it_end = end(); it != it_end; ++it) { size_type id = index_.perfect_hash(it->first); assert(id < new_values.size()); new_values[id] = *it; new_present[id] = true; + uint32_t h[4]; + uint32_t index = nest_index(it->first, h); + if (used_nests[index]) { + set_2bit_value(&(new_nests[0]), index, kNestCollision); + } + else { + set_2bit_value(&(new_nests[0]), index, index_.cuckoo_nest(it->first, h)); + assert(index_.perfect_hash(it->first) == index_.cuckoo_hash(h, index_.cuckoo_nest(it->first, h))); + used_nests[index] = true; + } } values_.swap(new_values); present_.swap(new_present); + nests_.swap(new_nests); slack_type().swap(slack_); } @@ -180,11 +202,15 @@ MPH_MAP_METHOD_DECL(void_type, clear)() { present_.clear(); slack_.clear(); index_.clear(); + nests_.clear(); + nests_.push_back(std::numeric_limits::max()); size_ = 0; } MPH_MAP_METHOD_DECL(void_type, erase)(iterator pos) { present_[pos - begin] = false; + uint32_t h[4]; + nests_[nest_index(pos->first, h)] = kNestCollision; *pos = value_type(); --size_; } @@ -196,7 +222,7 @@ MPH_MAP_METHOD_DECL(void_type, erase)(const key_type& k) { MPH_MAP_METHOD_DECL(const_iterator, find)(const key_type& k) const { uint32_t h[4]; - auto nest = nests_[index_.hash_vector(k, reinterpret_cast(&h))]; + auto nest = get_2bit_value(&(nests_[0]), nest_index(k, h)); if (nest != kNestCollision) { auto vit = values_.begin() + h[nest]; if (equal_(k, vit->first)) return make_iterator(vit); @@ -205,37 +231,44 @@ MPH_MAP_METHOD_DECL(const_iterator, find)(const key_type& k) const { } MPH_MAP_METHOD_DECL(const_iterator, slow_find)(const key_type& k) const { - auto id = index_.perfect_hash(k); - if (!present_[id]) return end(); - auto vit = values_.begin() + id; - if (equal_(k, vit->first)) return make_iterator(vit); - + if (index_.perfect_hash_size()) { + auto id = index_.perfect_hash(k); + if (present_[id]) { + auto vit = values_.begin() + id; + if (equal_(k, vit->first)) return make_iterator(vit); + } + } if (__builtin_expect(!slack_.empty(), 0)) { auto sit = slack_.find(k); - if (it != slack_.end()) return make_iterator(values_.begin() + sit->second); + if (sit != slack_.end()) return make_iterator(values_.begin() + sit->second); } return end(); } MPH_MAP_METHOD_DECL(iterator, find)(const key_type& k) { uint32_t h[4]; - auto nest = nests_[index_.hash_vector(k, reinterpret_cast(&h))]; + auto index = nest_index(k, h); + assert(nests_.size()); + assert(nests_.size() > index / 2); + auto nest = get_2bit_value(&(nests_[0]), index); if (nest != kNestCollision) { - auto vit = values_.begin() + h[nest]; + auto vit = values_.begin() + index_.cuckoo_hash(h, nest); if (equal_(k, vit->first)) return make_iterator(vit); } return slow_find(k); } MPH_MAP_METHOD_DECL(iterator, slow_find)(const key_type& k) { - auto id = index_.perfect_hash(k); - if (!present_[id]) return end(); - auto vit = values_.begin() + id; - if (equal_(k, vit->first)) return make_iterator(vit); - + if (index_.perfect_hash_size()) { + auto id = index_.perfect_hash(k); + if (present_[id]) { + auto vit = values_.begin() + id; + if (equal_(k, vit->first)) return make_iterator(vit); + } + } if (__builtin_expect(!slack_.empty(), 0)) { auto sit = slack_.find(k); - if (it != slack_.end()) return make_iterator(values_.begin() + sit->second); + if (sit != slack_.end()) return make_iterator(values_.begin() + sit->second); } return end(); } diff --git a/cxxmph/mph_map_test.cc b/cxxmph/mph_map_test.cc index 11bfbc9..ada71b3 100644 --- a/cxxmph/mph_map_test.cc +++ b/cxxmph/mph_map_test.cc @@ -17,6 +17,10 @@ int main(int argc, char** argv) { } for (int i = 0; i < num_keys; ++i) { auto it = b.find(i); + if (it == b.end()) { + std::cerr << "Failed to find " << i << std::endl; + exit(-1); + } if (it->first != it->second || it->first != i) { std::cerr << "Found " << it->first << " looking for " << i << std::endl; exit(-1); From 687cc1b194742ad7f8fcfedab4a07a5f3bdae8a7 Mon Sep 17 00:00:00 2001 From: Davi Reis Date: Wed, 14 Mar 2012 11:58:37 -0300 Subject: [PATCH 09/16] Added cuckoo stuff, uint64 became slower again. --- cxxmph/bm_map.cc | 2 - cxxmph/mph_bits.h | 7 ++++ cxxmph/mph_index.h | 16 +++++--- cxxmph/mph_map.h | 98 +++++++++++++++++++++++++++------------------- 4 files changed, 74 insertions(+), 49 deletions(-) diff --git a/cxxmph/bm_map.cc b/cxxmph/bm_map.cc index 44e3fe7..0a0b225 100644 --- a/cxxmph/bm_map.cc +++ b/cxxmph/bm_map.cc @@ -89,12 +89,10 @@ using namespace cxxmph; int main(int argc, char** argv) { srandom(4); - /* Benchmark::Register(new BM_CreateUrls>("URLS100k")); Benchmark::Register(new BM_CreateUrls>("URLS100k")); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0)); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0)); - */ Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0.9)); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0.9)); Benchmark::Register(new BM_SearchUint64>); diff --git a/cxxmph/mph_bits.h b/cxxmph/mph_bits.h index 03f7c08..6de8168 100644 --- a/cxxmph/mph_bits.h +++ b/cxxmph/mph_bits.h @@ -2,6 +2,7 @@ #define __CXXMPH_MPH_BITS_H__ #include // for uint32_t and friends +#include namespace cxxmph { @@ -12,6 +13,12 @@ static void set_2bit_value(uint8_t *d, uint32_t i, uint8_t v) { static uint32_t get_2bit_value(const uint8_t* d, uint32_t i) { return (d[(i >> 2)] >> (((i & 3) << 1)) & 3); } +static uint32_t nextpoweroftwo(uint32_t k) { + if (k == 0) return 1; + k--; + for (int i=1; i> i; + return k+1; +} } // namespace cxxmph diff --git a/cxxmph/mph_index.h b/cxxmph/mph_index.h index ad1d7f6..deccf22 100644 --- a/cxxmph/mph_index.h +++ b/cxxmph/mph_index.h @@ -68,8 +68,8 @@ class MPHIndex { // Crazy functions. Ignore. template // must agree with Reset uint32_t cuckoo_hash(const uint32_t* h, uint8_t nest) const; - template // must agree with Reset - uint8_t cuckoo_nest(const Key& x, const uint32_t* h) const; + template // must agree with Reset + uint8_t cuckoo_nest(const uint32_t* h) const; template // must agree with Reset uint32_t cuckoo_nest_index(const Key& x, uint32_t* h) const; template // must agree with Reset @@ -197,24 +197,28 @@ void MPHIndex::hash_vector(const Key& key, uint32_t* h) const { SeededHashFcn().hash64(key, hash_seed_[0], h); } -template -uint8_t MPHIndex::cuckoo_nest(const Key& key, const uint32_t* h) const { +template // must agree with Reset +uint8_t MPHIndex::cuckoo_nest(const uint32_t* h) const { uint32_t x[4]; + if (!g_size_) return 0; x[0] = (h[0] % r_) + nest_displacement_[0]; x[1] = (h[1] % r_) + nest_displacement_[1]; x[2] = (h[2] % r_) + nest_displacement_[2]; + assert((x[0] >> 2) > 2) > 2) uint32_t MPHIndex::perfect_hash(const Key& key) const { uint32_t h[4]; + if (!g_size_) return 0; SeededHashFcn().hash64(key, hash_seed_[0], h); // for (int i = 0; i < 3; ++i) h[i] = SeededHashFcn()(key, hash_seed_[i]); h[0] = (h[0] % r_) + nest_displacement_[0]; h[1] = (h[1] % r_) + nest_displacement_[1]; h[2] = (h[2] % r_) + nest_displacement_[2]; - if (!g_size_) return 0; // cerr << "g_.size() " << g_size_ << " h0 >> 2 " << (h[0] >> 2) << endl; assert((h[0] >> 2) > 2) (key); } uint32_t perfect_hash(const Key& key) const { return MPHIndex::perfect_hash(key); } uint32_t minimal_perfect_hash(const Key& key) const { return MPHIndex::minimal_perfect_hash(key); } - uint8_t cuckoo_nest(const Key& key, const uint32_t* h) const { return MPHIndex::cuckoo_nest(key, h); } + uint8_t cuckoo_nest(const uint32_t* h) const { return MPHIndex::cuckoo_nest(h); } uint32_t cuckoo_hash(const uint32_t* h, uint8_t nest) const { return MPHIndex::cuckoo_hash(h, nest); } void hash_vector(const Key& key, uint32_t* h) const { MPHIndex::hash_vector(key, h); } }; diff --git a/cxxmph/mph_map.h b/cxxmph/mph_map.h index 25fecab..687315e 100644 --- a/cxxmph/mph_map.h +++ b/cxxmph/mph_map.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include // for std::pair @@ -101,13 +102,19 @@ class mph_map { return hollow_const_iterator>(&values_, &present_, it); } - iterator slow_find(const key_type& k); - const_iterator slow_find(const key_type& k) const; + iterator slow_find(const key_type& k, uint32_t perfect_hash); + const_iterator slow_find(const key_type& k, uint32_t perfect_hash) const; static const uint8_t kNestCollision = 3; // biggest 2 bit value - uint32_t nest_index(const key_type& k, uint32_t* h) const { - index_.hash_vector(k, h); - // Use a pivot to prevent branch in the fast path - return h[3] % (index_.perfect_hash_size() + 1); + void set_nest_value(const uint32_t* h, uint8_t value) { + assert(get_nest_index(h) < nests_.size() * 4); + set_2bit_value(&(nests_[0]), get_nest_index(h), value); + } + uint32_t get_nest_value(const uint32_t* h) const { + assert(get_nest_index(h) < nests_.size() * 4); + return get_2bit_value(&(nests_[0]), get_nest_index(h)); + } + uint32_t get_nest_index(const uint32_t* h) const { + return h[3] & ((nests_.size() << 2) - 1); } void pack(); @@ -144,10 +151,11 @@ MPH_MAP_METHOD_DECL(insert_return_type, insert)(const value_type& x) { } values_.push_back(x); present_.push_back(true); - nests_.resize(ceil(values_.size() / 2.0), std::numeric_limits::max()); + auto nests_size = nextpoweroftwo(ceil(values_.size() / 4.0) + 1)*10; + nests_.resize(nests_size, std::numeric_limits::max()); uint32_t h[4]; - auto index = nest_index(x.first, h); - set_2bit_value(&(nests_[0]), index, kNestCollision); + index_.hash_vector(x.first, h); + set_nest_value(h, kNestCollision); ++size_; slack_.insert(make_pair(x.first, values_.size() - 1)); if (should_pack) pack(); @@ -157,6 +165,7 @@ MPH_MAP_METHOD_DECL(insert_return_type, insert)(const value_type& x) { MPH_MAP_METHOD_DECL(void_type, pack)() { if (values_.empty()) return; + assert(std::unordered_set(make_iterator_first(begin()), make_iterator_first(end())).size() == size()); bool success = index_.Reset( make_iterator_first(begin()), make_iterator_first(end()), size_); @@ -165,28 +174,33 @@ MPH_MAP_METHOD_DECL(void_type, pack)() { new_values.reserve(new_values.size() * 2); std::vector new_present(index_.perfect_hash_size(), false); new_present.reserve(new_present.size() * 2); - std::vector new_nests(ceil(index_.perfect_hash_size() / 2.0), std::numeric_limits::max()); + auto new_nests_size = nextpoweroftwo(ceil(new_values.size() / 4.0) + 1)*10; + std::vector new_nests(new_nests_size, std::numeric_limits::max()); new_nests.reserve(new_nests.size() * 2); - vector used_nests(new_nests.size() * 2); + nests_.swap(new_nests); + vector used_nests(nests_.size() * 4); + uint32_t collisions = 0; for (iterator it = begin(), it_end = end(); it != it_end; ++it) { size_type id = index_.perfect_hash(it->first); assert(id < new_values.size()); new_values[id] = *it; new_present[id] = true; uint32_t h[4]; - uint32_t index = nest_index(it->first, h); - if (used_nests[index]) { - set_2bit_value(&(new_nests[0]), index, kNestCollision); - } - else { - set_2bit_value(&(new_nests[0]), index, index_.cuckoo_nest(it->first, h)); - assert(index_.perfect_hash(it->first) == index_.cuckoo_hash(h, index_.cuckoo_nest(it->first, h))); - used_nests[index] = true; + index_.hash_vector(it->first, h); + // fprintf(stderr, "Nest index: %d\n", get_nest_index(h)); + assert(used_nests.size() > get_nest_index(h)); + if (used_nests[get_nest_index(h)]) { + set_nest_value(h, kNestCollision); + ++collisions; + } else { + set_nest_value(h, index_.cuckoo_nest(h)); + assert(index_.perfect_hash(it->first) == index_.cuckoo_hash(h, index_.cuckoo_nest(h))); + used_nests[get_nest_index(h)] = true; } } + fprintf(stderr, "Collision ratio: %f\n", collisions*1.0/size()); values_.swap(new_values); present_.swap(new_present); - nests_.swap(new_nests); slack_type().swap(slack_); } @@ -210,7 +224,8 @@ MPH_MAP_METHOD_DECL(void_type, clear)() { MPH_MAP_METHOD_DECL(void_type, erase)(iterator pos) { present_[pos - begin] = false; uint32_t h[4]; - nests_[nest_index(pos->first, h)] = kNestCollision; + index_.hash_vector(pos->first, &h); + nests_[get_nest_index(h)] = kNestCollision; *pos = value_type(); --size_; } @@ -222,19 +237,21 @@ MPH_MAP_METHOD_DECL(void_type, erase)(const key_type& k) { MPH_MAP_METHOD_DECL(const_iterator, find)(const key_type& k) const { uint32_t h[4]; - auto nest = get_2bit_value(&(nests_[0]), nest_index(k, h)); - if (nest != kNestCollision) { - auto vit = values_.begin() + h[nest]; + index_.hash_vector(k, h); + auto nest = get_nest_value(h); + if (__builtin_expect(nest != kNestCollision, 1)) { + auto vit = values_.begin() + index_.cuckoo_hash(h, nest); if (equal_(k, vit->first)) return make_iterator(vit); } - return slow_find(k); + nest = index_.cuckoo_nest(h); + assert(index_.perfect_hash(k) == index_.cuckoo_hash(h, nest)); + return slow_find(k, index_.cuckoo_hash(h, nest)); } -MPH_MAP_METHOD_DECL(const_iterator, slow_find)(const key_type& k) const { - if (index_.perfect_hash_size()) { - auto id = index_.perfect_hash(k); - if (present_[id]) { - auto vit = values_.begin() + id; +MPH_MAP_METHOD_DECL(const_iterator, slow_find)(const key_type& k, uint32_t perfect_hash) const { + if (__builtin_expect(index_.perfect_hash_size(), 0)) { + if (__builtin_expect(present_[perfect_hash], true)) { + auto vit = values_.begin() + perfect_hash; if (equal_(k, vit->first)) return make_iterator(vit); } } @@ -247,22 +264,21 @@ MPH_MAP_METHOD_DECL(const_iterator, slow_find)(const key_type& k) const { MPH_MAP_METHOD_DECL(iterator, find)(const key_type& k) { uint32_t h[4]; - auto index = nest_index(k, h); - assert(nests_.size()); - assert(nests_.size() > index / 2); - auto nest = get_2bit_value(&(nests_[0]), index); - if (nest != kNestCollision) { + index_.hash_vector(k, h); + auto nest = get_nest_value(h); + if (__builtin_expect(nest != kNestCollision, 1)) { auto vit = values_.begin() + index_.cuckoo_hash(h, nest); if (equal_(k, vit->first)) return make_iterator(vit); } - return slow_find(k); + nest = index_.cuckoo_nest(h); + // assert(index_.perfect_hash(k) == index_.cuckoo_hash(h, nest)); + return slow_find(k, index_.cuckoo_hash(h, nest)); } -MPH_MAP_METHOD_DECL(iterator, slow_find)(const key_type& k) { - if (index_.perfect_hash_size()) { - auto id = index_.perfect_hash(k); - if (present_[id]) { - auto vit = values_.begin() + id; +MPH_MAP_METHOD_DECL(iterator, slow_find)(const key_type& k, uint32_t perfect_hash) { + if (__builtin_expect(index_.perfect_hash_size(), 0)) { + if (__builtin_expect(present_[perfect_hash], true)) { + auto vit = values_.begin() + perfect_hash; if (equal_(k, vit->first)) return make_iterator(vit); } } From 9c4bb27dc426e95e7e2923dc7fe132ddbf3895f0 Mon Sep 17 00:00:00 2001 From: Davi Reis Date: Wed, 14 Mar 2012 12:07:08 -0300 Subject: [PATCH 10/16] Disabled cuckoo stuff to beat STL again. --- cxxmph/mph_map.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cxxmph/mph_map.h b/cxxmph/mph_map.h index 687315e..37cd2d1 100644 --- a/cxxmph/mph_map.h +++ b/cxxmph/mph_map.h @@ -236,6 +236,8 @@ MPH_MAP_METHOD_DECL(void_type, erase)(const key_type& k) { } MPH_MAP_METHOD_DECL(const_iterator, find)(const key_type& k) const { + return slow_find(k, index_.perfect_hash(k)); + /* uint32_t h[4]; index_.hash_vector(k, h); auto nest = get_nest_value(h); @@ -246,6 +248,7 @@ MPH_MAP_METHOD_DECL(const_iterator, find)(const key_type& k) const { nest = index_.cuckoo_nest(h); assert(index_.perfect_hash(k) == index_.cuckoo_hash(h, nest)); return slow_find(k, index_.cuckoo_hash(h, nest)); + */ } MPH_MAP_METHOD_DECL(const_iterator, slow_find)(const key_type& k, uint32_t perfect_hash) const { @@ -263,6 +266,8 @@ MPH_MAP_METHOD_DECL(const_iterator, slow_find)(const key_type& k, uint32_t perfe } MPH_MAP_METHOD_DECL(iterator, find)(const key_type& k) { + return slow_find(k, index_.perfect_hash(k)); + /* uint32_t h[4]; index_.hash_vector(k, h); auto nest = get_nest_value(h); @@ -273,6 +278,7 @@ MPH_MAP_METHOD_DECL(iterator, find)(const key_type& k) { nest = index_.cuckoo_nest(h); // assert(index_.perfect_hash(k) == index_.cuckoo_hash(h, nest)); return slow_find(k, index_.cuckoo_hash(h, nest)); + */ } MPH_MAP_METHOD_DECL(iterator, slow_find)(const key_type& k, uint32_t perfect_hash) { From b63f6182045af009a150a471c295cbe6e4ad2ae4 Mon Sep 17 00:00:00 2001 From: Davi Reis Date: Wed, 14 Mar 2012 12:40:50 -0300 Subject: [PATCH 11/16] bit methods need tests. --- cxxmph/mph_map.h | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/cxxmph/mph_map.h b/cxxmph/mph_map.h index 37cd2d1..6886cb3 100644 --- a/cxxmph/mph_map.h +++ b/cxxmph/mph_map.h @@ -106,8 +106,12 @@ class mph_map { const_iterator slow_find(const key_type& k, uint32_t perfect_hash) const; static const uint8_t kNestCollision = 3; // biggest 2 bit value void set_nest_value(const uint32_t* h, uint8_t value) { + auto index = get_nest_index(h); assert(get_nest_index(h) < nests_.size() * 4); - set_2bit_value(&(nests_[0]), get_nest_index(h), value); + assert(get_nest_index(h) >> 2 < nests_.size()); + assert(value < 4); + set_2bit_value(&nests_[0], index, value); + assert(get_2bit_value(&nests_[0], index) == value); } uint32_t get_nest_value(const uint32_t* h) const { assert(get_nest_index(h) < nests_.size() * 4); @@ -151,8 +155,6 @@ MPH_MAP_METHOD_DECL(insert_return_type, insert)(const value_type& x) { } values_.push_back(x); present_.push_back(true); - auto nests_size = nextpoweroftwo(ceil(values_.size() / 4.0) + 1)*10; - nests_.resize(nests_size, std::numeric_limits::max()); uint32_t h[4]; index_.hash_vector(x.first, h); set_nest_value(h, kNestCollision); @@ -191,13 +193,20 @@ MPH_MAP_METHOD_DECL(void_type, pack)() { assert(used_nests.size() > get_nest_index(h)); if (used_nests[get_nest_index(h)]) { set_nest_value(h, kNestCollision); + assert(get_nest_value(h) == kNestCollision); ++collisions; } else { set_nest_value(h, index_.cuckoo_nest(h)); - assert(index_.perfect_hash(it->first) == index_.cuckoo_hash(h, index_.cuckoo_nest(h))); + assert(get_nest_value(h) == index_.cuckoo_nest(h)); + assert(index_.perfect_hash(it->first) == index_.cuckoo_hash(h, get_nest_value(h))); used_nests[get_nest_index(h)] = true; } } + for (iterator it = begin(), it_end = end(); it != it_end; ++it) { + uint32_t h[4]; + index_.hash_vector(it->first, h); + assert(get_nest_value(h) == kNestCollision || index_.perfect_hash(it->first) == index_.cuckoo_hash(h, get_nest_value(h))); + } fprintf(stderr, "Collision ratio: %f\n", collisions*1.0/size()); values_.swap(new_values); present_.swap(new_present); @@ -266,19 +275,22 @@ MPH_MAP_METHOD_DECL(const_iterator, slow_find)(const key_type& k, uint32_t perfe } MPH_MAP_METHOD_DECL(iterator, find)(const key_type& k) { - return slow_find(k, index_.perfect_hash(k)); - /* + // return slow_find(k, index_.perfect_hash(k)); uint32_t h[4]; index_.hash_vector(k, h); auto nest = get_nest_value(h); if (__builtin_expect(nest != kNestCollision, 1)) { auto vit = values_.begin() + index_.cuckoo_hash(h, nest); - if (equal_(k, vit->first)) return make_iterator(vit); + assert(index_.perfect_hash(k) == index_.cuckoo_hash(h, nest)); + if (equal_(k, vit->first)) { + fprintf(stderr, "fast\n"); + return make_iterator(vit); + } } nest = index_.cuckoo_nest(h); + fprintf(stderr, "slow\n"); // assert(index_.perfect_hash(k) == index_.cuckoo_hash(h, nest)); return slow_find(k, index_.cuckoo_hash(h, nest)); - */ } MPH_MAP_METHOD_DECL(iterator, slow_find)(const key_type& k, uint32_t perfect_hash) { From 0335cbe6793d37b83c41bba44d1ce860090af21f Mon Sep 17 00:00:00 2001 From: Davi Reis Date: Wed, 14 Mar 2012 16:43:38 -0300 Subject: [PATCH 12/16] struggle --- cxxmph/Makefile.am | 4 +++- cxxmph/mph_bits.cc | 3 +++ cxxmph/mph_bits.h | 54 ++++++++++++++++++++++++++++++++++++++++++++-- cxxmph/mph_map.h | 2 +- 4 files changed, 59 insertions(+), 4 deletions(-) diff --git a/cxxmph/Makefile.am b/cxxmph/Makefile.am index 22c0bb2..db8ffa1 100644 --- a/cxxmph/Makefile.am +++ b/cxxmph/Makefile.am @@ -1,5 +1,5 @@ TESTS = $(check_PROGRAMS) -check_PROGRAMS = hollow_iterator_test mph_map_test mph_index_test trigraph_test +check_PROGRAMS = mph_bits_test hollow_iterator_test mph_map_test mph_index_test trigraph_test noinst_PROGRAMS = bm_index bm_map bin_PROGRAMS = cxxmph lib_LTLIBRARIES = libcxxmph.la @@ -27,4 +27,6 @@ cxxmph_LDADD = libcxxmph.la cxxmph_SOURCES = cxxmph.cc hollow_iterator_test_SOURCES = hollow_iterator_test.cc +mph_bits_test_SOURCES = mph_bits_test.cc +mph_bits_test_LDADD = libcxxmph.la diff --git a/cxxmph/mph_bits.cc b/cxxmph/mph_bits.cc index 9fb97bd..510572c 100644 --- a/cxxmph/mph_bits.cc +++ b/cxxmph/mph_bits.cc @@ -1,4 +1,7 @@ #include "mph_bits.h" namespace cxxmph { + +const uint8_t dynamic_2bitset::vmask[] = { 0xfc, 0xf3, 0xcf, 0x3f}; + } diff --git a/cxxmph/mph_bits.h b/cxxmph/mph_bits.h index 6de8168..7dcf0be 100644 --- a/cxxmph/mph_bits.h +++ b/cxxmph/mph_bits.h @@ -2,13 +2,63 @@ #define __CXXMPH_MPH_BITS_H__ #include // for uint32_t and friends +#include #include +#include +#include +#include +#include namespace cxxmph { -static const uint8_t valuemask[] = { 0xfc, 0xf3, 0xcf, 0x3f}; +class dynamic_2bitset { + public: + dynamic_2bitset() : data_(NULL), size_(0), one_initialized_(false) {} + dynamic_2bitset(uint32_t size, bool one_initialized = false) + : data_(NULL), size_(0), one_initialized_(one_initialized) { + resize(size); + } + ~dynamic_2bitset() { delete [] data_; } + + const uint8_t operator[](uint32_t i) const { return get(i); } + uint8_t get(uint32_t i) const { + return (data_[(i >> 2)] >> (((i & 3) << 1)) & 3); + } + uint8_t set(uint32_t i, uint8_t v) { + uint8_t sf = ((v << ((i & 3) << 1)) | dynamic_2bitset::vmask[i & 3]); + fprintf(stderr, "v %d sf %d\n", v, sf); + data_[(i >> 2)] &= ((v << ((i & 3) << 1)) | dynamic_2bitset::vmask[i & 3]); + assert(get(i) == v); + } + void resize(uint32_t size) { + uint8_t* new_data = new uint8_t[size << 2]; + assert(one_initialized_); + assert(one_initialized_ * ones() == ones()); + memset(new_data, one_initialized_*ones(), size << 2); + assert(new_data[0] == ones()); + uint8_t* old_data_ = data_; + for (int i = 0; i < size_; ++i) { + data_ = old_data_; + auto v = get(i); + data_ = new_data; + set(i, v); + } + size_ = size; + delete [] old_data_; + data_ = new_data; + assert(data_[0] == ones()); + assert(get(0) == 3); + } + static const uint8_t vmask[]; + private: + uint8_t* data_; + uint32_t size_; + bool one_initialized_; + uint8_t ones() { return std::numeric_limits::max(); } +}; + static void set_2bit_value(uint8_t *d, uint32_t i, uint8_t v) { - d[(i >> 2)] &= ((v << ((i & 3) << 1)) | valuemask[i & 3]); + d[(i >> 2)] &= ((v << ((i & 3) << 1)) | dynamic_2bitset::vmask[i & 3]); } static uint32_t get_2bit_value(const uint8_t* d, uint32_t i) { return (d[(i >> 2)] >> (((i & 3) << 1)) & 3); diff --git a/cxxmph/mph_map.h b/cxxmph/mph_map.h index 6886cb3..caddf12 100644 --- a/cxxmph/mph_map.h +++ b/cxxmph/mph_map.h @@ -126,7 +126,7 @@ class mph_map { std::vector present_; std::vector nests_; SimpleMPHIndex::hash_function> index_; - // TODO(davi) optimize slack to no hold a copy of the key + // TODO(davi) optimize slack to hold 128 unique bits from hash64 as key typedef unordered_map slack_type; slack_type slack_; size_type size_; From b96b71961d483a796bcbc77edb2e99fd25213e77 Mon Sep 17 00:00:00 2001 From: Davi Reis Date: Wed, 14 Mar 2012 16:44:16 -0300 Subject: [PATCH 13/16] struggle --- cxxmph/MurmurHash3.cpp | 335 ++++++++++++++++++++++++++++++++++++++++ cxxmph/MurmurHash3.h | 37 +++++ cxxmph/mph_bits_test.cc | 32 ++++ 3 files changed, 404 insertions(+) create mode 100644 cxxmph/MurmurHash3.cpp create mode 100644 cxxmph/MurmurHash3.h create mode 100644 cxxmph/mph_bits_test.cc diff --git a/cxxmph/MurmurHash3.cpp b/cxxmph/MurmurHash3.cpp new file mode 100644 index 0000000..09ffb26 --- /dev/null +++ b/cxxmph/MurmurHash3.cpp @@ -0,0 +1,335 @@ +//----------------------------------------------------------------------------- +// MurmurHash3 was written by Austin Appleby, and is placed in the public +// domain. The author hereby disclaims copyright to this source code. + +// Note - The x86 and x64 versions do _not_ produce the same results, as the +// algorithms are optimized for their respective platforms. You can still +// compile and run any of them on any platform, but your performance with the +// non-native version will be less than optimal. + +#include "MurmurHash3.h" + +//----------------------------------------------------------------------------- +// Platform-specific functions and macros + +// Microsoft Visual Studio + +#if defined(_MSC_VER) + +#define FORCE_INLINE __forceinline + +#include + +#define ROTL32(x,y) _rotl(x,y) +#define ROTL64(x,y) _rotl64(x,y) + +#define BIG_CONSTANT(x) (x) + +// Other compilers + +#else // defined(_MSC_VER) + +#define FORCE_INLINE __attribute__((always_inline)) + +inline uint32_t rotl32 ( uint32_t x, int8_t r ) +{ + return (x << r) | (x >> (32 - r)); +} + +inline uint64_t rotl64 ( uint64_t x, int8_t r ) +{ + return (x << r) | (x >> (64 - r)); +} + +#define ROTL32(x,y) rotl32(x,y) +#define ROTL64(x,y) rotl64(x,y) + +#define BIG_CONSTANT(x) (x##LLU) + +#endif // !defined(_MSC_VER) + +//----------------------------------------------------------------------------- +// Block read - if your platform needs to do endian-swapping or can only +// handle aligned reads, do the conversion here + +FORCE_INLINE uint32_t getblock ( const uint32_t * p, int i ) +{ + return p[i]; +} + +FORCE_INLINE uint64_t getblock ( const uint64_t * p, int i ) +{ + return p[i]; +} + +//----------------------------------------------------------------------------- +// Finalization mix - force all bits of a hash block to avalanche + +FORCE_INLINE uint32_t fmix ( uint32_t h ) +{ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + return h; +} + +//---------- + +FORCE_INLINE uint64_t fmix ( uint64_t k ) +{ + k ^= k >> 33; + k *= BIG_CONSTANT(0xff51afd7ed558ccd); + k ^= k >> 33; + k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53); + k ^= k >> 33; + + return k; +} + +//----------------------------------------------------------------------------- + +void MurmurHash3_x86_32 ( const void * key, int len, + uint32_t seed, void * out ) +{ + const uint8_t * data = (const uint8_t*)key; + const int nblocks = len / 4; + + uint32_t h1 = seed; + + uint32_t c1 = 0xcc9e2d51; + uint32_t c2 = 0x1b873593; + + //---------- + // body + + const uint32_t * blocks = (const uint32_t *)(data + nblocks*4); + + for(int i = -nblocks; i; i++) + { + uint32_t k1 = getblock(blocks,i); + + k1 *= c1; + k1 = ROTL32(k1,15); + k1 *= c2; + + h1 ^= k1; + h1 = ROTL32(h1,13); + h1 = h1*5+0xe6546b64; + } + + //---------- + // tail + + const uint8_t * tail = (const uint8_t*)(data + nblocks*4); + + uint32_t k1 = 0; + + switch(len & 3) + { + case 3: k1 ^= tail[2] << 16; + case 2: k1 ^= tail[1] << 8; + case 1: k1 ^= tail[0]; + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + }; + + //---------- + // finalization + + h1 ^= len; + + h1 = fmix(h1); + + *(uint32_t*)out = h1; +} + +//----------------------------------------------------------------------------- + +void MurmurHash3_x86_128 ( const void * key, const int len, + uint32_t seed, void * out ) +{ + const uint8_t * data = (const uint8_t*)key; + const int nblocks = len / 16; + + uint32_t h1 = seed; + uint32_t h2 = seed; + uint32_t h3 = seed; + uint32_t h4 = seed; + + uint32_t c1 = 0x239b961b; + uint32_t c2 = 0xab0e9789; + uint32_t c3 = 0x38b34ae5; + uint32_t c4 = 0xa1e38b93; + + //---------- + // body + + const uint32_t * blocks = (const uint32_t *)(data + nblocks*16); + + for(int i = -nblocks; i; i++) + { + uint32_t k1 = getblock(blocks,i*4+0); + uint32_t k2 = getblock(blocks,i*4+1); + uint32_t k3 = getblock(blocks,i*4+2); + uint32_t k4 = getblock(blocks,i*4+3); + + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + + h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b; + + k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2; + + h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747; + + k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3; + + h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35; + + k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4; + + h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17; + } + + //---------- + // tail + + const uint8_t * tail = (const uint8_t*)(data + nblocks*16); + + uint32_t k1 = 0; + uint32_t k2 = 0; + uint32_t k3 = 0; + uint32_t k4 = 0; + + switch(len & 15) + { + case 15: k4 ^= tail[14] << 16; + case 14: k4 ^= tail[13] << 8; + case 13: k4 ^= tail[12] << 0; + k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4; + + case 12: k3 ^= tail[11] << 24; + case 11: k3 ^= tail[10] << 16; + case 10: k3 ^= tail[ 9] << 8; + case 9: k3 ^= tail[ 8] << 0; + k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3; + + case 8: k2 ^= tail[ 7] << 24; + case 7: k2 ^= tail[ 6] << 16; + case 6: k2 ^= tail[ 5] << 8; + case 5: k2 ^= tail[ 4] << 0; + k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2; + + case 4: k1 ^= tail[ 3] << 24; + case 3: k1 ^= tail[ 2] << 16; + case 2: k1 ^= tail[ 1] << 8; + case 1: k1 ^= tail[ 0] << 0; + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + }; + + //---------- + // finalization + + h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len; + + h1 += h2; h1 += h3; h1 += h4; + h2 += h1; h3 += h1; h4 += h1; + + h1 = fmix(h1); + h2 = fmix(h2); + h3 = fmix(h3); + h4 = fmix(h4); + + h1 += h2; h1 += h3; h1 += h4; + h2 += h1; h3 += h1; h4 += h1; + + ((uint32_t*)out)[0] = h1; + ((uint32_t*)out)[1] = h2; + ((uint32_t*)out)[2] = h3; + ((uint32_t*)out)[3] = h4; +} + +//----------------------------------------------------------------------------- + +void MurmurHash3_x64_128 ( const void * key, const int len, + const uint32_t seed, void * out ) +{ + const uint8_t * data = (const uint8_t*)key; + const int nblocks = len / 16; + + uint64_t h1 = seed; + uint64_t h2 = seed; + + uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5); + uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f); + + //---------- + // body + + const uint64_t * blocks = (const uint64_t *)(data); + + for(int i = 0; i < nblocks; i++) + { + uint64_t k1 = getblock(blocks,i*2+0); + uint64_t k2 = getblock(blocks,i*2+1); + + k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1; + + h1 = ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729; + + k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2; + + h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5; + } + + //---------- + // tail + + const uint8_t * tail = (const uint8_t*)(data + nblocks*16); + + uint64_t k1 = 0; + uint64_t k2 = 0; + + switch(len & 15) + { + case 15: k2 ^= uint64_t(tail[14]) << 48; + case 14: k2 ^= uint64_t(tail[13]) << 40; + case 13: k2 ^= uint64_t(tail[12]) << 32; + case 12: k2 ^= uint64_t(tail[11]) << 24; + case 11: k2 ^= uint64_t(tail[10]) << 16; + case 10: k2 ^= uint64_t(tail[ 9]) << 8; + case 9: k2 ^= uint64_t(tail[ 8]) << 0; + k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2; + + case 8: k1 ^= uint64_t(tail[ 7]) << 56; + case 7: k1 ^= uint64_t(tail[ 6]) << 48; + case 6: k1 ^= uint64_t(tail[ 5]) << 40; + case 5: k1 ^= uint64_t(tail[ 4]) << 32; + case 4: k1 ^= uint64_t(tail[ 3]) << 24; + case 3: k1 ^= uint64_t(tail[ 2]) << 16; + case 2: k1 ^= uint64_t(tail[ 1]) << 8; + case 1: k1 ^= uint64_t(tail[ 0]) << 0; + k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1; + }; + + //---------- + // finalization + + h1 ^= len; h2 ^= len; + + h1 += h2; + h2 += h1; + + h1 = fmix(h1); + h2 = fmix(h2); + + h1 += h2; + h2 += h1; + + ((uint64_t*)out)[0] = h1; + ((uint64_t*)out)[1] = h2; +} + +//----------------------------------------------------------------------------- + diff --git a/cxxmph/MurmurHash3.h b/cxxmph/MurmurHash3.h new file mode 100644 index 0000000..54e9d3f --- /dev/null +++ b/cxxmph/MurmurHash3.h @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// MurmurHash3 was written by Austin Appleby, and is placed in the public +// domain. The author hereby disclaims copyright to this source code. + +#ifndef _MURMURHASH3_H_ +#define _MURMURHASH3_H_ + +//----------------------------------------------------------------------------- +// Platform-specific functions and macros + +// Microsoft Visual Studio + +#if defined(_MSC_VER) + +typedef unsigned char uint8_t; +typedef unsigned long uint32_t; +typedef unsigned __int64 uint64_t; + +// Other compilers + +#else // defined(_MSC_VER) + +#include + +#endif // !defined(_MSC_VER) + +//----------------------------------------------------------------------------- + +void MurmurHash3_x86_32 ( const void * key, int len, uint32_t seed, void * out ); + +void MurmurHash3_x86_128 ( const void * key, int len, uint32_t seed, void * out ); + +void MurmurHash3_x64_128 ( const void * key, int len, uint32_t seed, void * out ); + +//----------------------------------------------------------------------------- + +#endif // _MURMURHASH3_H_ diff --git a/cxxmph/mph_bits_test.cc b/cxxmph/mph_bits_test.cc new file mode 100644 index 0000000..c828f56 --- /dev/null +++ b/cxxmph/mph_bits_test.cc @@ -0,0 +1,32 @@ +#include +#include + +#include "mph_bits.h" + +using cxxmph::dynamic_2bitset; +int main(int argc, char** argv) { + int size = 256; + dynamic_2bitset bits(size, true /* fill with ones */); + for (int i = 0; i < size; ++i) { + if (bits[i] != 3) { + fprintf(stderr, "wrong bits %d at %d expected %d\n", bits[i], i, 3); + exit(-1); + } + } + for (int i = 0; i < size; ++i) bits.set(i, 0); + for (int i = 0; i < size; ++i) { + if (bits[i] != 0) { + fprintf(stderr, "wrong bits %d at %d expected %d\n", bits[i], i, 0); + exit(-1); + } + } + for (int i = 0; i < size; ++i) bits.set(i, i % 4); + for (int i = 0; i < size; ++i) { + if (bits[i] != i % 4) { + fprintf(stderr, "wrong bits %d at %d expected %d\n", bits[i], i, i % 4); + exit(-1); + } + } +} + + From e3ccde3ba048c91a12c2bb4d65ad1e31f150c665 Mon Sep 17 00:00:00 2001 From: Davi Reis Date: Wed, 14 Mar 2012 18:26:26 -0300 Subject: [PATCH 14/16] Working, but it sucks. --- cxxmph/mph_bits.h | 42 +++++++++++++------------------- cxxmph/mph_bits_test.cc | 17 +++++++++++++ cxxmph/mph_map.h | 54 +++++++++++++++++++++-------------------- 3 files changed, 62 insertions(+), 51 deletions(-) diff --git a/cxxmph/mph_bits.h b/cxxmph/mph_bits.h index 7dcf0be..06b2946 100644 --- a/cxxmph/mph_bits.h +++ b/cxxmph/mph_bits.h @@ -4,6 +4,7 @@ #include // for uint32_t and friends #include #include +#include #include #include #include @@ -13,47 +14,38 @@ namespace cxxmph { class dynamic_2bitset { public: - dynamic_2bitset() : data_(NULL), size_(0), one_initialized_(false) {} - dynamic_2bitset(uint32_t size, bool one_initialized = false) - : data_(NULL), size_(0), one_initialized_(one_initialized) { - resize(size); + dynamic_2bitset() : fill_(false) {} + dynamic_2bitset(uint32_t size, bool fill = false) + : size_(size), fill_(fill), data_(ceil(size / 4.0), ones()*fill) { } - ~dynamic_2bitset() { delete [] data_; } const uint8_t operator[](uint32_t i) const { return get(i); } uint8_t get(uint32_t i) const { return (data_[(i >> 2)] >> (((i & 3) << 1)) & 3); } uint8_t set(uint32_t i, uint8_t v) { - uint8_t sf = ((v << ((i & 3) << 1)) | dynamic_2bitset::vmask[i & 3]); - fprintf(stderr, "v %d sf %d\n", v, sf); + data_[(i >> 2)] |= ones() ^ dynamic_2bitset::vmask[i & 3]; data_[(i >> 2)] &= ((v << ((i & 3) << 1)) | dynamic_2bitset::vmask[i & 3]); + assert(v <= 3); assert(get(i) == v); } void resize(uint32_t size) { - uint8_t* new_data = new uint8_t[size << 2]; - assert(one_initialized_); - assert(one_initialized_ * ones() == ones()); - memset(new_data, one_initialized_*ones(), size << 2); - assert(new_data[0] == ones()); - uint8_t* old_data_ = data_; - for (int i = 0; i < size_; ++i) { - data_ = old_data_; - auto v = get(i); - data_ = new_data; - set(i, v); - } size_ = size; - delete [] old_data_; - data_ = new_data; - assert(data_[0] == ones()); - assert(get(0) == 3); + data_.resize(size >> 2, fill_*ones()); } + void swap(dynamic_2bitset& other) { + std::swap(other.size_, size_); + std::swap(other.fill_, fill_); + std::swap(other.data_, data_); + } + void clear() { data_.clear(); } + + uint32_t size() const { return size_; } static const uint8_t vmask[]; private: - uint8_t* data_; uint32_t size_; - bool one_initialized_; + bool fill_; + std::vector data_; uint8_t ones() { return std::numeric_limits::max(); } }; diff --git a/cxxmph/mph_bits_test.cc b/cxxmph/mph_bits_test.cc index c828f56..e6a764d 100644 --- a/cxxmph/mph_bits_test.cc +++ b/cxxmph/mph_bits_test.cc @@ -5,6 +5,15 @@ using cxxmph::dynamic_2bitset; int main(int argc, char** argv) { + dynamic_2bitset small(256, true); + for (int i = 0; i < small.size(); ++i) small.set(i, i % 4); + for (int i = 0; i < small.size(); ++i) { + if (small[i] != i % 4) { + fprintf(stderr, "wrong bits %d at %d expected %d\n", small[i], i, i % 4); + exit(-1); + } + } + int size = 256; dynamic_2bitset bits(size, true /* fill with ones */); for (int i = 0; i < size; ++i) { @@ -27,6 +36,14 @@ int main(int argc, char** argv) { exit(-1); } } + dynamic_2bitset size_corner1(1); + if (size_corner1.size() != 1) exit(-1); + dynamic_2bitset size_corner2(2); + if (size_corner2.size() != 2) exit(-1); + (dynamic_2bitset(4)).swap(size_corner2); + if (size_corner2.size() != 4) exit(-1); + + } diff --git a/cxxmph/mph_map.h b/cxxmph/mph_map.h index caddf12..a291986 100644 --- a/cxxmph/mph_map.h +++ b/cxxmph/mph_map.h @@ -17,6 +17,7 @@ #include #include // for std::pair +#include "mph_bits.h" #include "mph_index.h" #include "hollow_iterator.h" @@ -107,29 +108,34 @@ class mph_map { static const uint8_t kNestCollision = 3; // biggest 2 bit value void set_nest_value(const uint32_t* h, uint8_t value) { auto index = get_nest_index(h); - assert(get_nest_index(h) < nests_.size() * 4); + assert(get_nest_index(h) < nests_.size()); assert(get_nest_index(h) >> 2 < nests_.size()); assert(value < 4); - set_2bit_value(&nests_[0], index, value); - assert(get_2bit_value(&nests_[0], index) == value); + nests_.set(index, value); + assert(nests_[index] == value); } uint32_t get_nest_value(const uint32_t* h) const { - assert(get_nest_index(h) < nests_.size() * 4); - return get_2bit_value(&(nests_[0]), get_nest_index(h)); + assert(get_nest_index(h) < nests_.size()); + return nests_[get_nest_index(h)]; } uint32_t get_nest_index(const uint32_t* h) const { - return h[3] & ((nests_.size() << 2) - 1); + assert(nests_.size()); + return h[3] % nests_.size(); // a mod 2^n == a & 2^n - 1 + // return h[3] & (nests_.size() - 1); // a mod 2^n == a & 2^n - 1 } void pack(); std::vector values_; std::vector present_; - std::vector nests_; + dynamic_2bitset nests_; SimpleMPHIndex::hash_function> index_; // TODO(davi) optimize slack to hold 128 unique bits from hash64 as key typedef unordered_map slack_type; slack_type slack_; size_type size_; + + mutable uint64_t fast_; + mutable uint64_t slow_; }; MPH_MAP_TMPL_SPEC @@ -143,6 +149,7 @@ MPH_MAP_TMPL_SPEC MPH_MAP_CLASS_SPEC::mph_map() : size_(0) { } MPH_MAP_TMPL_SPEC MPH_MAP_CLASS_SPEC::~mph_map() { + fprintf(stderr, "Fast: %d Slow %d ratio %f\n", fast_, slow_, fast_*1.0/slow_); } MPH_MAP_METHOD_DECL(insert_return_type, insert)(const value_type& x) { @@ -176,11 +183,9 @@ MPH_MAP_METHOD_DECL(void_type, pack)() { new_values.reserve(new_values.size() * 2); std::vector new_present(index_.perfect_hash_size(), false); new_present.reserve(new_present.size() * 2); - auto new_nests_size = nextpoweroftwo(ceil(new_values.size() / 4.0) + 1)*10; - std::vector new_nests(new_nests_size, std::numeric_limits::max()); - new_nests.reserve(new_nests.size() * 2); - nests_.swap(new_nests); - vector used_nests(nests_.size() * 4); + auto new_nests_size = nextpoweroftwo(ceil(new_values.size())*10 + 1); + dynamic_2bitset(new_nests_size, true /* fill with 1s */).swap(nests_); + vector used_nests(nests_.size()); uint32_t collisions = 0; for (iterator it = begin(), it_end = end(); it != it_end; ++it) { size_type id = index_.perfect_hash(it->first); @@ -194,6 +199,7 @@ MPH_MAP_METHOD_DECL(void_type, pack)() { if (used_nests[get_nest_index(h)]) { set_nest_value(h, kNestCollision); assert(get_nest_value(h) == kNestCollision); + // fprintf(stderr, "Collision at nest index %d among %d positions\n", get_nest_index(h), nests_.size()); ++collisions; } else { set_nest_value(h, index_.cuckoo_nest(h)); @@ -207,7 +213,7 @@ MPH_MAP_METHOD_DECL(void_type, pack)() { index_.hash_vector(it->first, h); assert(get_nest_value(h) == kNestCollision || index_.perfect_hash(it->first) == index_.cuckoo_hash(h, get_nest_value(h))); } - fprintf(stderr, "Collision ratio: %f\n", collisions*1.0/size()); + // fprintf(stderr, "Collision ratio: %f\n", collisions*1.0/size()); values_.swap(new_values); present_.swap(new_present); slack_type().swap(slack_); @@ -225,8 +231,7 @@ MPH_MAP_METHOD_DECL(void_type, clear)() { present_.clear(); slack_.clear(); index_.clear(); - nests_.clear(); - nests_.push_back(std::numeric_limits::max()); + dynamic_2bitset(1, true /* fill with 1s */).swap(nests_); size_ = 0; } @@ -245,19 +250,19 @@ MPH_MAP_METHOD_DECL(void_type, erase)(const key_type& k) { } MPH_MAP_METHOD_DECL(const_iterator, find)(const key_type& k) const { - return slow_find(k, index_.perfect_hash(k)); - /* uint32_t h[4]; index_.hash_vector(k, h); auto nest = get_nest_value(h); if (__builtin_expect(nest != kNestCollision, 1)) { auto vit = values_.begin() + index_.cuckoo_hash(h, nest); - if (equal_(k, vit->first)) return make_iterator(vit); + if (equal_(k, vit->first)) { + ++fast_; + return make_iterator(vit); + } } nest = index_.cuckoo_nest(h); - assert(index_.perfect_hash(k) == index_.cuckoo_hash(h, nest)); + ++slow_; return slow_find(k, index_.cuckoo_hash(h, nest)); - */ } MPH_MAP_METHOD_DECL(const_iterator, slow_find)(const key_type& k, uint32_t perfect_hash) const { @@ -275,21 +280,18 @@ MPH_MAP_METHOD_DECL(const_iterator, slow_find)(const key_type& k, uint32_t perfe } MPH_MAP_METHOD_DECL(iterator, find)(const key_type& k) { - // return slow_find(k, index_.perfect_hash(k)); uint32_t h[4]; index_.hash_vector(k, h); auto nest = get_nest_value(h); if (__builtin_expect(nest != kNestCollision, 1)) { auto vit = values_.begin() + index_.cuckoo_hash(h, nest); - assert(index_.perfect_hash(k) == index_.cuckoo_hash(h, nest)); if (equal_(k, vit->first)) { - fprintf(stderr, "fast\n"); - return make_iterator(vit); + ++fast_; + return make_iterator(vit); } } nest = index_.cuckoo_nest(h); - fprintf(stderr, "slow\n"); - // assert(index_.perfect_hash(k) == index_.cuckoo_hash(h, nest)); + ++slow_; return slow_find(k, index_.cuckoo_hash(h, nest)); } From 7fe9527459792f910e4434b4cf62f3f11bbf0b4e Mon Sep 17 00:00:00 2001 From: Davi Reis Date: Wed, 14 Mar 2012 21:22:40 -0300 Subject: [PATCH 15/16] Interesting point, but get_cuckoo_nest is adding a lot and fast path is not that fast for int64. --- cxxmph/bm_map.cc | 4 ++-- cxxmph/mph_map.h | 45 ++++++++++++++++++++++++++++++++---------- cxxmph/mph_map_test.cc | 10 +++++++--- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/cxxmph/bm_map.cc b/cxxmph/bm_map.cc index 0a0b225..51d2ad0 100644 --- a/cxxmph/bm_map.cc +++ b/cxxmph/bm_map.cc @@ -93,8 +93,8 @@ int main(int argc, char** argv) { Benchmark::Register(new BM_CreateUrls>("URLS100k")); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0)); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0)); - Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0.9)); - Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0.9)); + // Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0.9)); + // Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0.9)); Benchmark::Register(new BM_SearchUint64>); Benchmark::Register(new BM_SearchUint64>); Benchmark::RunAll(); diff --git a/cxxmph/mph_map.h b/cxxmph/mph_map.h index a291986..471dafd 100644 --- a/cxxmph/mph_map.h +++ b/cxxmph/mph_map.h @@ -120,8 +120,10 @@ class mph_map { } uint32_t get_nest_index(const uint32_t* h) const { assert(nests_.size()); - return h[3] % nests_.size(); // a mod 2^n == a & 2^n - 1 - // return h[3] & (nests_.size() - 1); // a mod 2^n == a & 2^n - 1 + assert(nests_.size() % 2 == 0); + assert((nests_.size() & (nests_.size() - 1)) == 0); + assert((h[3] % nests_.size()) == (h[3] & (nests_.size() - 1))); + return (h[3] & (nests_.size() - 1)); // a mod 2^n == a & 2^n - 1 } void pack(); @@ -135,7 +137,9 @@ class mph_map { size_type size_; mutable uint64_t fast_; + mutable uint64_t fast_taken_; mutable uint64_t slow_; + mutable uint64_t very_slow_; }; MPH_MAP_TMPL_SPEC @@ -149,7 +153,7 @@ MPH_MAP_TMPL_SPEC MPH_MAP_CLASS_SPEC::mph_map() : size_(0) { } MPH_MAP_TMPL_SPEC MPH_MAP_CLASS_SPEC::~mph_map() { - fprintf(stderr, "Fast: %d Slow %d ratio %f\n", fast_, slow_, fast_*1.0/slow_); + fprintf(stderr, "Fast taken: %d Fast: %d Slow %d very_slow %d ratio %f\n", fast_taken_, fast_, slow_, very_slow_, fast_*1.0/slow_); } MPH_MAP_METHOD_DECL(insert_return_type, insert)(const value_type& x) { @@ -169,10 +173,15 @@ MPH_MAP_METHOD_DECL(insert_return_type, insert)(const value_type& x) { slack_.insert(make_pair(x.first, values_.size() - 1)); if (should_pack) pack(); it = find(x.first); + slow_ = 0; + very_slow_ = 0; + fast_ = 0; + fast_taken_ = 0; return make_pair(it, true); } MPH_MAP_METHOD_DECL(void_type, pack)() { + // fprintf(stderr, "Paki %d values\n", values_.size()); if (values_.empty()) return; assert(std::unordered_set(make_iterator_first(begin()), make_iterator_first(end())).size() == size()); bool success = index_.Reset( @@ -183,7 +192,7 @@ MPH_MAP_METHOD_DECL(void_type, pack)() { new_values.reserve(new_values.size() * 2); std::vector new_present(index_.perfect_hash_size(), false); new_present.reserve(new_present.size() * 2); - auto new_nests_size = nextpoweroftwo(ceil(new_values.size())*10 + 1); + auto new_nests_size = nextpoweroftwo(ceil(new_values.size())*100 + 1); dynamic_2bitset(new_nests_size, true /* fill with 1s */).swap(nests_); vector used_nests(nests_.size()); uint32_t collisions = 0; @@ -208,15 +217,24 @@ MPH_MAP_METHOD_DECL(void_type, pack)() { used_nests[get_nest_index(h)] = true; } } - for (iterator it = begin(), it_end = end(); it != it_end; ++it) { - uint32_t h[4]; - index_.hash_vector(it->first, h); - assert(get_nest_value(h) == kNestCollision || index_.perfect_hash(it->first) == index_.cuckoo_hash(h, get_nest_value(h))); - } // fprintf(stderr, "Collision ratio: %f\n", collisions*1.0/size()); values_.swap(new_values); present_.swap(new_present); slack_type().swap(slack_); + int32_t fast = 0; + int32_t slow= 0; + for (iterator it = begin(), it_end = end(); it != it_end; ++it) { + uint32_t h[4]; + index_.hash_vector(it->first, h); + if (get_nest_value(h) == kNestCollision) ++slow; + else { + ++fast; + auto cit = values_.begin() + index_.cuckoo_hash(h, get_nest_value(h)); + assert(index_.perfect_hash(it->first) == cit - values_.begin()); + assert(equal_(it->first, cit->first)); + } + } + // fprintf(stderr, "Predicted fast: %d slow %d\n", fast, slow); } MPH_MAP_METHOD_DECL(iterator, begin)() { return make_iterator(values_.begin()); } @@ -231,7 +249,7 @@ MPH_MAP_METHOD_DECL(void_type, clear)() { present_.clear(); slack_.clear(); index_.clear(); - dynamic_2bitset(1, true /* fill with 1s */).swap(nests_); + dynamic_2bitset(8, true /* fill with 1s */).swap(nests_); size_ = 0; } @@ -254,7 +272,10 @@ MPH_MAP_METHOD_DECL(const_iterator, find)(const key_type& k) const { index_.hash_vector(k, h); auto nest = get_nest_value(h); if (__builtin_expect(nest != kNestCollision, 1)) { + ++fast_taken_; auto vit = values_.begin() + index_.cuckoo_hash(h, nest); + // do not hold for unknown keys + assert(values_.size() != index_.perfect_hash_size() || equal_(k, vit->first)); if (equal_(k, vit->first)) { ++fast_; return make_iterator(vit); @@ -273,6 +294,7 @@ MPH_MAP_METHOD_DECL(const_iterator, slow_find)(const key_type& k, uint32_t perfe } } if (__builtin_expect(!slack_.empty(), 0)) { + ++very_slow_; auto sit = slack_.find(k); if (sit != slack_.end()) return make_iterator(values_.begin() + sit->second); } @@ -284,7 +306,9 @@ MPH_MAP_METHOD_DECL(iterator, find)(const key_type& k) { index_.hash_vector(k, h); auto nest = get_nest_value(h); if (__builtin_expect(nest != kNestCollision, 1)) { + ++fast_taken_; auto vit = values_.begin() + index_.cuckoo_hash(h, nest); + assert(values_.size() != index_.perfect_hash_size() || equal_(k, vit->first)); if (equal_(k, vit->first)) { ++fast_; return make_iterator(vit); @@ -303,6 +327,7 @@ MPH_MAP_METHOD_DECL(iterator, slow_find)(const key_type& k, uint32_t perfect_has } } if (__builtin_expect(!slack_.empty(), 0)) { + ++very_slow_; auto sit = slack_.find(k); if (sit != slack_.end()) return make_iterator(values_.begin() + sit->second); } diff --git a/cxxmph/mph_map_test.cc b/cxxmph/mph_map_test.cc index ada71b3..1d489c6 100644 --- a/cxxmph/mph_map_test.cc +++ b/cxxmph/mph_map_test.cc @@ -15,17 +15,20 @@ int main(int argc, char** argv) { for (int i = 0; i < num_keys; ++i) { b.insert(make_pair(i, i)); } - for (int i = 0; i < num_keys; ++i) { - auto it = b.find(i); + b.rehash(b.size()); + fprintf(stderr, "Insertion finished\n"); + for (int i = 0; i < 1000000; ++i) { + auto it = b.find(i % num_keys); if (it == b.end()) { std::cerr << "Failed to find " << i << std::endl; exit(-1); } - if (it->first != it->second || it->first != i) { + if (it->first != it->second || it->first != i % num_keys) { std::cerr << "Found " << it->first << " looking for " << i << std::endl; exit(-1); } } + /* mph_map h; h.insert(std::make_pair("-1",-1)); mph_map::const_iterator it; @@ -55,4 +58,5 @@ int main(int argc, char** argv) { if (key < num_valid && it->second != key) exit(-1); } } + */ } From 3c127c76908bc4bd022e2ff7a67b00acc1c261a5 Mon Sep 17 00:00:00 2001 From: Davi Reis Date: Wed, 14 Mar 2012 23:23:48 -0300 Subject: [PATCH 16/16] First tentative on the perfect hash design. --- cxxmph/bm_map.cc | 4 ++-- cxxmph/mph_map.h | 19 +++++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/cxxmph/bm_map.cc b/cxxmph/bm_map.cc index 51d2ad0..0a0b225 100644 --- a/cxxmph/bm_map.cc +++ b/cxxmph/bm_map.cc @@ -93,8 +93,8 @@ int main(int argc, char** argv) { Benchmark::Register(new BM_CreateUrls>("URLS100k")); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0)); Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0)); - // Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0.9)); - // Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0.9)); + Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0.9)); + Benchmark::Register(new BM_SearchUrls>("URLS100k", 10*1000 * 1000, 0.9)); Benchmark::Register(new BM_SearchUint64>); Benchmark::Register(new BM_SearchUint64>); Benchmark::RunAll(); diff --git a/cxxmph/mph_map.h b/cxxmph/mph_map.h index 471dafd..dd7bb08 100644 --- a/cxxmph/mph_map.h +++ b/cxxmph/mph_map.h @@ -69,8 +69,8 @@ class mph_map { void erase(iterator pos); void erase(const key_type& k); pair insert(const value_type& x); - iterator find(const key_type& k); - const_iterator find(const key_type& k) const; + iterator find(const key_type& k) { return slow_find(k, index_.perfect_hash(k)); } + const_iterator find(const key_type& k) const { return slow_find(k, index_.perfect_hash(k)); }; typedef int32_t my_int32_t; // help macros int32_t index(const key_type& k) const; data_type& operator[](const key_type &k); @@ -103,6 +103,9 @@ class mph_map { return hollow_const_iterator>(&values_, &present_, it); } + // Experimental functions, not always faster + iterator fast_find(const key_type& k); + const_iterator fast_find(const key_type& k) const; iterator slow_find(const key_type& k, uint32_t perfect_hash); const_iterator slow_find(const key_type& k, uint32_t perfect_hash) const; static const uint8_t kNestCollision = 3; // biggest 2 bit value @@ -153,7 +156,7 @@ MPH_MAP_TMPL_SPEC MPH_MAP_CLASS_SPEC::mph_map() : size_(0) { } MPH_MAP_TMPL_SPEC MPH_MAP_CLASS_SPEC::~mph_map() { - fprintf(stderr, "Fast taken: %d Fast: %d Slow %d very_slow %d ratio %f\n", fast_taken_, fast_, slow_, very_slow_, fast_*1.0/slow_); + // fprintf(stderr, "Fast taken: %d Fast: %d Slow %d very_slow %d ratio %f\n", fast_taken_, fast_, slow_, very_slow_, fast_*1.0/slow_); } MPH_MAP_METHOD_DECL(insert_return_type, insert)(const value_type& x) { @@ -192,7 +195,7 @@ MPH_MAP_METHOD_DECL(void_type, pack)() { new_values.reserve(new_values.size() * 2); std::vector new_present(index_.perfect_hash_size(), false); new_present.reserve(new_present.size() * 2); - auto new_nests_size = nextpoweroftwo(ceil(new_values.size())*100 + 1); + auto new_nests_size = nextpoweroftwo(ceil(new_values.size())*10000 + 1); dynamic_2bitset(new_nests_size, true /* fill with 1s */).swap(nests_); vector used_nests(nests_.size()); uint32_t collisions = 0; @@ -267,7 +270,7 @@ MPH_MAP_METHOD_DECL(void_type, erase)(const key_type& k) { erase(it); } -MPH_MAP_METHOD_DECL(const_iterator, find)(const key_type& k) const { +MPH_MAP_METHOD_DECL(const_iterator, fast_find)(const key_type& k) const { uint32_t h[4]; index_.hash_vector(k, h); auto nest = get_nest_value(h); @@ -287,7 +290,7 @@ MPH_MAP_METHOD_DECL(const_iterator, find)(const key_type& k) const { } MPH_MAP_METHOD_DECL(const_iterator, slow_find)(const key_type& k, uint32_t perfect_hash) const { - if (__builtin_expect(index_.perfect_hash_size(), 0)) { + if (__builtin_expect(index_.perfect_hash_size(), 1)) { if (__builtin_expect(present_[perfect_hash], true)) { auto vit = values_.begin() + perfect_hash; if (equal_(k, vit->first)) return make_iterator(vit); @@ -301,7 +304,7 @@ MPH_MAP_METHOD_DECL(const_iterator, slow_find)(const key_type& k, uint32_t perfe return end(); } -MPH_MAP_METHOD_DECL(iterator, find)(const key_type& k) { +MPH_MAP_METHOD_DECL(iterator, fast_find)(const key_type& k) { uint32_t h[4]; index_.hash_vector(k, h); auto nest = get_nest_value(h); @@ -320,7 +323,7 @@ MPH_MAP_METHOD_DECL(iterator, find)(const key_type& k) { } MPH_MAP_METHOD_DECL(iterator, slow_find)(const key_type& k, uint32_t perfect_hash) { - if (__builtin_expect(index_.perfect_hash_size(), 0)) { + if (__builtin_expect(index_.perfect_hash_size(), 1)) { if (__builtin_expect(present_[perfect_hash], true)) { auto vit = values_.begin() + perfect_hash; if (equal_(k, vit->first)) return make_iterator(vit);