Arcane  v4.1.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-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(size_t 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(size_t 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 m_max_memory_size_to_pool = v;
324}
325
326/*---------------------------------------------------------------------------*/
327/*---------------------------------------------------------------------------*/
328
329void MemoryPool::Impl::
330freeCachedMemory()
331{
332 UniqueArray<std::pair<void*, size_t>> values_to_free;
333 m_free_map.fillFreeMapAndClear(values_to_free);
334 for (const auto& v : values_to_free) {
335 m_allocator->freeMemory(v.first, v.second);
336 }
337}
338
339/*---------------------------------------------------------------------------*/
340/*---------------------------------------------------------------------------*/
341
342/*---------------------------------------------------------------------------*/
343/*---------------------------------------------------------------------------*/
344
345MemoryPool::
346MemoryPool(IMemoryPoolAllocator* allocator, const String& name)
347: m_p(std::make_shared<Impl>(allocator, name))
348{
349}
350
351/*---------------------------------------------------------------------------*/
352/*---------------------------------------------------------------------------*/
353
354MemoryPool::
355~MemoryPool()
356{
357}
358
359/*---------------------------------------------------------------------------*/
360/*---------------------------------------------------------------------------*/
361
362void* MemoryPool::allocateMemory(size_t size)
363{
364 return m_p->allocateMemory(size);
365}
366void MemoryPool::freeMemory(void* ptr, size_t size)
367{
368 m_p->freeMemory(ptr, size);
369}
370void MemoryPool::dumpStats(std::ostream& ostr)
371{
372 m_p->dumpStats(ostr);
373}
374void MemoryPool::
375dumpFreeMap(std::ostream& ostr)
376{
377 m_p->dumpFreeMap(ostr);
378}
379String MemoryPool::name() const
380{
381 return m_p->m_name;
382}
383void MemoryPool::
384setMaxCachedBlockSize(size_t v)
385{
386 m_p->setMaxCachedBlockSize(v);
387}
388void MemoryPool::
389freeCachedMemory()
390{
391 m_p->freeCachedMemory();
392}
393
394/*---------------------------------------------------------------------------*/
395/*---------------------------------------------------------------------------*/
396
397} // namespace Arcane::impl
398
399/*---------------------------------------------------------------------------*/
400/*---------------------------------------------------------------------------*/
Classe de base des vecteurs 1D de données.
Classe template pour convertir un type.
Chaîne de caractères unicode.
Tableau associatif des emplacements mémoire libres par taille.
void * getPointer(size_t size)
Récupère un pointeur pour une taille size.
void fillFreeMapAndClear(Array< std::pair< void *, size_t > > &values)
Remplit values avec les valeurs de m_free_memory_map et vide cette dernière.
FreedMap m_free_map
Liste des allocations libres dans le cache.
std::int32_t Int32
Type entier signé sur 32 bits.