KratosMultiphysics
KRATOS Multiphysics (Kratos) is a framework for building parallel, multi-disciplinary simulation software, aiming at modularity, extensibility, and high performance. Kratos is written in C++, and counts with an extensive Python interface.
thread_fixed_size_memory_pool.h
Go to the documentation of this file.
1 // | / |
2 // ' / __| _` | __| _ \ __|
3 // . \ | ( | | ( |\__ `
4 // _|\_\_| \__,_|\__|\___/ ____/
5 // Multi-Physics
6 //
7 // License: BSD License
8 // Kratos default license: kratos/license.txt
9 //
10 // Main authors: Pooyan Dadvand
11 //
12 //
13 
14 
15 #if !defined(KRATOS_THREAD_FIXED_SIZE_MEMORY_POOL_H_INCLUDED )
16 #define KRATOS_THREAD_FIXED_SIZE_MEMORY_POOL_H_INCLUDED
17 
18 #include <vector>
19 #include <list>
20 #include <algorithm>
21 #include <atomic>
22 
23 #include "concurrentqueue/concurrentqueue.h"
24 
25 #include "includes/chunk.h"
26 
27 namespace Kratos
28 {
29  std::atomic_flag ThreadFixedSizeMemoryPoolLock = ATOMIC_FLAG_INIT ;
30 
33 
36 
38 
45  {
46  public:
49 
51 
52  using ChunkList = std::list<Chunk*>;
53 
55 
56  static constexpr SizeType MaximumEmptyChunksToKeep = 4;
57  static constexpr SizeType MaximumPointersToKeep = 1024;
58 
61 
64 
67 
70 
72  ThreadFixedSizeMemoryPool(std::size_t BlockSizeInBytes, SizeType ChunkSize, std::size_t ThreadNumber)
73  : mBlockSizeInBytes(BlockSizeInBytes)
74  , mChunkSize(ChunkSize)
75  , mThreadNumber(ThreadNumber)
76  , mChunks()
77  , mAvailableChunks()
78  , mNumberOfReleasedChunks(0)
79  , mpCurrentChunk(nullptr)
80  {
81  }
82 
85 
86  }
87 
91 
94 
98 
100  void* Allocate() {
101  void* p_result = nullptr;
102  if(mAvailablePointers.try_dequeue(p_result))
103  return p_result;
104 
105  if(mpCurrentChunk == nullptr){
106  AddChunk();
107  }
108 
109  if (mpCurrentChunk->IsFull())
110  if (!mAvailableChunks.try_dequeue(mpCurrentChunk))
111  AddChunk();
112 
113 
114  if (mpCurrentChunk->IsReleased())
115  mpCurrentChunk->Initialize();
116  KRATOS_CHECK_IS_FALSE(mpCurrentChunk->IsFull());
117  p_result = mpCurrentChunk->Allocate();
118  KRATOS_DEBUG_CHECK_NOT_EQUAL(p_result, nullptr);
119 
120 
121  return p_result;
122  }
123 
124  bool Deallocate(void* pPointerToRelease) {
125  if(mAvailablePointers.size_approx() < MaximumPointersToKeep){
126  mAvailablePointers.enqueue(pPointerToRelease);
127  return true;
128  }
129 
130 
131  if (mpCurrentChunk->Has(pPointerToRelease)) {
132  DeallocateFromAvailableChunk(pPointerToRelease, mpCurrentChunk);
133  return true;
134  }
135 
136  for (auto i_chunk = mChunks.begin(); i_chunk != mChunks.end(); i_chunk++)
137  {
138  if (i_chunk->Has(pPointerToRelease)) {
139  if (i_chunk->IsFull())
140  DeallocateFromFullChunk(pPointerToRelease, &(*i_chunk));
141  else {
142  DeallocateFromAvailableChunk(pPointerToRelease, &(*i_chunk));
143  }
144  return true;
145  }
146  }
147 
148 
149  return false;
150  }
151 
152  void Release() {
153  mChunks.clear();
154  mNumberOfReleasedChunks += mChunks.size();
155  Chunk* p_dummy;
156  while(mAvailableChunks.try_dequeue(p_dummy));
157  }
158 
159  SizeType ChunkSize() const {
160  return mChunkSize;
161  }
162 
163  std::size_t MemoryUsed() const {
164  std::size_t memory_used = sizeof(ThreadFixedSizeMemoryPool) + (mChunks.size() * 2 * sizeof(std::size_t));
165  for (auto i_chunk = mChunks.begin(); i_chunk != mChunks.end(); i_chunk++)
166  memory_used += i_chunk->MemoryUsed();
167  return memory_used;
168  }
169 
170  std::size_t MemoryOverhead() const {
171  std::size_t memory_overhead = sizeof(ThreadFixedSizeMemoryPool) + (mAvailableChunks.size_approx() * sizeof(std::size_t));
172  for (auto i_chunk = mChunks.begin(); i_chunk != mChunks.end(); i_chunk++)
173  memory_overhead += i_chunk->MemoryOverhead();
174  return memory_overhead;
175  }
176 
177  void lock(){
178  while(std::atomic_flag_test_and_set_explicit(&ThreadFixedSizeMemoryPoolLock, std::memory_order_acquire))
179  ; // spin until the lock is acquired
180  }
181 
182  void unlock(){
183  std::atomic_flag_clear_explicit(&ThreadFixedSizeMemoryPoolLock, std::memory_order_release);
184  }
185 
189 
190  std::size_t GetNumberOfChunks() const {
191  return mChunks.size();
192  }
193 
194  std::size_t GetNumberOfAvailableChunks() const {
195  return mAvailableChunks.size_approx();
196  }
197 
198  std::size_t GetNumberOfReleasedChunks() const {
199  return mNumberOfReleasedChunks;
200  }
201 
205 
207  return !(mAvailableChunks.size_approx() == 0);
208  }
209 
210 
214 
216  std::string Info() const {
217  return "ThreadFixedSizeMemoryPool";
218  }
219 
221  void PrintInfo(std::ostream& rOStream) const {
222  rOStream << Info();
223  }
224 
226  void PrintData(std::ostream& rOStream) const {
227  std::size_t memory_used = MemoryUsed();
228  std::size_t memory_overhead = MemoryOverhead();
229  double overhead_percentage = memory_overhead;
230  if (memory_overhead < memory_used)
231  overhead_percentage = static_cast<double>(memory_overhead)/(memory_used - memory_overhead);
232  overhead_percentage *= 100.00;
233 
234  rOStream << GetNumberOfChunks() << " Chunks of "
235  << SizeInBytesToString(ChunkSize()) << " bytes each. Total memory usage: "
236  << SizeInBytesToString(MemoryUsed()) << " bytes and memory overhead "
237  << SizeInBytesToString(MemoryOverhead()) << "(" << overhead_percentage << "%)" << std::endl;
238  }
239 
241 
242 
243  private:
246 
247  std::size_t mBlockSizeInBytes;
248  SizeType mChunkSize;
249  std::size_t mThreadNumber;
250  std::list<Chunk> mChunks;
251  moodycamel::ConcurrentQueue<Chunk*> mAvailableChunks;
252  std::size_t mNumberOfReleasedChunks;
253  moodycamel::ConcurrentQueue<void*> mAvailablePointers;
254  Chunk* mpCurrentChunk= nullptr;
255 
256 
260 
261  void AddChunk() {
262  if (mThreadNumber != static_cast<std::size_t>(OpenMPUtils::ThisThread()))
263  KRATOS_ERROR << "Trying to add chunk in thread " << mThreadNumber << " pool by thread " << static_cast<std::size_t>(OpenMPUtils::ThisThread());
264 
265  KRATOS_DEBUG_CHECK_EQUAL(mThreadNumber, static_cast<std::size_t>(OpenMPUtils::ThisThread()));
266 
267  mChunks.emplace_back(mBlockSizeInBytes, mChunkSize);
268  mpCurrentChunk = &(mChunks.back());
269  mpCurrentChunk->Initialize();
270  // std::cout << "crating " << *p_available_chunk << std::endl;
271  }
272 
273  void DeallocateFromAvailableChunk(void* pPointrerToRelease, Chunk* pChunk) {
274  pChunk->Deallocate(pPointrerToRelease);
275  if (pChunk->IsEmpty())
276  if (mAvailableChunks.size_approx() - mNumberOfReleasedChunks > MaximumEmptyChunksToKeep)
277  ReleaseChunk(pChunk);
278  }
279 
280  void DeallocateFromFullChunk(void* pPointrerToRelease, Chunk* pChunk) {
281  // It will be available after deallocating but is not in the list yet
282  mAvailableChunks.enqueue(pChunk);
283  pChunk->Deallocate(pPointrerToRelease);
284  if (pChunk->IsEmpty()) // a rare case where a chunk has only one block! simptom of bad configuration
285  if (mAvailableChunks.size_approx() - mNumberOfReleasedChunks > MaximumEmptyChunksToKeep)
286  ReleaseChunk(pChunk);
287  }
288 
289  void ReleaseChunk(Chunk* pChunk) {
290  pChunk->Release();
291  // mAvailableChunks.splice(mAvailableChunks.end(), mAvailableChunks, iChunk);
292  mNumberOfReleasedChunks++;
293  }
294 
295  // std::list<Chunk*> const& GetAvailableChunks() {
296  // return mAvailableChunks;
297  // }
298 
299  std::string SizeInBytesToString(std::size_t Bytes) const {
300  std::stringstream buffer;
301  double result = Bytes;
302  constexpr int units_size = 5;
303  constexpr char units[units_size] = { ' ', 'k','M','G','T' };
304  int i = 0;
305  for (; i < units_size; i++)
306  if (result > 1024)
307  {
308  result /= 1024;
309  }
310  else
311  break;
312  buffer << result << units[i];
313 
314  return buffer.str();
315  }
316 
317 
319 
320  }; // Class ThreadFixedSizeMemoryPool
321 
325 
326 
328  inline std::ostream& operator << (std::ostream& rOStream,
329  const ThreadFixedSizeMemoryPool& rThis)
330  {
331  rThis.PrintInfo(rOStream);
332  rOStream << std::endl;
333  rThis.PrintData(rOStream);
334 
335  return rOStream;
336  }
338 
340 
341 } // namespace Kratos.
342 
343 #endif // KRATOS_THREAD_FIXED_SIZE_MEMORY_POOL_H_INCLUDED defined
Chunk is the smallest building block of Kratos memory management.
Definition: chunk.h:45
bool IsFull()
Definition: chunk.h:230
void * Allocate()
This function does not throw and returns zero if cannot allocate.
Definition: chunk.h:110
bool IsReleased() const
Definition: chunk.h:238
std::size_t SizeType
Definition: chunk.h:56
bool Has(const void *pThePointer) const
Definition: chunk.h:222
void Initialize()
Definition: chunk.h:97
static int ThisThread()
Wrapper for omp_get_thread_num().
Definition: openmp_utils.h:108
ThreadFixedSizeMemoryPool holds chunks belong to a certain thread and operate over them.
Definition: thread_fixed_size_memory_pool.h:45
std::size_t GetNumberOfReleasedChunks() const
Definition: thread_fixed_size_memory_pool.h:198
ThreadFixedSizeMemoryPool(ThreadFixedSizeMemoryPool &&rOther)=default
Move constructor to be used in STL containers.
void PrintData(std::ostream &rOStream) const
Print object's data.
Definition: thread_fixed_size_memory_pool.h:226
ThreadFixedSizeMemoryPool(std::size_t BlockSizeInBytes, SizeType ChunkSize, std::size_t ThreadNumber)
The constructor to be called.
Definition: thread_fixed_size_memory_pool.h:72
void Release()
Definition: thread_fixed_size_memory_pool.h:152
void lock()
Definition: thread_fixed_size_memory_pool.h:177
std::size_t MemoryUsed() const
Definition: thread_fixed_size_memory_pool.h:163
ThreadFixedSizeMemoryPool(ThreadFixedSizeMemoryPool const &rOther)=delete
Copy constructor is deleted.
std::size_t GetNumberOfChunks() const
Definition: thread_fixed_size_memory_pool.h:190
bool HasAvailableChunk()
Definition: thread_fixed_size_memory_pool.h:206
virtual ~ThreadFixedSizeMemoryPool()
Destructor.
Definition: thread_fixed_size_memory_pool.h:84
std::string Info() const
Turn back information as a string.
Definition: thread_fixed_size_memory_pool.h:216
static constexpr SizeType MaximumEmptyChunksToKeep
Definition: thread_fixed_size_memory_pool.h:56
void PrintInfo(std::ostream &rOStream) const
Print information about this object.
Definition: thread_fixed_size_memory_pool.h:221
bool Deallocate(void *pPointerToRelease)
Definition: thread_fixed_size_memory_pool.h:124
Chunk::SizeType SizeType
Definition: thread_fixed_size_memory_pool.h:50
std::size_t GetNumberOfAvailableChunks() const
Definition: thread_fixed_size_memory_pool.h:194
static constexpr SizeType MaximumPointersToKeep
Definition: thread_fixed_size_memory_pool.h:57
SizeType ChunkSize() const
Definition: thread_fixed_size_memory_pool.h:159
std::size_t MemoryOverhead() const
Definition: thread_fixed_size_memory_pool.h:170
ThreadFixedSizeMemoryPool()=delete
Default constructor is deleted.
void * Allocate()
This function does not throw and returns zero if cannot allocate.
Definition: thread_fixed_size_memory_pool.h:100
ThreadFixedSizeMemoryPool & operator=(ThreadFixedSizeMemoryPool const &rOther)=delete
Assignment operator is deleted.
void unlock()
Definition: thread_fixed_size_memory_pool.h:182
std::list< Chunk * > ChunkList
Definition: thread_fixed_size_memory_pool.h:52
#define KRATOS_DEBUG_CHECK_EQUAL(a, b)
Definition: checks.h:217
#define KRATOS_ERROR
Definition: exception.h:161
#define KRATOS_CHECK_IS_FALSE(IsFalse)
Definition: checks.h:32
#define KRATOS_DEBUG_CHECK_NOT_EQUAL(a, b)
Definition: checks.h:218
REF: G. R. Cowper, GAUSSIAN QUADRATURE FORMULAS FOR TRIANGLES.
Definition: mesh_condition.cpp:21
std::size_t SizeType
The definition of the size type.
Definition: mortar_classes.h:43
std::ostream & operator<<(std::ostream &rOStream, const LinearMasterSlaveConstraint &rThis)
output stream function
Definition: linear_master_slave_constraint.h:432
std::atomic_flag ThreadFixedSizeMemoryPoolLock
Definition: thread_fixed_size_memory_pool.h:29
integer i
Definition: TensorModule.f:17