[mathicgb] 67/393: Added custom chained hash table for MonomialMap and now uses a bit-technique for faster equality check when looking up monomials in a hash table. Takes hcyc8 from 8.4s to 8.0s for a 4.8% improvement.

Doug Torrance dtorrance-guest at moszumanska.debian.org
Fri Apr 3 15:58:33 UTC 2015


This is an automated email from the git hooks/post-receive script.

dtorrance-guest pushed a commit to branch upstream
in repository mathicgb.

commit 4f7f944119994b6637e06e3d065e8df0fe880d6a
Author: Bjarke Hammersholt Roune <bjarkehr.code at gmail.com>
Date:   Fri Oct 19 14:31:50 2012 +0200

    Added custom chained hash table for MonomialMap and now uses a bit-technique for faster equality check when looking up monomials in a hash table. Takes hcyc8 from 8.4s to 8.0s for a 4.8% improvement.
---
 src/mathicgb/MonomialMap.hpp | 243 +++++++++++++++++++++++++++++++++----------
 src/mathicgb/PolyRing.hpp    |  46 +++++++-
 2 files changed, 231 insertions(+), 58 deletions(-)

diff --git a/src/mathicgb/MonomialMap.hpp b/src/mathicgb/MonomialMap.hpp
index d2ca751..56a3bce 100755
--- a/src/mathicgb/MonomialMap.hpp
+++ b/src/mathicgb/MonomialMap.hpp
@@ -4,9 +4,20 @@
 #include "PolyRing.hpp"
 #include <limits>
 
-#define MATHICGB_USE_STD_HASH
+#if defined(MATHICGB_USE_STD_HASH) && defined(MATHICGB_USE_CUSTOM_HASH)
+#error Only select one kind of hash table
+#endif
+
+// set default hash table type if nothing has been specified
+#if !defined(MATHICGB_USE_STD_HASH) && !defined(MATHICGB_USE_CUSTOM_HASH)
+#define MATHICGB_USE_CUSTOM_HASH
+#endif
 
-#ifdef MATHICGB_USE_STD_HASH
+#ifdef MATHICGB_USE_CUSTOM_HASH
+#include <memtailor.h>
+#include <vector>
+#include <algorithm>
+#elif defined(MATHICGB_USE_STD_HASH)
 #include <unordered_map>
 #else
 #include <map>
@@ -24,35 +35,156 @@ namespace MonomialMapInternal {
   // this namespace to avoid cluttering the class definition with
   // a lot of ifdef's.
 
-#ifndef MATHICGB_USE_STD_HASH
-  /// We need SOME ordering to make std::map work.
-  class ArbitraryOrdering {
+#ifdef MATHICGB_USE_CUSTOM_HASH
+class HashControlExample
+{
+public:
+  typedef int * KeyType;
+  typedef int ValueType;
+
+  size_t hash_value(KeyType k) const { return static_cast<size_t>(k - static_cast<int *>(0)); }
+  bool is_equal(KeyType k1, KeyType k2) const { return k1 == k2; }
+  void combine(ValueType &v, ValueType w) const { v += w; }
+  void show(std::ostream &o, KeyType k, ValueType v) const { o << "[" << k << " " << v << "]"; }
+};
+
+  template<class MTT>
+  class MapClass {
+  public:
+    typedef MapClass Map;
+    typedef std::pair<const_monomial, MTT> value_type;
+
+  private:
+    typedef exponent HashValue;
+    struct Node {
+      Node* next;
+      value_type value;
+    };
+
   public:
-    ArbitraryOrdering(const PolyRing& ring): mRing(ring) {}
-    bool operator()(const_monomial a, const_monomial b) const {
-      return mRing.monomialLT(a, b);
+    MapClass(const PolyRing& ring):
+      mRing(ring),
+      mTable(),
+      mNodeAlloc(sizeof(Node))
+    {
+      mGrowWhenThisManyEntries = 0;
+      mTableSize = mTable.size();
+      mEntryCount = 0;
+      growTable();
     }
+
+    Map& map() {return *this;}
+    const Map& map() const {return *this;}
     const PolyRing& ring() const {return mRing;}
 
-  private:
-    const PolyRing& mRing;
-  };
+    value_type* find(const_monomial mono) {
+      const HashValue monoHash = mRing.monomialHashValue(mono);
+      Node* node = entry(hashToIndex(monoHash));
+      for (; node != 0; node = node->next) {
+        if (monoHash == mRing.monomialHashValue(node->value.first) &&
+          mRing.monomialEqualHintTrue(mono, node->value.first)
+        ) {
+          return &node->value;
+        }
+      }
+      return 0;
+    }
 
-  template<class MTT>
-  class MapClass {
-  public:
-    typedef std::map<const_monomial, MTT, ArbitraryOrdering> Map;
+    value_type* findProduct(const_monomial a, const_monomial b) {
+      const HashValue abHash = mRing.monomialHashOfProduct(a, b);
+      Node* node = entry(hashToIndex(abHash));
+      for (; node != 0; node = node->next) {
+        if (abHash == mRing.monomialHashValue(node->value.first) &&
+          mRing.monomialIsProductOfHintTrue(a, b, node->value.first)
+        ) {
+          return &node->value;
+        }
+      }
+      return 0;
+    }
 
-    MapClass(const PolyRing& ring): mMap(ArbitraryOrdering(ring)) {}
-    Map& map() {return mMap;}
-    const Map& map() const {return mMap;}
-    const PolyRing& ring() const {return mMap.key_cmp().ring();}
+    void insert(const value_type& value) {
+      Node* node = static_cast<Node*>(mNodeAlloc.alloc());
+      const size_t index = hashToIndex(mRing.monomialHashValue(value.first));
+      node->value = value;
+      node->next = entry(index);
+      mTable[index] = node;
+      ++mEntryCount;
+      MATHICGB_ASSERT(mEntryCount <= mGrowWhenThisManyEntries);
+      if (mEntryCount == mGrowWhenThisManyEntries)
+        growTable();
+    }
 
-  private:
-    Map mMap;
-  };
+    void clear() {
+      std::fill(mTable.begin(), mTable.end(), static_cast<Node*>(0));
+      mNodeAlloc.freeAllBuffers();
+      mEntryCount = 0;
+    }
 
-#else
+    size_t size() const {return mEntryCount;}
+
+  private:
+    size_t hashToIndex(HashValue hash) const {
+      const auto index = hash & mHashToIndexMask;
+      MATHICGB_ASSERT(index == hash % mTable.size());
+      return index;
+    }
+
+    Node* entry(size_t index) {
+      MATHICGB_ASSERT(index < mTable.size());
+      return mTable[index];
+    }
+
+    void growTable() {
+      // Determine parameters for larger hash table
+      const size_t initialTableSize = 1 << 16; // must be a power of two!!!
+      const float maxLoadFactor = 0.33f; // max value of size() / mTable.size()
+      const float growthFactor = 2.0f; // multiply table size by this on growth
+
+      const size_t newTableSize = mTable.empty() ?
+        initialTableSize :
+        static_cast<size_t>(mTable.size() * growthFactor + 0.5f); // round up
+      const auto newGrowWhenThisManyEntries =
+        static_cast<size_t>(newTableSize / maxLoadFactor); // round down
+
+      MATHICGB_ASSERT((newTableSize & (newTableSize - 1)) == 0); // power of two
+
+      // Move nodes from current table into new table
+      decltype(mTable) newTable(newTableSize);
+      HashValue newHashToIndexMask = static_cast<HashValue>(newTableSize - 1);
+      const auto tableEnd = mTable.end();
+      for (auto tableIt = mTable.begin(); tableIt != tableEnd; ++tableIt) {
+        Node* node = *tableIt;
+        while (node != 0) {
+          const size_t index =
+            mRing.monomialHashValue(node->value.first) & newHashToIndexMask;
+          MATHICGB_ASSERT(index < newTable.size());
+          Node* const next = node->next;
+          node->next = newTable[index];
+          newTable[index] = node;
+          node = next;
+        }
+      }
+
+      // Move newly calculated table into place
+      mTableSize = newTableSize;      
+      mTable = std::move(newTable);
+      mHashToIndexMask = newHashToIndexMask;
+      mGrowWhenThisManyEntries = newGrowWhenThisManyEntries;
+
+      MATHICGB_ASSERT(mTableSize < mGrowWhenThisManyEntries);
+    }
+
+    size_t mGrowWhenThisManyEntries;
+    size_t mTableSize;
+    HashValue mHashToIndexMask;
+    size_t mEntryCount;
+    const PolyRing& mRing;
+    std::vector<Node*> mTable;
+    memt::BufferPool mNodeAlloc; // nodes are allocated from here.
+  };
+
+#elif defined(MATHICGB_USE_STD_HASH)
   class Hash {
   public:
     Hash(const PolyRing& ring): mRing(ring) {}
@@ -116,6 +248,7 @@ namespace MonomialMapInternal {
   private:
     mutable memt::Arena& mArena;
   };
+
   template<class MTT>
   class MapClass {
   public:
@@ -153,26 +286,38 @@ namespace MonomialMapInternal {
       return 0;
     }
 
+  private:
+    memt::Arena mArena;
+    Map mMap;
+    monomial mTmp;
+  };
+#elif defined(MATHICGB_USE_CUSTOM_HASH)
+#else
+  /// We need SOME ordering to make std::map work.
+  class ArbitraryOrdering {
+  public:
+    ArbitraryOrdering(const PolyRing& ring): mRing(ring) {}
+    bool operator()(const_monomial a, const_monomial b) const {
+      return mRing.monomialLT(a, b);
+    }
+    const PolyRing& ring() const {return mRing;}
+
+  private:
+    const PolyRing& mRing;
+  };
 
-/*
-    size_t bucket = mMonomialToCol.
-	iterator lower_bound(const key_type& _Keyval)
-		{	// find leftmost not less than _Keyval in mutable hash table
-		size_type _Bucket = _Hashval(_Keyval);
-		for (_Unchecked_iterator _Where = _Begin(_Bucket);
-			_Where != _End(_Bucket); ++_Where)
-			if (!((_Traits&)*this)(this->_Kfn(*_Where), _Keyval))
-				return (((_Traits&)*this)(_Keyval,
-					this->_Kfn(*_Where)) ? end() : _Make_iter(_Where));
-		return (end());
-		}
+  template<class MTT>
+  class MapClass {
+  public:
+    typedef std::map<const_monomial, MTT, ArbitraryOrdering> Map;
 
-        */
+    MapClass(const PolyRing& ring): mMap(ArbitraryOrdering(ring)) {}
+    Map& map() {return mMap;}
+    const Map& map() const {return mMap;}
+    const PolyRing& ring() const {return mMap.key_cmp().ring();}
 
   private:
-    memt::Arena mArena;
     Map mMap;
-    monomial mTmp;
   };
 #endif
 }
@@ -183,24 +328,18 @@ public:
   typedef MTT mapped_type;
   typedef MonomialMapInternal::MapClass<MTT> MapType;
 
-  typedef typename MapType::Map::iterator iterator;
-  typedef typename MapType::Map::const_iterator const_iterator;
+  //typedef typename MapType::Map::iterator iterator;
+  //typedef typename MapType::Map::const_iterator const_iterator;
   typedef typename MapType::Map::value_type value_type;
 
   MonomialMap(const PolyRing& ring): mMap(ring) {}
 
-  iterator begin() {return map().begin();}
+/*  iterator begin() {return map().begin();}
   const_iterator begin() const {return map().begin();}
   iterator end() {return map().end();}
-  const_iterator end() const {return map().end();}
+  const_iterator end() const {return map().end();}*/
 
-  value_type* find(const_monomial m) {
-    auto it = map().find(m);
-    if (it == map().end())
-      return 0;
-    else
-      return &*it;
-  }
+  value_type* find(const_monomial m) {return mMap.find(m);}
 
   value_type* findProduct(const_monomial a, const_monomial b) {
     return mMap.findProduct(a, b);
@@ -216,8 +355,8 @@ public:
 
   size_t size() const {return map().size();}
   void clear() {map().clear();}
-  std::pair<iterator, bool> insert(const value_type& val) {
-    return map().insert(val);
+  void insert(const value_type& val) {
+    map().insert(val);
   }
 
   inline const PolyRing& ring() const {return mMap.ring();}
@@ -229,6 +368,4 @@ private:
   MapType mMap;
 };
 
-
-
 #endif
diff --git a/src/mathicgb/PolyRing.hpp b/src/mathicgb/PolyRing.hpp
index cb48d46..808e3a3 100755
--- a/src/mathicgb/PolyRing.hpp
+++ b/src/mathicgb/PolyRing.hpp
@@ -374,6 +374,9 @@ public:
 
   bool monomialEQ(ConstMonomial a, ConstMonomial b) const;
 
+  /// as monomialEQ, but optimized for the case that the answer is true.
+  bool monomialEqualHintTrue(ConstMonomial a, ConstMonomial b) const;
+
   size_t monomialSize(ConstMonomial) const { return mMaxMonomialSize; }
 
   int monomialGetComponent(ConstMonomial a) const { return *a.mValue; }
@@ -405,6 +408,11 @@ public:
   bool monomialIsProductOf
     (ConstMonomial a, ConstMonomial b, ConstMonomial ab) const;
 
+  /**As monomialIsProductOf but optimized for the case that the result
+    is true. */
+  bool monomialIsProductOfHintTrue
+    (ConstMonomial a, ConstMonomial b, ConstMonomial ab) const;
+
   /// Returns the hash of the product of a and b.
   exponent monomialHashOfProduct(ConstMonomial a, ConstMonomial b) const {
     return a[mHashIndex] + b[mHashIndex];
@@ -598,19 +606,47 @@ inline exponent PolyRing::weight(ConstMonomial a) const {
 inline bool PolyRing::monomialEQ(ConstMonomial a, ConstMonomial b) const
 {
   for (size_t i = 0; i <= mNumVars; ++i)
-    if (a[i] != b[i]) return false;
+    if (a[i] != b[i])
+      return false;
   return true;
 }
 
+inline bool PolyRing::monomialEqualHintTrue(
+  const ConstMonomial a,
+  const ConstMonomial b
+) const {
+  // if a[i] != b[i] then a[i] ^ b[i] != 0, so the or of all xors is zero
+  // if and only if a equals b. This way we avoid having a branch to check
+  // equality for every iteration of the loop, which is a win in the case
+  // that none of the early-exit branches are taken - that is, when a equals b.
+  exponent orOfXor = 0;
+  for (size_t i = mNumVars; i != 0; --i)
+    orOfXor |= a[i] ^ b[i];
+  const bool areEqual = (orOfXor == 0);
+  MATHICGB_ASSERT(areEqual == monomialEQ(a, b));
+  return areEqual;
+}
+
+inline bool PolyRing::monomialIsProductOfHintTrue(
+  const ConstMonomial a, 
+  const ConstMonomial b, 
+  const ConstMonomial ab
+) const {
+  // same idea as monomialEqualHintTrue, just applied to the sum of a and b.
+  exponent orOfXor = 0;
+  for (size_t i = mNumVars; i != 0; --i)
+    orOfXor |= ab[i] ^ (a[i] + b[i]);
+  const bool isProduct = (orOfXor == 0);
+  MATHICGB_ASSERT(isProduct == monomialIsProductOf(a, b, ab));
+  return isProduct;
+}
+
 inline bool PolyRing::monomialIsProductOf(
   ConstMonomial a, 
   ConstMonomial b, 
   ConstMonomial ab
 ) const {
-  MATHICGB_ASSERT(hashValid(a));
-  MATHICGB_ASSERT(hashValid(b));
-  MATHICGB_ASSERT(hashValid(ab));
-  for (size_t i = mHashIndex; i != static_cast<size_t>(-1); --i)
+  for (size_t i = 0; i <= mNumVars; ++i)
     if (ab[i] != a[i] + b[i])
       return false;
   return true;

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/mathicgb.git



More information about the debian-science-commits mailing list