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