From 78693fb196bfd800a733279fb199c2e9a7076703 Mon Sep 17 00:00:00 2001 From: Matthias Hochsteger Date: Thu, 29 Aug 2019 14:21:18 +0200 Subject: [PATCH] Add Table and HashTable from NGSolve --- libsrc/core/CMakeLists.txt | 3 +- libsrc/core/hashtable.hpp | 1109 ++++++++++++++++++++++++++++++++++++ libsrc/core/ngcore.hpp | 2 + libsrc/core/table.cpp | 177 ++++++ libsrc/core/table.hpp | 496 ++++++++++++++++ libsrc/core/utils.hpp | 29 + 6 files changed, 1815 insertions(+), 1 deletion(-) create mode 100644 libsrc/core/hashtable.hpp create mode 100644 libsrc/core/table.cpp create mode 100644 libsrc/core/table.hpp diff --git a/libsrc/core/CMakeLists.txt b/libsrc/core/CMakeLists.txt index 9f074709..0760ce6c 100644 --- a/libsrc/core/CMakeLists.txt +++ b/libsrc/core/CMakeLists.txt @@ -7,6 +7,7 @@ add_library(ngcore SHARED flags.cpp paje_trace.cpp profiler.cpp + table.cpp taskmanager.cpp utils.cpp ) @@ -59,7 +60,7 @@ target_link_libraries(ngcore PUBLIC netgen_mpi PRIVATE ${CMAKE_THREAD_LIBS_INIT} install(FILES ngcore.hpp archive.hpp type_traits.hpp version.hpp ngcore_api.hpp logging.hpp exception.hpp symboltable.hpp paje_trace.hpp utils.hpp profiler.hpp mpi_wrapper.hpp array.hpp taskmanager.hpp concurrentqueue.h localheap.hpp python_ngcore.hpp flags.hpp - xbool.hpp signal.hpp bitarray.hpp + xbool.hpp signal.hpp bitarray.hpp table.hpp hashtable.hpp DESTINATION ${NG_INSTALL_DIR_INCLUDE}/core COMPONENT netgen_devel) if(ENABLE_CPP_CORE_GUIDELINES_CHECK) diff --git a/libsrc/core/hashtable.hpp b/libsrc/core/hashtable.hpp new file mode 100644 index 00000000..6e768915 --- /dev/null +++ b/libsrc/core/hashtable.hpp @@ -0,0 +1,1109 @@ +#ifndef FILE_NGSTD_HASHTABLE +#define FILE_NGSTD_HASHTABLE + +/**************************************************************************/ +/* File: hashtable.hpp */ +/* Author: Joachim Schoeberl */ +/* Date: 01. Jun. 95 */ +/**************************************************************************/ + +#include +#include + +#include "mpi_wrapper.hpp" +#include "ngcore_api.hpp" +#include "table.hpp" +#include "utils.hpp" + +namespace ngcore +{ + + + template + class MakeTupleFromInt + { + public: + template + auto operator()(I & i) + { return tuple_cat(MakeTupleFromInt ()(i), std::tie(i[K-1])); } + }; + + template <> + class MakeTupleFromInt<1> + { + public: + template + auto operator()(I & i) { return std::tie(i[0]); } + }; + + + + + /// N integers + template + class INT + { + /// data + T i[(N>0)?N:1]; + + public: + /// + NETGEN_INLINE INT () { } + + /// init all + NETGEN_INLINE INT (T ai1) + { + for (int j = 0; j < N; j++) { i[j] = ai1; } + } + + /// init i[0], i[1] + NETGEN_INLINE INT (T ai1, T ai2) + { i[0] = ai1; i[1] = ai2; } + + /// init i[0], i[1], i[2] + NETGEN_INLINE INT (T ai1, T ai2, T ai3) + { i[0] = ai1; i[1] = ai2; i[2] = ai3; } + + /// init i[0], i[1], i[2] + NETGEN_INLINE INT (T ai1, T ai2, T ai3, T ai4) + { i[0] = ai1; i[1] = ai2; i[2] = ai3; i[3] = ai4; } + + /// init i[0], i[1], i[2] + NETGEN_INLINE INT (T ai1, T ai2, T ai3, T ai4, T ai5) + { i[0] = ai1; i[1] = ai2; i[2] = ai3; i[3] = ai4; i[4] = ai5;} + + /// init i[0], i[1], i[2] + NETGEN_INLINE INT (T ai1, T ai2, T ai3, T ai4, T ai5, T ai6, T ai7, T ai8, T ai9) + { i[0] = ai1; i[1] = ai2; i[2] = ai3; i[3] = ai4; i[4] = ai5; i[5] = ai6; i[6] = ai7; i[7] = ai8; i[8] = ai9; } + + void DoArchive(Archive& ar) + { + ar.Do(i, N); + } + + template + NETGEN_INLINE INT (const INT & in2) + { + if (N2 <= N) + { + for (int j = 0; j < N2; j++) + i[j] = in2[j]; + for (int j = N2; j < N; j++) + i[j] = 0; + } + else + { + for (int j = 0; j < N; j++) + i[j] = in2[j]; + } + } + + template + NETGEN_INLINE INT (const BaseArrayObject & ao) + { + for (int j = 0; j < N; j++) + i[j] = ao.Spec()[j]; + } + + NETGEN_INLINE size_t Size() const { return N; } + /// all ints equal ? + NETGEN_INLINE bool operator== (const INT & in2) const + { + for (int j = 0; j < N; j++) + if (i[j] != in2.i[j]) return 0; + return 1; + } + + /// any ints unequal ? + NETGEN_INLINE bool operator!= (const INT & in2) const + { + for (int j = 0; j < N; j++) + if (i[j] != in2.i[j]) return 1; + return 0; + } + + /// sort integers + NETGEN_INLINE INT & Sort () & + { + for (int k = 0; k < N; k++) + for (int l = k+1; l < N; l++) + if (i[k] > i[l]) + Swap (i[k], i[l]); + return *this; + } + + NETGEN_INLINE INT Sort () && + { + for (int k = 0; k < N; k++) + for (int l = k+1; l < N; l++) + if (i[k] > i[l]) + Swap (i[k], i[l]); + return *this; + } + + /// access + NETGEN_INLINE T & operator[] (int j) + { return i[j]; } + + /// access + NETGEN_INLINE const T & operator[] (int j) const + { return i[j]; } + + template + T get() const { return i[J]; } + + operator FlatArray () { return FlatArray (N, &i[0]); } + + NETGEN_INLINE INT & operator= (T value) + { + for (int j = 0; j < N; j++) + i[j] = value; + return *this; + } + + template + NETGEN_INLINE INT & operator= (INT v2) + { + for (int j = 0; j < N; j++) + i[j] = v2[j]; + return *this; + } + + template + operator std::tuple () + { + return MakeTupleFromInt()(*this); + } + }; + + /// sort 2 integers + template <> + NETGEN_INLINE INT<2> & INT<2>::Sort () & + { + if (i[0] > i[1]) Swap (i[0], i[1]); + return *this; + } + + template <> + NETGEN_INLINE INT<2> INT<2>::Sort () && + { + if (i[0] > i[1]) Swap (i[0], i[1]); + return *this; + } + + /// sort 3 integers + template <> + NETGEN_INLINE INT<3> INT<3>::Sort () && + { + if (i[0] > i[1]) Swap (i[0], i[1]); + if (i[1] > i[2]) Swap (i[1], i[2]); + if (i[0] > i[1]) Swap (i[0], i[1]); + return *this; + } + + /// Print integers + template + inline ostream & operator<<(ostream & s, const INT & i2) + { + for (int j = 0; j < N; j++) + s << (int) i2[j] << " "; + return s; + } + + template + auto begin(const INT & ind) + { + return AOWrapperIterator> (ind, 0); + } + + template + auto end(const INT & ind) + { + return AOWrapperIterator> (ind, N); + } + + + + + + + template + NETGEN_INLINE size_t HashValue (const INT & ind, size_t size) + { + INT lind = ind; + size_t sum = 0; + for (int i = 0; i < N; i++) + sum += lind[i]; + return sum % size; + } + + /// hash value of 1 int + template + NETGEN_INLINE size_t HashValue (const INT<1,TI> & ind, size_t size) + { + return ind[0] % size; + } + + /// hash value of 2 int + template + NETGEN_INLINE size_t HashValue (const INT<2,TI> & ind, size_t size) + { + INT<2,size_t> lind = ind; + return (113*lind[0]+lind[1]) % size; + } + + /// hash value of 3 int + template + NETGEN_INLINE size_t HashValue (const INT<3,TI> & ind, size_t size) + { + INT<3,size_t> lind = ind; + return (113*lind[0]+59*lind[1]+lind[2]) % size; + } + + NETGEN_INLINE size_t HashValue (size_t ind, size_t size) + { + return ind%size; + } + NETGEN_INLINE size_t HashValue (int ind, size_t size) + { + return size_t(ind)%size; + } + + + + + + + + template + NETGEN_INLINE size_t HashValue2 (const INT & ind, size_t mask) + { + INT lind = ind; + size_t sum = 0; + for (int i = 0; i < N; i++) + sum += lind[i]; + return sum & mask; + } + + /// hash value of 1 int + template + NETGEN_INLINE size_t HashValue2 (const INT<1,TI> & ind, size_t mask) + { + return ind[0] & mask; + } + + /// hash value of 2 int + template + NETGEN_INLINE size_t HashValue2 (const INT<2,TI> & ind, size_t mask) + { + INT<2,size_t> lind = ind; + return (113*lind[0]+lind[1]) & mask; + } + + /// hash value of 3 int + template + NETGEN_INLINE size_t HashValue2 (const INT<3,TI> & ind, size_t mask) + { + INT<3,size_t> lind = ind; + return (113*lind[0]+59*lind[1]+lind[2]) & mask; + } + + NETGEN_INLINE size_t HashValue2 (size_t ind, size_t mask) + { + return ind & mask; + } + NETGEN_INLINE size_t HashValue2 (int ind, size_t mask) + { + return size_t(ind) & mask; + } + + + + + + // using ngstd::max; + + template + NETGEN_INLINE T Max (const INT & i) + { + if (D == 0) return 0; + T m = i[0]; + for (int j = 1; j < D; j++) + if (i[j] > m) m = i[j]; + return m; + } + + template + NETGEN_INLINE T Min (const INT & i) + { + if (D == 0) return 0; + T m = i[0]; + for (int j = 1; j < D; j++) + if (i[j] < m) m = i[j]; + return m; + } + + template + NETGEN_INLINE INT Max (INT i1, INT i2) + { + INT tmp; + for (int i = 0; i < D; i++) + tmp[i] = std::max(i1[i], i2[i]); + return tmp; + } + + template + NETGEN_INLINE INT operator+ (INT i1, INT i2) + { + INT tmp; + for (int i = 0; i < D; i++) + tmp[i] = i1[i]+i2[i]; + return tmp; + } + + + + + + + + + + + + /** + A hash-table. + Generic identifiers are mapped to the generic type T. + An open hashtable. The table is implemented by a DynamicTable. + Identifiers must provide a HashValue method. + */ + template + class HashTable + { + /* + DynamicTable hash; + DynamicTable cont; + */ + DynamicTable> table; + public: + /// Constructs a hashtable of size bags. + NETGEN_INLINE HashTable (int size) + // : hash(size), cont(size) + : table(size) + { ; } + NETGEN_INLINE ~HashTable () { ; } + + /// Sets identifier ahash to value acont + void Set (const T_HASH & ahash, const T & acont) + { + int bnr = HashValue (ahash, Size()); + int pos = CheckPosition (bnr, ahash); + if (pos != -1) + // cont.Set (bnr, pos, acont); + table[bnr][pos].second = acont; + else + { + // hash.Add (bnr, ahash); + // cont.Add (bnr, acont); + table.Add (bnr, std::make_pair(ahash, acont)); + } + } + + /// get value of identifier ahash, exception if unused + const T & Get (const T_HASH & ahash) const + { + int bnr = HashValue (ahash, Size()); + int pos = Position (bnr, ahash); + // return cont.Get (bnr, pos); + return table.Get (bnr, pos).second; + } + + /// get value of identifier ahash, exception if unused + const T & Get (int bnr, int pos) const + { + // return cont.Get (bnr, pos); + return table.Get (bnr, pos).second; + } + + /// is identifier used ? + bool Used (const T_HASH & ahash) const + { + // return (CheckPosition (HashValue (ahash, hash.Size()), ahash) != -1); + return (CheckPosition (HashValue (ahash, table.Size()), ahash) != -1); + } + + /// is identifier used ? + bool Used (const T_HASH & ahash, int & bnr, int & pos) const + { + // bnr = HashValue (ahash, hash.Size()); + bnr = HashValue (ahash, Size()); + pos = CheckPosition (bnr, ahash); + return (pos != -1); + } + + + /// number of hash entries + size_t Size () const + { + // return hash.Size(); + return table.Size(); + } + + /// size of hash entry + size_t EntrySize (int bnr) const + { + // return hash[bnr].Size(); + return table[bnr].Size(); + } + + /// get identifier and value of entry bnr, position colnr + void GetData (int bnr, int colnr, T_HASH & ahash, T & acont) const + { + // ahash = hash[bnr][colnr]; + // acont = cont[bnr][colnr]; + ahash = table[bnr][colnr].first; + acont = table[bnr][colnr].second; + } + + /// set identifier and value of entry bnr, position colnr + void SetData (int bnr, int colnr, const T_HASH & ahash, const T & acont) + { + // hash[bnr][colnr] = ahash; + // cont[bnr][colnr] = acont; + table[bnr][colnr] = std::make_pair(ahash, acont); + } + + /// returns position of index. returns -1 on unused + int CheckPosition (int bnr, const T_HASH & ind) const + { + /* + for (int i = 0; i < hash[bnr].Size(); i++) + if (hash[bnr][i] == ind) + return i; + */ + for (int i = 0; i < table[bnr].Size(); i++) + if (table[bnr][i].first == ind) + return i; + return -1; + } + + /// returns position of index. exception on unused + int Position (int bnr, const T_HASH & ind) const + { + for (int i = 0; i < table[bnr].Size(); i++) + if (table[bnr][i].first == ind) + return i; + throw Exception ("Ask for unsused hash-value"); + } + + T & operator[] (T_HASH ahash) + { + int bnr, pos; + if (Used (ahash, bnr, pos)) + return table[bnr][pos].second; + else + { + // hash.Add (bnr, ahash); + // cont.Add (bnr, T(0)); + table.Add (bnr, std::make_pair(ahash, T(0))); + // return cont[bnr][cont[bnr].Size()-1]; + return table[bnr][table[bnr].Size()-1].second; + } + } + + const T & operator[] (T_HASH ahash) const + { + return Get(ahash); + } + + class Iterator + { + const HashTable & ht; + int bnr; + int pos; + public: + Iterator (const HashTable & aht, int abnr, int apos) + : ht(aht), bnr(abnr), pos(apos) { ; } + std::pair operator* () const + { + T_HASH hash; + T data; + ht.GetData (bnr, pos, hash, data); + return std::pair (hash, data); + } + + Iterator & operator++() + { + pos++; + if (pos == ht.EntrySize(bnr)) + { + pos = 0; + bnr++; + for ( ; bnr < ht.Size(); bnr++) + if (ht.EntrySize(bnr) != 0) break; + } + return *this; + } + + bool operator!= (const Iterator & it2) { return bnr != it2.bnr || pos != it2.pos; } + }; + + Iterator begin () const + { + int i = 0; + for ( ; i < Size(); i++) + if (EntrySize(i) != 0) break; + return Iterator(*this, i,0); + } + Iterator end () const { return Iterator(*this, Size(),0); } + }; + + + + inline size_t RoundUp2 (size_t i) + { + size_t res = 1; + while (res < i) res *= 2; // hope it will never be too large + return res; + } + + + + /** + A closed hash-table. + All information is stored in one fixed array. + The array should be allocated with the double size of the expected number of entries. + */ + template + class ClosedHashTable + { + protected: + /// + size_t size; + size_t mask; + /// + size_t used; + /// + Array hash; + /// + Array cont; + /// + T_HASH invalid; + public: + /// + ClosedHashTable (size_t asize = 128) + : size(RoundUp2(asize)), used(0), hash(size), cont(size) + { + mask = size-1; + invalid = -1; + hash = T_HASH(invalid); + } + + ClosedHashTable (ClosedHashTable && ht2) = default; + + // who needs that ? + ClosedHashTable (FlatArray _hash, FlatArray _cont) + : size(_hash.Size()), used(0), hash(_hash.Size(), _hash.Addr(0)), cont(_cont.Size(), _cont.Addr(0)) + { + invalid = -1; + hash = T_HASH(invalid); + } + + /// allocate on local heap + ClosedHashTable (size_t asize, LocalHeap & lh) + : size(asize), hash(asize, lh), cont(asize, lh) + { + invalid = -1; + hash = T_HASH(invalid); + } + + ClosedHashTable & operator= (ClosedHashTable && ht2) = default; + + /// + size_t Size() const + { + return size; + } + + /// is position used + bool UsedPos (size_t pos) const + { + return ! (hash[pos] == invalid); + } + + /// number of used elements + size_t UsedElements () const + { + return used; + /* + size_t cnt = 0; + for (size_t i = 0; i < size; i++) + if (hash[i] != invalid) + cnt++; + return cnt; + */ + } + + size_t Position (const T_HASH ind) const + { + size_t i = HashValue2(ind, mask); + while (1) + { + if (hash[i] == ind) return i; + if (hash[i] == invalid) return size_t(-1); + i++; + if (i >= size) i = 0; + } + } + + void DoubleSize() + { + ClosedHashTable tmp(2*Size()); + for (auto both : *this) + tmp[both.first] = both.second; + *this = std::move(tmp); + } + + // returns true if new position is created + bool PositionCreate (const T_HASH ind, size_t & apos) + { + if (UsedElements()*2 > Size()) DoubleSize(); + + size_t i = HashValue2 (ind, mask); + + while (1) + { + if (hash[i] == invalid) + { + hash[i] = ind; + apos = i; + used++; + return true; + } + if (hash[i] == ind) + { + apos = i; + return false; + } + i++; + if (i >= size) i = 0; + } + } + + + /// + void Set (const T_HASH & ahash, const T & acont) + { + size_t pos; + PositionCreate (ahash, pos); + hash[pos] = ahash; + cont[pos] = acont; + } + + /// + const T & Get (const T_HASH & ahash) const + { + size_t pos = Position (ahash); + if (pos == size_t(-1)) + throw Exception (std::string("illegal key: ") + ToString(ahash) ); + return cont[pos]; + } + + /// + bool Used (const T_HASH & ahash) const + { + return (Position (ahash) != size_t(-1)); + } + + void SetData (size_t pos, const T_HASH & ahash, const T & acont) + { + hash[pos] = ahash; + cont[pos] = acont; + } + + void GetData (size_t pos, T_HASH & ahash, T & acont) const + { + ahash = hash[pos]; + acont = cont[pos]; + } + + void SetData (size_t pos, const T & acont) + { + cont[pos] = acont; + } + + void GetData (size_t pos, T & acont) const + { + acont = cont[pos]; + } + + std::pair GetBoth (size_t pos) const + { + return std::pair (hash[pos], cont[pos]); + } + + const T & operator[] (T_HASH key) const { return Get(key); } + T & operator[] (T_HASH key) + { + size_t pos; + PositionCreate(key, pos); + return cont[pos]; + } + + void SetSize (size_t asize) + { + size = asize; + hash.Alloc(size); + cont.Alloc(size); + + // for (size_t i = 0; i < size; i++) + // hash[i] = invalid; + hash = T_HASH(invalid); + } + + void Delete (T_HASH key) + { + size_t pos = Position(key); + if (pos == size_t(-1)) return; + hash[pos] = invalid; used--; + + while (1) + { + size_t nextpos = pos+1; + if (nextpos == size) nextpos = 0; + if (hash[nextpos] == invalid) break; + + auto key = hash[nextpos]; + auto val = cont[nextpos]; + hash[pos] = invalid; used--; + + Set (key, val); + pos = nextpos; + } + } + + class Iterator + { + const ClosedHashTable & tab; + size_t nr; + public: + Iterator (const ClosedHashTable & _tab, size_t _nr) + : tab(_tab), nr(_nr) + { + while (nr < tab.Size() && !tab.UsedPos(nr)) nr++; + } + Iterator & operator++() + { + nr++; + while (nr < tab.Size() && !tab.UsedPos(nr)) nr++; + return *this; + } + bool operator!= (const Iterator & it2) { return nr != it2.nr; } + auto operator* () const + { + T_HASH hash; + T val; + tab.GetData(nr, hash,val); + return std::make_pair(hash,val); + } + }; + + Iterator begin() const { return Iterator(*this, 0); } + Iterator end() const { return Iterator(*this, Size()); } + }; + + template + ostream & operator<< (ostream & ost, + const ClosedHashTable & tab) + { + for (size_t i = 0; i < tab.Size(); i++) + if (tab.UsedPos(i)) + { + T_HASH key; + T val; + tab.GetData (i, key, val); + ost << key << ": " << val << ", "; + } + return ost; + } + + + template + NETGEN_INLINE size_t HashValue (const INT<2,TI> ind) + { + INT<2,size_t> lind = ind; + return 113*lind[0]+lind[1]; + } + + template + NETGEN_INLINE size_t HashValue (const INT<1,TI> ind) + { + return ind[0]; + } + + + template + class ParallelHashTable + { + class ClosedHT + { + Array keys; + Array values; + size_t used; + + public: + ClosedHT(size_t asize = 256) : keys(asize), values(asize), used(0) + { + keys = TKEY(-1); + } + + size_t Size () const { return keys.Size(); } + size_t Used () const { return used; } + + ClosedHT & operator= (ClosedHT&&) = default; + + void Resize() + { + ClosedHT tmp(keys.Size()*2); + for (size_t i = 0; i < keys.Size(); i++) + if (keys[i] != TKEY(-1)) + { + TKEY hkey = keys[i]; + T hval = values[i]; + size_t hhash = HashValue(hkey); + size_t hhash2 = hhash / 256; + tmp.DoSave(hkey, [hval] (T & v) { v = hval; }, hhash2); + } + (*this) = std::move(tmp); + } + + template + auto Do (TKEY key, TFUNC func, size_t hash) + { + if (used > keys.Size()/2) + Resize(); + return DoSave (key, func, hash); + } + + template + auto DoSave (TKEY key, TFUNC func, size_t hash) + { + size_t pos = hash & (keys.Size()-1); + while (1) + { + if (keys[pos] == key) + break; + if (keys[pos] == TKEY(-1)) + { + keys[pos] = key; + values[pos] = T(0); + used++; + break; + } + pos++; + if (pos == keys.Size()) pos = 0; + } + return func(values[pos]); + } + + T Get (TKEY key, size_t hash) + { + size_t pos = hash & (keys.Size()-1); + while (1) + { + if (keys[pos] == key) + return values[pos]; + if (keys[pos] == TKEY(-1)) + throw Exception ("ParallelHashTable::Get of unused key"); + pos++; + if (pos == keys.Size()) pos = 0; + } + } + + size_t GetCosts (TKEY key, size_t hash) + { + size_t pos = hash & (keys.Size()-1); + size_t costs = 1; + while (1) + { + if (keys[pos] == key) + return costs; + if (keys[pos] == TKEY(-1)) + throw Exception ("ParallelHashTable::Get of unused key"); + costs++; + pos++; + if (pos == keys.Size()) pos = 0; + } + } + + + template + void Iterate (TFUNC func) const + { + for (size_t i = 0; i < keys.Size(); i++) + if (keys[i] != TKEY(-1)) + func(keys[i], values[i]); + } + + void Print (ostream & ost) const + { + for (size_t i = 0; i < keys.Size(); i++) + if (keys[i] != TKEY(-1)) + ost << keys[i] << ": " << values[i] << ", "; + } + }; + + Array hts; + class alignas(64) MyMutex64 : public MyMutex { }; + + Array locks; + + public: + ParallelHashTable() : hts(256), locks(256) { ; } + size_t NumBuckets() const { return hts.Size(); } + auto & Bucket(size_t nr) { return hts[nr]; } + size_t BucketSize(size_t nr) const { return hts[nr].Size(); } + size_t Used (size_t nr) const { return hts[nr].Used(); } + size_t Used() const + { + size_t used = 0; + for (auto & ht : hts) + used += ht.Used(); + return used; + } + template + auto Do (TKEY key, TFUNC func) + { + size_t hash = HashValue(key); + size_t hash1 = hash % 256; + size_t hash2 = hash / 256; + + // locks[hash1].lock(); + // hts[hash1].Do (key, func, hash2); + // locks[hash1].unlock(); + MyLock lock(locks[hash1]); + return hts[hash1].Do (key, func, hash2); + } + + T Get (TKEY key) + { + size_t hash = HashValue(key); + size_t hash1 = hash % 256; + size_t hash2 = hash / 256; + + return hts[hash1].Get (key, hash2); + } + + auto GetCosts (TKEY key) + { + size_t hash = HashValue(key); + size_t hash1 = hash % 256; + size_t hash2 = hash / 256; + + return hts[hash1].GetCosts (key, hash2); + } + + + template + void Iterate(TFUNC func) const + { + for (auto & bucket : hts) + bucket.Iterate(func); + } + + template + void Iterate(size_t nr, TFUNC func) const + { + hts[nr].Iterate(func); + } + + + template + void IterateParallel (FUNC func) + { + Array base(NumBuckets()); + size_t sum = 0; + for (size_t i = 0; i < NumBuckets(); i++) + { + base[i] = sum; + sum += Used(i); + } + ParallelFor(NumBuckets(), + [&] (size_t nr) + { + size_t cnt = base[nr]; + Iterate(nr, + [&cnt, func] (TKEY key, T val) + { + func(cnt, key, val); + cnt++; + }); + }); + } + + + + + void Print (ostream & ost) const + { + for (size_t i : Range(hts)) + if (hts[i].Used() > 0) + { + ost << i << ": "; + hts[i].Print(ost); + } + } + }; + + template + inline ostream & operator<< (ostream & ost, const ParallelHashTable & ht) + { + ht.Print(ost); + return ost; + } + + + + template + Archive & operator & (Archive & archive, INT & mi) + { + for (int i = 0; i < N; i++) + archive & mi[i]; + return archive; + } +} // namespace ngcore + + + +#ifdef PARALLEL +namespace ngcore { + template + class MPI_typetrait > + { + public: + /// gets the MPI datatype + static MPI_Datatype MPIType () + { + static MPI_Datatype MPI_T = 0; + if (!MPI_T) + { + MPI_Type_contiguous ( S, MPI_typetrait::MPIType(), &MPI_T); + MPI_Type_commit ( &MPI_T ); + } + return MPI_T; + } + }; +} +#endif + + + +namespace std +{ + // structured binding support + template + struct tuple_size> : std::integral_constant {}; + template struct tuple_element> { using type = T; }; +} + +#endif diff --git a/libsrc/core/ngcore.hpp b/libsrc/core/ngcore.hpp index 9aa69a6f..91d65bde 100644 --- a/libsrc/core/ngcore.hpp +++ b/libsrc/core/ngcore.hpp @@ -6,12 +6,14 @@ #include "bitarray.hpp" #include "exception.hpp" #include "flags.hpp" +#include "hashtable.hpp" #include "localheap.hpp" #include "logging.hpp" #include "mpi_wrapper.hpp" #include "profiler.hpp" #include "signal.hpp" #include "symboltable.hpp" +#include "table.hpp" #include "taskmanager.hpp" #include "version.hpp" #include "xbool.hpp" diff --git a/libsrc/core/table.cpp b/libsrc/core/table.cpp new file mode 100644 index 00000000..62f544d0 --- /dev/null +++ b/libsrc/core/table.cpp @@ -0,0 +1,177 @@ +/**************************************************************************/ +/* File: table.cpp */ +/* Author: Joachim Schoeberl */ +/* Date: 25. Mar. 2000 */ +/**************************************************************************/ + +/* + Abstract data type Table +*/ + +#include "table.hpp" + +namespace ngcore +{ + template + size_t * TablePrefixSum2 (FlatArray entrysize) + { + size_t size = entrysize.Size(); + size_t * index = new size_t[size+1]; + + Array partial_sums(TaskManager::GetNumThreads()+1); + partial_sums[0] = 0; + ParallelJob + ([&] (TaskInfo ti) + { + IntRange r = IntRange(size).Split(ti.task_nr, ti.ntasks); + size_t mysum = 0; + for (size_t i : r) + mysum += entrysize[i]; + partial_sums[ti.task_nr+1] = mysum; + }); + + for (size_t i = 1; i < partial_sums.Size(); i++) + partial_sums[i] += partial_sums[i-1]; + + ParallelJob + ([&] (TaskInfo ti) + { + IntRange r = IntRange(size).Split(ti.task_nr, ti.ntasks); + size_t mysum = partial_sums[ti.task_nr]; + for (size_t i : r) + { + index[i] = mysum; + mysum += entrysize[i]; + } + }); + index[size] = partial_sums.Last(); + + return index; + } + + NGCORE_API size_t * TablePrefixSum32 (FlatArray entrysize) + { return TablePrefixSum2 (entrysize); } + NGCORE_API size_t * TablePrefixSum64 (FlatArray entrysize) + { return TablePrefixSum2 (entrysize); } + + + BaseDynamicTable :: BaseDynamicTable (int size) + : data(size) + { + for (int i = 0; i < size; i++) + { + data[i].maxsize = 0; + data[i].size = 0; + data[i].col = NULL; + } + oneblock = NULL; + } + + BaseDynamicTable :: BaseDynamicTable (const Array & entrysizes, int elemsize) + : data(entrysizes.Size()) + { + int cnt = 0; + int n = entrysizes.Size(); + + for (int i = 0; i < n; i++) + cnt += entrysizes[i]; + oneblock = new char[elemsize * cnt]; + + cnt = 0; + for (int i = 0; i < n; i++) + { + data[i].maxsize = entrysizes[i]; + data[i].size = 0; + + data[i].col = &oneblock[elemsize * cnt]; + cnt += entrysizes[i]; + } + } + + + BaseDynamicTable :: ~BaseDynamicTable () + { + if (oneblock) + delete [] oneblock; + else + for (int i = 0; i < data.Size(); i++) + delete [] static_cast (data[i].col); + } + + void BaseDynamicTable :: SetSize (int size) + { + for (int i = 0; i < data.Size(); i++) + delete [] static_cast (data[i].col); + + data.SetSize(size); + for (int i = 0; i < size; i++) + { + data[i].maxsize = 0; + data[i].size = 0; + data[i].col = NULL; + } + } + + void BaseDynamicTable :: IncSize (int i, int elsize) + { + if (i < 0 || i >= data.Size()) + { + std::cerr << "BaseDynamicTable::Inc: Out of range, i = " << i << ", size = " << data.Size() << std::endl; + return; + } + + linestruct & line = data[i]; + + if (line.size == line.maxsize) + { + void * p = new char [(2*line.maxsize+5) * elsize]; + + memcpy (p, line.col, line.maxsize * elsize); + delete [] static_cast (line.col); + line.col = p; + line.maxsize = 2*line.maxsize+5; + } + + line.size++; + } + + void BaseDynamicTable :: DecSize (int i) + { + if (i < 0 || i >= data.Size()) + { + std::cerr << "BaseDynamicTable::Dec: Out of range" << std::endl; + return; + } + + linestruct & line = data[i]; + + if (line.size == 0) + { + std::cerr << "BaseDynamicTable::Dec: EntrySize < 0" << std::endl; + return; + } + + line.size--; + } + + void FilteredTableCreator::Add (size_t blocknr, int data) + { + if (!takedofs||takedofs->Test(data)) + TableCreator::Add(blocknr,data); + } + + void FilteredTableCreator::Add (size_t blocknr, IntRange range) + { + for (size_t i=range.First(); iTest(i)) + TableCreator::Add(blocknr,i); + } + + void FilteredTableCreator::Add (size_t blocknr, FlatArray dofs) + { + for (size_t i = 0; i < dofs.Size(); i++) + if (!takedofs||takedofs->Test(dofs[i])) + TableCreator::Add(blocknr,dofs[i]); + } + +} // namespace ngcore diff --git a/libsrc/core/table.hpp b/libsrc/core/table.hpp new file mode 100644 index 00000000..8526ac06 --- /dev/null +++ b/libsrc/core/table.hpp @@ -0,0 +1,496 @@ +#ifndef NETGEN_CORE_TABLE_HPP +#define NETGEN_CORE_TABLE_HPP + +/**************************************************************************/ +/* File: table.hpp */ +/* Author: Joachim Schoeberl */ +/* Date: 25. Mar. 2000 */ +/**************************************************************************/ + +#include +#include + +#include "array.hpp" +#include "bitarray.hpp" +#include "taskmanager.hpp" +#include "ngcore_api.hpp" + +namespace ngcore +{ + + +template +class FlatTable +{ +protected: + /// number of rows + size_t size; + /// pointer to first in row + size_t * index; + /// array of data + T * data; + +public: + FlatTable() = delete; + + NETGEN_INLINE FlatTable(size_t as, size_t * aindex, T * adata) + : size(as), index(aindex), data(adata) { ; } + + /// Size of table + NETGEN_INLINE size_t Size() const { return size; } + + /// Access entry + NETGEN_INLINE const FlatArray operator[] (size_t i) const + { + return FlatArray (index[i+1]-index[i], data+index[i]); + } + + NETGEN_INLINE T * Data() const { return data; } + + NETGEN_INLINE FlatArray AsArray() const + { + return FlatArray (index[size]-index[0], data+index[0]); + } + + NETGEN_INLINE FlatArray IndexArray() const + { + return FlatArray (size+1, index); + } + + /// takes range starting from position start of end-start elements + NETGEN_INLINE FlatTable Range (size_t start, size_t end) const + { + return FlatTable (end-start, index+start, data); + } + + /// takes range starting from position start of end-start elements + NETGEN_INLINE FlatTable Range (T_Range range) const + { + return FlatTable (range.Size(), index+range.First(), data); + } + + + class Iterator + { + const FlatTable & tab; + size_t row; + public: + Iterator (const FlatTable & _tab, size_t _row) : tab(_tab), row(_row) { ; } + Iterator & operator++ () { ++row; return *this; } + FlatArray operator* () const { return tab[row]; } + bool operator!= (const Iterator & it2) { return row != it2.row; } + }; + + Iterator begin() const { return Iterator(*this, 0); } + Iterator end() const { return Iterator(*this, size); } +}; + + NGCORE_API extern size_t * TablePrefixSum32 (FlatArray entrysize); + NGCORE_API extern size_t * TablePrefixSum64 (FlatArray entrysize); + + + NETGEN_INLINE size_t * TablePrefixSum (FlatArray entrysize) + { return TablePrefixSum32 (entrysize); } + NETGEN_INLINE size_t * TablePrefixSum (FlatArray entrysize) + { return TablePrefixSum32 (FlatArray (entrysize.Size(), (unsigned int*)(int*)(entrysize.Addr(0)))); } + NETGEN_INLINE size_t * TablePrefixSum (FlatArray> entrysize) + { return TablePrefixSum32 (FlatArray (entrysize.Size(), (unsigned int*)(std::atomic*)entrysize.Addr(0))); } + NETGEN_INLINE size_t * TablePrefixSum (FlatArray entrysize) + { return TablePrefixSum64 (entrysize); } + + +/** + A compact Table container. + A table contains size entries of variable size. + The entry sizes must be known at construction. +*/ +template +class Table : public FlatTable +{ +protected: + + using FlatTable::size; + using FlatTable::index; + using FlatTable::data; + +public: + /// + NETGEN_INLINE Table () : FlatTable (0,nullptr,nullptr) { ; } + /// Construct table of uniform entrysize + NETGEN_INLINE Table (size_t asize, size_t entrysize) + : FlatTable( asize, new size_t[asize+1], new T[asize*entrysize] ) + { + for (size_t i : Range(size)) + index[i] = i*entrysize; + } + + /// Construct table of variable entrysize + template + NETGEN_INLINE Table (FlatArray entrysize) + : FlatTable (0, nullptr, nullptr) + { + size = entrysize.Size(); + index = TablePrefixSum (entrysize); + size_t cnt = index[size]; + data = new T[cnt]; + } + + explicit NETGEN_INLINE Table (const Table & tab2) + : FlatTable(0, nullptr, nullptr) + { + size = tab2.Size(); + + index = new size_t[size+1]; + for (size_t i = 0; i <= size; i++) + index[i] = tab2.index[i]; + + size_t cnt = index[size]; + data = new T[cnt]; + for (size_t i = 0; i < cnt; i++) + data[i] = tab2.data[i]; + } + + NETGEN_INLINE Table (Table && tab2) + : FlatTable(0, nullptr, nullptr) + { + Swap (size, tab2.size); + Swap (index, tab2.index); + Swap (data, tab2.data); + } + + NETGEN_INLINE Table & operator= (Table && tab2) + { + Swap (size, tab2.size); + Swap (index, tab2.index); + Swap (data, tab2.data); + return *this; + } + + + + /// Delete data + NETGEN_INLINE ~Table () + { + delete [] data; + delete [] index; + } + + /// Size of table + using FlatTable::Size; + + /// number of elements in all rows + NETGEN_INLINE size_t NElements() const { return index[size]; } + + using FlatTable::operator[]; +}; + + +/// Print table +template +inline ostream & operator<< (ostream & s, const Table & table) +{ + for (auto i : Range(table)) + { + s << i << ":"; + for (auto el : table[i]) + s << " " << el; + s << "\n"; + } + s << std::flush; + return s; +} + + + + +template + class TableCreator + { + protected: + int mode; // 1 .. cnt, 2 .. cnt entries, 3 .. fill table + std::atomic nd; + Array> cnt; + Table table; + public: + TableCreator() + { nd = 0; mode = 1; } + TableCreator (size_t acnt) + { nd = acnt; SetMode(2); } + + Table MoveTable() + { + return std::move(table); + } + + bool Done () { return mode > 3; } + void operator++(int) { SetMode (mode+1); } + + int GetMode () const { return mode; } + void SetMode (int amode) + { + mode = amode; + if (mode == 2) + { + // cnt.SetSize(nd); // atomic has no copy + cnt = Array> (nd); + for (auto & ci : cnt) ci.store (0, std::memory_order_relaxed); + } + if (mode == 3) + { + table = Table (cnt); + // for (auto & ci : cnt) ci = 0; + for (auto & ci : cnt) ci.store (0, std::memory_order_relaxed); + // cnt = 0; + } + } + + void SetSize (size_t _nd) + { + if (mode == 1) + nd = _nd; + else + { + if (nd != _nd) + throw Exception ("cannot change size of table-creator"); + } + } + + void Add (size_t blocknr, const T & data) + { + switch (mode) + { + case 1: + { + size_t oldval = nd; + while (blocknr+1>nd) { + nd.compare_exchange_weak (oldval, blocknr+1); + oldval = nd; + } + break; + } + case 2: + cnt[blocknr]++; + break; + case 3: + int ci = cnt[blocknr]++; + table[blocknr][ci] = data; + break; + } + } + + + void Add (size_t blocknr, IntRange range) + { + switch (mode) + { + case 1: + { + size_t oldval = nd; + while (blocknr+1>nd) { + nd.compare_exchange_weak (oldval, blocknr+1); + oldval = nd; + } + break; + } + case 2: + cnt[blocknr] += range.Size(); + break; + case 3: + size_t ci = ( cnt[blocknr] += range.Size() ) - range.Size(); + for (size_t j = 0; j < range.Size(); j++) + table[blocknr][ci+j] = range.First()+j; + break; + } + } + + void Add (size_t blocknr, const FlatArray & dofs) + { + switch (mode) + { + case 1: + { + size_t oldval = nd; + while (blocknr+1>nd) { + nd.compare_exchange_weak (oldval, blocknr+1); + oldval = nd; + } + break; + } + case 2: + cnt[blocknr] += dofs.Size(); + break; + case 3: + size_t ci = ( cnt[blocknr] += dofs.Size() ) - dofs.Size(); + for (size_t j = 0; j < dofs.Size(); j++) + table[blocknr][ci+j] = dofs[j]; + break; + } + } + }; + + class NGCORE_API FilteredTableCreator : public TableCreator + { + protected: + const BitArray* takedofs; + public: + FilteredTableCreator(const BitArray* atakedofs) + : TableCreator(), takedofs(atakedofs) { }; + FilteredTableCreator(int acnt, const BitArray* atakedofs) + : TableCreator(acnt),takedofs(atakedofs) { }; + void Add (size_t blocknr, int data); + void Add (size_t blocknr, IntRange range); + void Add (size_t blocknr, FlatArray dofs); + }; + + + /// Base class to generic DynamicTable. + class BaseDynamicTable + { + protected: + + /// + struct linestruct + { + /// + int size; + /// + int maxsize; + /// + void * col; + }; + + /// + Array data; + /// + char * oneblock; + + public: + /// + NGCORE_API BaseDynamicTable (int size); + /// + NGCORE_API BaseDynamicTable (const Array & entrysizes, int elemsize); + /// + NGCORE_API ~BaseDynamicTable (); + + /// Changes Size of table to size, deletes data + NGCORE_API void SetSize (int size); + /// + NGCORE_API void IncSize (int i, int elsize); + + NGCORE_API void DecSize (int i); + }; + + + + /** + A dynamic table class. + + A DynamicTable contains entries of variable size. Entry sizes can + be increased dynamically. + */ + template + class DynamicTable : public BaseDynamicTable + { + public: + /// Creates table of size size + DynamicTable (int size = 0) + : BaseDynamicTable (size) { ; } + + /// Creates table with a priori fixed entry sizes. + DynamicTable (const Array & entrysizes) + : BaseDynamicTable (entrysizes, sizeof(T)) { ; } + + /// Inserts element acont into row i. Does not test if already used. + void Add (int i, const T & acont) + { + if (data[i].size == data[i].maxsize) + IncSize (i, sizeof (T)); + else + data[i].size++; + static_cast (data[i].col) [data[i].size-1] = acont; + } + + /// Inserts element acont into row i, iff not yet exists. + void AddUnique (int i, const T & cont) + { + int es = EntrySize (i); + int * line = const_cast (GetLine (i)); + for (int j = 0; j < es; j++) + if (line[j] == cont) + return; + Add (i, cont); + } + + + /// Inserts element acont into row i. Does not test if already used. + void AddEmpty (int i) + { + IncSize (i, sizeof (T)); + } + + /** Set the nr-th element in the i-th row to acont. + Does not check for overflow. */ + void Set (int i, int nr, const T & acont) + { static_cast (data[i].col)[nr] = acont; } + + + /** Returns the nr-th element in the i-th row. + Does not check for overflow. */ + const T & Get (int i, int nr) const + { return static_cast (data[i].col)[nr]; } + + + /** Returns pointer to the first element in row i. */ + const T * GetLine (int i) const + { return static_cast (data[i].col); } + + + /// Returns size of the table. + int Size () const + { return data.Size(); } + + /// Returns size of the i-th row. + int EntrySize (int i) const + { return data[i].size; } + + /// + void DecEntrySize (int i) + { DecSize(i); } + + /// Access entry i + FlatArray operator[] (int i) + { return FlatArray (data[i].size, static_cast (data[i].col)); } + + /* + typedef const FlatArray ConstFlatArray; + /// Access entry i + ConstFlatArray operator[] (int i) const + { return FlatArray (data[i].size, static_cast (data[i].col)); } + */ + FlatArray operator[] (int i) const + { return FlatArray (data[i].size, static_cast (data[i].col)); } + }; + + + + + /// Print table + template + inline ostream & operator<< (ostream & s, const DynamicTable & table) + { + for (int i = 0; i < table.Size(); i++) + { + s << i << ":"; + for (int j = 0; j < table[i].Size(); j++) + s << " " << table[i][j]; + s << "\n"; + } + s << std::flush; + return s; + } + + typedef DynamicTable IntTable; + +} // namespace ngcore + +#endif // NETGEN_CORE_TABLE_HPP diff --git a/libsrc/core/utils.hpp b/libsrc/core/utils.hpp index 84d1dade..c0d92054 100644 --- a/libsrc/core/utils.hpp +++ b/libsrc/core/utils.hpp @@ -146,6 +146,35 @@ namespace ngcore template using index_type = typename detail::IndexTypeHelper::type; + class MyMutex + { + std::atomic m; + public: + MyMutex() { m.store(false, std::memory_order_relaxed); } + void lock() + { + bool should = false; + while (!m.compare_exchange_weak(should, true)) + { + should = false; + _mm_pause(); + } + } + void unlock() + { + m = false; + } + }; + + class MyLock + { + MyMutex & mutex; + public: + MyLock (MyMutex & amutex) : mutex(amutex) { mutex.lock(); } + ~MyLock () { mutex.unlock(); } + }; + + } // namespace ngcore #endif // NETGEN_CORE_UTILS_HPP