From 8391cc48973f3556c4b51fa99097b8365173f8c5 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Thu, 30 Mar 2023 13:38:39 +0100 Subject: [PATCH 01/22] Count peak allocators --- src/snmalloc/mem/pool.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/snmalloc/mem/pool.h b/src/snmalloc/mem/pool.h index 36737207d..9dc686e43 100644 --- a/src/snmalloc/mem/pool.h +++ b/src/snmalloc/mem/pool.h @@ -34,9 +34,15 @@ namespace snmalloc FlagWord lock{}; capptr::Alloc list{nullptr}; + std::atomic count{0}; public: constexpr PoolState() = default; + + size_t get_count() + { + return count.load(std::memory_order_relaxed); + } }; /** @@ -156,6 +162,8 @@ namespace snmalloc p->list_next = pool.list; pool.list = p; + pool.count++; + p->set_in_use(); return p.unsafe_ptr(); } From af80ccc357a29d866c12c9dd93052eb7e90368ad Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Thu, 30 Mar 2023 13:39:13 +0100 Subject: [PATCH 02/22] Add test for exploring memory usage --- src/test/func/cleanup/cleanup.cc | 56 ++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 src/test/func/cleanup/cleanup.cc diff --git a/src/test/func/cleanup/cleanup.cc b/src/test/func/cleanup/cleanup.cc new file mode 100644 index 000000000..3fdab64c1 --- /dev/null +++ b/src/test/func/cleanup/cleanup.cc @@ -0,0 +1,56 @@ +#include + +#include +#include + +void ecall() +{ + snmalloc::ScopedAllocator a; + std::vector allocs; + size_t count = 0; + for (size_t j = 0; j < 1000; j++) + { + allocs.push_back(a.alloc.alloc(j % 1024)); + count += j % 1024; + } + auto p = a.alloc.alloc(1 * 1024 * 1024); + memset(p, 0, 1 * 1024 * 1024); + + for (size_t j = 0; j < allocs.size(); j++) + a.alloc.dealloc(allocs[j]); + + a.alloc.dealloc(p); +} + +void thread_body() +{ + for (int i = 0; i < 10000; i++) { + ecall(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } +} + +void monitor_body() +{ + for (int i = 0; i < 10000; i++) { + std::cout << "Current: " << snmalloc::Alloc::StateHandle::get_current_usage() << std::endl; + std::cout << "Peak : " << snmalloc::Alloc::StateHandle::get_peak_usage() << std::endl; + std::cout << "Allocs : " << snmalloc::Alloc::StateHandle::pool().get_count() << std::endl; + std::cout << "--------------------------------------------" << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(1)); + } +} + +int main() +{ + std::vector threads; + for (int i = 0; i < 8; i++) + { + threads.push_back(std::thread(thread_body)); + } + threads.push_back(std::thread(monitor_body)); + + for (auto& t : threads) + t.join(); + return 0; +} \ No newline at end of file From ff431c3d8caa36b476bc76032b4c82b40d041d11 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Mon, 17 Apr 2023 11:53:33 +0100 Subject: [PATCH 03/22] Track remote inflight. snmalloc::RemoteDeallocCache::remote_inflight tracks the amount of bytes that are inbetween threads. --- src/snmalloc/mem/corealloc.h | 20 +++++++++++++------- src/snmalloc/mem/globalalloc.h | 3 +++ src/snmalloc/mem/localalloc.h | 2 +- src/snmalloc/mem/remotecache.h | 30 ++++++++++++++++-------------- 4 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/snmalloc/mem/corealloc.h b/src/snmalloc/mem/corealloc.h index c7fc79b72..5c18a8562 100644 --- a/src/snmalloc/mem/corealloc.h +++ b/src/snmalloc/mem/corealloc.h @@ -478,14 +478,18 @@ namespace snmalloc SNMALLOC_FAST_PATH_LAMBDA { return capptr_domesticate(local_state, p); }; - auto cb = [this, - &need_post](freelist::HeadPtr msg) SNMALLOC_FAST_PATH_LAMBDA { + + size_t received_bytes = 0; + + auto cb = [this, &need_post, &received_bytes]( + freelist::HeadPtr msg) SNMALLOC_FAST_PATH_LAMBDA { #ifdef SNMALLOC_TRACING message<1024>("Handling remote"); #endif auto& entry = Config::Backend::template get_metaentry(snmalloc::address_cast(msg)); + received_bytes += sizeclass_full_to_size(entry.get_sizeclass()); handle_dealloc_remote(entry, msg.as_void(), need_post); @@ -514,6 +518,9 @@ namespace snmalloc post(); } + // Push size to global statistics + RemoteDeallocCache::remote_inflight -= received_bytes; + return action(args...); } @@ -542,10 +549,7 @@ namespace snmalloc } else { - if ( - !need_post && - !attached_cache->remote_dealloc_cache.reserve_space(entry)) - need_post = true; + need_post = attached_cache->remote_dealloc_cache.reserve_space(entry); attached_cache->remote_dealloc_cache .template dealloc( entry.get_remote()->trunc_id(), p.as_void()); @@ -834,7 +838,7 @@ namespace snmalloc { auto p_wild = message_queue().destroy(); auto p_tame = domesticate(p_wild); - + size_t received_bytes = 0; while (p_tame != nullptr) { bool need_post = true; // Always going to post, so ignore. @@ -842,9 +846,11 @@ namespace snmalloc p_tame->atomic_read_next(RemoteAllocator::key_global, domesticate); const PagemapEntry& entry = Config::Backend::get_metaentry(snmalloc::address_cast(p_tame)); + received_bytes += sizeclass_full_to_size(entry.get_sizeclass()); handle_dealloc_remote(entry, p_tame.as_void(), need_post); p_tame = n_tame; } + RemoteDeallocCache::remote_inflight -= received_bytes; } else { diff --git a/src/snmalloc/mem/globalalloc.h b/src/snmalloc/mem/globalalloc.h index dc9528f66..14bdc1fbf 100644 --- a/src/snmalloc/mem/globalalloc.h +++ b/src/snmalloc/mem/globalalloc.h @@ -87,6 +87,9 @@ namespace snmalloc } } + if (result == nullptr && RemoteDeallocCache::remote_inflight.load() != 0) + error("ERROR: RemoteDeallocCache::remote_inflight != 0"); + if (result != nullptr) { *result = okay; diff --git a/src/snmalloc/mem/localalloc.h b/src/snmalloc/mem/localalloc.h index c85d30b2b..219d57bc7 100644 --- a/src/snmalloc/mem/localalloc.h +++ b/src/snmalloc/mem/localalloc.h @@ -418,7 +418,7 @@ namespace snmalloc message<1024>("flush(): core_alloc={}", core_alloc); #endif local_cache.remote_allocator = &Config::unused_remote; - local_cache.remote_dealloc_cache.capacity = 0; + local_cache.remote_dealloc_cache.cache_bytes = REMOTE_CACHE; } } diff --git a/src/snmalloc/mem/remotecache.h b/src/snmalloc/mem/remotecache.h index 96f5e0973..d81bca5da 100644 --- a/src/snmalloc/mem/remotecache.h +++ b/src/snmalloc/mem/remotecache.h @@ -19,14 +19,17 @@ namespace snmalloc { std::array, REMOTE_SLOTS> list; + static inline std::atomic remote_inflight{0}; + /** - * The total amount of memory we are waiting for before we will dispatch - * to other allocators. Zero can mean we have not initialised the allocator - * yet. This is initialised to the 0 so that we always hit a slow path to - * start with, when we hit the slow path and need to dispatch everything, we - * can check if we are a real allocator and lazily provide a real allocator. + * The total amount of bytes of memory in the cache. + * + * REMOTE_CACHE is used as the initial value, so that we always hit a slow + * path to start with, when we hit the slow path and need to dispatch + * everything, we can check if we are a real allocator and lazily provide a + * real allocator. */ - int64_t capacity{0}; + size_t cache_bytes{REMOTE_CACHE}; #ifndef NDEBUG bool initialised = false; @@ -56,13 +59,10 @@ namespace snmalloc template SNMALLOC_FAST_PATH bool reserve_space(const Entry& entry) { - auto size = - static_cast(sizeclass_full_to_size(entry.get_sizeclass())); + auto size = sizeclass_full_to_size(entry.get_sizeclass()); - bool result = capacity > size; - if (result) - capacity -= size; - return result; + cache_bytes += size; + return cache_bytes < REMOTE_CACHE; } template @@ -91,6 +91,8 @@ namespace snmalloc return capptr_domesticate(local_state, p); }; + // We are about to post cache_bytes bytes to other allocators. + remote_inflight += cache_bytes; while (true) { auto my_slot = get_slot(id, post_round); @@ -152,7 +154,7 @@ namespace snmalloc } // Reset capacity as we have empty everything - capacity = REMOTE_CACHE; + cache_bytes = 0; return sent_something; } @@ -177,7 +179,7 @@ namespace snmalloc // a null address. l.init(0, RemoteAllocator::key_global); } - capacity = REMOTE_CACHE; + cache_bytes = 0; } }; } // namespace snmalloc From 4764aa812f4da8f480e98286f7d12a31137cfc1d Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 2 May 2023 14:01:07 +0100 Subject: [PATCH 04/22] Factor out common peak statistic --- src/snmalloc/backend_helpers/statsrange.h | 29 ++++------ src/snmalloc/ds_core/ds_core.h | 1 + src/snmalloc/ds_core/stats.h | 64 +++++++++++++++++++++++ src/snmalloc/mem/globalalloc.h | 2 +- src/snmalloc/mem/remotecache.h | 2 +- 5 files changed, 77 insertions(+), 21 deletions(-) create mode 100644 src/snmalloc/ds_core/stats.h diff --git a/src/snmalloc/backend_helpers/statsrange.h b/src/snmalloc/backend_helpers/statsrange.h index 8548be9cb..885457f97 100644 --- a/src/snmalloc/backend_helpers/statsrange.h +++ b/src/snmalloc/backend_helpers/statsrange.h @@ -17,8 +17,7 @@ namespace snmalloc { using ContainsParent::parent; - static inline std::atomic current_usage{}; - static inline std::atomic peak_usage{}; + static inline Stat usage{}; public: static constexpr bool Aligned = ParentRange::Aligned; @@ -29,36 +28,28 @@ namespace snmalloc constexpr Type() = default; - CapPtr alloc_range(size_t size) + capptr::Arena alloc_range(size_t size) { - auto result = parent.alloc_range(size); - if (result != nullptr) - { - auto prev = current_usage.fetch_add(size); - auto curr = peak_usage.load(); - while (curr < prev + size) - { - if (peak_usage.compare_exchange_weak(curr, prev + size)) - break; - } - } - return result; + auto r = parent.alloc_range(size); + if (r != nullptr) + usage += size; + return r; } - void dealloc_range(CapPtr base, size_t size) + void dealloc_range(capptr::Arena base, size_t size) { - current_usage -= size; + usage -= size; parent.dealloc_range(base, size); } size_t get_current_usage() { - return current_usage.load(); + return usage.get_curr(); } size_t get_peak_usage() { - return peak_usage.load(); + return usage.get_peak(); } }; }; diff --git a/src/snmalloc/ds_core/ds_core.h b/src/snmalloc/ds_core/ds_core.h index 2083190bc..11115df73 100644 --- a/src/snmalloc/ds_core/ds_core.h +++ b/src/snmalloc/ds_core/ds_core.h @@ -15,3 +15,4 @@ #include "ptrwrap.h" #include "redblacktree.h" #include "seqset.h" +#include "stats.h" diff --git a/src/snmalloc/ds_core/stats.h b/src/snmalloc/ds_core/stats.h new file mode 100644 index 000000000..a9f0f26c1 --- /dev/null +++ b/src/snmalloc/ds_core/stats.h @@ -0,0 +1,64 @@ +#include +#include +#include "defines.h" + +namespace snmalloc +{ + /** + * Very basic statistic that tracks current and peak values. + */ + class Stat + { + private: + std::atomic curr{0}; + std::atomic peak{0}; + + public: + void increase(size_t amount) + { + size_t c = (curr += amount); + size_t p = peak.load(std::memory_order_relaxed); + while (c > p) + { + if (peak.compare_exchange_strong(p, c)) + break; + } + } + + void decrease(size_t amount) + { + size_t prev = curr.fetch_sub(amount); + SNMALLOC_ASSERT(prev >= amount); + } + + size_t get_curr() + { + return curr.load(std::memory_order_relaxed); + } + + size_t get_peak() + { + return peak.load(std::memory_order_relaxed); + } + + void operator+=(size_t amount) + { + increase(amount); + } + + void operator-=(size_t amount) + { + decrease(amount); + } + + void operator++() + { + increase(1); + } + + void operator--() + { + decrease(1); + } + }; +} // namespace snmalloc diff --git a/src/snmalloc/mem/globalalloc.h b/src/snmalloc/mem/globalalloc.h index 14bdc1fbf..af434aabb 100644 --- a/src/snmalloc/mem/globalalloc.h +++ b/src/snmalloc/mem/globalalloc.h @@ -87,7 +87,7 @@ namespace snmalloc } } - if (result == nullptr && RemoteDeallocCache::remote_inflight.load() != 0) + if (result == nullptr && RemoteDeallocCache::remote_inflight.get_curr() != 0) error("ERROR: RemoteDeallocCache::remote_inflight != 0"); if (result != nullptr) diff --git a/src/snmalloc/mem/remotecache.h b/src/snmalloc/mem/remotecache.h index d81bca5da..a53737f24 100644 --- a/src/snmalloc/mem/remotecache.h +++ b/src/snmalloc/mem/remotecache.h @@ -19,7 +19,7 @@ namespace snmalloc { std::array, REMOTE_SLOTS> list; - static inline std::atomic remote_inflight{0}; + static inline Stat remote_inflight; /** * The total amount of bytes of memory in the cache. From 5422e2f5b6b90cd6321727789d706f5044cb07a6 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 2 May 2023 16:48:15 +0100 Subject: [PATCH 05/22] Temporarily disable an assert --- src/snmalloc/ds_core/stats.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/snmalloc/ds_core/stats.h b/src/snmalloc/ds_core/stats.h index a9f0f26c1..2b3ba0941 100644 --- a/src/snmalloc/ds_core/stats.h +++ b/src/snmalloc/ds_core/stats.h @@ -28,7 +28,9 @@ namespace snmalloc void decrease(size_t amount) { size_t prev = curr.fetch_sub(amount); - SNMALLOC_ASSERT(prev >= amount); +// TODO Fix this to be true. +// SNMALLOC_ASSERT_MSG(prev >= amount, "prev = {}, amount = {}", prev, amount); + UNUSED(prev); } size_t get_curr() From af3c9da66eca4ada433b72fc0f69e03b7ce2563b Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 2 May 2023 16:50:08 +0100 Subject: [PATCH 06/22] Build per allocation statistics. --- src/snmalloc/mem/allocstats.h | 64 +++++++++++++++++++++++++++++++ src/snmalloc/mem/corealloc.h | 22 ++++++++++- src/snmalloc/mem/globalalloc.h | 32 ++++++++++++++++ src/snmalloc/mem/localalloc.h | 7 +++- src/snmalloc/mem/localcache.h | 4 +- src/test/func/statistics/stats.cc | 20 +++++++++- 6 files changed, 142 insertions(+), 7 deletions(-) create mode 100644 src/snmalloc/mem/allocstats.h diff --git a/src/snmalloc/mem/allocstats.h b/src/snmalloc/mem/allocstats.h new file mode 100644 index 000000000..e86f3284f --- /dev/null +++ b/src/snmalloc/mem/allocstats.h @@ -0,0 +1,64 @@ +#include "../ds_core/ds_core.h" +#include +#include "sizeclasstable.h" + +namespace snmalloc +{ + class MonotoneStat + { + size_t value{0}; + public: + void operator++(int) + { + value++; + } + + void operator+=(const MonotoneStat& other) + { + value += other.value; + } + + size_t operator*() + { + return value; + } + }; + + struct AllocStat + { + MonotoneStat objects_allocated{}; + MonotoneStat objects_deallocated{}; + // MonotoneStat slabs_allocated; + // MonotoneStat slabs_deallocated; + }; + + class AllocStats + { + std::array sizeclass{}; + + public: + AllocStat& operator[](sizeclass_t index) + { + auto i = index.raw(); + return sizeclass[i]; + } + + AllocStat& operator[](smallsizeclass_t index) + { + return sizeclass[sizeclass_t::from_small_class(index).raw()]; + } + + AllocStat operator+=(const AllocStats& other) + { + AllocStat result; + for (size_t i = 0; i < SIZECLASS_REP_SIZE; i++) + { + sizeclass[i].objects_allocated += other.sizeclass[i].objects_allocated; + sizeclass[i].objects_deallocated += other.sizeclass[i].objects_deallocated; + // result.slabs_allocated += other.sizeclass[i].slabs_allocated; + // result.slabs_deallocated += other.sizeclass[i].slabs_deallocated; + } + return result; + } + }; +} // namespace snmalloc \ No newline at end of file diff --git a/src/snmalloc/mem/corealloc.h b/src/snmalloc/mem/corealloc.h index 5c18a8562..cd87517bd 100644 --- a/src/snmalloc/mem/corealloc.h +++ b/src/snmalloc/mem/corealloc.h @@ -111,6 +111,11 @@ namespace snmalloc */ Ticker ticker; + /** + * Tracks this allocators memory usage + */ + AllocStats stats; + /** * The message queue needs to be accessible from other threads * @@ -672,13 +677,14 @@ namespace snmalloc // pointers auto& entry = Config::Backend::template get_metaentry(snmalloc::address_cast(p)); - if (SNMALLOC_LIKELY(dealloc_local_object_fast(entry, p, entropy))) + if (SNMALLOC_LIKELY(dealloc_local_object_fast(entry, p, entropy))) return; dealloc_local_object_slow(p, entry); } - SNMALLOC_FAST_PATH static bool dealloc_local_object_fast( + template + SNMALLOC_FAST_PATH bool dealloc_local_object_fast( const PagemapEntry& entry, CapPtr p, LocalEntropy& entropy) @@ -699,6 +705,10 @@ namespace snmalloc // Update the head and the next pointer in the free list. meta->free_queue.add(cp, key, entropy); + if constexpr (Statistics) + { + stats[entry.get_sizeclass()].objects_deallocated++; + } return SNMALLOC_LIKELY(!meta->return_object()); } @@ -745,6 +755,7 @@ namespace snmalloc } auto r = finish_alloc(p, sizeclass); + stats[sizeclass].objects_allocated++; return ticker.check_tick(r); } return small_alloc_slow(sizeclass, fast_free_list); @@ -817,6 +828,8 @@ namespace snmalloc } auto r = finish_alloc(p, sizeclass); + + stats[sizeclass].objects_allocated++; return ticker.check_tick(r); } @@ -992,6 +1005,11 @@ namespace snmalloc return debug_is_empty_impl(result); } + + const AllocStats& get_stats() + { + return stats; + } }; /** diff --git a/src/snmalloc/mem/globalalloc.h b/src/snmalloc/mem/globalalloc.h index af434aabb..0a16279ee 100644 --- a/src/snmalloc/mem/globalalloc.h +++ b/src/snmalloc/mem/globalalloc.h @@ -137,4 +137,36 @@ namespace snmalloc } } + template + inline static AllocStats get_stats() + { + auto alloc = AllocPool::iterate(); + AllocStats stats; + while (alloc != nullptr) + { + stats += alloc->get_stats(); + alloc = AllocPool::iterate(alloc); + } + return stats; + } + + template + inline static void print_alloc_stats() + { +#ifndef SNMALLOC_PASS_THROUGH // This test depends on snmalloc internals + auto stats = snmalloc::get_stats(); + for (size_t i = 0; i < snmalloc::SIZECLASS_REP_SIZE; i++) + { + auto sc = snmalloc::sizeclass_t::from_raw(i); + auto allocated = *stats[sc].objects_allocated; + auto deallocated = *stats[sc].objects_deallocated; + if (allocated == 0 && deallocated == 0) + continue; + auto size = + snmalloc::sizeclass_full_to_size(snmalloc::sizeclass_t::from_raw(i)); + auto in_use = allocated - deallocated; + snmalloc::message<1024>("{},{},{},{},{}", i, size, allocated, deallocated, in_use); + } +#endif + } } // namespace snmalloc diff --git a/src/snmalloc/mem/localalloc.h b/src/snmalloc/mem/localalloc.h index 219d57bc7..fe7282417 100644 --- a/src/snmalloc/mem/localalloc.h +++ b/src/snmalloc/mem/localalloc.h @@ -211,6 +211,9 @@ namespace snmalloc chunk.unsafe_ptr(), bits::next_pow2(size)); } + if (chunk.unsafe_ptr() != nullptr) + core_alloc->stats[size_to_sizeclass_full(size)].objects_allocated++; + return capptr_chunk_is_alloc(capptr_to_user_address_control(chunk)); }); } @@ -246,7 +249,7 @@ namespace snmalloc }; return local_cache.template alloc( - domesticate, size, slowpath); + domesticate, core_alloc->stats, size, slowpath); } /** @@ -648,7 +651,7 @@ namespace snmalloc { dealloc_cheri_checks(p_tame.unsafe_ptr()); - if (SNMALLOC_LIKELY(CoreAlloc::dealloc_local_object_fast( + if (SNMALLOC_LIKELY(core_alloc->dealloc_local_object_fast( entry, p_tame, local_cache.entropy))) return; core_alloc->dealloc_local_object_slow(p_tame, entry); diff --git a/src/snmalloc/mem/localcache.h b/src/snmalloc/mem/localcache.h index cfbbaa576..d04c2c8bb 100644 --- a/src/snmalloc/mem/localcache.h +++ b/src/snmalloc/mem/localcache.h @@ -1,6 +1,7 @@ #pragma once #include "../ds/ds.h" +#include "allocstats.h" #include "freelist.h" #include "remotecache.h" #include "sizeclasstable.h" @@ -95,7 +96,7 @@ namespace snmalloc typename Slowpath, typename Domesticator> SNMALLOC_FAST_PATH capptr::Alloc - alloc(Domesticator domesticate, size_t size, Slowpath slowpath) + alloc(Domesticator domesticate, AllocStats& stats, size_t size, Slowpath slowpath) { auto& key = entropy.get_free_list_key(); smallsizeclass_t sizeclass = size_to_sizeclass(size); @@ -103,6 +104,7 @@ namespace snmalloc if (SNMALLOC_LIKELY(!fl.empty())) { auto p = fl.take(key, domesticate); + stats[sizeclass].objects_allocated++; return finish_alloc(p, sizeclass); } return slowpath(sizeclass, &fl); diff --git a/src/test/func/statistics/stats.cc b/src/test/func/statistics/stats.cc index c8db1cad7..d2da95ed5 100644 --- a/src/test/func/statistics/stats.cc +++ b/src/test/func/statistics/stats.cc @@ -18,6 +18,7 @@ void debug_check_empty_1() auto r = a.alloc(size); snmalloc::debug_check_empty(&result); + snmalloc::print_alloc_stats(); if (result != false) { std::cout << "debug_check_empty failed to detect leaked memory:" << size @@ -25,7 +26,11 @@ void debug_check_empty_1() abort(); } - a.dealloc(r); + snmalloc::print_alloc_stats(); + + a.dealloc(r); + + snmalloc::print_alloc_stats(); snmalloc::debug_check_empty(&result); if (result != true) @@ -34,7 +39,11 @@ void debug_check_empty_1() abort(); } - r = a.alloc(size); + snmalloc::print_alloc_stats(); + + r = a.alloc(16); + + snmalloc::print_alloc_stats(); snmalloc::debug_check_empty(&result); if (result != false) @@ -44,14 +53,21 @@ void debug_check_empty_1() abort(); } + snmalloc::print_alloc_stats(); + a.dealloc(r); + snmalloc::print_alloc_stats(); + snmalloc::debug_check_empty(&result); if (result != true) { std::cout << "debug_check_empty failed to say empty:" << size << std::endl; abort(); } + + snmalloc::print_alloc_stats(); +#endif } template From c14f82a7749ef8ffd74707c6908b012de50e8d46 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 2 May 2023 16:51:05 +0100 Subject: [PATCH 07/22] Add a tag --- src/snmalloc/mem/globalalloc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/snmalloc/mem/globalalloc.h b/src/snmalloc/mem/globalalloc.h index 0a16279ee..18065e34c 100644 --- a/src/snmalloc/mem/globalalloc.h +++ b/src/snmalloc/mem/globalalloc.h @@ -165,7 +165,7 @@ namespace snmalloc auto size = snmalloc::sizeclass_full_to_size(snmalloc::sizeclass_t::from_raw(i)); auto in_use = allocated - deallocated; - snmalloc::message<1024>("{},{},{},{},{}", i, size, allocated, deallocated, in_use); + snmalloc::message<1024>("SNMALLOCallocs,{},{},{},{},{}", i, size, allocated, deallocated, in_use); } #endif } From 6e524ea269cedee4f0bfbcdcafb82fb1cd7ba4c5 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 2 May 2023 16:51:36 +0100 Subject: [PATCH 08/22] Example to represent ECall work load --- src/test/perf/churn/churn.cc | 86 ++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 src/test/perf/churn/churn.cc diff --git a/src/test/perf/churn/churn.cc b/src/test/perf/churn/churn.cc new file mode 100644 index 000000000..9c6daad4c --- /dev/null +++ b/src/test/perf/churn/churn.cc @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include + +int main() +{ + std::vector threads; + std::atomic running; + snmalloc::Stat requests; + + for (size_t i = 0; i < 16; i++) + { + std::thread([&running, &requests]() { + std::queue q; + while (true) + { + snmalloc::ScopedAllocator alloc; + running++; + + if (rand() % 1000 == 0) + { + // Deallocate everything in the queue + while (q.size() > 0) + { + auto p = q.front(); + requests -= *p; + alloc->dealloc(p); + q.pop(); + } + } + + for (size_t j = 0; j < 1000; j++) + { + if (q.size() >= 20000 || (q.size() > 0 && (rand() % 10 == 0))) + { + auto p = q.front(); + requests -= *p; + alloc->dealloc(p); + q.pop(); + } + else + { + size_t size = + (rand() % 1024 == 0) ? 16 * 1024 * (1 << (rand() % 3)) : 48; + requests += size; + auto p = (size_t*)alloc->alloc(size); + *p = size; + q.push(p); + } + } + + running--; + std::this_thread::sleep_for(std::chrono::microseconds(rand() % 2000)); + } + }).detach(); + } + + std::thread([&requests]() { + size_t count = 0; + while (count < 2000) + { + count++; + std::this_thread::sleep_for(std::chrono::seconds(1)); + // std::cout << "Inflight: " << + // snmalloc::RemoteDeallocCache::remote_inflight << std::endl; std::cout + // << "Current reservation: " << snmalloc::Globals::get_current_usage() << + // std::endl; std::cout << "Peak reservation: " << + // snmalloc::Globals::get_peak_usage() << std::endl; std::cout << + // "Allocator count: " << snmalloc::Globals::pool().get_count() << + // std::endl; std::cout << "Running threads: " << running << + // std::endl; std::cout << "Index: " << count << std::endl; + // std::cout << "------------------------------------------" << std::endl; + std::cout << count << "," << snmalloc::Alloc::Config::Backend::get_peak_usage() << "," + << snmalloc::Alloc::Config::Backend::get_current_usage() << "," << requests.get_curr() + << "," << requests.get_peak() << "," + << snmalloc::RemoteDeallocCache::remote_inflight.get_peak() + << "," << snmalloc::RemoteDeallocCache::remote_inflight.get_curr() + << std::endl; + snmalloc::print_alloc_stats(); + } + }).join(); + + return 0; +} \ No newline at end of file From e78e2e82369340229838834fba618d00af29a777 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Thu, 11 May 2023 13:41:13 +0100 Subject: [PATCH 09/22] Per allocation stats --- src/snmalloc/mem/globalalloc.h | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/snmalloc/mem/globalalloc.h b/src/snmalloc/mem/globalalloc.h index 18065e34c..043689b39 100644 --- a/src/snmalloc/mem/globalalloc.h +++ b/src/snmalloc/mem/globalalloc.h @@ -137,24 +137,23 @@ namespace snmalloc } } - template + template inline static AllocStats get_stats() { - auto alloc = AllocPool::iterate(); + auto alloc = AllocPool::iterate(); AllocStats stats; while (alloc != nullptr) { stats += alloc->get_stats(); - alloc = AllocPool::iterate(alloc); + alloc = AllocPool::iterate(alloc); } return stats; } - template + template inline static void print_alloc_stats() { -#ifndef SNMALLOC_PASS_THROUGH // This test depends on snmalloc internals - auto stats = snmalloc::get_stats(); + auto stats = snmalloc::get_stats(); for (size_t i = 0; i < snmalloc::SIZECLASS_REP_SIZE; i++) { auto sc = snmalloc::sizeclass_t::from_raw(i); @@ -167,6 +166,5 @@ namespace snmalloc auto in_use = allocated - deallocated; snmalloc::message<1024>("SNMALLOCallocs,{},{},{},{},{}", i, size, allocated, deallocated, in_use); } -#endif } } // namespace snmalloc From c82afddd904346a59a636772fa700ac94e257618 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Thu, 11 May 2023 16:33:19 +0100 Subject: [PATCH 10/22] Improve stats --- src/snmalloc/mem/globalalloc.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/snmalloc/mem/globalalloc.h b/src/snmalloc/mem/globalalloc.h index 043689b39..22cc7dcbd 100644 --- a/src/snmalloc/mem/globalalloc.h +++ b/src/snmalloc/mem/globalalloc.h @@ -153,7 +153,17 @@ namespace snmalloc template inline static void print_alloc_stats() { + static std::atomic dump{0}; + + auto l_dump = dump++; + if (l_dump == 0) + { + message<1024>("snmalloc_allocs,dumpid,sizeclass,size,allocated,deallocated,in_use,bytes"); + message<1024>("snmalloc_totals,dumpid,backend bytes,peak backend bytes,requested"); + } + auto stats = snmalloc::get_stats(); + size_t total_live{0}; for (size_t i = 0; i < snmalloc::SIZECLASS_REP_SIZE; i++) { auto sc = snmalloc::sizeclass_t::from_raw(i); @@ -164,7 +174,10 @@ namespace snmalloc auto size = snmalloc::sizeclass_full_to_size(snmalloc::sizeclass_t::from_raw(i)); auto in_use = allocated - deallocated; - snmalloc::message<1024>("SNMALLOCallocs,{},{},{},{},{}", i, size, allocated, deallocated, in_use); + auto amount = in_use * size; + total_live += amount; + snmalloc::message<1024>("snmalloc_allocs,{},{},{},{},{},{},{}", l_dump, i, size, allocated, deallocated, in_use,amount); } + snmalloc::message<1024>("snmalloc_totals,{},{},{},{}", l_dump, Config::Backend::get_current_usage(), Config::Backend::get_peak_usage(), total_live); } } // namespace snmalloc From d3b98f96d330bbbc648a6634f55fa8bf03c625ea Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 6 Jun 2023 03:24:22 +0100 Subject: [PATCH 11/22] Fix integration. --- src/test/func/cleanup/cleanup.cc | 6 +++--- src/test/func/statistics/stats.cc | 7 +++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/test/func/cleanup/cleanup.cc b/src/test/func/cleanup/cleanup.cc index 3fdab64c1..da6c21044 100644 --- a/src/test/func/cleanup/cleanup.cc +++ b/src/test/func/cleanup/cleanup.cc @@ -33,9 +33,9 @@ void thread_body() void monitor_body() { for (int i = 0; i < 10000; i++) { - std::cout << "Current: " << snmalloc::Alloc::StateHandle::get_current_usage() << std::endl; - std::cout << "Peak : " << snmalloc::Alloc::StateHandle::get_peak_usage() << std::endl; - std::cout << "Allocs : " << snmalloc::Alloc::StateHandle::pool().get_count() << std::endl; + std::cout << "Current: " << snmalloc::Alloc::Config::Backend::get_current_usage() << std::endl; + std::cout << "Peak : " << snmalloc::Alloc::Config::Backend::get_peak_usage() << std::endl; + std::cout << "Allocs : " << snmalloc::Alloc::Config::pool().get_count() << std::endl; std::cout << "--------------------------------------------" << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); } diff --git a/src/test/func/statistics/stats.cc b/src/test/func/statistics/stats.cc index d2da95ed5..e2af650b1 100644 --- a/src/test/func/statistics/stats.cc +++ b/src/test/func/statistics/stats.cc @@ -26,11 +26,11 @@ void debug_check_empty_1() abort(); } - snmalloc::print_alloc_stats(); + snmalloc::print_alloc_stats(); a.dealloc(r); - snmalloc::print_alloc_stats(); + snmalloc::print_alloc_stats(); snmalloc::debug_check_empty(&result); if (result != true) @@ -53,7 +53,7 @@ void debug_check_empty_1() abort(); } - snmalloc::print_alloc_stats(); + snmalloc::print_alloc_stats(); a.dealloc(r); @@ -67,7 +67,6 @@ void debug_check_empty_1() } snmalloc::print_alloc_stats(); -#endif } template From 55427c1638c0505fc998839f0e1fd6e07fa66e08 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 6 Jun 2023 04:55:25 +0100 Subject: [PATCH 12/22] Slab counting. --- src/snmalloc/mem/allocstats.h | 12 +++++------- src/snmalloc/mem/corealloc.h | 5 +++++ src/snmalloc/mem/globalalloc.h | 21 ++++++++++++++------- src/snmalloc/mem/localalloc.h | 7 +++++-- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/snmalloc/mem/allocstats.h b/src/snmalloc/mem/allocstats.h index e86f3284f..3a05c4a68 100644 --- a/src/snmalloc/mem/allocstats.h +++ b/src/snmalloc/mem/allocstats.h @@ -28,8 +28,8 @@ namespace snmalloc { MonotoneStat objects_allocated{}; MonotoneStat objects_deallocated{}; - // MonotoneStat slabs_allocated; - // MonotoneStat slabs_deallocated; + MonotoneStat slabs_allocated{}; + MonotoneStat slabs_deallocated{}; }; class AllocStats @@ -48,17 +48,15 @@ namespace snmalloc return sizeclass[sizeclass_t::from_small_class(index).raw()]; } - AllocStat operator+=(const AllocStats& other) + void operator+=(const AllocStats& other) { - AllocStat result; for (size_t i = 0; i < SIZECLASS_REP_SIZE; i++) { sizeclass[i].objects_allocated += other.sizeclass[i].objects_allocated; sizeclass[i].objects_deallocated += other.sizeclass[i].objects_deallocated; - // result.slabs_allocated += other.sizeclass[i].slabs_allocated; - // result.slabs_deallocated += other.sizeclass[i].slabs_deallocated; + sizeclass[i].slabs_allocated += other.sizeclass[i].slabs_allocated; + sizeclass[i].slabs_deallocated += other.sizeclass[i].slabs_deallocated; } - return result; } }; } // namespace snmalloc \ No newline at end of file diff --git a/src/snmalloc/mem/corealloc.h b/src/snmalloc/mem/corealloc.h index cd87517bd..74e50fded 100644 --- a/src/snmalloc/mem/corealloc.h +++ b/src/snmalloc/mem/corealloc.h @@ -369,6 +369,8 @@ namespace snmalloc // don't touch the cache lines at this point in snmalloc_check_client. auto start = clear_slab(meta, sizeclass); + stats[sizeclass].slabs_deallocated++; + Config::Backend::dealloc_chunk( get_backend_local_state(), *meta, @@ -405,6 +407,8 @@ namespace snmalloc // Remove from set of fully used slabs. meta->node.remove(); + stats[entry.get_sizeclass()].slabs_deallocated++; + Config::Backend::dealloc_chunk( get_backend_local_state(), *meta, p, size); @@ -830,6 +834,7 @@ namespace snmalloc auto r = finish_alloc(p, sizeclass); stats[sizeclass].objects_allocated++; + stats[sizeclass].slabs_allocated++; return ticker.check_tick(r); } diff --git a/src/snmalloc/mem/globalalloc.h b/src/snmalloc/mem/globalalloc.h index 22cc7dcbd..f0d702fe2 100644 --- a/src/snmalloc/mem/globalalloc.h +++ b/src/snmalloc/mem/globalalloc.h @@ -158,26 +158,33 @@ namespace snmalloc auto l_dump = dump++; if (l_dump == 0) { - message<1024>("snmalloc_allocs,dumpid,sizeclass,size,allocated,deallocated,in_use,bytes"); - message<1024>("snmalloc_totals,dumpid,backend bytes,peak backend bytes,requested"); + message<1024>("snmalloc_allocs,dumpid,sizeclass,size,allocated,deallocated,in_use,bytes,slabs allocated,slabs deallocated,slabs in_use,slabs bytes"); + message<1024>("snmalloc_totals,dumpid,backend bytes,peak backend bytes,requested,slabs requested bytes"); } auto stats = snmalloc::get_stats(); - size_t total_live{0}; + size_t total_live{0}; + size_t total_live_slabs{0}; for (size_t i = 0; i < snmalloc::SIZECLASS_REP_SIZE; i++) { auto sc = snmalloc::sizeclass_t::from_raw(i); auto allocated = *stats[sc].objects_allocated; auto deallocated = *stats[sc].objects_deallocated; + auto slabs_allocated = *stats[sc].slabs_allocated; + auto slabs_deallocated = *stats[sc].slabs_deallocated; if (allocated == 0 && deallocated == 0) continue; - auto size = - snmalloc::sizeclass_full_to_size(snmalloc::sizeclass_t::from_raw(i)); + auto size = snmalloc::sizeclass_full_to_size(sc); + auto slab_size = snmalloc::sizeclass_full_to_slab_size(sc); auto in_use = allocated - deallocated; auto amount = in_use * size; total_live += amount; - snmalloc::message<1024>("snmalloc_allocs,{},{},{},{},{},{},{}", l_dump, i, size, allocated, deallocated, in_use,amount); + auto in_use_slabs = slabs_allocated - slabs_deallocated; + auto amount_slabs = in_use_slabs * slab_size; + total_live_slabs += amount_slabs; + + snmalloc::message<1024>("snmalloc_allocs,{},{},{},{},{},{},{},{},{},{},{}", l_dump, i, size, allocated, deallocated, in_use, amount, slabs_allocated, slabs_deallocated, in_use_slabs, amount_slabs); } - snmalloc::message<1024>("snmalloc_totals,{},{},{},{}", l_dump, Config::Backend::get_current_usage(), Config::Backend::get_peak_usage(), total_live); + snmalloc::message<1024>("snmalloc_totals,{},{},{},{},{}", l_dump, Config::Backend::get_current_usage(), Config::Backend::get_peak_usage(), total_live, total_live_slabs); } } // namespace snmalloc diff --git a/src/snmalloc/mem/localalloc.h b/src/snmalloc/mem/localalloc.h index fe7282417..f8110e049 100644 --- a/src/snmalloc/mem/localalloc.h +++ b/src/snmalloc/mem/localalloc.h @@ -212,8 +212,11 @@ namespace snmalloc } if (chunk.unsafe_ptr() != nullptr) - core_alloc->stats[size_to_sizeclass_full(size)].objects_allocated++; - + { + auto sc = size_to_sizeclass_full(size); + core_alloc->stats[sc].objects_allocated++; + core_alloc->stats[sc].slabs_allocated++; + } return capptr_chunk_is_alloc(capptr_to_user_address_control(chunk)); }); } From cc9743846281919d64ec4cd8bcdca328a7509e8d Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 6 Jun 2023 04:57:19 +0100 Subject: [PATCH 13/22] Clangformat. --- src/snmalloc/backend_helpers/statsrange.h | 2 +- src/snmalloc/ds_core/stats.h | 8 ++-- src/snmalloc/mem/allocstats.h | 7 +++- src/snmalloc/mem/corealloc.h | 2 +- src/snmalloc/mem/globalalloc.h | 33 ++++++++++++--- src/snmalloc/mem/localcache.h | 7 +++- src/test/func/cleanup/cleanup.cc | 50 +++++++++++++---------- src/test/func/statistics/stats.cc | 2 +- src/test/perf/churn/churn.cc | 10 +++-- 9 files changed, 80 insertions(+), 41 deletions(-) diff --git a/src/snmalloc/backend_helpers/statsrange.h b/src/snmalloc/backend_helpers/statsrange.h index 885457f97..635c2348a 100644 --- a/src/snmalloc/backend_helpers/statsrange.h +++ b/src/snmalloc/backend_helpers/statsrange.h @@ -17,7 +17,7 @@ namespace snmalloc { using ContainsParent::parent; - static inline Stat usage{}; + static inline Stat usage{}; public: static constexpr bool Aligned = ParentRange::Aligned; diff --git a/src/snmalloc/ds_core/stats.h b/src/snmalloc/ds_core/stats.h index 2b3ba0941..bd2d066b8 100644 --- a/src/snmalloc/ds_core/stats.h +++ b/src/snmalloc/ds_core/stats.h @@ -1,6 +1,7 @@ +#include "defines.h" + #include #include -#include "defines.h" namespace snmalloc { @@ -28,8 +29,9 @@ namespace snmalloc void decrease(size_t amount) { size_t prev = curr.fetch_sub(amount); -// TODO Fix this to be true. -// SNMALLOC_ASSERT_MSG(prev >= amount, "prev = {}, amount = {}", prev, amount); + // TODO Fix this to be true. + // SNMALLOC_ASSERT_MSG(prev >= amount, "prev = {}, amount = {}", + // prev, amount); UNUSED(prev); } diff --git a/src/snmalloc/mem/allocstats.h b/src/snmalloc/mem/allocstats.h index 3a05c4a68..0394f6276 100644 --- a/src/snmalloc/mem/allocstats.h +++ b/src/snmalloc/mem/allocstats.h @@ -1,12 +1,14 @@ #include "../ds_core/ds_core.h" -#include #include "sizeclasstable.h" +#include + namespace snmalloc { class MonotoneStat { size_t value{0}; + public: void operator++(int) { @@ -53,7 +55,8 @@ namespace snmalloc for (size_t i = 0; i < SIZECLASS_REP_SIZE; i++) { sizeclass[i].objects_allocated += other.sizeclass[i].objects_allocated; - sizeclass[i].objects_deallocated += other.sizeclass[i].objects_deallocated; + sizeclass[i].objects_deallocated += + other.sizeclass[i].objects_deallocated; sizeclass[i].slabs_allocated += other.sizeclass[i].slabs_allocated; sizeclass[i].slabs_deallocated += other.sizeclass[i].slabs_deallocated; } diff --git a/src/snmalloc/mem/corealloc.h b/src/snmalloc/mem/corealloc.h index 74e50fded..728f03006 100644 --- a/src/snmalloc/mem/corealloc.h +++ b/src/snmalloc/mem/corealloc.h @@ -370,7 +370,7 @@ namespace snmalloc auto start = clear_slab(meta, sizeclass); stats[sizeclass].slabs_deallocated++; - + Config::Backend::dealloc_chunk( get_backend_local_state(), *meta, diff --git a/src/snmalloc/mem/globalalloc.h b/src/snmalloc/mem/globalalloc.h index f0d702fe2..01012e8a4 100644 --- a/src/snmalloc/mem/globalalloc.h +++ b/src/snmalloc/mem/globalalloc.h @@ -87,7 +87,8 @@ namespace snmalloc } } - if (result == nullptr && RemoteDeallocCache::remote_inflight.get_curr() != 0) + if ( + result == nullptr && RemoteDeallocCache::remote_inflight.get_curr() != 0) error("ERROR: RemoteDeallocCache::remote_inflight != 0"); if (result != nullptr) @@ -158,8 +159,12 @@ namespace snmalloc auto l_dump = dump++; if (l_dump == 0) { - message<1024>("snmalloc_allocs,dumpid,sizeclass,size,allocated,deallocated,in_use,bytes,slabs allocated,slabs deallocated,slabs in_use,slabs bytes"); - message<1024>("snmalloc_totals,dumpid,backend bytes,peak backend bytes,requested,slabs requested bytes"); + message<1024>( + "snmalloc_allocs,dumpid,sizeclass,size,allocated,deallocated,in_use," + "bytes,slabs allocated,slabs deallocated,slabs in_use,slabs bytes"); + message<1024>( + "snmalloc_totals,dumpid,backend bytes,peak backend " + "bytes,requested,slabs requested bytes"); } auto stats = snmalloc::get_stats(); @@ -183,8 +188,26 @@ namespace snmalloc auto amount_slabs = in_use_slabs * slab_size; total_live_slabs += amount_slabs; - snmalloc::message<1024>("snmalloc_allocs,{},{},{},{},{},{},{},{},{},{},{}", l_dump, i, size, allocated, deallocated, in_use, amount, slabs_allocated, slabs_deallocated, in_use_slabs, amount_slabs); + snmalloc::message<1024>( + "snmalloc_allocs,{},{},{},{},{},{},{},{},{},{},{}", + l_dump, + i, + size, + allocated, + deallocated, + in_use, + amount, + slabs_allocated, + slabs_deallocated, + in_use_slabs, + amount_slabs); } - snmalloc::message<1024>("snmalloc_totals,{},{},{},{},{}", l_dump, Config::Backend::get_current_usage(), Config::Backend::get_peak_usage(), total_live, total_live_slabs); + snmalloc::message<1024>( + "snmalloc_totals,{},{},{},{},{}", + l_dump, + Config::Backend::get_current_usage(), + Config::Backend::get_peak_usage(), + total_live, + total_live_slabs); } } // namespace snmalloc diff --git a/src/snmalloc/mem/localcache.h b/src/snmalloc/mem/localcache.h index d04c2c8bb..0bac2541f 100644 --- a/src/snmalloc/mem/localcache.h +++ b/src/snmalloc/mem/localcache.h @@ -95,8 +95,11 @@ namespace snmalloc typename Config, typename Slowpath, typename Domesticator> - SNMALLOC_FAST_PATH capptr::Alloc - alloc(Domesticator domesticate, AllocStats& stats, size_t size, Slowpath slowpath) + SNMALLOC_FAST_PATH capptr::Alloc alloc( + Domesticator domesticate, + AllocStats& stats, + size_t size, + Slowpath slowpath) { auto& key = entropy.get_free_list_key(); smallsizeclass_t sizeclass = size_to_sizeclass(size); diff --git a/src/test/func/cleanup/cleanup.cc b/src/test/func/cleanup/cleanup.cc index da6c21044..3f524ccac 100644 --- a/src/test/func/cleanup/cleanup.cc +++ b/src/test/func/cleanup/cleanup.cc @@ -1,30 +1,30 @@ -#include - #include +#include #include void ecall() { - snmalloc::ScopedAllocator a; - std::vector allocs; - size_t count = 0; - for (size_t j = 0; j < 1000; j++) - { - allocs.push_back(a.alloc.alloc(j % 1024)); - count += j % 1024; - } - auto p = a.alloc.alloc(1 * 1024 * 1024); - memset(p, 0, 1 * 1024 * 1024); + snmalloc::ScopedAllocator a; + std::vector allocs; + size_t count = 0; + for (size_t j = 0; j < 1000; j++) + { + allocs.push_back(a.alloc.alloc(j % 1024)); + count += j % 1024; + } + auto p = a.alloc.alloc(1 * 1024 * 1024); + memset(p, 0, 1 * 1024 * 1024); - for (size_t j = 0; j < allocs.size(); j++) - a.alloc.dealloc(allocs[j]); - - a.alloc.dealloc(p); + for (size_t j = 0; j < allocs.size(); j++) + a.alloc.dealloc(allocs[j]); + + a.alloc.dealloc(p); } void thread_body() { - for (int i = 0; i < 10000; i++) { + for (int i = 0; i < 10000; i++) + { ecall(); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } @@ -32,13 +32,19 @@ void thread_body() void monitor_body() { - for (int i = 0; i < 10000; i++) { - std::cout << "Current: " << snmalloc::Alloc::Config::Backend::get_current_usage() << std::endl; - std::cout << "Peak : " << snmalloc::Alloc::Config::Backend::get_peak_usage() << std::endl; - std::cout << "Allocs : " << snmalloc::Alloc::Config::pool().get_count() << std::endl; + for (int i = 0; i < 10000; i++) + { + std::cout << "Current: " + << snmalloc::Alloc::Config::Backend::get_current_usage() + << std::endl; + std::cout << "Peak : " + << snmalloc::Alloc::Config::Backend::get_peak_usage() + << std::endl; + std::cout << "Allocs : " << snmalloc::Alloc::Config::pool().get_count() + << std::endl; std::cout << "--------------------------------------------" << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); - } + } } int main() diff --git a/src/test/func/statistics/stats.cc b/src/test/func/statistics/stats.cc index e2af650b1..f5790dcce 100644 --- a/src/test/func/statistics/stats.cc +++ b/src/test/func/statistics/stats.cc @@ -28,7 +28,7 @@ void debug_check_empty_1() snmalloc::print_alloc_stats(); - a.dealloc(r); + a.dealloc(r); snmalloc::print_alloc_stats(); diff --git a/src/test/perf/churn/churn.cc b/src/test/perf/churn/churn.cc index 9c6daad4c..6323e9121 100644 --- a/src/test/perf/churn/churn.cc +++ b/src/test/perf/churn/churn.cc @@ -72,11 +72,13 @@ int main() // std::endl; std::cout << "Running threads: " << running << // std::endl; std::cout << "Index: " << count << std::endl; // std::cout << "------------------------------------------" << std::endl; - std::cout << count << "," << snmalloc::Alloc::Config::Backend::get_peak_usage() << "," - << snmalloc::Alloc::Config::Backend::get_current_usage() << "," << requests.get_curr() - << "," << requests.get_peak() << "," + std::cout << count << "," + << snmalloc::Alloc::Config::Backend::get_peak_usage() << "," + << snmalloc::Alloc::Config::Backend::get_current_usage() << "," + << requests.get_curr() << "," << requests.get_peak() << "," << snmalloc::RemoteDeallocCache::remote_inflight.get_peak() - << "," << snmalloc::RemoteDeallocCache::remote_inflight.get_curr() + << "," + << snmalloc::RemoteDeallocCache::remote_inflight.get_curr() << std::endl; snmalloc::print_alloc_stats(); } From eb3be849f76aabb0b81ace091b3fedebf9b867c3 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 6 Jun 2023 05:06:16 +0100 Subject: [PATCH 14/22] Expose remote_inflight and max allocators --- src/snmalloc/mem/globalalloc.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/snmalloc/mem/globalalloc.h b/src/snmalloc/mem/globalalloc.h index 01012e8a4..433192501 100644 --- a/src/snmalloc/mem/globalalloc.h +++ b/src/snmalloc/mem/globalalloc.h @@ -164,7 +164,7 @@ namespace snmalloc "bytes,slabs allocated,slabs deallocated,slabs in_use,slabs bytes"); message<1024>( "snmalloc_totals,dumpid,backend bytes,peak backend " - "bytes,requested,slabs requested bytes"); + "bytes,requested,slabs requested bytes,remote inflight bytes,allocator count"); } auto stats = snmalloc::get_stats(); @@ -203,11 +203,13 @@ namespace snmalloc amount_slabs); } snmalloc::message<1024>( - "snmalloc_totals,{},{},{},{},{}", + "snmalloc_totals,{},{},{},{},{},{},{}", l_dump, Config::Backend::get_current_usage(), Config::Backend::get_peak_usage(), total_live, - total_live_slabs); + total_live_slabs, + RemoteDeallocCache::remote_inflight.get_curr(), + Config::pool().get_count()); } } // namespace snmalloc From 4c050d142827b0fffdb4a4a85e1ced50d17c4d5a Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 6 Jun 2023 05:06:42 +0100 Subject: [PATCH 15/22] Clangformat --- src/snmalloc/mem/globalalloc.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/snmalloc/mem/globalalloc.h b/src/snmalloc/mem/globalalloc.h index 433192501..30e317efe 100644 --- a/src/snmalloc/mem/globalalloc.h +++ b/src/snmalloc/mem/globalalloc.h @@ -164,7 +164,8 @@ namespace snmalloc "bytes,slabs allocated,slabs deallocated,slabs in_use,slabs bytes"); message<1024>( "snmalloc_totals,dumpid,backend bytes,peak backend " - "bytes,requested,slabs requested bytes,remote inflight bytes,allocator count"); + "bytes,requested,slabs requested bytes,remote inflight bytes,allocator " + "count"); } auto stats = snmalloc::get_stats(); From 4e1a252eeb65fb59312d693d3562869df7a90b1e Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 6 Jun 2023 06:57:27 +0100 Subject: [PATCH 16/22] Missing include. --- src/test/func/cleanup/cleanup.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/func/cleanup/cleanup.cc b/src/test/func/cleanup/cleanup.cc index 3f524ccac..bc3dddf4f 100644 --- a/src/test/func/cleanup/cleanup.cc +++ b/src/test/func/cleanup/cleanup.cc @@ -1,6 +1,7 @@ #include #include #include +#include void ecall() { From 3a54e8141a86b058e8000298a1a30efb80bea387 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 6 Jun 2023 07:14:52 +0100 Subject: [PATCH 17/22] Made tests terminate --- src/test/func/cleanup/cleanup.cc | 6 ++---- src/test/perf/churn/churn.cc | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/test/func/cleanup/cleanup.cc b/src/test/func/cleanup/cleanup.cc index bc3dddf4f..f733e5ed0 100644 --- a/src/test/func/cleanup/cleanup.cc +++ b/src/test/func/cleanup/cleanup.cc @@ -7,11 +7,9 @@ void ecall() { snmalloc::ScopedAllocator a; std::vector allocs; - size_t count = 0; for (size_t j = 0; j < 1000; j++) { allocs.push_back(a.alloc.alloc(j % 1024)); - count += j % 1024; } auto p = a.alloc.alloc(1 * 1024 * 1024); memset(p, 0, 1 * 1024 * 1024); @@ -24,7 +22,7 @@ void ecall() void thread_body() { - for (int i = 0; i < 10000; i++) + for (int i = 0; i < 1000; i++) { ecall(); std::this_thread::sleep_for(std::chrono::milliseconds(10)); @@ -33,7 +31,7 @@ void thread_body() void monitor_body() { - for (int i = 0; i < 10000; i++) + for (int i = 0; i < 60; i++) { std::cout << "Current: " << snmalloc::Alloc::Config::Backend::get_current_usage() diff --git a/src/test/perf/churn/churn.cc b/src/test/perf/churn/churn.cc index 6323e9121..34edc6621 100644 --- a/src/test/perf/churn/churn.cc +++ b/src/test/perf/churn/churn.cc @@ -59,7 +59,7 @@ int main() std::thread([&requests]() { size_t count = 0; - while (count < 2000) + while (count < 60) { count++; std::this_thread::sleep_for(std::chrono::seconds(1)); From e05cd7fe66b4c98807e55c461d792ffccdbde9e8 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 6 Jun 2023 08:57:42 +0100 Subject: [PATCH 18/22] CR and CI changes --- src/snmalloc/backend_helpers/statsrange.h | 4 ++-- src/snmalloc/ds_core/stats.h | 29 ++++++++++++++++++++--- src/snmalloc/mem/allocstats.h | 29 ++++------------------- src/snmalloc/mem/corealloc.h | 2 +- src/test/perf/churn/churn.cc | 12 +++++++--- 5 files changed, 42 insertions(+), 34 deletions(-) diff --git a/src/snmalloc/backend_helpers/statsrange.h b/src/snmalloc/backend_helpers/statsrange.h index 635c2348a..7e92a801a 100644 --- a/src/snmalloc/backend_helpers/statsrange.h +++ b/src/snmalloc/backend_helpers/statsrange.h @@ -28,7 +28,7 @@ namespace snmalloc constexpr Type() = default; - capptr::Arena alloc_range(size_t size) + CapPtr alloc_range(size_t size) { auto r = parent.alloc_range(size); if (r != nullptr) @@ -36,7 +36,7 @@ namespace snmalloc return r; } - void dealloc_range(capptr::Arena base, size_t size) + void dealloc_range(CapPtr base, size_t size) { usage -= size; parent.dealloc_range(base, size); diff --git a/src/snmalloc/ds_core/stats.h b/src/snmalloc/ds_core/stats.h index bd2d066b8..b3e4efb6c 100644 --- a/src/snmalloc/ds_core/stats.h +++ b/src/snmalloc/ds_core/stats.h @@ -29,9 +29,8 @@ namespace snmalloc void decrease(size_t amount) { size_t prev = curr.fetch_sub(amount); - // TODO Fix this to be true. - // SNMALLOC_ASSERT_MSG(prev >= amount, "prev = {}, amount = {}", - // prev, amount); + SNMALLOC_ASSERT_MSG( + prev >= amount, "prev = {}, amount = {}", prev, amount); UNUSED(prev); } @@ -65,4 +64,28 @@ namespace snmalloc decrease(1); } }; + + /** + * Very basic statistic that can only grow. Not thread-safe. + */ + class MonotoneLocalStat + { + size_t value{0}; + + public: + void operator++(int) + { + value++; + } + + void operator+=(const MonotoneLocalStat& other) + { + value += other.value; + } + + size_t operator*() + { + return value; + } + }; } // namespace snmalloc diff --git a/src/snmalloc/mem/allocstats.h b/src/snmalloc/mem/allocstats.h index 0394f6276..bfa789c36 100644 --- a/src/snmalloc/mem/allocstats.h +++ b/src/snmalloc/mem/allocstats.h @@ -5,33 +5,12 @@ namespace snmalloc { - class MonotoneStat - { - size_t value{0}; - - public: - void operator++(int) - { - value++; - } - - void operator+=(const MonotoneStat& other) - { - value += other.value; - } - - size_t operator*() - { - return value; - } - }; - struct AllocStat { - MonotoneStat objects_allocated{}; - MonotoneStat objects_deallocated{}; - MonotoneStat slabs_allocated{}; - MonotoneStat slabs_deallocated{}; + MonotoneLocalStat objects_allocated{}; + MonotoneLocalStat objects_deallocated{}; + MonotoneLocalStat slabs_allocated{}; + MonotoneLocalStat slabs_deallocated{}; }; class AllocStats diff --git a/src/snmalloc/mem/corealloc.h b/src/snmalloc/mem/corealloc.h index 728f03006..1bb126a51 100644 --- a/src/snmalloc/mem/corealloc.h +++ b/src/snmalloc/mem/corealloc.h @@ -558,7 +558,7 @@ namespace snmalloc } else { - need_post = attached_cache->remote_dealloc_cache.reserve_space(entry); + need_post |= attached_cache->remote_dealloc_cache.reserve_space(entry); attached_cache->remote_dealloc_cache .template dealloc( entry.get_remote()->trunc_id(), p.as_void()); diff --git a/src/test/perf/churn/churn.cc b/src/test/perf/churn/churn.cc index 34edc6621..435cf4575 100644 --- a/src/test/perf/churn/churn.cc +++ b/src/test/perf/churn/churn.cc @@ -9,12 +9,13 @@ int main() std::vector threads; std::atomic running; snmalloc::Stat requests; + std::atomic done{false}; for (size_t i = 0; i < 16; i++) { - std::thread([&running, &requests]() { + threads.push_back(std::thread([&running, &requests, &done]() { std::queue q; - while (true) + while (!done) { snmalloc::ScopedAllocator alloc; running++; @@ -54,7 +55,7 @@ int main() running--; std::this_thread::sleep_for(std::chrono::microseconds(rand() % 2000)); } - }).detach(); + })); } std::thread([&requests]() { @@ -84,5 +85,10 @@ int main() } }).join(); + done = true; + + for (auto& t : threads) + t.join(); + return 0; } \ No newline at end of file From 7350be4746979b9c04bfec46f452d8dfaaecb8fd Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 6 Jun 2023 09:40:03 +0100 Subject: [PATCH 19/22] Seeking TSAN's approval --- src/snmalloc/ds_core/stats.h | 8 ++++---- src/snmalloc/mem/globalalloc.h | 7 +++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/snmalloc/ds_core/stats.h b/src/snmalloc/ds_core/stats.h index b3e4efb6c..de92906de 100644 --- a/src/snmalloc/ds_core/stats.h +++ b/src/snmalloc/ds_core/stats.h @@ -70,22 +70,22 @@ namespace snmalloc */ class MonotoneLocalStat { - size_t value{0}; + std::atomic value{0}; public: void operator++(int) { - value++; + value.fetch_add(1, std::memory_order_relaxed); } void operator+=(const MonotoneLocalStat& other) { - value += other.value; + value.fetch_add(other.value.load(std::memory_order_relaxed), std::memory_order_relaxed); } size_t operator*() { - return value; + return value.load(std::memory_order_relaxed); } }; } // namespace snmalloc diff --git a/src/snmalloc/mem/globalalloc.h b/src/snmalloc/mem/globalalloc.h index 30e317efe..faeff6750 100644 --- a/src/snmalloc/mem/globalalloc.h +++ b/src/snmalloc/mem/globalalloc.h @@ -139,16 +139,14 @@ namespace snmalloc } template - inline static AllocStats get_stats() + inline static void get_stats(AllocStats& stats) { auto alloc = AllocPool::iterate(); - AllocStats stats; while (alloc != nullptr) { stats += alloc->get_stats(); alloc = AllocPool::iterate(alloc); } - return stats; } template @@ -168,7 +166,8 @@ namespace snmalloc "count"); } - auto stats = snmalloc::get_stats(); + AllocStats stats; + snmalloc::get_stats(stats); size_t total_live{0}; size_t total_live_slabs{0}; for (size_t i = 0; i < snmalloc::SIZECLASS_REP_SIZE; i++) From e61e147c21255def2ea166b588d563f4f9a343a3 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 6 Jun 2023 09:45:26 +0100 Subject: [PATCH 20/22] CR feedback --- src/snmalloc/mem/globalalloc.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/snmalloc/mem/globalalloc.h b/src/snmalloc/mem/globalalloc.h index faeff6750..e618ed729 100644 --- a/src/snmalloc/mem/globalalloc.h +++ b/src/snmalloc/mem/globalalloc.h @@ -87,9 +87,8 @@ namespace snmalloc } } - if ( - result == nullptr && RemoteDeallocCache::remote_inflight.get_curr() != 0) - error("ERROR: RemoteDeallocCache::remote_inflight != 0"); + if (result == nullptr) + SNMALLOC_CHECK(RemoteDeallocCache::remote_inflight.get_curr() == 0); if (result != nullptr) { From 167c0c71a29c46481178239d871fc0cfc52eb5ff Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 6 Jun 2023 09:46:52 +0100 Subject: [PATCH 21/22] Clangformat. --- src/snmalloc/ds_core/stats.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/snmalloc/ds_core/stats.h b/src/snmalloc/ds_core/stats.h index de92906de..06e0c3b7f 100644 --- a/src/snmalloc/ds_core/stats.h +++ b/src/snmalloc/ds_core/stats.h @@ -80,7 +80,8 @@ namespace snmalloc void operator+=(const MonotoneLocalStat& other) { - value.fetch_add(other.value.load(std::memory_order_relaxed), std::memory_order_relaxed); + value.fetch_add( + other.value.load(std::memory_order_relaxed), std::memory_order_relaxed); } size_t operator*() From b7650c2dfb822e97b3f5329b60cb5219f112e96f Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Fri, 9 Jun 2023 13:51:08 +0100 Subject: [PATCH 22/22] Minor change for tsan. --- src/snmalloc/ds_core/stats.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/snmalloc/ds_core/stats.h b/src/snmalloc/ds_core/stats.h index 06e0c3b7f..6fa203769 100644 --- a/src/snmalloc/ds_core/stats.h +++ b/src/snmalloc/ds_core/stats.h @@ -80,8 +80,8 @@ namespace snmalloc void operator+=(const MonotoneLocalStat& other) { - value.fetch_add( - other.value.load(std::memory_order_relaxed), std::memory_order_relaxed); + auto v = other.value.load(std::memory_order_relaxed); + value.fetch_add(v, std::memory_order_relaxed); } size_t operator*()