diff --git a/include/verilated_unordered_set_map.h b/include/verilated_unordered_set_map.h index 73e6ecad4..368ebba83 100644 --- a/include/verilated_unordered_set_map.h +++ b/include/verilated_unordered_set_map.h @@ -67,7 +67,7 @@ inline size_t vl_hash_bytes(const void* vbufp, size_t nbytes) { template <> inline size_t vl_hash::operator()(const unsigned int& k) const { - return vl_hash_bytes(&k, sizeof(k)); + return k; } template <> inline bool @@ -93,7 +93,9 @@ vl_equal_to::operator()(const std::string& a, template struct vl_hash { size_t operator()(T* kp) const { - return vl_hash_bytes(&kp, sizeof(kp)); + return ((sizeof(size_t) == sizeof(kp)) + ? reinterpret_cast(kp) + : vl_hash_bytes(&kp, sizeof(kp))); } }; @@ -106,13 +108,13 @@ template struct vl_equal_to { //=================================================================== // /// Functional clone of the std::unordered_set hash table. -template , - class Equal = vl_equal_to > class vl_unordered_set { +template , + class T_Equal = vl_equal_to > +class vl_unordered_set { public: // TYPES - typedef std::list Bucket; - typedef vluint64_t size_type; + typedef std::list Bucket; enum RehashType {GROW, SHRINK}; template returns a non-const value_type*, so keep this + // operator-> returns a non-const ValueType*, so keep this // non-const to avoid having to define a whole separate iterator // for unordered_map. - Key* operator->() const { + T_Key* operator->() const { return &(*m_bit); } bool operator==(const iterator& other) const { @@ -180,14 +182,14 @@ public: private: // MEMBERS - size_type m_numElements; // Number of entries present. - size_type m_log2Buckets; // Log-base-2 of the number of buckets. + size_t m_numElements; // Number of entries present. + size_t m_log2Buckets; // Log-base-2 of the number of buckets. mutable Bucket* m_bucketsp; // Hash table buckets. May be NULL; // // we'll allocate it on the fly when // // the first entries are created. Bucket m_emptyBucket; // A fake bucket, used to construct end(). - Hash m_hash; // Hash function provider. - Equal m_equal; // Equal-to function provider. + T_Hash m_hash; // Hash function provider. + T_Equal m_equal; // Equal-to function provider. public: // CONSTRUCTORS @@ -206,7 +208,7 @@ public: , m_equal() { if (other.m_bucketsp) { m_bucketsp = new Bucket[numBuckets()]; - for (size_type i = 0; i < numBuckets(); i++) { + for (size_t i = 0; i < numBuckets(); i++) { m_bucketsp[i] = other.m_bucketsp[i]; } } @@ -220,7 +222,7 @@ public: m_log2Buckets = other.m_log2Buckets; if (other.m_bucketsp) { m_bucketsp = new Bucket[numBuckets()]; - for (size_type i = 0; i < numBuckets(); i++) { + for (size_t i = 0; i < numBuckets(); i++) { m_bucketsp[i] = other.m_bucketsp[i]; } } else { @@ -262,20 +264,43 @@ public: bool empty() const { return m_numElements == 0; } - size_type size() const { return m_numElements; } + size_t size() const { return m_numElements; } - size_type count(const Key& key) const { + size_t count(const T_Key& key) const { return (find(key) == end()) ? 0 : 1; } - iterator find_internal(const Key& key, size_type& bucketIdxOut) { - size_type hash = m_hash.operator()(key); - bucketIdxOut = hash & ((VL_ULL(1) << m_log2Buckets) - 1); + size_t hashToBucket(size_t hashVal) const { + return hashToBucket(hashVal, m_log2Buckets); + } + static size_t hashToBucket(size_t hashVal, unsigned log2Buckets) { + // Fibonacci hashing + // See https://probablydance.com/2018/06/16/fibonacci-hashing-the-optimization-that-the-world-forgot-or-a-better-alternative-to-integer-modulo/ + // + // * The magic numbers below are UINT_MAX/phi where phi is the + // golden ratio number (1.618...) for either 64- or 32-bit + // values of UINT_MAX. + // + // * Fibonacci hashing mixes the result of the client's hash + // function further. This permits the use of very fast client + // hash funcs (like just returning the int or pointer value as + // is!) and tolerates crappy client hash functions pretty well. + size_t mult = hashVal * ((sizeof(size_t) == 8) + ? VL_ULL(11400714819323198485) + : 2654435769lu); + size_t result = (mult >> (((sizeof(size_t) == 8) + ? 64 : 32) - log2Buckets)); + return result; + } + + iterator find_internal(const T_Key& key, size_t& bucketIdxOut) { + size_t hash = m_hash.operator()(key); + bucketIdxOut = hashToBucket(hash); initBuckets(); Bucket* bucketp = &m_bucketsp[bucketIdxOut]; for (typename Bucket::iterator it = bucketp->begin(); - it != bucketp->end(); ++it) { + it != bucketp->end(); ++it) { if (m_equal.operator()(*it, key)) { return iterator(bucketIdxOut, it, this); } @@ -283,19 +308,19 @@ public: return end(); } - const_iterator find(const Key& key) const { - size_type bucketIdx; + const_iterator find(const T_Key& key) const { + size_t bucketIdx; return const_cast(this)->find_internal(key, bucketIdx); } - iterator find(const Key& key) { - size_type bucketIdx; + iterator find(const T_Key& key) { + size_t bucketIdx; return find_internal(key, bucketIdx); } - std::pair insert(const Key &val) { - size_type bucketIdx; + std::pair insert(const T_Key &val) { + size_t bucketIdx; iterator existIt = find_internal(val, bucketIdx); if (existIt != end()) { // Collision with existing element. @@ -330,8 +355,8 @@ public: return next_it; } - size_type erase(const Key &key) { - size_type bucketIdx; + size_t erase(const T_Key &key) { + size_t bucketIdx; iterator it = find_internal(key, bucketIdx); if (it != end()) { m_bucketsp[bucketIdx].erase(it.bit()); @@ -357,9 +382,9 @@ public: } private: - size_type numBuckets() const { return (VL_ULL(1) << m_log2Buckets); } + size_t numBuckets() const { return (VL_ULL(1) << m_log2Buckets); } - Bucket* getBucket(size_type idx) { + Bucket* getBucket(size_t idx) { initBuckets(); return &m_bucketsp[idx]; } @@ -377,7 +402,7 @@ private: } void rehash(RehashType rt) { - size_type new_log2Buckets; + size_t new_log2Buckets; if (rt == GROW) { new_log2Buckets = m_log2Buckets + 2; } else { @@ -390,14 +415,14 @@ private: new_log2Buckets = m_log2Buckets - 2; } - size_type new_num_buckets = VL_ULL(1) << new_log2Buckets; + size_t new_num_buckets = VL_ULL(1) << new_log2Buckets; Bucket* new_bucketsp = new Bucket[new_num_buckets]; - for (size_type i=0; i, - class Equal = vl_equal_to > class vl_unordered_map { - private: +template , + class T_Equal = vl_equal_to > +class vl_unordered_map { +private: // TYPES - typedef vluint64_t size_type; - typedef std::pair value_type; + typedef std::pair KeyValPair; class KeyHash { private: - Hash key_hash; + T_Hash key_hash; public: KeyHash() {} - size_t operator()(const value_type& kv_pair) const { + size_t operator()(const KeyValPair& kv_pair) const { return key_hash.operator()(kv_pair.first); } }; class KeyEqual { private: - Equal key_eq; + T_Equal key_eq; public: KeyEqual() {} - bool operator()(const value_type& kv_a, const value_type& kv_b) const { + bool operator()(const KeyValPair& kv_a, const KeyValPair& kv_b) const { return key_eq.operator()(kv_a.first, kv_b.first); } }; // MEMBERS - typedef vl_unordered_set MapSet; + typedef vl_unordered_set MapSet; MapSet m_set; // Wrap this vl_unordered_set which holds all state. - public: +public: // CONSTRUCTORS vl_unordered_map() {} ~vl_unordered_map() {} @@ -460,43 +485,43 @@ template begin(); - it != bucketp->end(); ++it) { + it != bucketp->end(); ++it) { if (mapEq.operator()(it->first, k)) { return iterator(bucketIdxOut, it, &m_set); } } return end(); } - const_iterator find(const Key& k) const { + const_iterator find(const T_Key& k) const { return const_cast(this)->find(k); } - std::pair insert(const value_type& val) { + std::pair insert(const KeyValPair& val) { return m_set.insert(val); } iterator erase(iterator it) { return m_set.erase(it); } - size_type erase(const Key& k) { + size_t erase(const T_Key& k) { iterator it = find(k); if (it == end()) { return 0; } m_set.erase(it); return 1; } - Value& operator[](const Key& k) { - // Here we can assume Value() is defined, as + T_Value& operator[](const T_Key& k) { + // Here we can assume T_Value() is defined, as // std::unordered_map::operator[] relies on it too. - value_type dummy = std::make_pair(k, Value()); + KeyValPair dummy = std::make_pair(k, T_Value()); iterator it = m_set.find(dummy); if (it == m_set.end()) { it = m_set.insert(dummy).first; @@ -506,18 +531,18 @@ template second; } - Value& at(const Key& k) { + T_Value& at(const T_Key& k) { iterator it = find(k); if (it == end()) { throw std::out_of_range("sorry"); } return it->second; } - const Value& at(const Key& k) const { + const T_Value& at(const T_Key& k) const { iterator it = find(k); if (it == end()) { throw std::out_of_range("sorry"); } return it->second; } void clear() { m_set.clear(); } - size_type size() const { return m_set.size(); } + size_t size() const { return m_set.size(); } }; #endif diff --git a/src/V3TSP.cpp b/src/V3TSP.cpp index 316c0be54..085187cc6 100644 --- a/src/V3TSP.cpp +++ b/src/V3TSP.cpp @@ -61,37 +61,37 @@ namespace V3TSP { } // namespace V3TSP // Vertex that tracks a per-vertex key -template +template class TspVertexTmpl : public V3GraphVertex { private: - Key m_key; + T_Key m_key; public: - TspVertexTmpl(V3Graph* graphp, const Key& k) + TspVertexTmpl(V3Graph* graphp, const T_Key& k) : V3GraphVertex(graphp), m_key(k) {} virtual ~TspVertexTmpl() {} - const Key& key() const { return m_key; } + const T_Key& key() const { return m_key; } private: VL_UNCOPYABLE(TspVertexTmpl); }; // TspGraphTmpl represents a complete graph, templatized to work with -// different Key types. -template +// different T_Key types. +template class TspGraphTmpl : public V3Graph { public: // TYPES - typedef TspVertexTmpl Vertex; + typedef TspVertexTmpl Vertex; // MEMBERS - typedef vl_unordered_map VMap; - VMap m_vertices; // Key to Vertex lookup map + typedef vl_unordered_map VMap; + VMap m_vertices; // T_Key to Vertex lookup map // CONSTRUCTORS TspGraphTmpl() : V3Graph() {} virtual ~TspGraphTmpl() {} // METHODS - void addVertex(const Key &key) { + void addVertex(const T_Key &key) { typename VMap::iterator itr = m_vertices.find(key); UASSERT(itr == m_vertices.end(), "Vertex already exists with same key"); Vertex *v = new Vertex(this, key); @@ -102,7 +102,7 @@ public: // Map that onto the normally-directional V3Graph by creating // a matched pairs of opposite-directional edges to represent // each non-directional edge: - void addEdge(const Key& from, const Key& to, int cost) { + void addEdge(const T_Key& from, const T_Key& to, int cost) { UASSERT(from != to, "Adding edge would form a loop"); Vertex* fp = findVertex(from); Vertex* tp = findVertex(to); @@ -121,7 +121,7 @@ public: bool empty() const { return m_vertices.empty(); } - std::list keysToVertexList(const std::vector& odds) { + std::list keysToVertexList(const std::vector& odds) { std::list vertices; for(unsigned i = 0; i < odds.size(); ++i) { vertices.push_back(findVertex(odds.at(i))); @@ -231,7 +231,7 @@ public: // Populate *outp with a minimal perfect matching of *this. // *outp must be initially empty. - void perfectMatching(const std::vector& oddKeys, + void perfectMatching(const std::vector& oddKeys, TspGraphTmpl* outp) { UASSERT(outp->empty(), "Output graph must start empty"); @@ -305,7 +305,7 @@ public: void findEulerTourRecurse(vl_unordered_set* markedEdgesp, Vertex* startp, - std::vector* sortedOutp) { + std::vector* sortedOutp) { Vertex* cur_vertexp = startp; // Go on a random tour. Fun! @@ -390,7 +390,7 @@ public: } } - void findEulerTour(std::vector* sortedOutp) { + void findEulerTour(std::vector* sortedOutp) { UASSERT(sortedOutp->empty(), "Output graph must start empty"); if (debug() >= 6) dumpDotFilePrefixed("findEulerTour"); vl_unordered_set markedEdges; @@ -399,8 +399,8 @@ public: findEulerTourRecurse(&markedEdges, start_vertexp, sortedOutp); } - std::vector getOddDegreeKeys() const { - std::vector result; + std::vector getOddDegreeKeys() const { + std::vector result; for (V3GraphVertex* vxp = verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { Vertex* tspvp = castVertexp(vxp); vluint32_t degree = 0; @@ -415,7 +415,7 @@ public: } private: - Vertex* findVertex(const Key& key) const { + Vertex* findVertex(const T_Key& key) const { typename VMap::const_iterator it = m_vertices.find(key); UASSERT(it != m_vertices.end(), "Vertex not found"); return it->second;