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>;
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;
192 explicit Impl(IMemoryPoolAllocator* allocator,
const String& name)
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);
218 void freeCachedMemory();
222 IMemoryPoolAllocator* m_allocator =
nullptr;
224 AllocatedMap m_allocated_map;
227 std::atomic<size_t> m_total_allocated = 0;
228 std::atomic<size_t> 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) {
248 return m_allocator->allocateMemory(size);
253 m_total_free -= size;
257 ptr = m_allocator->allocateMemory(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))
364void* MemoryPool::allocateMemory(
size_t size)
366 return m_p->allocateMemory(size);
368void MemoryPool::freeMemory(
void* ptr,
size_t 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
386setMaxCachedBlockSize(
Int32 v)
388 m_p->setMaxCachedBlockSize(v);
393 m_p->freeCachedMemory();
396totalAllocated()
const
398 return m_p->m_total_allocated;
403 return m_p->m_total_free;
#define ARCCORE_FATAL(...)
Macro envoyant une exception FatalErrorException.
#define ARCCORE_FATAL_IF(cond,...)
Macro envoyant une exception FatalErrorException si cond est vrai.
Classe de base des vecteurs 1D de données.
Classe template pour convertir un type.
Tableau associatif des emplacements mémoire libres par taille.
void fillFreeMapAndClear(Array< std::pair< void *, size_t > > &values)
Remplit values avec les valeurs de m_free_memory_map et vide cette dernière.
void * getPointer(size_t size)
Récupère un pointeur pour une taille size.
FreedMap m_free_map
Liste des allocations libres dans le cache.
Chaîne de caractères unicode.
std::int32_t Int32
Type entier signé sur 32 bits.