.. _program_listing_file_include_shad_data_structures_hashmap.h: Program Listing for File hashmap.h ================================== |exhale_lsh| :ref:`Return to documentation for file ` (``include/shad/data_structures/hashmap.h``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp //===------------------------------------------------------------*- C++ -*-===// // // SHAD // // The Scalable High-performance Algorithms and Data Structure Library // //===----------------------------------------------------------------------===// // // Copyright 2018 Battelle Memorial Institute // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy // of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. // //===----------------------------------------------------------------------===// #ifndef INCLUDE_SHAD_DATA_STRUCTURES_HASHMAP_H_ #define INCLUDE_SHAD_DATA_STRUCTURES_HASHMAP_H_ #include #include #include #include #include #include "shad/data_structures/abstract_data_structure.h" #include "shad/data_structures/buffer.h" #include "shad/data_structures/compare_and_hash_utils.h" #include "shad/data_structures/local_hashmap.h" #include "shad/distributed_iterator_traits.h" #include "shad/runtime/runtime.h" namespace shad { template class map_iterator; template , typename INSERT_POLICY = Overwriter> class Hashmap : public AbstractDataStructure< Hashmap> { template friend class AbstractDataStructure; friend class map_iterator, const std::pair, std::pair>; friend class map_iterator, const std::pair, std::pair>; public: using value_type = std::pair; using HmapT = Hashmap; using LMapT = LocalHashmap; using ObjectID = typename AbstractDataStructure::ObjectID; using ShadHashmapPtr = typename AbstractDataStructure::SharedPtr; using iterator = map_iterator, const std::pair, std::pair>; using const_iterator = map_iterator, const std::pair, std::pair>; using local_iterator = lmap_iterator, const std::pair>; using const_local_iterator = lmap_iterator, const std::pair>; struct EntryT { EntryT(const KTYPE &k, const VTYPE &v) : key(k), value(v) {} EntryT() = default; KTYPE key; VTYPE value; }; using BuffersVector = typename impl::BuffersVector; #ifdef DOXYGEN_IS_RUNNING static ShadHashmapPtr Create(const size_t numEntries); #endif ObjectID GetGlobalID() const { return oid_; } size_t Size() const; std::pair Insert(const KTYPE &key, const VTYPE &value); void AsyncInsert(rt::Handle &handle, const KTYPE &key, const VTYPE &value); void BufferedInsert(const KTYPE &key, const VTYPE &value); void BufferedAsyncInsert(rt::Handle &handle, const KTYPE &key, const VTYPE &value); void WaitForBufferedInsert() { auto flushLambda_ = [](const ObjectID &oid) { auto ptr = HmapT::GetPtr(oid); ptr->buffers_.FlushAll(); }; rt::executeOnAll(flushLambda_, oid_); } void Erase(const KTYPE &key); void AsyncErase(rt::Handle &handle, const KTYPE &key); void Clear() { auto clearLambda = [](const ObjectID &oid) { auto mapPtr = HmapT::GetPtr(oid); mapPtr->localMap_.Clear(); }; rt::executeOnAll(clearLambda, oid_); } using LookupResult = typename LocalHashmap::LookupResult; bool Lookup(const KTYPE &key, VTYPE *res); void AsyncLookup(rt::Handle &handle, const KTYPE &key, LookupResult *res); template void Apply(const KTYPE &key, ApplyFunT &&function, Args &... args); template void AsyncApply(rt::Handle &handle, const KTYPE &key, ApplyFunT &&function, Args &... args); template void ForEachEntry(ApplyFunT &&function, Args &... args); template void AsyncForEachEntry(rt::Handle &handle, ApplyFunT &&function, Args &... args); template void ForEachKey(ApplyFunT &&function, Args &... args); template void AsyncForEachKey(rt::Handle &handle, ApplyFunT &&function, Args &... args); void PrintAllEntries() { auto printLambda = [](const ObjectID &oid) { auto mapPtr = HmapT::GetPtr(oid); std::cout << "---- Locality: " << rt::thisLocality() << std::endl; mapPtr->localMap_.PrintAllEntries(); }; rt::executeOnAll(printLambda, oid_); } // FIXME it should be protected void BufferEntryInsert(const EntryT &entry) { localMap_.Insert(entry.key, entry.value); } iterator begin() { return iterator::map_begin(this); } iterator end() { return iterator::map_end(this); } const_iterator cbegin() const { return const_iterator::map_begin(this); } const_iterator cend() const { return const_iterator::map_end(this); } const_iterator begin() const { return cbegin(); } const_iterator end() const { return cend(); } local_iterator local_begin() { return local_iterator::lmap_begin(&localMap_); } local_iterator local_end() { return local_iterator::lmap_end(&localMap_); } const_local_iterator clocal_begin() { return const_local_iterator::lmap_begin(&localMap_); } const_local_iterator clocal_end() { return const_local_iterator::lmap_end(&localMap_); } std::pair insert(const value_type &value) { return Insert(value.first, value.second); } std::pair insert(const_iterator, const value_type &value) { return insert(value); } void buffered_async_insert(rt::Handle &h, const value_type &value) { BufferedAsyncInsert(h, value.first, value.second); } void buffered_async_wait(rt::Handle &h) { rt::waitForCompletion(h); } void buffered_async_flush() { WaitForBufferedInsert(); } private: ObjectID oid_; LocalHashmap localMap_; BuffersVector buffers_; struct InsertArgs { ObjectID oid; KTYPE key; VTYPE value; }; struct LookupArgs { ObjectID oid; KTYPE key; }; protected: Hashmap(ObjectID oid, const size_t numEntries) : oid_(oid), localMap_(std::max( numEntries / (constants::kDefaultNumEntriesPerBucket * rt::numLocalities()), 1lu)), buffers_(oid) {} }; template inline size_t Hashmap::Size() const { size_t size = localMap_.size_; size_t remoteSize; auto sizeLambda = [](const ObjectID &oid, size_t *res) { auto mapPtr = HmapT::GetPtr(oid); *res = mapPtr->localMap_.size_; }; for (auto tgtLoc : rt::allLocalities()) { if (tgtLoc != rt::thisLocality()) { rt::executeAtWithRet(tgtLoc, sizeLambda, oid_, &remoteSize); size += remoteSize; } } return size; } template inline std::pair< typename Hashmap::iterator, bool> Hashmap::Insert(const KTYPE &key, const VTYPE &value) { using itr_traits = distributed_iterator_traits; size_t targetId = shad::hash{}(key) % rt::numLocalities(); rt::Locality targetLocality(targetId); std::pair res; if (targetLocality == rt::thisLocality()) { auto lres = localMap_.Insert(key, value); res.first = itr_traits::iterator_from_local(begin(), end(), lres.first); res.second = lres.second; } else { auto insertLambda = [](const std::tuple &args_, std::pair *res_ptr) { auto &args(std::get<2>(args_)); auto mapPtr = HmapT::GetPtr(args.oid); auto lres = mapPtr->localMap_.Insert(args.key, args.value); res_ptr->first = itr_traits::iterator_from_local( std::get<0>(args_), std::get<1>(args_), lres.first); res_ptr->second = lres.second; }; rt::executeAtWithRet( targetLocality, insertLambda, std::make_tuple(begin(), end(), InsertArgs{oid_, key, value}), &res); } return res; } template inline void Hashmap::AsyncInsert( rt::Handle &handle, const KTYPE &key, const VTYPE &value) { size_t targetId = shad::hash{}(key) % rt::numLocalities(); rt::Locality targetLocality(targetId); if (targetLocality == rt::thisLocality()) { localMap_.AsyncInsert(handle, key, value); } else { auto insertLambda = [](rt::Handle &handle, const InsertArgs &args) { auto mapPtr = HmapT::GetPtr(args.oid); mapPtr->localMap_.AsyncInsert(handle, args.key, args.value); }; InsertArgs args = {oid_, key, value}; rt::asyncExecuteAt(handle, targetLocality, insertLambda, args); } } template inline void Hashmap::BufferedInsert( const KTYPE &key, const VTYPE &value) { size_t targetId = shad::hash{}(key) % rt::numLocalities(); rt::Locality targetLocality(targetId); buffers_.Insert(EntryT(key, value), targetLocality); } template inline void Hashmap::BufferedAsyncInsert(rt::Handle &handle, const KTYPE &key, const VTYPE &value) { size_t targetId = shad::hash{}(key) % rt::numLocalities(); rt::Locality targetLocality(targetId); buffers_.AsyncInsert(handle, EntryT(key, value), targetLocality); } template inline void Hashmap::Erase( const KTYPE &key) { size_t targetId = shad::hash{}(key) % rt::numLocalities(); rt::Locality targetLocality(targetId); if (targetLocality == rt::thisLocality()) { localMap_.Erase(key); } else { auto eraseLambda = [](const LookupArgs &args) { auto mapPtr = HmapT::GetPtr(args.oid); mapPtr->localMap_.Erase(args.key); }; LookupArgs args = {oid_, key}; rt::executeAt(targetLocality, eraseLambda, args); } } template inline void Hashmap::AsyncErase( rt::Handle &handle, const KTYPE &key) { size_t targetId = shad::hash{}(key) % rt::numLocalities(); rt::Locality targetLocality(targetId); if (targetLocality == rt::thisLocality()) { localMap_.AsyncErase(handle, key); } else { auto eraseLambda = [](rt::Handle &handle, const LookupArgs &args) { auto mapPtr = HmapT::GetPtr(args.oid); mapPtr->localMap_.AsyncErase(handle, args.key); }; LookupArgs args = {oid_, key}; rt::asyncExecuteAt(handle, targetLocality, eraseLambda, args); } } template inline bool Hashmap::Lookup( const KTYPE &key, VTYPE *res) { size_t targetId = shad::hash{}(key) % rt::numLocalities(); rt::Locality targetLocality(targetId); if (targetLocality == rt::thisLocality()) { return localMap_.Lookup(key, res); } else { auto lookupLambda = [](const LookupArgs &args, LookupResult *res) { auto mapPtr = HmapT::GetPtr(args.oid); res->found = mapPtr->localMap_.Lookup(args.key, &res->value); }; LookupArgs args = {oid_, key}; LookupResult lres; rt::executeAtWithRet(targetLocality, lookupLambda, args, &lres); if (lres.found) { *res = std::move(lres.value); } return lres.found; } return false; } template inline void Hashmap::AsyncLookup( rt::Handle &handle, const KTYPE &key, LookupResult *res) { size_t targetId = shad::hash{}(key) % rt::numLocalities(); rt::Locality targetLocality(targetId); if (targetLocality == rt::thisLocality()) { localMap_.AsyncLookup(handle, key, res); } else { auto lookupLambda = [](rt::Handle &, const LookupArgs &args, LookupResult *res) { auto mapPtr = HmapT::GetPtr(args.oid); LookupResult tres; mapPtr->localMap_.Lookup(args.key, &tres); *res = tres; }; LookupArgs args = {oid_, key}; rt::asyncExecuteAtWithRet(handle, targetLocality, lookupLambda, args, res); } } template template void Hashmap::ForEachEntry( ApplyFunT &&function, Args &... args) { using FunctionTy = void (*)(const KTYPE &, VTYPE &, Args &...); FunctionTy fn = std::forward(function); using feArgs = std::tuple>; using LMapPtr = LocalHashmap *; using ArgsTuple = std::tuple>; feArgs arguments(oid_, fn, std::tuple(args...)); auto feLambda = [](const feArgs &args) { auto mapPtr = HmapT::GetPtr(std::get<0>(args)); ArgsTuple argsTuple(&mapPtr->localMap_, std::get<1>(args), std::get<2>(args)); rt::forEachAt(rt::thisLocality(), LMapT::template ForEachEntryFunWrapper, argsTuple, mapPtr->localMap_.numBuckets_); }; rt::executeOnAll(feLambda, arguments); } template template void Hashmap::AsyncForEachEntry( rt::Handle &handle, ApplyFunT &&function, Args &... args) { using FunctionTy = void (*)(rt::Handle &, const KTYPE &, VTYPE &, Args &...); FunctionTy fn = std::forward(function); using feArgs = std::tuple>; using ArgsTuple = std::tuple>; feArgs arguments(oid_, fn, std::tuple(args...)); auto feLambda = [](rt::Handle &handle, const feArgs &args) { auto mapPtr = HmapT::GetPtr(std::get<0>(args)); ArgsTuple argsTuple(&mapPtr->localMap_, std::get<1>(args), std::get<2>(args)); rt::asyncForEachAt( handle, rt::thisLocality(), LMapT::template AsyncForEachEntryFunWrapper, argsTuple, mapPtr->localMap_.numBuckets_); }; rt::asyncExecuteOnAll(handle, feLambda, arguments); } template template void Hashmap::ForEachKey( ApplyFunT &&function, Args &... args) { using FunctionTy = void (*)(const KTYPE &, Args &...); FunctionTy fn = std::forward(function); using feArgs = std::tuple>; using ArgsTuple = std::tuple>; feArgs arguments(oid_, fn, std::tuple(args...)); auto feLambda = [](const feArgs &args) { auto mapPtr = HmapT::GetPtr(std::get<0>(args)); ArgsTuple argsTuple(&mapPtr->localMap_, std::get<1>(args), std::get<2>(args)); rt::forEachAt(rt::thisLocality(), LMapT::template ForEachKeyFunWrapper, argsTuple, mapPtr->localMap_.numBuckets_); }; rt::executeOnAll(feLambda, arguments); } template template void Hashmap::AsyncForEachKey( rt::Handle &handle, ApplyFunT &&function, Args &... args) { using FunctionTy = void (*)(rt::Handle &, const KTYPE &, Args &...); FunctionTy fn = std::forward(function); using feArgs = std::tuple>; using ArgsTuple = std::tuple>; feArgs arguments(oid_, fn, std::tuple(args...)); auto feLambda = [](rt::Handle &handle, const feArgs &args) { auto mapPtr = HmapT::GetPtr(std::get<0>(args)); ArgsTuple argsTuple(&mapPtr->localMap_, std::get<1>(args), std::get<2>(args)); rt::asyncForEachAt( handle, rt::thisLocality(), LMapT::template AsyncForEachKeyFunWrapper, argsTuple, mapPtr->localMap_.numBuckets_); }; rt::asyncExecuteOnAll(handle, feLambda, arguments); } template template void Hashmap::Apply( const KTYPE &key, ApplyFunT &&function, Args &... args) { size_t targetId = shad::hash{}(key) % rt::numLocalities(); rt::Locality targetLocality(targetId); if (targetLocality == rt::thisLocality()) { localMap_.Apply(key, function, args...); } else { using FunctionTy = void (*)(const KTYPE &, VTYPE &, Args &...); FunctionTy fn = std::forward(function); using ArgsTuple = std::tuple>; ArgsTuple arguments(oid_, key, fn, std::tuple(args...)); auto feLambda = [](const ArgsTuple &args) { constexpr auto Size = std::tuple_size< typename std::decay(args))>::type>::value; ArgsTuple &tuple = const_cast(args); LMapT *mapPtr = &(HmapT::GetPtr(std::get<0>(tuple))->localMap_); LMapT::CallApplyFun(mapPtr, std::get<1>(tuple), std::get<2>(tuple), std::get<3>(tuple), std::make_index_sequence{}); }; rt::executeAt(targetLocality, feLambda, arguments); } } template template void Hashmap::AsyncApply( rt::Handle &handle, const KTYPE &key, ApplyFunT &&function, Args &... args) { size_t targetId = shad::hash{}(key) % rt::numLocalities(); rt::Locality targetLocality(targetId); if (targetLocality == rt::thisLocality()) { localMap_.AsyncApply(handle, key, function, args...); } else { using FunctionTy = void (*)(rt::Handle &, const KTYPE &, VTYPE &, Args &...); FunctionTy fn = std::forward(function); using ArgsTuple = std::tuple>; ArgsTuple arguments(oid_, key, fn, std::tuple(args...)); auto feLambda = [](rt::Handle &handle, const ArgsTuple &args) { constexpr auto Size = std::tuple_size< typename std::decay(args))>::type>::value; ArgsTuple &tuple(const_cast(args)); LMapT *mapPtr = &(HmapT::GetPtr(std::get<0>(tuple))->localMap_); LMapT::AsyncCallApplyFun(handle, mapPtr, std::get<1>(tuple), std::get<2>(tuple), std::get<3>(tuple), std::make_index_sequence{}); }; rt::asyncExecuteAt(handle, targetLocality, feLambda, arguments); } } template class map_iterator : public std::iterator { public: using OIDT = typename MapT::ObjectID; using LMap = typename MapT::LMapT; using local_iterator_type = lmap_iterator; using value_type = NonConstT; map_iterator() {} map_iterator(uint32_t locID, const OIDT mapOID, local_iterator_type &lit, T element) { data_ = {locID, mapOID, lit, element}; } map_iterator(uint32_t locID, const OIDT mapOID, local_iterator_type &lit) { auto mapPtr = MapT::GetPtr(mapOID); const LMap *lmapPtr = &(mapPtr->localMap_); if (lit != local_iterator_type::lmap_end(lmapPtr)) data_ = itData(locID, mapOID, lit, *lit); else *this = map_end(mapPtr.get()); } static map_iterator map_begin(const MapT *mapPtr) { const LMap *lmapPtr = &(mapPtr->localMap_); auto localEnd = local_iterator_type::lmap_end(lmapPtr); if (static_cast(rt::thisLocality()) == 0) { auto localBegin = local_iterator_type::lmap_begin(lmapPtr); if (localBegin != localEnd) { return map_iterator(0, mapPtr->oid_, localBegin); } map_iterator beg(0, mapPtr->oid_, localEnd, T()); return ++beg; } auto getItLambda = [](const OIDT &mapOID, map_iterator *res) { auto mapPtr = MapT::GetPtr(mapOID); const LMap *lmapPtr = &(mapPtr->localMap_); auto localEnd = local_iterator_type::lmap_end(lmapPtr); auto localBegin = local_iterator_type::lmap_begin(lmapPtr); if (localBegin != localEnd) { *res = map_iterator(0, mapOID, localBegin); } else { map_iterator beg(0, mapOID, localEnd, T()); *res = ++beg; } }; map_iterator beg(0, mapPtr->oid_, localEnd, T()); rt::executeAtWithRet(rt::Locality(0), getItLambda, mapPtr->oid_, &beg); return beg; } static map_iterator map_end(const MapT *mapPtr) { local_iterator_type lend = local_iterator_type::lmap_end(&(mapPtr->localMap_)); map_iterator end(rt::numLocalities(), OIDT(0), lend, T()); return end; } bool operator==(const map_iterator &other) const { return (data_ == other.data_); } bool operator!=(const map_iterator &other) const { return !(*this == other); } T operator*() const { return data_.element_; } map_iterator &operator++() { auto mapPtr = MapT::GetPtr(data_.oid_); if (static_cast(rt::thisLocality()) == data_.locId_) { const LMap *lmapPtr = &(mapPtr->localMap_); auto lend = local_iterator_type::lmap_end(lmapPtr); if (data_.lmapIt_ != lend) { ++(data_.lmapIt_); } if (data_.lmapIt_ != lend) { data_.element_ = *(data_.lmapIt_); return *this; } else { // find the local begin on next localities itData itd; for (uint32_t i = data_.locId_ + 1; i < rt::numLocalities(); ++i) { rt::executeAtWithRet(rt::Locality(i), getLocBeginIt, data_.oid_, &itd); if (itd.locId_ != rt::numLocalities()) { // It Data is valid data_ = itd; return *this; } } data_ = itData(rt::numLocalities(), OIDT(0), lend, T()); return *this; } } itData itd; rt::executeAtWithRet(rt::Locality(data_.locId_), getRemoteIt, data_, &itd); data_ = itd; return *this; } map_iterator operator++(int) { map_iterator tmp = *this; operator++(); return tmp; } class local_iterator_range { public: local_iterator_range(local_iterator_type B, local_iterator_type E) : begin_(B), end_(E) {} local_iterator_type begin() { return begin_; } local_iterator_type end() { return end_; } private: local_iterator_type begin_; local_iterator_type end_; }; static local_iterator_range local_range(map_iterator &B, map_iterator &E) { auto mapPtr = MapT::GetPtr(B.data_.oid_); local_iterator_type lbeg, lend; uint32_t thisLocId = static_cast(rt::thisLocality()); if (B.data_.locId_ == thisLocId) { lbeg = B.data_.lmapIt_; } else { lbeg = local_iterator_type::lmap_begin(&(mapPtr->localMap_)); } if (E.data_.locId_ == thisLocId) { lend = E.data_.lmapIt_; } else { lend = local_iterator_type::lmap_end(&(mapPtr->localMap_)); } return local_iterator_range(lbeg, lend); } static rt::localities_range localities(map_iterator &B, map_iterator &E) { return rt::localities_range(rt::Locality(B.data_.locId_), rt::Locality(std::min( rt::numLocalities(), E.data_.locId_ + 1))); } static map_iterator iterator_from_local(map_iterator &B, map_iterator &E, local_iterator_type itr) { return map_iterator(static_cast(rt::thisLocality()), B.data_.oid_, itr); } private: struct itData { itData() : oid_(0), lmapIt_(nullptr, 0, 0, nullptr, nullptr) {} itData(uint32_t locId, OIDT oid, local_iterator_type lmapIt, T element) : locId_(locId), oid_(oid), lmapIt_(lmapIt), element_(element) {} bool operator==(const itData &other) const { return (locId_ == other.locId_) && (lmapIt_ == other.lmapIt_); } bool operator!=(itData &other) const { return !(*this == other); } uint32_t locId_; OIDT oid_; local_iterator_type lmapIt_; NonConstT element_; }; itData data_; static void getLocBeginIt(const OIDT &mapOID, itData *res) { auto mapPtr = MapT::GetPtr(mapOID); auto lmapPtr = &(mapPtr->localMap_); auto localEnd = local_iterator_type::lmap_end(lmapPtr); auto localBegin = local_iterator_type::lmap_begin(lmapPtr); if (localBegin != localEnd) { *res = itData(static_cast(rt::thisLocality()), mapOID, localBegin, *localBegin); } else { *res = itData(rt::numLocalities(), OIDT(0), localEnd, T()); } } static void getRemoteIt(const itData &itd, itData *res) { auto mapPtr = MapT::GetPtr(itd.oid_); auto lmapPtr = &(mapPtr->localMap_); auto localEnd = local_iterator_type::lmap_end(lmapPtr); local_iterator_type cit = itd.lmapIt_; ++cit; if (cit != localEnd) { *res = itData(static_cast(rt::thisLocality()), itd.oid_, cit, *cit); return; } else { itData outitd; for (uint32_t i = itd.locId_ + 1; i < rt::numLocalities(); ++i) { rt::executeAtWithRet(rt::Locality(i), getLocBeginIt, itd.oid_, &outitd); if (outitd.locId_ != rt::numLocalities()) { // It Data is valid *res = outitd; return; } } *res = itData(rt::numLocalities(), OIDT(0), localEnd, T()); } } }; } // namespace shad #endif // INCLUDE_SHAD_DATA_STRUCTURES_HASHMAP_H_