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>;
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;
193 explicit Impl(IMemoryPoolAllocator* allocator,
const String& name)
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);
219 void freeCachedMemory();
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) {
249 return m_allocator->allocateMemory(size);
254 m_total_free -= size;
258 ptr = m_allocator->allocateMemory(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))
365void* MemoryPool::allocateMemory(
Int64 size)
367 return m_p->allocateMemory(size);
369void MemoryPool::freeMemory(
void* ptr,
Int64 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
387setMaxCachedBlockSize(
Int32 v)
389 m_p->setMaxCachedBlockSize(v);
394 m_p->freeCachedMemory();
397totalAllocated()
const
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.
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.
Unicode character string.
std::int64_t Int64
Signed integer type of 64 bits.
std::int32_t Int32
Signed integer type of 32 bits.