Arcane  v4.1.2.0
Documentation utilisateur
Chargement...
Recherche...
Aucune correspondance
MemoryPool.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* MemoryPool.cc (C) 2000-2025 */
9/* */
10/* Classe pour gérer une liste de zone allouées. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arccore/common/internal/MemoryPool.h"
15
16#include "arccore/base/FatalErrorException.h"
17#include "arccore/base/PlatformUtils.h"
18#include "arccore/base/Convert.h"
19
20#include "arccore/common/Array.h"
21
22#include <unordered_map>
23#include <map>
24#include <atomic>
25#include <mutex>
26
27/*---------------------------------------------------------------------------*/
28/*---------------------------------------------------------------------------*/
29
30namespace Arcane::Impl
31{
32
33/*---------------------------------------------------------------------------*/
34/*---------------------------------------------------------------------------*/
35
37{
38 //! Tableau associatif des pointeurs alloués et la taille associée
39 class AllocatedMap
40 {
41 public:
42
43 using MapType = std::unordered_map<void*, size_t>;
44 using ValueType = MapType::value_type;
45 using MapIterator = MapType::iterator;
46
47 public:
48
49 explicit AllocatedMap(const String& name)
50 : m_name(name)
51 {}
52
53 public:
54
55 void removePointer(void* ptr, size_t size)
56 {
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()) {
60 ++m_nb_error;
61 String str = String::format("MemoryPool '{0}': pointer {1} is not in the allocated map", m_name, ptr);
62 ARCCORE_FATAL_IF(m_is_throw_on_error, str);
63 std::cerr << "ERROR: " << str << "\n";
64 return;
65 }
66
67 size_t allocated_size = x->second;
68 if (size != allocated_size) {
69 ++m_nb_error;
70 String str = String::format("MemoryPool '{0}': Incoherent size saved_size={1} arg_size={2}",
71 m_name, allocated_size, size);
72 ARCCORE_FATAL_IF(m_is_throw_on_error, str);
73 std::cerr << "ERROR: " << str << "\n";
74 }
75
76 m_allocated_memory_map.erase(x);
77 }
78
79 void addPointer(void* ptr, size_t size)
80 {
81 std::unique_lock<std::mutex> lg(m_mutex);
82 auto x = m_allocated_memory_map.find(ptr);
83 ARCCORE_FATAL_IF((x != m_allocated_memory_map.end()),
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));
87 }
88
89 size_t size() const
90 {
91 std::unique_lock<std::mutex> lg(m_mutex);
92 return m_allocated_memory_map.size();
93 }
94
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; }
98
99 private:
100
101 MapType m_allocated_memory_map;
102 String m_name;
103 std::atomic<Int32> m_nb_error = 0;
104 bool m_is_throw_on_error = false;
105 mutable std::mutex m_mutex;
106 };
107
108 public:
109
110 //! Tableau associatif des emplacements mémoire libres par taille
111 class FreedMap
112 {
113 public:
114
115 using MapType = std::unordered_multimap<size_t, void*>;
116
117 public:
118
119 explicit FreedMap(const String& name)
120 : m_name(name)
121 {}
122
123 public:
124
125 /*!
126 * \brief Récupère un pointeur pour une taille \a size.
127 *
128 * Retourne nullptr s'il n'y a aucune valeur dans le cache
129 * pour cette taille. Sinon, le pointeur retourné est supprimé du cache.
130 */
131 void* getPointer(size_t size)
132 {
133 std::unique_lock<std::mutex> lg(m_mutex);
134 void* ptr = nullptr;
135 auto x = m_free_memory_map.find(size);
136 if (x != m_free_memory_map.end()) {
137 ptr = x->second;
138 m_free_memory_map.erase(x);
139 }
140 return ptr;
141 }
142
143 void addPointer(void* ptr, size_t size)
144 {
145 std::unique_lock<std::mutex> lg(m_mutex);
146 m_free_memory_map.insert(std::make_pair(size, ptr));
147 }
148
149 size_t size() const
150 {
151 std::unique_lock<std::mutex> lg(m_mutex);
152 return m_free_memory_map.size();
153 }
154
155 void dump(std::ostream& ostr)
156 {
157 std::map<size_t, Int32> nb_alloc_per_size;
158 {
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));
164 else
165 ++x->second;
166 }
167 }
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";
171 }
172
173 //! Remplit \a values avec les valeurs de \a m_free_memory_map et vide cette dernière
174 void fillFreeMapAndClear(Array<std::pair<void*, size_t>>& values)
175 {
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();
181 }
182
183 private:
184
185 MapType m_free_memory_map;
186 String m_name;
187 mutable std::mutex m_mutex;
188 };
189
190 public:
191
192 explicit Impl(IMemoryPoolAllocator* allocator, const String& name)
193 : m_allocator(allocator)
194 , m_allocated_map(name)
195 , m_free_map(name)
196 , m_name(name)
197 {
198 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ARCANE_MEMORY_POOL_THROW_ON_ERROR", true)) {
199 bool throw_on_error = (v.value() != 0);
200 m_allocated_map.setIsThrowOnError(throw_on_error);
201 }
202 }
203 ~Impl()
204 {
205 // Ne libère pas la mémoire dans m_free_map
206 // car ce destructeur peut être appelé en fin d'exécution
207 // et sur accélérateur on ne peut plus à ce moment la
208 // appeler des fonctions du runtime.
209 }
210
211 public:
212
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();
219
220 public:
221
222 IMemoryPoolAllocator* m_allocator = nullptr;
223 // Contient une liste de couples (taille_mémoire,pointeur) de mémoire allouée.
224 AllocatedMap m_allocated_map;
225 //! Liste des allocations libres dans le cache
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;
232 String m_name;
233
234 private:
235
236 void _freeMemory(void* ptr);
237 void _addAllocated(void* ptr, size_t size);
238};
239
240/*---------------------------------------------------------------------------*/
241/*---------------------------------------------------------------------------*/
242
243void* MemoryPool::Impl::
244allocateMemory(size_t size)
245{
246 if (m_max_memory_size_to_pool != 0 && size > m_max_memory_size_to_pool) {
247 ++m_nb_no_cached;
248 return m_allocator->allocateMemory(size);
249 }
250
251 void* ptr = m_free_map.getPointer(size);
252 if (ptr) {
253 m_total_free -= size;
254 ++m_nb_cached;
255 }
256 else {
257 ptr = m_allocator->allocateMemory(size);
258 }
259 _addAllocated(ptr, size);
260 return ptr;
261}
262
263/*---------------------------------------------------------------------------*/
264/*---------------------------------------------------------------------------*/
265
266void MemoryPool::Impl::
267freeMemory(void* ptr, size_t size)
268{
269 if (m_max_memory_size_to_pool != 0 && size > m_max_memory_size_to_pool)
270 return m_allocator->freeMemory(ptr, size);
271
272 m_allocated_map.removePointer(ptr, size);
273
274 m_free_map.addPointer(ptr, size);
275 m_total_allocated -= size;
276 m_total_free += size;
277}
278
279/*---------------------------------------------------------------------------*/
280/*---------------------------------------------------------------------------*/
281
282void MemoryPool::Impl::
283_addAllocated(void* ptr, size_t size)
284{
285 m_allocated_map.addPointer(ptr, size);
286 m_total_allocated += size;
287}
288
289/*---------------------------------------------------------------------------*/
290/*---------------------------------------------------------------------------*/
291
292void MemoryPool::Impl::
293dumpStats(std::ostream& ostr)
294{
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()
303 << "\n";
304}
305
306/*---------------------------------------------------------------------------*/
307/*---------------------------------------------------------------------------*/
308
309void MemoryPool::Impl::
310dumpFreeMap(std::ostream& ostr)
311{
312 m_free_map.dump(ostr);
313}
314
315/*---------------------------------------------------------------------------*/
316/*---------------------------------------------------------------------------*/
317
318void MemoryPool::Impl::
319setMaxCachedBlockSize(Int32 v)
320{
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");
323 if (v < 0)
324 v = 0;
325 m_max_memory_size_to_pool = v;
326}
327
328/*---------------------------------------------------------------------------*/
329/*---------------------------------------------------------------------------*/
330
331void MemoryPool::Impl::
332freeCachedMemory()
333{
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);
338 }
339}
340
341/*---------------------------------------------------------------------------*/
342/*---------------------------------------------------------------------------*/
343
344/*---------------------------------------------------------------------------*/
345/*---------------------------------------------------------------------------*/
346
347MemoryPool::
348MemoryPool(IMemoryPoolAllocator* allocator, const String& name)
349: m_p(std::make_unique<Impl>(allocator, name))
350{
351}
352
353/*---------------------------------------------------------------------------*/
354/*---------------------------------------------------------------------------*/
355
356MemoryPool::
357~MemoryPool()
358{
359}
360
361/*---------------------------------------------------------------------------*/
362/*---------------------------------------------------------------------------*/
363
364void* MemoryPool::allocateMemory(size_t size)
365{
366 return m_p->allocateMemory(size);
367}
368void MemoryPool::freeMemory(void* ptr, size_t size)
369{
370 m_p->freeMemory(ptr, size);
371}
372void MemoryPool::dumpStats(std::ostream& ostr)
373{
374 m_p->dumpStats(ostr);
375}
376void MemoryPool::
377dumpFreeMap(std::ostream& ostr)
378{
379 m_p->dumpFreeMap(ostr);
380}
381String MemoryPool::name() const
382{
383 return m_p->m_name;
384}
385void MemoryPool::
386setMaxCachedBlockSize(Int32 v)
387{
388 m_p->setMaxCachedBlockSize(v);
389}
390void MemoryPool::
391freeCachedMemory()
392{
393 m_p->freeCachedMemory();
394}
395size_t MemoryPool::
396totalAllocated() const
397{
398 return m_p->m_total_allocated;
399}
400size_t MemoryPool::
401totalCached() const
402{
403 return m_p->m_total_free;
404}
405
406/*---------------------------------------------------------------------------*/
407/*---------------------------------------------------------------------------*/
408
409} // namespace Arcane::Impl
410
411/*---------------------------------------------------------------------------*/
412/*---------------------------------------------------------------------------*/
#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.