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>
44 using MapType = std::unordered_map<void*, size_t>;
45 using ValueType = MapType::value_type;
46 using MapIterator = MapType::iterator;
50 explicit AllocatedMap(
const String& name)
56 void removePointer(
void* ptr,
size_t size)
58 std::unique_lock<std::mutex> lg(m_mutex);
59 auto x = m_allocated_memory_map.find(ptr);
60 if (x == m_allocated_memory_map.end()) {
62 String str = String::format(
"MemoryPool '{0}': pointer {1} is not in the allocated map", m_name, ptr);
64 std::cerr <<
"ERROR: " << str <<
"\n";
68 size_t allocated_size = x->second;
69 if (size != allocated_size) {
71 String str = String::format(
"MemoryPool '{0}': Incoherent size saved_size={1} arg_size={2}",
72 m_name, allocated_size, size);
74 std::cerr <<
"ERROR: " << str <<
"\n";
77 m_allocated_memory_map.erase(x);
80 void addPointer(
void* ptr,
size_t size)
82 std::unique_lock<std::mutex> lg(m_mutex);
83 auto x = m_allocated_memory_map.find(ptr);
85 "MemoryPool '{0}': pointer {1} (for size={2}) is already in the allocated map (with size={3})",
86 m_name, ptr, size, x->second);
87 m_allocated_memory_map.insert(std::make_pair(ptr, size));
92 std::unique_lock<std::mutex> lg(m_mutex);
93 return m_allocated_memory_map.size();
96 bool isThrowOnError()
const {
return m_is_throw_on_error; }
97 void setIsThrowOnError(
bool v) { m_is_throw_on_error = v; }
98 Int32 nbError()
const {
return m_nb_error; }
102 MapType m_allocated_memory_map;
104 std::atomic<Int32> m_nb_error = 0;
105 bool m_is_throw_on_error =
false;
106 mutable std::mutex m_mutex;
116 using MapType = std::unordered_multimap<size_t, void*>;
120 explicit FreedMap(
const String& name)
134 std::unique_lock<std::mutex> lg(m_mutex);
136 auto x = m_free_memory_map.find(size);
137 if (x != m_free_memory_map.end()) {
139 m_free_memory_map.erase(x);
144 void addPointer(
void* ptr,
size_t size)
146 std::unique_lock<std::mutex> lg(m_mutex);
147 m_free_memory_map.insert(std::make_pair(size, ptr));
152 std::unique_lock<std::mutex> lg(m_mutex);
153 return m_free_memory_map.size();
156 void dump(std::ostream& ostr)
158 std::map<size_t, Int32> nb_alloc_per_size;
160 std::unique_lock<std::mutex> lg(m_mutex);
161 for (
const auto& [key, value] : m_free_memory_map) {
162 auto x = nb_alloc_per_size.find(key);
163 if (x == nb_alloc_per_size.end())
164 nb_alloc_per_size.insert(std::make_pair(key, 1));
169 ostr <<
"FreedMap '" << m_name <<
"\n";
170 for (
const auto& [key, value] : nb_alloc_per_size)
171 ostr <<
"Map size=" << key <<
" nb_allocated=" << value <<
" page_modulo=" << (key % 4096) <<
"\n";
177 std::unique_lock<std::mutex> lg(m_mutex);
178 values.reserve(m_free_memory_map.size());
179 for (
const auto& [size, ptr] : m_free_memory_map)
180 values.add(std::make_pair(ptr, size));
181 m_free_memory_map.clear();
186 MapType m_free_memory_map;
188 mutable std::mutex m_mutex;
194 : m_allocator(allocator)
195 , m_allocated_map(name)
200 bool throw_on_error = (v.value() != 0);
201 m_allocated_map.setIsThrowOnError(throw_on_error);
214 void* allocateMemory(
size_t size);
215 void freeMemory(
void* ptr,
size_t size);
216 void dumpStats(std::ostream& ostr);
217 void dumpFreeMap(std::ostream& ostr);
218 void setMaxCachedBlockSize(Int32 v);
223 IMemoryPoolAllocator* m_allocator =
nullptr;
225 AllocatedMap m_allocated_map;
228 std::atomic<Int64> m_total_allocated = 0;
229 std::atomic<Int64> m_total_free = 0;
230 std::atomic<Int32> m_nb_cached = 0;
231 std::atomic<Int32> m_nb_no_cached = 0;
232 size_t m_max_memory_size_to_pool = 1024 * 64 * 4 * 4;
237 void _freeMemory(
void* ptr);
238 void _addAllocated(
void* ptr,
size_t size);
244void* MemoryPool::Impl::
245allocateMemory(
size_t size)
247 if (m_max_memory_size_to_pool != 0 && size > m_max_memory_size_to_pool) {
254 m_total_free -= size;
260 _addAllocated(ptr, size);
267void MemoryPool::Impl::
268freeMemory(
void* ptr,
size_t size)
270 if (m_max_memory_size_to_pool != 0 && size > m_max_memory_size_to_pool)
271 return m_allocator->freeMemory(ptr, size);
273 m_allocated_map.removePointer(ptr, size);
275 m_free_map.addPointer(ptr, size);
276 m_total_allocated -= size;
277 m_total_free += size;
283void MemoryPool::Impl::
284_addAllocated(
void* ptr,
size_t size)
286 m_allocated_map.addPointer(ptr, size);
287 m_total_allocated += size;
293void MemoryPool::Impl::
294dumpStats(std::ostream& ostr)
296 ostr <<
"Stats '" << m_name <<
"' max_block=" << m_max_memory_size_to_pool
297 <<
" TotalAllocated=" << m_total_allocated
298 <<
" TotalFree=" << m_total_free
299 <<
" nb_allocated=" << m_allocated_map.size()
300 <<
" nb_free=" << m_free_map.size()
301 <<
" nb_cached=" << m_nb_cached
302 <<
" nb_no_cached=" << m_nb_no_cached
303 <<
" nb_error=" << m_allocated_map.nbError()
310void MemoryPool::Impl::
311dumpFreeMap(std::ostream& ostr)
313 m_free_map.dump(ostr);
319void MemoryPool::Impl::
320setMaxCachedBlockSize(
Int32 v)
322 if (m_allocated_map.size() != 0 || m_free_map.size() != 0)
323 ARCCORE_FATAL(
"Can not change maximum cached block size on non empty pool");
326 m_max_memory_size_to_pool = v;
332void MemoryPool::Impl::
335 UniqueArray<std::pair<void*, size_t>> values_to_free;
336 m_free_map.fillFreeMapAndClear(values_to_free);
337 for (
const auto& v : values_to_free) {
338 m_allocator->freeMemory(v.first, v.second);
349MemoryPool(IMemoryPoolAllocator* allocator,
const String& name)
350: m_p(std::make_unique<
Impl>(allocator, name))
367 return m_p->allocateMemory(size);
371 m_p->freeMemory(ptr, size);
373void MemoryPool::dumpStats(std::ostream& ostr)
375 m_p->dumpStats(ostr);
378dumpFreeMap(std::ostream& ostr)
380 m_p->dumpFreeMap(ostr);
382String MemoryPool::name()
const
389 m_p->setMaxCachedBlockSize(v);
394 m_p->freeCachedMemory();
399 return m_p->m_total_allocated;
404 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.