Arcane  v3.14.10.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-2024 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-2024 */
9/* */
10/* Classe pour gérer une liste de zone allouées. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/internal/MemoryPool.h"
15
16#include "arcane/utils/FatalErrorException.h"
17#include "arcane/utils/PlatformUtils.h"
18#include "arcane/utils/ValueConvert.h"
19
20#include <unordered_map>
21#include <map>
22#include <atomic>
23#include <mutex>
24
25/*---------------------------------------------------------------------------*/
26/*---------------------------------------------------------------------------*/
27
28namespace Arcane::impl
29{
30
31/*---------------------------------------------------------------------------*/
32/*---------------------------------------------------------------------------*/
33
35{
36 //! Tableau associatif des pointeurs alloués et la taille associée
37 class AllocatedMap
38 {
39 public:
40
41 using MapType = std::unordered_map<void*, size_t>;
42 using ValueType = MapType::value_type;
43 using MapIterator = MapType::iterator;
44
45 public:
46
47 explicit AllocatedMap(const String& name)
48 : m_name(name)
49 {}
50
51 public:
52
53 void removePointer(void* ptr, size_t size)
54 {
55 std::unique_lock<std::mutex> lg(m_mutex);
56 auto x = m_allocated_memory_map.find(ptr);
57 if (x == m_allocated_memory_map.end()) {
58 ++m_nb_error;
59 String str = String::format("MemoryPool '{0}': pointer {1} is not in the allocated map", m_name, ptr);
60 if (m_is_throw_on_error)
61 ARCANE_FATAL(str);
62 else {
63 std::cerr << "ERROR: " << str << "\n";
64 return;
65 }
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 if (m_is_throw_on_error)
74 ARCANE_FATAL(str);
75 else
76 std::cerr << "ERROR: " << str << "\n";
77 }
78
79 m_allocated_memory_map.erase(x);
80 }
81
82 void addPointer(void* ptr, size_t size)
83 {
84 std::unique_lock<std::mutex> lg(m_mutex);
85 auto x = m_allocated_memory_map.find(ptr);
86 if (x != m_allocated_memory_map.end())
87 ARCANE_FATAL("MemoryPool '{0}': pointer {1} (for size={2}) is already in the allocated map (with size={3})",
88 m_name, ptr, size, x->second);
89
90 m_allocated_memory_map.insert(std::make_pair(ptr, size));
91 }
92
93 size_t size() const
94 {
95 std::unique_lock<std::mutex> lg(m_mutex);
96 return m_allocated_memory_map.size();
97 }
98
99 bool isThrowOnError() const { return m_is_throw_on_error; }
100 void setIsThrowOnError(bool v) { m_is_throw_on_error = v; }
101 Int32 nbError() const { return m_nb_error; }
102
103 private:
104
105 MapType m_allocated_memory_map;
106 String m_name;
107 std::atomic<Int32> m_nb_error = 0;
108 bool m_is_throw_on_error = false;
109 mutable std::mutex m_mutex;
110 };
111
112 public:
113
114 //! Tableau associatif des emplacements mémoire libres par taille
116 {
117 public:
118
119 using MapType = std::unordered_multimap<size_t, void*>;
120
121 public:
122
123 explicit FreedMap(const String& name)
124 : m_name(name)
125 {}
126
127 public:
128
129 /*!
130 * \brief Récupère un pointeur pour une taille \a size.
131 *
132 * Retourne nullptr s'il n'y a aucune valeur dans le cache
133 * pour cette taille. Sinon, le pointeur retourné est supprimé du cache.
134 */
135 void* getPointer(size_t size)
136 {
137 std::unique_lock<std::mutex> lg(m_mutex);
138 void* ptr = nullptr;
139 auto x = m_free_memory_map.find(size);
140 if (x != m_free_memory_map.end()) {
141 ptr = x->second;
142 m_free_memory_map.erase(x);
143 }
144 return ptr;
145 }
146
147 void addPointer(void* ptr, size_t size)
148 {
149 std::unique_lock<std::mutex> lg(m_mutex);
150 m_free_memory_map.insert(std::make_pair(size, ptr));
151 }
152
153 size_t size() const
154 {
155 std::unique_lock<std::mutex> lg(m_mutex);
156 return m_free_memory_map.size();
157 }
158
159 void dump(std::ostream& ostr)
160 {
161 std::map<size_t, Int32> nb_alloc_per_size;
162 {
163 std::unique_lock<std::mutex> lg(m_mutex);
164 for (const auto& [key, value] : m_free_memory_map) {
165 auto x = nb_alloc_per_size.find(key);
166 if (x == nb_alloc_per_size.end())
167 nb_alloc_per_size.insert(std::make_pair(key, 1));
168 else
169 ++x->second;
170 }
171 }
172 ostr << "FreedMap '" << m_name << "\n";
173 for (const auto& [key, value] : nb_alloc_per_size)
174 ostr << "Map size=" << key << " nb_allocated=" << value << " page_modulo=" << (key % 4096) << "\n";
175 }
176
177 //! Remplit \a values avec les valeurs de \a m_free_memory_map et vide cette dernière
178 void fillFreeMapAndClear(Array<std::pair<void*, size_t>>& values)
179 {
180 std::unique_lock<std::mutex> lg(m_mutex);
181 values.reserve(m_free_memory_map.size());
182 for (const auto& [size, ptr] : m_free_memory_map)
183 values.add(std::make_pair(ptr, size));
184 m_free_memory_map.clear();
185 }
186
187 private:
188
189 MapType m_free_memory_map;
190 String m_name;
191 mutable std::mutex m_mutex;
192 };
193
194 public:
195
196 explicit Impl(IMemoryPoolAllocator* allocator, const String& name)
197 : m_allocator(allocator)
198 , m_allocated_map(name)
199 , m_free_map(name)
200 , m_name(name)
201 {
202 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ARCANE_MEMORY_POOL_THROW_ON_ERROR", true)) {
203 bool throw_on_error = (v.value() != 0);
204 m_allocated_map.setIsThrowOnError(throw_on_error);
205 }
206 }
207 ~Impl()
208 {
209 // Ne libère pas la mémoire dans m_free_map
210 // car ce destructeur peut être appelé en fin d'exécution
211 // et sur accélérateur on ne peut plus à ce moment la
212 // appeler des fonctions du runtime.
213 }
214
215 public:
216
217 void* allocateMemory(size_t size);
218 void freeMemory(void* ptr, size_t size);
219 void dumpStats(std::ostream& ostr);
220 void dumpFreeMap(std::ostream& ostr);
221 void setMaxCachedBlockSize(size_t v);
222 void freeCachedMemory();
223
224 public:
225
226 IMemoryPoolAllocator* m_allocator = nullptr;
227 // Contient une liste de couples (taille_mémoire,pointeur) de mémoire allouée.
228 AllocatedMap m_allocated_map;
229 //! Liste des allocations libres dans le cache
231 std::atomic<size_t> m_total_allocated = 0;
232 std::atomic<size_t> m_total_free = 0;
233 std::atomic<Int32> m_nb_cached = 0;
234 std::atomic<Int32> m_nb_no_cached = 0;
235 size_t m_max_memory_size_to_pool = 1024 * 64 * 4 * 4;
236 String m_name;
237
238 private:
239
240 void _freeMemory(void* ptr);
241 void _addAllocated(void* ptr, size_t size);
242};
243
244/*---------------------------------------------------------------------------*/
245/*---------------------------------------------------------------------------*/
246
247void* MemoryPool::Impl::
248allocateMemory(size_t size)
249{
250 if (m_max_memory_size_to_pool != 0 && size > m_max_memory_size_to_pool) {
251 ++m_nb_no_cached;
252 return m_allocator->allocateMemory(size);
253 }
254
255 void* ptr = m_free_map.getPointer(size);
256 if (ptr) {
257 m_total_free -= size;
258 ++m_nb_cached;
259 }
260 else {
261 ptr = m_allocator->allocateMemory(size);
262 }
263 _addAllocated(ptr, size);
264 return ptr;
265}
266
267/*---------------------------------------------------------------------------*/
268/*---------------------------------------------------------------------------*/
269
270void MemoryPool::Impl::
271freeMemory(void* ptr, size_t size)
272{
273 if (m_max_memory_size_to_pool != 0 && size > m_max_memory_size_to_pool)
274 return m_allocator->freeMemory(ptr, size);
275
276 m_allocated_map.removePointer(ptr, size);
277
278 m_free_map.addPointer(ptr, size);
279 m_total_allocated -= size;
280 m_total_free += size;
281}
282
283/*---------------------------------------------------------------------------*/
284/*---------------------------------------------------------------------------*/
285
286void MemoryPool::Impl::
287_addAllocated(void* ptr, size_t size)
288{
289 m_allocated_map.addPointer(ptr, size);
290 m_total_allocated += size;
291}
292
293/*---------------------------------------------------------------------------*/
294/*---------------------------------------------------------------------------*/
295
296void MemoryPool::Impl::
297dumpStats(std::ostream& ostr)
298{
299 ostr << "Stats '" << m_name << "' max_block=" << m_max_memory_size_to_pool
300 << " TotalAllocated=" << m_total_allocated
301 << " TotalFree=" << m_total_free
302 << " nb_allocated=" << m_allocated_map.size()
303 << " nb_free=" << m_free_map.size()
304 << " nb_cached=" << m_nb_cached
305 << " nb_no_cached=" << m_nb_no_cached
306 << " nb_error=" << m_allocated_map.nbError()
307 << "\n";
308}
309
310/*---------------------------------------------------------------------------*/
311/*---------------------------------------------------------------------------*/
312
313void MemoryPool::Impl::
314dumpFreeMap(std::ostream& ostr)
315{
316 m_free_map.dump(ostr);
317}
318
319/*---------------------------------------------------------------------------*/
320/*---------------------------------------------------------------------------*/
321
322void MemoryPool::Impl::
323setMaxCachedBlockSize(size_t v)
324{
325 if (m_allocated_map.size() != 0 || m_free_map.size() != 0)
326 ARCANE_FATAL("Can not change maximum cached block size on non empty pool");
327 m_max_memory_size_to_pool = v;
328}
329
330/*---------------------------------------------------------------------------*/
331/*---------------------------------------------------------------------------*/
332
333void MemoryPool::Impl::
334freeCachedMemory()
335{
336 UniqueArray<std::pair<void*, size_t>> values_to_free;
337 m_free_map.fillFreeMapAndClear(values_to_free);
338 for (const auto& v : values_to_free) {
339 m_allocator->freeMemory(v.first, v.second);
340 }
341}
342
343/*---------------------------------------------------------------------------*/
344/*---------------------------------------------------------------------------*/
345
346/*---------------------------------------------------------------------------*/
347/*---------------------------------------------------------------------------*/
348
349MemoryPool::
350MemoryPool(IMemoryPoolAllocator* allocator, const String& name)
351: m_p(std::make_shared<Impl>(allocator, name))
352{
353}
354
355/*---------------------------------------------------------------------------*/
356/*---------------------------------------------------------------------------*/
357
358MemoryPool::
359~MemoryPool()
360{
361}
362
363/*---------------------------------------------------------------------------*/
364/*---------------------------------------------------------------------------*/
365
366void* MemoryPool::allocateMemory(size_t size)
367{
368 return m_p->allocateMemory(size);
369}
370void MemoryPool::freeMemory(void* ptr, size_t size)
371{
372 m_p->freeMemory(ptr, size);
373}
374void MemoryPool::dumpStats(std::ostream& ostr)
375{
376 m_p->dumpStats(ostr);
377}
378void MemoryPool::
379dumpFreeMap(std::ostream& ostr)
380{
381 m_p->dumpFreeMap(ostr);
382}
383String MemoryPool::name() const
384{
385 return m_p->m_name;
386}
387void MemoryPool::
388setMaxCachedBlockSize(size_t v)
389{
390 m_p->setMaxCachedBlockSize(v);
391}
392void MemoryPool::
393freeCachedMemory()
394{
395 m_p->freeCachedMemory();
396}
397
398/*---------------------------------------------------------------------------*/
399/*---------------------------------------------------------------------------*/
400
401} // namespace Arcane::impl
402
403/*---------------------------------------------------------------------------*/
404/*---------------------------------------------------------------------------*/
#define ARCANE_FATAL(...)
Macro envoyant une exception FatalErrorException.
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.
Classe de base des vecteurs 1D de données.
Chaîne de caractères unicode.