14#include "arccore/common/internal/MemoryPool.h"
16#include "arccore/base/FatalErrorException.h"
17#include "arccore/base/PlatformUtils.h"
18#include "arccore/base/Convert.h"
20#include "arccore/common/Array.h"
22#include <unordered_map>
43 using MapType = std::unordered_map<void*, size_t>;
44 using ValueType = MapType::value_type;
45 using MapIterator = MapType::iterator;
49 explicit AllocatedMap(
const String& name)
55 void removePointer(
void* ptr,
size_t size)
57 std::unique_lock<std::mutex> lg(m_mutex);
58 auto x = m_allocated_memory_map.find(ptr);
59 if (x == m_allocated_memory_map.end()) {
61 String str = String::format(
"MemoryPool '{0}': pointer {1} is not in the allocated map", m_name, ptr);
63 std::cerr <<
"ERROR: " << str <<
"\n";
67 size_t allocated_size = x->second;
68 if (size != allocated_size) {
70 String str = String::format(
"MemoryPool '{0}': Incoherent size saved_size={1} arg_size={2}",
71 m_name, allocated_size, size);
73 std::cerr <<
"ERROR: " << str <<
"\n";
76 m_allocated_memory_map.erase(x);
79 void addPointer(
void* ptr,
size_t size)
81 std::unique_lock<std::mutex> lg(m_mutex);
82 auto x = m_allocated_memory_map.find(ptr);
84 "MemoryPool '{0}': pointer {1} (for size={2}) is already in the allocated map (with size={3})",
85 m_name, ptr, size, x->second);
86 m_allocated_memory_map.insert(std::make_pair(ptr, size));
91 std::unique_lock<std::mutex> lg(m_mutex);
92 return m_allocated_memory_map.size();
95 bool isThrowOnError()
const {
return m_is_throw_on_error; }
96 void setIsThrowOnError(
bool v) { m_is_throw_on_error = v; }
97 Int32 nbError()
const {
return m_nb_error; }
101 MapType m_allocated_memory_map;
103 std::atomic<Int32> m_nb_error = 0;
104 bool m_is_throw_on_error =
false;
105 mutable std::mutex m_mutex;
115 using MapType = std::unordered_multimap<size_t, void*>;
119 explicit FreedMap(
const String& name)
133 std::unique_lock<std::mutex> lg(m_mutex);
135 auto x = m_free_memory_map.find(size);
136 if (x != m_free_memory_map.end()) {
138 m_free_memory_map.erase(x);
143 void addPointer(
void* ptr,
size_t size)
145 std::unique_lock<std::mutex> lg(m_mutex);
146 m_free_memory_map.insert(std::make_pair(size, ptr));
151 std::unique_lock<std::mutex> lg(m_mutex);
152 return m_free_memory_map.size();
155 void dump(std::ostream& ostr)
157 std::map<size_t, Int32> nb_alloc_per_size;
159 std::unique_lock<std::mutex> lg(m_mutex);
160 for (
const auto& [key, value] : m_free_memory_map) {
161 auto x = nb_alloc_per_size.find(key);
162 if (x == nb_alloc_per_size.end())
163 nb_alloc_per_size.insert(std::make_pair(key, 1));
168 ostr <<
"FreedMap '" << m_name <<
"\n";
169 for (
const auto& [key, value] : nb_alloc_per_size)
170 ostr <<
"Map size=" << key <<
" nb_allocated=" << value <<
" page_modulo=" << (key % 4096) <<
"\n";
176 std::unique_lock<std::mutex> lg(m_mutex);
177 values.reserve(m_free_memory_map.size());
178 for (
const auto& [size, ptr] : m_free_memory_map)
179 values.add(std::make_pair(ptr, size));
180 m_free_memory_map.clear();
185 MapType m_free_memory_map;
187 mutable std::mutex m_mutex;
193 : m_allocator(allocator)
194 , m_allocated_map(name)
199 bool throw_on_error = (v.value() != 0);
200 m_allocated_map.setIsThrowOnError(throw_on_error);
213 void* allocateMemory(
size_t size);
214 void freeMemory(
void* ptr,
size_t size);
215 void dumpStats(std::ostream& ostr);
216 void dumpFreeMap(std::ostream& ostr);
217 void setMaxCachedBlockSize(Int32 v);
222 IMemoryPoolAllocator* m_allocator =
nullptr;
224 AllocatedMap m_allocated_map;
227 std::atomic<Int64> m_total_allocated = 0;
228 std::atomic<Int64> m_total_free = 0;
229 std::atomic<Int32> m_nb_cached = 0;
230 std::atomic<Int32> m_nb_no_cached = 0;
231 size_t m_max_memory_size_to_pool = 1024 * 64 * 4 * 4;
236 void _freeMemory(
void* ptr);
237 void _addAllocated(
void* ptr,
size_t size);
243void* MemoryPool::Impl::
244allocateMemory(
size_t size)
246 if (m_max_memory_size_to_pool != 0 && size > m_max_memory_size_to_pool) {
253 m_total_free -= size;
259 _addAllocated(ptr, size);
266void MemoryPool::Impl::
267freeMemory(
void* ptr,
size_t size)
269 if (m_max_memory_size_to_pool != 0 && size > m_max_memory_size_to_pool)
270 return m_allocator->freeMemory(ptr, size);
272 m_allocated_map.removePointer(ptr, size);
274 m_free_map.addPointer(ptr, size);
275 m_total_allocated -= size;
276 m_total_free += size;
282void MemoryPool::Impl::
283_addAllocated(
void* ptr,
size_t size)
285 m_allocated_map.addPointer(ptr, size);
286 m_total_allocated += size;
292void MemoryPool::Impl::
293dumpStats(std::ostream& ostr)
295 ostr <<
"Stats '" << m_name <<
"' max_block=" << m_max_memory_size_to_pool
296 <<
" TotalAllocated=" << m_total_allocated
297 <<
" TotalFree=" << m_total_free
298 <<
" nb_allocated=" << m_allocated_map.size()
299 <<
" nb_free=" << m_free_map.size()
300 <<
" nb_cached=" << m_nb_cached
301 <<
" nb_no_cached=" << m_nb_no_cached
302 <<
" nb_error=" << m_allocated_map.nbError()
309void MemoryPool::Impl::
310dumpFreeMap(std::ostream& ostr)
312 m_free_map.dump(ostr);
318void MemoryPool::Impl::
319setMaxCachedBlockSize(
Int32 v)
321 if (m_allocated_map.size() != 0 || m_free_map.size() != 0)
322 ARCCORE_FATAL(
"Can not change maximum cached block size on non empty pool");
325 m_max_memory_size_to_pool = v;
331void MemoryPool::Impl::
334 UniqueArray<std::pair<void*, size_t>> values_to_free;
335 m_free_map.fillFreeMapAndClear(values_to_free);
336 for (
const auto& v : values_to_free) {
337 m_allocator->freeMemory(v.first, v.second);
348MemoryPool(IMemoryPoolAllocator* allocator,
const String& name)
349: m_p(std::make_unique<
Impl>(allocator, name))
366 return m_p->allocateMemory(size);
370 m_p->freeMemory(ptr, size);
372void MemoryPool::dumpStats(std::ostream& ostr)
374 m_p->dumpStats(ostr);
377dumpFreeMap(std::ostream& ostr)
379 m_p->dumpFreeMap(ostr);
381String MemoryPool::name()
const
388 m_p->setMaxCachedBlockSize(v);
393 m_p->freeCachedMemory();
398 return m_p->m_total_allocated;
403 return m_p->m_total_free;
#define ARCCORE_FATAL(...)
Macro throwing a FatalErrorException.
#define ARCCORE_FATAL_IF(cond,...)
Macro throwing a FatalErrorException if cond is true.
Base class for 1D data vectors.
Template class for converting a type.
Interface for an allocator for a MemoryPool.
virtual void * allocateMemory(Int64 size)=0
Allocates a block for size bytes.
Associative array of free memory locations by size.
void fillFreeMapAndClear(Array< std::pair< void *, size_t > > &values)
Fills values with the values from m_free_memory_map and clears the latter.
void * getPointer(size_t size)
Retrieves a pointer for a size size.
FreedMap m_free_map
List of free allocations in the cache.
void setMaxCachedBlockSize(Int32 v) override
Implementation of IMemoryPool.
void freeMemory(void *ptr, Int64 size) override
Frees the block located at address address containing size bytes.
void * allocateMemory(Int64 size) override
Allocates a block for size bytes.
Int64 totalAllocated() const override
Total size (in bytes) allocated in the memory pool.
void freeCachedMemory() override
Frees the memory in the cache.
Int64 totalCached() const override
Total size (in bytes) in the cache.
Unicode character string.
std::int64_t Int64
Signed integer type of 64 bits.
std::int32_t Int32
Signed integer type of 32 bits.