diff --git a/Event/EventContainers/EventContainers/IdentifiableContainerBase.h b/Event/EventContainers/EventContainers/IdentifiableContainerBase.h index b3b1d6d7ab0f0407bbfc7d48e63781f676a225c6..3f355408cbc62e131aeb912ad86ef17660d8a19e 100644 --- a/Event/EventContainers/EventContainers/IdentifiableContainerBase.h +++ b/Event/EventContainers/EventContainers/IdentifiableContainerBase.h @@ -10,7 +10,7 @@ namespace EventContainers{ class IdentifiableCacheBase; -enum class Mode { OfflineLowMemory, OfflineFast }; +enum class Mode { OfflineLowMemory, OfflineFast, OfflineMap }; class IdentifiableContainerBase{ public: #include "EventContainers/deleter.h" diff --git a/Event/EventContainers/EventContainers/IdentifiableContainerMT.h b/Event/EventContainers/EventContainers/IdentifiableContainerMT.h index 8791b57b881f699fa51b2edad021676cc006f323..a4ccd3517c288730d5e0f7d2e98759584b441606 100644 --- a/Event/EventContainers/EventContainers/IdentifiableContainerMT.h +++ b/Event/EventContainers/EventContainers/IdentifiableContainerMT.h @@ -208,6 +208,8 @@ public: return m_link->fullSize(); } + void prepareItr() const { m_link->wait(); } + /// return number of collections virtual size_t numberOfCollections() const override final{ return IdentifiableContainerBase::numberOfCollections(); diff --git a/Event/EventContainers/EventContainers/InternalOfflineMap.h b/Event/EventContainers/EventContainers/InternalOfflineMap.h new file mode 100644 index 0000000000000000000000000000000000000000..c69d08186f736b57129ae7294293f1681ad6aef7 --- /dev/null +++ b/Event/EventContainers/EventContainers/InternalOfflineMap.h @@ -0,0 +1,51 @@ +/* + Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration +*/ + +#ifndef EVENTCONTAINERS_InternalOfflineMap_H +#define EVENTCONTAINERS_InternalOfflineMap_H +#include "EventContainers/I_InternalIDC.h" +#include "CxxUtils/checker_macros.h" +#include <atomic> +#include <mutex> +#include <unordered_map> + +namespace EventContainers{ +/* +This class implements the IdentifiableContainer code for the offline case. +This class balances speed against memory usage by using an unordered_map + +Moderately fast random access, fast iteration, moderate memory usage. +*/ +class InternalOfflineMap final : public I_InternalIDC { +public: + InternalOfflineMap(size_t max); + virtual ~InternalOfflineMap()=default; + virtual InternalConstItr cbegin() const override; + virtual InternalConstItr cend() const override; + virtual InternalConstItr indexFind( IdentifierHash hashId ) const override; + virtual const std::vector < hashPair >& getAllHashPtrPair() const override; + mutable std::vector<std::pair<IdentifierHash::value_type, const void*>> m_map; + std::unordered_map<IdentifierHash::value_type, const void*> m_fullMap; + mutable std::mutex m_waitMutex ATLAS_THREAD_SAFE; + mutable std::atomic<bool> m_needsupdate ATLAS_THREAD_SAFE; //These mutables are carefully thought out, do not change + virtual bool tryAddFromCache(IdentifierHash hashId, EventContainers::IDC_WriteHandleBase &lock) override; + virtual bool tryAddFromCache(IdentifierHash hashId) override; + virtual void wait() const override; + virtual std::vector<IdentifierHash> getAllCurrentHashes() const override; + virtual size_t numberOfCollections() const override; + virtual void cleanUp(deleter_f* deleter) noexcept override; + virtual size_t fullSize() const noexcept override {return m_maxsize;} + virtual StatusCode fetchOrCreate(IdentifierHash hashId) override; + virtual StatusCode fetchOrCreate(const std::vector<IdentifierHash> &hashIds) override; + virtual bool insert(IdentifierHash hashId, const void* ptr) override; + virtual const void* findIndexPtr(IdentifierHash hashId) const noexcept override; + virtual StatusCode addLock(IdentifierHash hashId, const void* ptr) override; + virtual void* removeCollection( IdentifierHash hashId ) override; + virtual void destructor(deleter_f*) noexcept override; +private: + const size_t m_maxsize; +}; + +} +#endif diff --git a/Event/EventContainers/src/IdentifiableContainerBase.cxx b/Event/EventContainers/src/IdentifiableContainerBase.cxx index 3c6fdcc09acb4f510c297fd022165761455de6c5..7ed316fa3350416a173c3ed27606ededa51a11f5 100644 --- a/Event/EventContainers/src/IdentifiableContainerBase.cxx +++ b/Event/EventContainers/src/IdentifiableContainerBase.cxx @@ -8,6 +8,7 @@ #include "EventContainers/InternalOnline.h" #include "EventContainers/InternalOffline.h" #include "EventContainers/InternalOfflineFast.h" +#include "EventContainers/InternalOfflineMap.h" using namespace EventContainers; @@ -27,6 +28,7 @@ using namespace EventContainers; m_OnlineMode = false; if(mode == Mode::OfflineLowMemory) m_link = std::make_unique<EventContainers::InternalOffline>(max); else if(mode == Mode::OfflineFast) m_link = std::make_unique<EventContainers::InternalOfflineFast>(max); + else if(mode == Mode::OfflineMap) m_link = std::make_unique<EventContainers::InternalOfflineMap>(max); else{ throw std::runtime_error("Invalid Mode specified"); } diff --git a/Event/EventContainers/src/InternalOfflineMap.cxx b/Event/EventContainers/src/InternalOfflineMap.cxx new file mode 100644 index 0000000000000000000000000000000000000000..f60ec109d92dccf3235c90a5b6a02c942c633881 --- /dev/null +++ b/Event/EventContainers/src/InternalOfflineMap.cxx @@ -0,0 +1,135 @@ +/* + Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration +*/ + +#include "EventContainers/InternalOfflineMap.h" +#include <algorithm> +#include "EventContainers/IDC_WriteHandleBase.h" + +using namespace EventContainers; +typedef I_InternalIDC::InternalConstItr InternalConstItr; +InternalOfflineMap::InternalOfflineMap(size_t max) : m_needsupdate(false), m_maxsize(max) {} + + +bool InternalOfflineMap::tryAddFromCache(IdentifierHash hash, EventContainers::IDC_WriteHandleBase&) { + return m_fullMap.count(hash); +} + +bool InternalOfflineMap::tryAddFromCache(IdentifierHash hash) +{ + return m_fullMap.count(hash); +} + +void InternalOfflineMap::wait() const { + std::lock_guard lock (m_waitMutex); + if(m_needsupdate == false) return; + m_map.clear(); + m_map.reserve(m_fullMap.size()); + for(const auto &pair : m_fullMap){ + m_map.emplace_back(pair.first, pair.second); + } + std::sort(m_map.begin(), m_map.end()); + m_needsupdate.store(false); +} + +std::vector<IdentifierHash> InternalOfflineMap::getAllCurrentHashes() const { + std::vector<IdentifierHash> ids; + ids.reserve(m_fullMap.size()); + if(m_needsupdate == true){ + for(const auto &pair : m_fullMap){ + ids.emplace_back(pair.first); + } + std::sort(ids.begin(), ids.end()); + }else{ + for(const auto &pair : m_map){ + ids.emplace_back(pair.first); + } + } + return ids; +} + +InternalConstItr + InternalOfflineMap::cend() const { + if(m_needsupdate) wait(); + return m_map.cend(); +} + +const std::vector < I_InternalIDC::hashPair >& InternalOfflineMap::getAllHashPtrPair() const{ + if(m_needsupdate) wait(); + return m_map; +} + +InternalConstItr + InternalOfflineMap::cbegin() const { + if(m_needsupdate) wait(); + return m_map.cbegin(); +} + +InternalConstItr InternalOfflineMap::indexFind( IdentifierHash hashId ) const{ + if(m_needsupdate) wait(); + auto itr = std::lower_bound( m_map.cbegin(), m_map.cend(), hashId.value(), [](const hashPair &lhs, IdentifierHash::value_type rhs) -> bool { return lhs.first < rhs; } ); + if(itr!= m_map.cend() && itr->first==hashId) return itr; + return m_map.cend(); +} + +size_t InternalOfflineMap::numberOfCollections() const { + return m_fullMap.size(); +} + +void InternalOfflineMap::cleanUp(deleter_f* deleter) noexcept { + destructor(deleter); + m_map.clear(); + m_fullMap.clear(); + m_needsupdate.store(false, std::memory_order_relaxed); +} + +bool InternalOfflineMap::insert(IdentifierHash hashId, const void* ptr) { + if(hashId >= m_maxsize) return false; + auto &mapptr = m_fullMap[hashId]; + if(mapptr!=nullptr) return false; //already present + mapptr = ptr; + m_needsupdate.store(true, std::memory_order_relaxed); + return true; +} + +const void* InternalOfflineMap::findIndexPtr(IdentifierHash hashId) const noexcept{ + if(hashId >= m_maxsize) return nullptr; + auto search = m_fullMap.find(hashId); + if(search!=m_fullMap.cend()) return search->second; + return nullptr; +} + +StatusCode InternalOfflineMap::addLock(IdentifierHash hashId, const void* ptr) { + bool added = insert(hashId, ptr); + if(!added) { +#ifndef NDEBUG + std::cout << "IDC WARNING Deletion shouldn't occur in addLock paradigm" << std::endl; +#endif + return StatusCode::FAILURE; + } + return StatusCode::SUCCESS; +} + +void* InternalOfflineMap::removeCollection( IdentifierHash hashId ) { + auto search = m_fullMap.find(hashId); + if(search==m_fullMap.cend()) return nullptr; + void* ptr = const_cast< void* > (search->second); + m_fullMap.erase(search); + m_needsupdate.store(true, std::memory_order_relaxed); + return ptr; +} + +StatusCode InternalOfflineMap::fetchOrCreate(IdentifierHash) { + throw std::runtime_error("Not implemented in offline mode"); +} +StatusCode InternalOfflineMap::fetchOrCreate(const std::vector<IdentifierHash>&) +{ + throw std::runtime_error("Not implemented in offline mode"); +} + +void InternalOfflineMap::destructor(deleter_f* deleter) noexcept { + if(!m_needsupdate) for(const auto& x : m_map) deleter(x.second); + else { + for(const auto &pair : m_fullMap) { deleter(pair.second); } + } +} diff --git a/Event/EventContainers/test/IDC_Benchmark.cxx b/Event/EventContainers/test/IDC_Benchmark.cxx index 6e7b9bfddfe43d1ea168e9398288d63c969ee498..85c66d3dc51fcdf00d1de43f8f49bd95dbfe916b 100644 --- a/Event/EventContainers/test/IDC_Benchmark.cxx +++ b/Event/EventContainers/test/IDC_Benchmark.cxx @@ -30,6 +30,12 @@ void timebackwardsfill(std::string name, IdentifiableContainerMT<long unsigned i void accessTime(std::string name, IdentifiableContainerMT<long unsigned int>& container){ + auto startwait = std::chrono::steady_clock::now(); + container.prepareItr(); + auto endwait = std::chrono::steady_clock::now(); + std::chrono::duration<double> offlinewait = endwait-startwait; + std::cout << name << " wait time " << offlinewait.count() << std::endl; + auto start3 = std::chrono::steady_clock::now(); auto offlinecnt = container.GetAllCurrentHashes(); for(auto hash : offlinecnt) if(hash != *container.indexFindPtr(hash) ) std::abort(); @@ -81,25 +87,40 @@ int main(){ auto end3 = std::chrono::steady_clock::now(); std::chrono::duration<double> offlinefillfast = end3-start3; std::cout << "offlinefast fill time " << offlinefillfast.count() << std::endl; + + auto start4 = std::chrono::steady_clock::now(); + auto offlinemap = new IdentifiableContainerMT<long unsigned int>(50000, EventContainers::Mode::OfflineMap); + for(size_t i =3;i<50000;i+=3){ + offlinemap->addCollection(new long unsigned int(i) ,i).ignore(); + } + auto end4 = std::chrono::steady_clock::now(); + std::chrono::duration<double> offlinefillmap = end4-start4; + std::cout << "offlinemap fill time " << offlinefillmap.count() << std::endl; + accessTime("online ", *online); accessTime("offline ", *offline); accessTime("offlinefast ", *offlinefast); + accessTime("offlinemap ", *offlinemap); timedelete("onlineCont ", online); timedelete("onlineCache ", cache); timedelete("offline ", offline); timedelete("offlinefast ", offlinefast); + timedelete("offlinemap ", offlinemap); auto offlinefast2 = new IdentifiableContainerMT<long unsigned int>(50000, EventContainers::Mode::OfflineFast); auto cache2 = new IdentifiableCache<long unsigned int>(50000, nullptr); auto online2 = new IdentifiableContainerMT<long unsigned int>(cache2); auto offline2 = new IdentifiableContainerMT<long unsigned int>(50000); + auto offlinemap2 = new IdentifiableContainerMT<long unsigned int>(50000, EventContainers::Mode::OfflineMap); timebackwardsfill("offlinefast", offlinefast2); + timebackwardsfill("offlinemap", offlinemap2); timebackwardsfill("offline", offline2); timebackwardsfill("online", online2); delete offline2; delete online2; delete cache2; delete offlinefast2; + delete offlinemap2; std::cout << "Test Successful" << std::endl; return 0; }