[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