From 5ebb133223730576232300e0fd503df02aa159c3 Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Fri, 1 Mar 2024 11:59:24 +0100 Subject: [PATCH 01/13] Refactor pool --- src/nrncvode/hocevent.cpp | 2 +- src/nrncvode/netcvode.cpp | 4 +- src/nrncvode/pool.hpp | 135 +++++++++++++++----------------------- 3 files changed, 57 insertions(+), 84 deletions(-) diff --git a/src/nrncvode/hocevent.cpp b/src/nrncvode/hocevent.cpp index 5654ea0b7d..ef0d2c1ec7 100644 --- a/src/nrncvode/hocevent.cpp +++ b/src/nrncvode/hocevent.cpp @@ -27,7 +27,7 @@ HocEvent* HocEvent::alloc(const char* stmt, Object* ppobj, int reinit, Object* p if (!hepool_) { nrn_hoc_lock(); if (!hepool_) { - hepool_ = new HocEventPool(100, 1); + hepool_ = new HocEventPool(); } nrn_hoc_unlock(); } diff --git a/src/nrncvode/netcvode.cpp b/src/nrncvode/netcvode.cpp index 1573d190e4..8cdc8200a9 100644 --- a/src/nrncvode/netcvode.cpp +++ b/src/nrncvode/netcvode.cpp @@ -1032,10 +1032,10 @@ Object** NetCvode::netconlist() { #define ITE_SIZE 10 NetCvodeThreadData::NetCvodeThreadData() { - tpool_ = new TQItemPool(1000, 1); + tpool_ = new TQItemPool(); // tqe_ accessed only by thread i so no locking tqe_ = new TQueue(tpool_, 0); - sepool_ = new SelfEventPool(1000, 1); + sepool_ = new SelfEventPool(); selfqueue_ = nullptr; psl_thr_ = nullptr; tq_ = nullptr; diff --git a/src/nrncvode/pool.hpp b/src/nrncvode/pool.hpp index c062e93ad3..b436e5b720 100644 --- a/src/nrncvode/pool.hpp +++ b/src/nrncvode/pool.hpp @@ -1,5 +1,4 @@ -#ifndef pool_h -#define pool_h +#pragma once // create and manage a vector of objects as a memory pool of those objects // the object must have a void clear() method which takes care of any @@ -9,127 +8,101 @@ // the chain of Pool // is only for extra items in a pool_ and no other fields are used. // the pool doubles in size every time a chain Pool is added. -// maxget() tells the most number of pool items used at once. #include -#include +#include +#include -template +template class MutexPool { public: - MutexPool(long count, int mkmut = 0); + MutexPool(); ~MutexPool(); T* alloc(); void hpfree(T*); - int maxget() { - return maxget_; - } void free_all(); private: + // Add a new vector to the pools_ with the double of size of the previous one + // Reset put_ and get_ void grow(); - T** items_{}; - T* pool_{}; - long pool_size_{}; - long count_{}; - long get_{}; - long put_{}; - long nget_{}; - long maxget_{}; - MutexPool* chain_{}; + // A ring-buffer covering all the pools_ + // put_ is the place where to put the next freed item + // get_ is the place where to get a new item + std::vector items_{}; + std::size_t get_{}; + std::size_t put_{}; + // Number of items currently allocated in the pools + // When this number equal the total number of elements it will be time to grow() + std::size_t nget_{}; + // A vector of all the pools_ + std::vector> pools_{}; MUTDEC }; -template -MutexPool::MutexPool(long count, int mkmut) { - count_ = count; - pool_ = new T[count_]; - pool_size_ = count; - items_ = new T*[count_]; - for (long i = 0; i < count_; ++i) { - items_[i] = pool_ + i; +template +MutexPool::MutexPool() { + auto* ptr = pools_.emplace_back(Count).data(); + items_.resize(Count); + for (std::size_t i = 0; i < Count; ++i) { + items_[i] = ptr + i; } - MUTCONSTRUCT(mkmut) + MUTCONSTRUCT(1) } -template -void MutexPool::grow() { - assert(get_ == put_); - MutexPool* p = new MutexPool(count_); - p->chain_ = chain_; - chain_ = p; - long newcnt = 2 * count_; - T** itms = new T*[newcnt]; - put_ += count_; - for (long i = 0; i < get_; ++i) { - itms[i] = items_[i]; - } - for (long i = get_, j = 0; j < count_; ++i, ++j) { - itms[i] = p->items_[j]; - } - for (long i = put_, j = get_; j < count_; ++i, ++j) { - itms[i] = items_[j]; +template +void MutexPool::grow() { + std::size_t total_size = items_.size(); + + // Everything is already allocated so reset put_ to the beginning of items + // and set get_ to the new space allocated pointing to the new pool + put_ = 0; + get_ = total_size; + + items_.resize(2 * total_size); + + auto* ptr = pools_.emplace_back(total_size).data(); + for (std::size_t i = total_size, j = 0; j < 2 * total_size; ++i, ++j) { + items_[i] = ptr + j; } - delete[] items_; - delete[] p->items_; - p->items_ = 0; - items_ = itms; - count_ = newcnt; } -template -MutexPool::~MutexPool() { - delete chain_; - delete[] pool_; - delete[] items_; +template +MutexPool::~MutexPool() { MUTDESTRUCT } -template -T* MutexPool::alloc() { +template +T* MutexPool::alloc() { MUTLOCK - if (nget_ >= count_) { + if (nget_ == items_.size()) { grow(); } - T* item = items_[get_]; - get_ = (get_ + 1) % count_; ++nget_; - - maxget_ = std::max(nget_, maxget_); + T* item = items_[get_]; + get_ = (++get_) % items_.size(); MUTUNLOCK return item; } -template -void MutexPool::hpfree(T* item) { +template +void MutexPool::hpfree(T* item) { MUTLOCK - assert(nget_ > 0); - items_[put_] = item; - put_ = (put_ + 1) % count_; --nget_; + items_[put_] = item; + put_ = (++put_) % items_.size(); MUTUNLOCK } -template -void MutexPool::free_all() { +template +void MutexPool::free_all() { MUTLOCK - MutexPool* pp; - long i; - nget_ = 0; get_ = 0; put_ = 0; - for (pp = this; pp; pp = pp->chain_) { - for (i = 0; i < pp->pool_size_; ++i) { - items_[put_++] = pp->pool_ + i; - pp->pool_[i].clear(); - } + for (auto& item: items_) { + item->clear(); } - assert(put_ == count_); - put_ = 0; MUTUNLOCK } - - -#endif From e02c2b67b0c92c89035706eddd8b1fcdb3727da2 Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Fri, 1 Mar 2024 12:27:53 +0100 Subject: [PATCH 02/13] Make it an allocator --- src/nrncvode/hocevent.cpp | 4 +-- src/nrncvode/netcvode.cpp | 6 ++--- src/nrncvode/pool.hpp | 53 +++++++++++++++++++++------------------ src/nrncvode/tqueue.cpp | 16 ++++++------ 4 files changed, 42 insertions(+), 37 deletions(-) diff --git a/src/nrncvode/hocevent.cpp b/src/nrncvode/hocevent.cpp index ef0d2c1ec7..75896a0445 100644 --- a/src/nrncvode/hocevent.cpp +++ b/src/nrncvode/hocevent.cpp @@ -31,7 +31,7 @@ HocEvent* HocEvent::alloc(const char* stmt, Object* ppobj, int reinit, Object* p } nrn_hoc_unlock(); } - HocEvent* he = hepool_->alloc(); + HocEvent* he = hepool_->allocate(); he->stmt_ = nullptr; he->ppobj_ = ppobj; he->reinit_ = reinit; @@ -48,7 +48,7 @@ void HocEvent::hefree() { delete stmt_; stmt_ = nullptr; } - hepool_->hpfree(this); + hepool_->deallocate(this); } void HocEvent::clear() { diff --git a/src/nrncvode/netcvode.cpp b/src/nrncvode/netcvode.cpp index 8cdc8200a9..bb23e59ea1 100644 --- a/src/nrncvode/netcvode.cpp +++ b/src/nrncvode/netcvode.cpp @@ -2295,7 +2295,7 @@ void nrn_net_send(Datum* v, double* weight, Point_process* pnt, double td, doubl STATISTICS(SelfEvent::selfevent_send_); NrnThread* nt = PP2NT(pnt); NetCvodeThreadData& p = net_cvode_instance->p[nt->id]; - SelfEvent* se = p.sepool_->alloc(); + SelfEvent* se = p.sepool_->allocate(); se->flag_ = flag; se->target_ = pnt; se->weight_ = weight; @@ -2331,7 +2331,7 @@ void artcell_net_send(Datum* v, double* weight, Point_process* pnt, double td, d STATISTICS(SelfEvent::selfevent_send_); NrnThread* nt = PP2NT(pnt); NetCvodeThreadData& p = net_cvode_instance->p[nt->id]; - SelfEvent* se = p.sepool_->alloc(); + SelfEvent* se = p.sepool_->allocate(); se->flag_ = flag; se->target_ = pnt; se->weight_ = weight; @@ -3366,7 +3366,7 @@ void SelfEvent::call_net_receive(NetCvode* ns) { } NetCvodeThreadData& nctd = ns->p[PP2NT(target_)->id]; --nctd.unreffed_event_cnt_; - nctd.sepool_->hpfree(this); + nctd.sepool_->deallocate(this); } void SelfEvent::pr(const char* s, double tt, NetCvode* ns) { diff --git a/src/nrncvode/pool.hpp b/src/nrncvode/pool.hpp index b436e5b720..b4fe53d13f 100644 --- a/src/nrncvode/pool.hpp +++ b/src/nrncvode/pool.hpp @@ -3,26 +3,29 @@ // create and manage a vector of objects as a memory pool of those objects // the object must have a void clear() method which takes care of any // data the object contains which should be deleted upon free_all. -// clear() is NOT called on free, only on free_all. +// clear() is NOT called on deallocate, only on free_all. -// the chain of Pool -// is only for extra items in a pool_ and no other fields are used. -// the pool doubles in size every time a chain Pool is added. - -#include +// the pool doubles in size every time a new pool is added. #include #include +#include +#include template class MutexPool { public: + using value_type = T; MutexPool(); - ~MutexPool(); - T* alloc(); - void hpfree(T*); + T* allocate(std::size_t = 1); + void deallocate(T*, std::size_t = 1); void free_all(); + // For now this pool only allow allocation of 1 byte at a time + std::size_t max_size() { + return 1; + } + private: // Add a new vector to the pools_ with the double of size of the previous one // Reset put_ and get_ @@ -38,7 +41,9 @@ class MutexPool { std::size_t nget_{}; // A vector of all the pools_ std::vector> pools_{}; - MUTDEC +#if NRN_ENABLE_THREADS + std::recursive_mutex mut_; +#endif }; template @@ -48,7 +53,6 @@ MutexPool::MutexPool() { for (std::size_t i = 0; i < Count; ++i) { items_[i] = ptr + i; } - MUTCONSTRUCT(1) } template @@ -69,13 +73,13 @@ void MutexPool::grow() { } template -MutexPool::~MutexPool() { - MUTDESTRUCT -} - -template -T* MutexPool::alloc() { - MUTLOCK +T* MutexPool::allocate(std::size_t n) { +#if NRN_ENABLE_THREADS + std::lock_guard l(mut_); +#endif + if (n != 1) { + throw std::runtime_error("MutexPool allocator can only allocate one object at a time"); + } if (nget_ == items_.size()) { grow(); } @@ -83,26 +87,27 @@ T* MutexPool::alloc() { T* item = items_[get_]; get_ = (++get_) % items_.size(); - MUTUNLOCK return item; } template -void MutexPool::hpfree(T* item) { - MUTLOCK +void MutexPool::deallocate(T* item, std::size_t) { +#if NRN_ENABLE_THREADS + std::lock_guard l(mut_); +#endif --nget_; items_[put_] = item; put_ = (++put_) % items_.size(); - MUTUNLOCK } template void MutexPool::free_all() { - MUTLOCK +#if NRN_ENABLE_THREADS + std::lock_guard l(mut_); +#endif get_ = 0; put_ = 0; for (auto& item: items_) { item->clear(); } - MUTUNLOCK } diff --git a/src/nrncvode/tqueue.cpp b/src/nrncvode/tqueue.cpp index 8e51035838..f54eaad2c2 100644 --- a/src/nrncvode/tqueue.cpp +++ b/src/nrncvode/tqueue.cpp @@ -81,7 +81,7 @@ TQueue::~TQueue() { } void TQueue::deleteitem(TQItem* i) { - tpool_->hpfree(i); + tpool_->deallocate(i); } void TQueue::print() { @@ -186,7 +186,7 @@ void TQueue::spike_stat(double* d) { TQItem* TQueue::insert(double t, void* d) { MUTLOCK STAT(ninsert); - TQItem* i = tpool_->alloc(); + TQItem* i = tpool_->allocate(); i->data_ = d; i->t_ = t; i->cnt_ = -1; @@ -205,7 +205,7 @@ TQItem* TQueue::insert(double t, void* d) { TQItem* TQueue::enqueue_bin(double td, void* d) { MUTLOCK STAT(ninsert); - TQItem* i = tpool_->alloc(); + TQItem* i = tpool_->allocate(); i->data_ = d; i->t_ = td; binq_->enqueue(td, i); @@ -215,7 +215,7 @@ TQItem* TQueue::enqueue_bin(double td, void* d) { void TQueue::release(TQItem* q) { // if lockable then the pool is internally handles locking - tpool_->hpfree(q); + tpool_->deallocate(q); } void TQueue::remove(TQItem* q) { @@ -233,7 +233,7 @@ void TQueue::remove(TQItem* q) { } else { spdelete(q, sptree_); } - tpool_->hpfree(q); + tpool_->deallocate(q); } MUTUNLOCK } @@ -401,7 +401,7 @@ SelfQueue::~SelfQueue() { } TQItem* SelfQueue::insert(void* d) { MUTLOCK - TQItem* q = tpool_->alloc(); + TQItem* q = tpool_->allocate(); q->left_ = nullptr; q->right_ = head_; if (head_) { @@ -423,14 +423,14 @@ void* SelfQueue::remove(TQItem* q) { if (q == head_) { head_ = q->right_; } - tpool_->hpfree(q); + tpool_->deallocate(q); MUTUNLOCK return q->data_; } void SelfQueue::remove_all() { MUTLOCK for (TQItem* q = first(); q; q = next(q)) { - tpool_->hpfree(q); + tpool_->deallocate(q); } head_ = nullptr; MUTUNLOCK From 7863b0ade6bb3ff0691651be2c7bf721a564873c Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Fri, 1 Mar 2024 13:16:14 +0100 Subject: [PATCH 03/13] Fix some things --- src/nrncvode/pool.hpp | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/nrncvode/pool.hpp b/src/nrncvode/pool.hpp index b4fe53d13f..d45ecbadee 100644 --- a/src/nrncvode/pool.hpp +++ b/src/nrncvode/pool.hpp @@ -12,7 +12,7 @@ #include #include -template +template class MutexPool { public: using value_type = T; @@ -22,7 +22,7 @@ class MutexPool { void free_all(); // For now this pool only allow allocation of 1 byte at a time - std::size_t max_size() { + std::size_t max_size() const { return 1; } @@ -46,17 +46,18 @@ class MutexPool { #endif }; -template -MutexPool::MutexPool() { - auto* ptr = pools_.emplace_back(Count).data(); - items_.resize(Count); - for (std::size_t i = 0; i < Count; ++i) { +template +MutexPool::MutexPool() { + constexpr std::size_t count = 1000; + auto* ptr = pools_.emplace_back(count).data(); + items_.resize(count); + for (std::size_t i = 0; i < count; ++i) { items_[i] = ptr + i; } } -template -void MutexPool::grow() { +template +void MutexPool::grow() { std::size_t total_size = items_.size(); // Everything is already allocated so reset put_ to the beginning of items @@ -67,13 +68,13 @@ void MutexPool::grow() { items_.resize(2 * total_size); auto* ptr = pools_.emplace_back(total_size).data(); - for (std::size_t i = total_size, j = 0; j < 2 * total_size; ++i, ++j) { + for (std::size_t i = total_size, j = 0; j < total_size; ++i, ++j) { items_[i] = ptr + j; } } -template -T* MutexPool::allocate(std::size_t n) { +template +T* MutexPool::allocate(std::size_t n) { #if NRN_ENABLE_THREADS std::lock_guard l(mut_); #endif @@ -90,8 +91,8 @@ T* MutexPool::allocate(std::size_t n) { return item; } -template -void MutexPool::deallocate(T* item, std::size_t) { +template +void MutexPool::deallocate(T* item, std::size_t) { #if NRN_ENABLE_THREADS std::lock_guard l(mut_); #endif @@ -100,8 +101,8 @@ void MutexPool::deallocate(T* item, std::size_t) { put_ = (++put_) % items_.size(); } -template -void MutexPool::free_all() { +template +void MutexPool::free_all() { #if NRN_ENABLE_THREADS std::lock_guard l(mut_); #endif From b15b8a9288c2a57d317705914cf9cbb066d10e96 Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Mon, 4 Mar 2024 05:37:03 +0100 Subject: [PATCH 04/13] Small improvvment --- src/nrncvode/pool.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/nrncvode/pool.hpp b/src/nrncvode/pool.hpp index d45ecbadee..4ab65d876c 100644 --- a/src/nrncvode/pool.hpp +++ b/src/nrncvode/pool.hpp @@ -42,7 +42,7 @@ class MutexPool { // A vector of all the pools_ std::vector> pools_{}; #if NRN_ENABLE_THREADS - std::recursive_mutex mut_; + std::mutex mut_; #endif }; @@ -58,7 +58,7 @@ MutexPool::MutexPool() { template void MutexPool::grow() { - std::size_t total_size = items_.size(); + const std::size_t total_size = items_.size(); // Everything is already allocated so reset put_ to the beginning of items // and set get_ to the new space allocated pointing to the new pool @@ -76,7 +76,7 @@ void MutexPool::grow() { template T* MutexPool::allocate(std::size_t n) { #if NRN_ENABLE_THREADS - std::lock_guard l(mut_); + std::lock_guard l(mut_); #endif if (n != 1) { throw std::runtime_error("MutexPool allocator can only allocate one object at a time"); @@ -94,7 +94,7 @@ T* MutexPool::allocate(std::size_t n) { template void MutexPool::deallocate(T* item, std::size_t) { #if NRN_ENABLE_THREADS - std::lock_guard l(mut_); + std::lock_guard l(mut_); #endif --nget_; items_[put_] = item; @@ -104,7 +104,7 @@ void MutexPool::deallocate(T* item, std::size_t) { template void MutexPool::free_all() { #if NRN_ENABLE_THREADS - std::lock_guard l(mut_); + std::lock_guard l(mut_); #endif get_ = 0; put_ = 0; From 2120e26198c3c1f458f4a5e29c74870aed66809a Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Mon, 4 Mar 2024 06:09:59 +0100 Subject: [PATCH 05/13] Move Pool to utils --- src/nrncvode/hocevent.cpp | 4 +- src/nrncvode/netcon.h | 4 +- src/nrncvode/netcvode.cpp | 3 +- src/nrncvode/netcvode.h | 2 +- src/nrncvode/tqueue.cpp | 1 - src/nrncvode/tqueue.hpp | 4 +- src/nrniv/cxprop.cpp | 10 +- src/nrniv/structpool.h | 151 ------------------------------- src/{nrncvode => utils}/pool.hpp | 66 ++++++++++---- 9 files changed, 59 insertions(+), 186 deletions(-) delete mode 100644 src/nrniv/structpool.h rename src/{nrncvode => utils}/pool.hpp (65%) diff --git a/src/nrncvode/hocevent.cpp b/src/nrncvode/hocevent.cpp index 75896a0445..c0e75efdd9 100644 --- a/src/nrncvode/hocevent.cpp +++ b/src/nrncvode/hocevent.cpp @@ -1,10 +1,10 @@ #include -#include +#include #include #include #include -using HocEventPool = MutexPool; +using HocEventPool = Pool; HocEventPool* HocEvent::hepool_; HocEvent::HocEvent() { diff --git a/src/nrncvode/netcon.h b/src/nrncvode/netcon.h index 107e25fd64..a521444fc1 100644 --- a/src/nrncvode/netcon.h +++ b/src/nrncvode/netcon.h @@ -7,7 +7,7 @@ #include "neuron/container/data_handle.hpp" #include "nrnmpi.h" #include "nrnneosm.h" -#include "pool.hpp" +#include "utils/pool.hpp" #include "tqitem.hpp" #include @@ -29,7 +29,7 @@ class TQueue; struct NrnThread; class NetCvode; class HocEvent; -using HocEventPool = MutexPool; +using HocEventPool = Pool; class HocCommand; struct STETransition; class IvocVect; diff --git a/src/nrncvode/netcvode.cpp b/src/nrncvode/netcvode.cpp index bb23e59ea1..6a1a1668ce 100644 --- a/src/nrncvode/netcvode.cpp +++ b/src/nrncvode/netcvode.cpp @@ -13,7 +13,7 @@ #include "parse.hpp" #include "cvodeobj.h" #include "hoclist.h" -#include "pool.hpp" +#include "utils/pool.hpp" #include "tqueue.hpp" #include "ocobserv.h" #include "nrnneosm.h" @@ -427,7 +427,6 @@ struct InterThreadEvent { }; typedef std::vector WatchList; -using SelfEventPool = MutexPool; typedef std::vector TQList; // allows marshalling of all items in the event queue that need to be diff --git a/src/nrncvode/netcvode.h b/src/nrncvode/netcvode.h index 65d0765887..30f73dabc1 100644 --- a/src/nrncvode/netcvode.h +++ b/src/nrncvode/netcvode.h @@ -20,7 +20,7 @@ using PreSynTable = std::unordered_map, P class NetCon; class DiscreteEvent; class SelfEvent; -using SelfEventPool = MutexPool; +using SelfEventPool = Pool; struct hoc_Item; class PlayRecord; class IvocVect; diff --git a/src/nrncvode/tqueue.cpp b/src/nrncvode/tqueue.cpp index f54eaad2c2..e325de15ef 100644 --- a/src/nrncvode/tqueue.cpp +++ b/src/nrncvode/tqueue.cpp @@ -7,7 +7,6 @@ #include #include "tqueue.hpp" -#include "pool.hpp" #define PROFILE 0 #include "profile.h" diff --git a/src/nrncvode/tqueue.hpp b/src/nrncvode/tqueue.hpp index 4ee6cf23cc..61220d2eff 100644 --- a/src/nrncvode/tqueue.hpp +++ b/src/nrncvode/tqueue.hpp @@ -5,11 +5,11 @@ #include #include -#include +#include #include "tqitem.hpp" -using TQItemPool = MutexPool; +using TQItemPool = Pool; // bin queue for the fixed step method for NetCons and PreSyns. Splay tree // for others. diff --git a/src/nrniv/cxprop.cpp b/src/nrniv/cxprop.cpp index 08921d655b..300b97b776 100644 --- a/src/nrniv/cxprop.cpp +++ b/src/nrniv/cxprop.cpp @@ -1,7 +1,7 @@ #include "arraypool.h" // ArrayPool #include "hocdec.h" // Datum #include "section.h" // Section -#include "structpool.h" // Pool +#include "utils/pool.hpp" // Pool #include "../neuron/model_data.hpp" #include @@ -84,9 +84,9 @@ void nrn_delete_mechanism_prop_datum(int type) { Section* nrn_section_alloc() { if (!secpool_) { - secpool_ = new SectionPool(1000); + secpool_ = new SectionPool(); } - auto* const sec = secpool_->alloc(); + auto* const sec = secpool_->allocate(); // Call the Section constructor new (sec) Section(); return sec; @@ -95,14 +95,14 @@ Section* nrn_section_alloc() { void nrn_section_free(Section* s) { // Call the Section destructor s->~Section(); - secpool_->hpfree(s); + secpool_->deallocate(s); } int nrn_is_valid_section_ptr(void* v) { if (!secpool_) { return 0; } - return secpool_->is_valid_ptr(v); + return secpool_->is_valid_ptr(static_cast(v)); } void nrn_poolshrink(int shrink) { diff --git a/src/nrniv/structpool.h b/src/nrniv/structpool.h deleted file mode 100644 index 452753c956..0000000000 --- a/src/nrniv/structpool.h +++ /dev/null @@ -1,151 +0,0 @@ -#ifndef structpool_h -#define structpool_h - -// same as ../nrncvode/pool.h but items do not require a clear method. - -// create and manage a vector of objects as a memory pool of those objects -// the object must have a void clear() method which takes care of any -// data the object contains which should be deleted upon free_all. -// clear() is NOT called on free, only on free_all. - -// the chain of Pool -// is only for extra items in a pool_ and no other fields are used. -// the pool doubles in size every time a chain Pool is added. -// maxget() tells the most number of pool items used at once. - -template -class Pool { - public: - Pool(long count); - ~Pool(); - T* alloc(); - void hpfree(T*); - int maxget() { - return maxget_; - } - void free_all(); - int is_valid_ptr(void*); - - private: - void grow(); - - private: - T** items_; - T* pool_; - long pool_size_; - long count_; - long get_; - long put_; - long nget_; - long maxget_; - Pool* chain_; -}; - - -template -Pool::Pool(long count) { - count_ = count; - pool_ = new T[count_]; - pool_size_ = count; - items_ = new T*[count_]; - for (long i = 0; i < count_; ++i) - items_[i] = pool_ + i; - get_ = 0; - put_ = 0; - nget_ = 0; - maxget_ = 0; - chain_ = 0; -} - -template -void Pool::grow() { - assert(get_ == put_); - Pool* p = new Pool(count_); - p->chain_ = chain_; - chain_ = p; - long newcnt = 2 * count_; - T** itms = new T*[newcnt]; - long i, j; - put_ += count_; - for (i = 0; i < get_; ++i) { - itms[i] = items_[i]; - } - for (i = get_, j = 0; j < count_; ++i, ++j) { - itms[i] = p->items_[j]; - } - for (i = put_, j = get_; j < count_; ++i, ++j) { - itms[i] = items_[j]; - } - delete[] items_; - delete[] p->items_; - p->items_ = 0; - items_ = itms; - count_ = newcnt; -} - -template -Pool::~Pool() { - if (chain_) { - delete chain_; - } - delete[] pool_; - if (items_) { - delete[] items_; - } -} - -template -int Pool::is_valid_ptr(void* v) { - Pool* pp; - for (pp = this; pp; pp = pp->chain_) { - void* vp = (void*) (pp->pool_); - if (v >= vp && v < (void*) (pp->pool_ + pp->pool_size_)) { - if ((((char*) v - (char*) vp) % sizeof(T)) == 0) { - return 1; - } else { - return 0; - } - } - } - return 0; -} - -template -T* Pool::alloc() { - if (nget_ >= count_) { - grow(); - } - T* item = items_[get_]; - get_ = (get_ + 1) % count_; - ++nget_; - if (nget_ > maxget_) { - maxget_ = nget_; - } - return item; -} - -template -void Pool::hpfree(T* item) { - assert(nget_ > 0); - items_[put_] = item; - put_ = (put_ + 1) % count_; - --nget_; -} - -template -void Pool::free_all() { - Pool* pp; - long i; - nget_ = 0; - get_ = 0; - put_ = 0; - for (pp = this; pp; pp = pp->chain_) { - for (i = 0; i < pp->pool_size_; ++i) { - items_[put_++] = pp->pool_ + i; - } - } - assert(put_ == count_); - put_ = 0; -} - -#endif diff --git a/src/nrncvode/pool.hpp b/src/utils/pool.hpp similarity index 65% rename from src/nrncvode/pool.hpp rename to src/utils/pool.hpp index 4ab65d876c..3ee0e307ea 100644 --- a/src/nrncvode/pool.hpp +++ b/src/utils/pool.hpp @@ -11,12 +11,20 @@ #include #include #include +#include -template -class MutexPool { +constexpr bool PoolMutexed = true; + +template +class Pool { public: + using value_type = T; - MutexPool(); + Pool(); + template + void set_function(F f) { + clear_(f); + } T* allocate(std::size_t = 1); void deallocate(T*, std::size_t = 1); void free_all(); @@ -26,6 +34,8 @@ class MutexPool { return 1; } + bool is_valid_ptr(const T*) const; + private: // Add a new vector to the pools_ with the double of size of the previous one // Reset put_ and get_ @@ -41,13 +51,16 @@ class MutexPool { std::size_t nget_{}; // A vector of all the pools_ std::vector> pools_{}; -#if NRN_ENABLE_THREADS + + std::function clear_{}; + +#if NRN_ENABLE_THREADS && M std::mutex mut_; #endif }; -template -MutexPool::MutexPool() { +template +Pool::Pool() { constexpr std::size_t count = 1000; auto* ptr = pools_.emplace_back(count).data(); items_.resize(count); @@ -56,8 +69,8 @@ MutexPool::MutexPool() { } } -template -void MutexPool::grow() { +template +void Pool::grow() { const std::size_t total_size = items_.size(); // Everything is already allocated so reset put_ to the beginning of items @@ -73,13 +86,13 @@ void MutexPool::grow() { } } -template -T* MutexPool::allocate(std::size_t n) { -#if NRN_ENABLE_THREADS +template +T* Pool::allocate(std::size_t n) { +#if NRN_ENABLE_THREADS && M std::lock_guard l(mut_); #endif if (n != 1) { - throw std::runtime_error("MutexPool allocator can only allocate one object at a time"); + throw std::runtime_error("Pool allocator can only allocate one object at a time"); } if (nget_ == items_.size()) { grow(); @@ -91,9 +104,9 @@ T* MutexPool::allocate(std::size_t n) { return item; } -template -void MutexPool::deallocate(T* item, std::size_t) { -#if NRN_ENABLE_THREADS +template +void Pool::deallocate(T* item, std::size_t) { +#if NRN_ENABLE_THREADS && M std::lock_guard l(mut_); #endif --nget_; @@ -101,14 +114,27 @@ void MutexPool::deallocate(T* item, std::size_t) { put_ = (++put_) % items_.size(); } -template -void MutexPool::free_all() { -#if NRN_ENABLE_THREADS +template +void Pool::free_all() { +#if NRN_ENABLE_THREADS && M std::lock_guard l(mut_); #endif get_ = 0; put_ = 0; - for (auto& item: items_) { - item->clear(); + if (clear_) { + for (auto& item: items_) { + std::invoke(clear_, item); + } + } +} + +template +bool Pool::is_valid_ptr(const T* p) const { + for (const auto& pool: pools_) { + if (p >= &pool.front() && p <= &pool.back()) { + // Check that the pointer is on a value and not in the middle + return (p - &pool.front()) % sizeof(T) == 0; + } } + return true; } From ffd975ab8d89f4a6ae966d8ac4f2b0abb9c061e1 Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Mon, 4 Mar 2024 06:37:50 +0100 Subject: [PATCH 06/13] Array pool --- src/nrniv/arraypool.h | 28 +--------------------------- src/nrniv/cxprop.cpp | 6 +++--- src/utils/pool.hpp | 3 +-- 3 files changed, 5 insertions(+), 32 deletions(-) diff --git a/src/nrniv/arraypool.h b/src/nrniv/arraypool.h index 7b0051e29c..25a4902fc7 100644 --- a/src/nrniv/arraypool.h +++ b/src/nrniv/arraypool.h @@ -25,9 +25,6 @@ class ArrayPool { ~ArrayPool(); T* alloc(); void hpfree(T*); - int maxget() { - return maxget_; - } int size() { return count_; } @@ -35,12 +32,6 @@ class ArrayPool { T* pool() { return pool_; } - long get() { - return get_; - } - long put() { - return put_; - } long nget() { return nget_; } @@ -50,24 +41,13 @@ class ArrayPool { long d2() { return d2_; } - T* element(long i) { - return pool_ + i * d2_; - } - T** items() { - return items_; - } - void grow(long ninc); ArrayPool* chain() { return chain_; } - long chain_size() { - return pool_size_; - } private: void grow_(long ninc); - private: T** items_; T* pool_; long pool_size_; @@ -98,16 +78,10 @@ ArrayPool::ArrayPool(long count, long d2) { nget_ = 0; ntget_ = 0; maxget_ = 0; - chain_ = 0; + chain_ = nullptr; chainlast_ = this; } -template -void ArrayPool::grow(long ninc) { - grow_(ninc); - put_ = get_; -} - template void ArrayPool::grow_(long ninc) { assert(get_ == put_); diff --git a/src/nrniv/cxprop.cpp b/src/nrniv/cxprop.cpp index 300b97b776..1e915c4ea7 100644 --- a/src/nrniv/cxprop.cpp +++ b/src/nrniv/cxprop.cpp @@ -1,6 +1,6 @@ -#include "arraypool.h" // ArrayPool -#include "hocdec.h" // Datum -#include "section.h" // Section +#include "arraypool.h" // ArrayPool +#include "hocdec.h" // Datum +#include "section.h" // Section #include "utils/pool.hpp" // Pool #include "../neuron/model_data.hpp" diff --git a/src/utils/pool.hpp b/src/utils/pool.hpp index 3ee0e307ea..a0bbc3052a 100644 --- a/src/utils/pool.hpp +++ b/src/utils/pool.hpp @@ -15,10 +15,9 @@ constexpr bool PoolMutexed = true; -template +template class Pool { public: - using value_type = T; Pool(); template From c937f48df714716304fde1977181abc732955c01 Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Sun, 17 Mar 2024 08:35:13 +0100 Subject: [PATCH 07/13] Forget one pool --- src/nrniv/multisend.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/nrniv/multisend.cpp b/src/nrniv/multisend.cpp index 22bb8cc78b..b258d80a8d 100644 --- a/src/nrniv/multisend.cpp +++ b/src/nrniv/multisend.cpp @@ -43,6 +43,8 @@ of spikes sent is equal to the number of spikes sent. // setup time with a bgp.dma_send_ so as to pass on the spike to the // phase2 list of target hosts. +#include + // asm/msr.h no longer compiles on my machine. // only for basic testing of logic when not on blue gene/p #define USE_RDTSCL 0 @@ -94,7 +96,6 @@ struct Phase2Buffer { double spiketime; }; -#include using SpkPool = Pool; @@ -163,7 +164,7 @@ Multisend_ReceiveBuffer::Multisend_ReceiveBuffer() { count_ = 0; size_ = MULTISEND_RECEIVEBUFFER_SIZE; buffer_ = new NRNMPI_Spike*[size_]; - pool_ = new SpkPool(MULTISEND_RECEIVEBUFFER_SIZE); + pool_ = new SpkPool{}; psbuf_ = 0; #if ENQUEUE == 1 psbuf_ = new PreSyn*[size_]; @@ -174,7 +175,7 @@ Multisend_ReceiveBuffer::Multisend_ReceiveBuffer() { Multisend_ReceiveBuffer::~Multisend_ReceiveBuffer() { assert(busy_ == 0); for (int i = 0; i < count_; ++i) { - pool_->hpfree(buffer_[i]); + pool_->deallocate(buffer_[i]); } delete[] buffer_; delete pool_; @@ -187,7 +188,7 @@ void Multisend_ReceiveBuffer::init(int index) { timebase_ = 0; nsend_cell_ = nsend_ = nrecv_ = busy_ = maxcount_ = 0; for (int i = 0; i < count_; ++i) { - pool_->hpfree(buffer_[i]); + pool_->deallocate(buffer_[i]); } count_ = 0; phase2_head_ = phase2_tail_ = 0; @@ -210,7 +211,7 @@ void Multisend_ReceiveBuffer::incoming(int gid, double spiketime) { psbuf_ = new PreSyn*[size_]; } } - NRNMPI_Spike* spk = pool_->alloc(); + NRNMPI_Spike* spk = pool_->allocate(); spk->gid = gid; spk->spiketime = spiketime; buffer_[count_++] = spk; @@ -241,7 +242,7 @@ void Multisend_ReceiveBuffer::enqueue() { pb.spiketime = spk->spiketime; } ps->send(spk->spiketime, net_cvode_instance, nrn_threads); - pool_->hpfree(spk); + pool_->deallocate(spk); } #endif count_ = 0; @@ -289,7 +290,7 @@ void Multisend_ReceiveBuffer::enqueue2() { NRNMPI_Spike* spk = buffer_[i]; PreSyn* ps = psbuf_[i]; ps->send(spk->spiketime, net_cvode_instance, nrn_threads); - pool_->hpfree(spk); + pool_->deallocate(spk); } count_ = 0; nrecv_ = 0; From af7f2e901b81b172a646737707fc036603e8ca2a Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Mon, 18 Mar 2024 07:25:23 +0100 Subject: [PATCH 08/13] Several fixes --- src/nrniv/multisend.cpp | 4 +--- src/utils/pool.hpp | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/nrniv/multisend.cpp b/src/nrniv/multisend.cpp index b258d80a8d..731f91e813 100644 --- a/src/nrniv/multisend.cpp +++ b/src/nrniv/multisend.cpp @@ -174,9 +174,7 @@ Multisend_ReceiveBuffer::Multisend_ReceiveBuffer() { } Multisend_ReceiveBuffer::~Multisend_ReceiveBuffer() { assert(busy_ == 0); - for (int i = 0; i < count_; ++i) { - pool_->deallocate(buffer_[i]); - } + pool_->free_all(); delete[] buffer_; delete pool_; if (psbuf_) diff --git a/src/utils/pool.hpp b/src/utils/pool.hpp index a0bbc3052a..0557bf6651 100644 --- a/src/utils/pool.hpp +++ b/src/utils/pool.hpp @@ -7,7 +7,6 @@ // the pool doubles in size every time a new pool is added. -#include #include #include #include @@ -61,11 +60,9 @@ class Pool { template Pool::Pool() { constexpr std::size_t count = 1000; - auto* ptr = pools_.emplace_back(count).data(); + auto ptr = pools_.emplace_back(count); items_.resize(count); - for (std::size_t i = 0; i < count; ++i) { - items_[i] = ptr + i; - } + std::transform(ptr.begin(), ptr.end(), items_.begin(), [](auto &it) { return ⁢ }); } template @@ -79,10 +76,8 @@ void Pool::grow() { items_.resize(2 * total_size); - auto* ptr = pools_.emplace_back(total_size).data(); - for (std::size_t i = total_size, j = 0; j < total_size; ++i, ++j) { - items_[i] = ptr + j; - } + auto ptr = pools_.emplace_back(total_size); + std::transform(ptr.begin(), ptr.end(), items_.begin() + total_size, [](auto &it) { return ⁢ }); } template @@ -120,6 +115,13 @@ void Pool::free_all() { #endif get_ = 0; put_ = 0; + nget_ = 0; + // Populate again items_ with real ones + auto it = items_.begin(); + for (auto& pool: pools_) { + std::transform(pool.begin(), pool.end(), it, [](auto& i){return &i;}); + it += pool.size(); + } if (clear_) { for (auto& item: items_) { std::invoke(clear_, item); @@ -135,5 +137,5 @@ bool Pool::is_valid_ptr(const T* p) const { return (p - &pool.front()) % sizeof(T) == 0; } } - return true; + return false; } From 925b5a5a98384419e7527e72c94548bd37ae09a6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 06:26:38 +0000 Subject: [PATCH 09/13] Fix formatting --- src/utils/pool.hpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/utils/pool.hpp b/src/utils/pool.hpp index 0557bf6651..bd132cbe7e 100644 --- a/src/utils/pool.hpp +++ b/src/utils/pool.hpp @@ -62,7 +62,7 @@ Pool::Pool() { constexpr std::size_t count = 1000; auto ptr = pools_.emplace_back(count); items_.resize(count); - std::transform(ptr.begin(), ptr.end(), items_.begin(), [](auto &it) { return ⁢ }); + std::transform(ptr.begin(), ptr.end(), items_.begin(), [](auto& it) { return ⁢ }); } template @@ -77,7 +77,9 @@ void Pool::grow() { items_.resize(2 * total_size); auto ptr = pools_.emplace_back(total_size); - std::transform(ptr.begin(), ptr.end(), items_.begin() + total_size, [](auto &it) { return ⁢ }); + std::transform(ptr.begin(), ptr.end(), items_.begin() + total_size, [](auto& it) { + return ⁢ + }); } template @@ -119,7 +121,7 @@ void Pool::free_all() { // Populate again items_ with real ones auto it = items_.begin(); for (auto& pool: pools_) { - std::transform(pool.begin(), pool.end(), it, [](auto& i){return &i;}); + std::transform(pool.begin(), pool.end(), it, [](auto& i) { return &i; }); it += pool.size(); } if (clear_) { From 6534451b7952c0724e210406376c13045efd3a64 Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Mon, 18 Mar 2024 08:25:02 +0100 Subject: [PATCH 10/13] Ref! --- src/utils/pool.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/pool.hpp b/src/utils/pool.hpp index 0557bf6651..2dbddb42ba 100644 --- a/src/utils/pool.hpp +++ b/src/utils/pool.hpp @@ -60,7 +60,7 @@ class Pool { template Pool::Pool() { constexpr std::size_t count = 1000; - auto ptr = pools_.emplace_back(count); + auto& ptr = pools_.emplace_back(count); items_.resize(count); std::transform(ptr.begin(), ptr.end(), items_.begin(), [](auto &it) { return ⁢ }); } @@ -76,7 +76,7 @@ void Pool::grow() { items_.resize(2 * total_size); - auto ptr = pools_.emplace_back(total_size); + auto& ptr = pools_.emplace_back(total_size); std::transform(ptr.begin(), ptr.end(), items_.begin() + total_size, [](auto &it) { return ⁢ }); } From 920ce96e1bfbef8f69b0c53294387ca12ebfa3d7 Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Mon, 25 Mar 2024 17:57:01 +0100 Subject: [PATCH 11/13] Fix around clear() --- src/nrncvode/hocevent.cpp | 1 + src/utils/pool.hpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nrncvode/hocevent.cpp b/src/nrncvode/hocevent.cpp index c0e75efdd9..28ba91225e 100644 --- a/src/nrncvode/hocevent.cpp +++ b/src/nrncvode/hocevent.cpp @@ -28,6 +28,7 @@ HocEvent* HocEvent::alloc(const char* stmt, Object* ppobj, int reinit, Object* p nrn_hoc_lock(); if (!hepool_) { hepool_ = new HocEventPool(); + hepool_->set_function(&HocEvent::clear); } nrn_hoc_unlock(); } diff --git a/src/utils/pool.hpp b/src/utils/pool.hpp index 0e3d3c66a9..9487680f1a 100644 --- a/src/utils/pool.hpp +++ b/src/utils/pool.hpp @@ -21,7 +21,7 @@ class Pool { Pool(); template void set_function(F f) { - clear_(f); + clear_ = f; } T* allocate(std::size_t = 1); void deallocate(T*, std::size_t = 1); From 0d14170574a06d6dfd683bf3cb7da2b4f6e9add5 Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Tue, 26 Mar 2024 13:39:39 +0100 Subject: [PATCH 12/13] Split Pool and MutexedPool --- src/nrncvode/hocevent.cpp | 4 ++-- src/nrncvode/netcon.h | 4 ++-- src/nrncvode/netcvode.h | 4 +++- src/nrncvode/tqueue.hpp | 4 ++-- src/utils/pool.hpp | 42 ++++++++++++--------------------------- 5 files changed, 22 insertions(+), 36 deletions(-) diff --git a/src/nrncvode/hocevent.cpp b/src/nrncvode/hocevent.cpp index 28ba91225e..b1ecbb9360 100644 --- a/src/nrncvode/hocevent.cpp +++ b/src/nrncvode/hocevent.cpp @@ -1,10 +1,10 @@ #include -#include +#include #include #include #include -using HocEventPool = Pool; +using HocEventPool = MutexedPool; HocEventPool* HocEvent::hepool_; HocEvent::HocEvent() { diff --git a/src/nrncvode/netcon.h b/src/nrncvode/netcon.h index 47ec9f7234..3063516856 100644 --- a/src/nrncvode/netcon.h +++ b/src/nrncvode/netcon.h @@ -6,7 +6,7 @@ #include "neuron/container/data_handle.hpp" #include "nrnmpi.h" #include "nrnneosm.h" -#include "utils/pool.hpp" +#include "utils/mutexed_pool.hpp" #include "tqitem.hpp" #include @@ -28,7 +28,7 @@ class TQueue; struct NrnThread; class NetCvode; class HocEvent; -using HocEventPool = Pool; +using HocEventPool = MutexedPool; class HocCommand; struct STETransition; class IvocVect; diff --git a/src/nrncvode/netcvode.h b/src/nrncvode/netcvode.h index b41bcaf1a6..ae6c08baf7 100644 --- a/src/nrncvode/netcvode.h +++ b/src/nrncvode/netcvode.h @@ -12,6 +12,8 @@ #include #include +#include + struct NrnThread; class PreSyn; class HocDataPaths; @@ -19,7 +21,7 @@ using PreSynTable = std::unordered_map, P class NetCon; class DiscreteEvent; class SelfEvent; -using SelfEventPool = Pool; +using SelfEventPool = MutexedPool; struct hoc_Item; class PlayRecord; class IvocVect; diff --git a/src/nrncvode/tqueue.hpp b/src/nrncvode/tqueue.hpp index 61220d2eff..787d19500a 100644 --- a/src/nrncvode/tqueue.hpp +++ b/src/nrncvode/tqueue.hpp @@ -5,11 +5,11 @@ #include #include -#include +#include #include "tqitem.hpp" -using TQItemPool = Pool; +using TQItemPool = MutexedPool; // bin queue for the fixed step method for NetCons and PreSyns. Splay tree // for others. diff --git a/src/utils/pool.hpp b/src/utils/pool.hpp index 9487680f1a..034a550995 100644 --- a/src/utils/pool.hpp +++ b/src/utils/pool.hpp @@ -9,12 +9,9 @@ #include #include -#include #include -constexpr bool PoolMutexed = true; - -template +template class Pool { public: using value_type = T; @@ -51,22 +48,18 @@ class Pool { std::vector> pools_{}; std::function clear_{}; - -#if NRN_ENABLE_THREADS && M - std::mutex mut_; -#endif }; -template -Pool::Pool() { +template +Pool::Pool() { constexpr std::size_t count = 1000; auto& ptr = pools_.emplace_back(count); items_.resize(count); std::transform(ptr.begin(), ptr.end(), items_.begin(), [](auto& it) { return ⁢ }); } -template -void Pool::grow() { +template +void Pool::grow() { const std::size_t total_size = items_.size(); // Everything is already allocated so reset put_ to the beginning of items @@ -82,11 +75,8 @@ void Pool::grow() { }); } -template -T* Pool::allocate(std::size_t n) { -#if NRN_ENABLE_THREADS && M - std::lock_guard l(mut_); -#endif +template +T* Pool::allocate(std::size_t n) { if (n != 1) { throw std::runtime_error("Pool allocator can only allocate one object at a time"); } @@ -100,21 +90,15 @@ T* Pool::allocate(std::size_t n) { return item; } -template -void Pool::deallocate(T* item, std::size_t) { -#if NRN_ENABLE_THREADS && M - std::lock_guard l(mut_); -#endif +template +void Pool::deallocate(T* item, std::size_t) { --nget_; items_[put_] = item; put_ = (++put_) % items_.size(); } -template -void Pool::free_all() { -#if NRN_ENABLE_THREADS && M - std::lock_guard l(mut_); -#endif +template +void Pool::free_all() { get_ = 0; put_ = 0; nget_ = 0; @@ -131,8 +115,8 @@ void Pool::free_all() { } } -template -bool Pool::is_valid_ptr(const T* p) const { +template +bool Pool::is_valid_ptr(const T* p) const { for (const auto& pool: pools_) { if (p >= &pool.front() && p <= &pool.back()) { // Check that the pointer is on a value and not in the middle From 2b1719ad36aa1137ab71f3aacc4da306dcef6395 Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Tue, 26 Mar 2024 13:54:14 +0100 Subject: [PATCH 13/13] Add the missing file --- src/utils/mutexed_pool.hpp | 141 +++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 src/utils/mutexed_pool.hpp diff --git a/src/utils/mutexed_pool.hpp b/src/utils/mutexed_pool.hpp new file mode 100644 index 0000000000..fe29b49bdd --- /dev/null +++ b/src/utils/mutexed_pool.hpp @@ -0,0 +1,141 @@ +#pragma once + +// create and manage a vector of objects as a memory pool of those objects +// the object must have a void clear() method which takes care of any +// data the object contains which should be deleted upon free_all. +// clear() is NOT called on deallocate, only on free_all. + +// the pool doubles in size every time a new pool is added. + +#include +#include +#include +#include + +template +class MutexedPool { + public: + using value_type = T; + MutexedPool(); + template + void set_function(F f) { + clear_ = f; + } + T* allocate(std::size_t = 1); + void deallocate(T*, std::size_t = 1); + void free_all(); + + // For now this pool only allow allocation of 1 byte at a time + std::size_t max_size() const { + return 1; + } + + bool is_valid_ptr(const T*) const; + + private: + // Add a new vector to the pools_ with the double of size of the previous one + // Reset put_ and get_ + void grow(); + // A ring-buffer covering all the pools_ + // put_ is the place where to put the next freed item + // get_ is the place where to get a new item + std::vector items_{}; + std::size_t get_{}; + std::size_t put_{}; + // Number of items currently allocated in the pools + // When this number equal the total number of elements it will be time to grow() + std::size_t nget_{}; + // A vector of all the pools_ + std::vector> pools_{}; + + std::function clear_{}; + +#if NRN_ENABLE_THREADS + MutexType mut_; +#endif +}; + +template +MutexedPool::MutexedPool() { + constexpr std::size_t count = 1000; + auto& ptr = pools_.emplace_back(count); + items_.resize(count); + std::transform(ptr.begin(), ptr.end(), items_.begin(), [](auto& it) { return ⁢ }); +} + +template +void MutexedPool::grow() { + const std::size_t total_size = items_.size(); + + // Everything is already allocated so reset put_ to the beginning of items + // and set get_ to the new space allocated pointing to the new pool + put_ = 0; + get_ = total_size; + + items_.resize(2 * total_size); + + auto& ptr = pools_.emplace_back(total_size); + std::transform(ptr.begin(), ptr.end(), items_.begin() + total_size, [](auto& it) { + return ⁢ + }); +} + +template +T* MutexedPool::allocate(std::size_t n) { +#if NRN_ENABLE_THREADS + std::lock_guard l(mut_); +#endif + if (n != 1) { + throw std::runtime_error("MutexedPool allocator can only allocate one object at a time"); + } + if (nget_ == items_.size()) { + grow(); + } + ++nget_; + T* item = items_[get_]; + get_ = (++get_) % items_.size(); + + return item; +} + +template +void MutexedPool::deallocate(T* item, std::size_t) { +#if NRN_ENABLE_THREADS + std::lock_guard l(mut_); +#endif + --nget_; + items_[put_] = item; + put_ = (++put_) % items_.size(); +} + +template +void MutexedPool::free_all() { +#if NRN_ENABLE_THREADS + std::lock_guard l(mut_); +#endif + get_ = 0; + put_ = 0; + nget_ = 0; + // Populate again items_ with real ones + auto it = items_.begin(); + for (auto& pool: pools_) { + std::transform(pool.begin(), pool.end(), it, [](auto& i) { return &i; }); + it += pool.size(); + } + if (clear_) { + for (auto& item: items_) { + std::invoke(clear_, item); + } + } +} + +template +bool MutexedPool::is_valid_ptr(const T* p) const { + for (const auto& pool: pools_) { + if (p >= &pool.front() && p <= &pool.back()) { + // Check that the pointer is on a value and not in the middle + return (p - &pool.front()) % sizeof(T) == 0; + } + } + return false; +}