#include "Memory_Resource.h" #include #include #include namespace { struct Allocation_Counters { std::atomic live_bytes{}; std::atomic peak_live_bytes{}; std::atomic allocated_bytes_total{}; std::atomic allocation_count{}; std::atomic deallocation_count{}; std::atomic pmr_live_bytes{}; std::atomic pmr_peak_live_bytes{}; std::atomic pmr_allocated_bytes_total{}; std::atomic pmr_allocation_count{}; std::atomic pmr_deallocation_count{}; }; constinit Allocation_Counters allocation_counters; void update_peak(std::atomic& peak, std::uint64_t value) { auto current = peak.load(std::memory_order_relaxed); while (current < value && !peak.compare_exchange_weak(current, value, std::memory_order_relaxed)) { } } void record_allocation(void* pointer, bool pmr) { if (pointer == nullptr) return; const auto bytes = static_cast(mi_usable_size(pointer)); const auto live = allocation_counters.live_bytes.fetch_add(bytes, std::memory_order_relaxed) + bytes; allocation_counters.allocated_bytes_total.fetch_add(bytes, std::memory_order_relaxed); allocation_counters.allocation_count.fetch_add(1, std::memory_order_relaxed); update_peak(allocation_counters.peak_live_bytes, live); if (!pmr) return; const auto pmr_live = allocation_counters.pmr_live_bytes.fetch_add(bytes, std::memory_order_relaxed) + bytes; allocation_counters.pmr_allocated_bytes_total.fetch_add(bytes, std::memory_order_relaxed); allocation_counters.pmr_allocation_count.fetch_add(1, std::memory_order_relaxed); update_peak(allocation_counters.pmr_peak_live_bytes, pmr_live); } void record_deallocation(void* pointer, bool pmr) { if (pointer == nullptr) return; const auto bytes = static_cast(mi_usable_size(pointer)); allocation_counters.live_bytes.fetch_sub(bytes, std::memory_order_relaxed); allocation_counters.deallocation_count.fetch_add(1, std::memory_order_relaxed); if (!pmr) return; allocation_counters.pmr_live_bytes.fetch_sub(bytes, std::memory_order_relaxed); allocation_counters.pmr_deallocation_count.fetch_add(1, std::memory_order_relaxed); } void free_tracked(void* pointer) noexcept { record_deallocation(pointer, false); mi_free(pointer); } class Mimalloc_Memory_Resource final : public std::pmr::memory_resource { protected: void* do_allocate(std::size_t bytes, std::size_t alignment) override { auto pointer = mi_new_aligned(bytes, alignment); record_allocation(pointer, true); return pointer; } void do_deallocate(void* pointer, std::size_t, std::size_t) override { record_deallocation(pointer, true); mi_free(pointer); } bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { return this == &other; } }; } void* operator new(std::size_t bytes) { auto pointer = mi_new(bytes); record_allocation(pointer, false); return pointer; } void* operator new[](std::size_t bytes) { auto pointer = mi_new(bytes); record_allocation(pointer, false); return pointer; } void* operator new(std::size_t bytes, const std::nothrow_t&) noexcept { auto pointer = mi_new_nothrow(bytes); record_allocation(pointer, false); return pointer; } void* operator new[](std::size_t bytes, const std::nothrow_t&) noexcept { auto pointer = mi_new_nothrow(bytes); record_allocation(pointer, false); return pointer; } void operator delete(void* pointer) noexcept { free_tracked(pointer); } void operator delete[](void* pointer) noexcept { free_tracked(pointer); } void operator delete(void* pointer, const std::nothrow_t&) noexcept { free_tracked(pointer); } void operator delete[](void* pointer, const std::nothrow_t&) noexcept { free_tracked(pointer); } void operator delete(void* pointer, std::size_t) noexcept { free_tracked(pointer); } void operator delete[](void* pointer, std::size_t) noexcept { free_tracked(pointer); } void* operator new(std::size_t bytes, std::align_val_t alignment) { auto pointer = mi_new_aligned(bytes, static_cast(alignment)); record_allocation(pointer, false); return pointer; } void* operator new[](std::size_t bytes, std::align_val_t alignment) { auto pointer = mi_new_aligned(bytes, static_cast(alignment)); record_allocation(pointer, false); return pointer; } void* operator new(std::size_t bytes, std::align_val_t alignment, const std::nothrow_t&) noexcept { auto pointer = mi_new_aligned_nothrow(bytes, static_cast(alignment)); record_allocation(pointer, false); return pointer; } void* operator new[](std::size_t bytes, std::align_val_t alignment, const std::nothrow_t&) noexcept { auto pointer = mi_new_aligned_nothrow(bytes, static_cast(alignment)); record_allocation(pointer, false); return pointer; } void operator delete(void* pointer, std::align_val_t) noexcept { free_tracked(pointer); } void operator delete[](void* pointer, std::align_val_t) noexcept { free_tracked(pointer); } void operator delete(void* pointer, std::size_t, std::align_val_t) noexcept { free_tracked(pointer); } void operator delete[](void* pointer, std::size_t, std::align_val_t) noexcept { free_tracked(pointer); } void operator delete(void* pointer, std::align_val_t, const std::nothrow_t&) noexcept { free_tracked(pointer); } void operator delete[](void* pointer, std::align_val_t, const std::nothrow_t&) noexcept { free_tracked(pointer); } std::pmr::memory_resource* server_memory_resource() { static Mimalloc_Memory_Resource resource; return &resource; } void initialize_server_memory_resource() { mi_process_init(); std::pmr::set_default_resource(server_memory_resource()); } Server_Allocator_Snapshot server_allocator_snapshot() { Server_Allocator_Snapshot result; result.live_bytes = allocation_counters.live_bytes.load(std::memory_order_relaxed); result.peak_live_bytes = allocation_counters.peak_live_bytes.load(std::memory_order_relaxed); result.allocated_bytes_total = allocation_counters.allocated_bytes_total.load(std::memory_order_relaxed); result.allocation_count = allocation_counters.allocation_count.load(std::memory_order_relaxed); result.deallocation_count = allocation_counters.deallocation_count.load(std::memory_order_relaxed); result.pmr_live_bytes = allocation_counters.pmr_live_bytes.load(std::memory_order_relaxed); result.pmr_peak_live_bytes = allocation_counters.pmr_peak_live_bytes.load(std::memory_order_relaxed); result.pmr_allocated_bytes_total = allocation_counters.pmr_allocated_bytes_total.load(std::memory_order_relaxed); result.pmr_allocation_count = allocation_counters.pmr_allocation_count.load(std::memory_order_relaxed); result.pmr_deallocation_count = allocation_counters.pmr_deallocation_count.load(std::memory_order_relaxed); result.allocator_version = mi_version(); return result; } void collect_server_memory(bool force) { mi_collect(force); }