SST  10.0.0
StructuralSimulationToolkit
mempool.h
1 // Copyright 2009-2020 NTESS. Under the terms
2 // of Contract DE-NA0003525 with NTESS, the U.S.
3 // Government retains certain rights in this software.
4 //
5 // Copyright (c) 2009-2020, NTESS
6 // All rights reserved.
7 //
8 // This file is part of the SST software package. For license
9 // information, see the LICENSE file in the top level directory of the
10 // distribution.
11 
12 #ifndef SST_CORE_MEMPOOL_H
13 #define SST_CORE_MEMPOOL_H
14 
15 #include <list>
16 #include <deque>
17 
18 #include <cstddef>
19 #include <cstdlib>
20 #include <cinttypes>
21 #include <cstdint>
22 #include <sys/mman.h>
23 
24 #include "sst/core/threadsafe.h"
25 
26 namespace SST {
27 namespace Core {
28 
29 /**
30  * Simple Memory Pool class
31  */
32 class MemPool
33 {
34  template <typename LOCK_t>
35  class FreeList {
36  LOCK_t mtx;
37  std::vector<void*> list;
38  public:
39  inline void insert(void *ptr) {
40  std::lock_guard<LOCK_t> lock(mtx);
41  list.push_back(ptr);
42  }
43 
44  inline void* try_remove() {
45  std::lock_guard<LOCK_t> lock(mtx);
46  if ( list.empty() ) return nullptr;
47  void *p = list.back();
48  list.pop_back();
49  return p;
50  }
51 
52  size_t size() const { return list.size(); }
53  };
54 
55 
56 public:
57  /** Create a new Memory Pool.
58  * @param elementSize - Size of each Element
59  * @param initialSize - Size of the memory pool (in bytes)
60  */
61  MemPool(size_t elementSize, size_t initialSize=(2<<20)) :
62  numAlloc(0), numFree(0),
63  elemSize(elementSize), arenaSize(initialSize),
64  allocating(false)
65  {
66  allocPool();
67  }
68 
69  ~MemPool()
70  {
71  for ( std::list<uint8_t*>::iterator i = arenas.begin() ; i != arenas.end() ; ++i ) {
72  ::free(*i);
73  }
74  }
75 
76  /** Allocate a new element from the memory pool */
77  inline void* malloc()
78  {
79  void *ret = freeList.try_remove();
80  while ( !ret ) {
81  bool ok = allocPool();
82  if ( !ok ) return nullptr;
83 #if ( defined( __amd64 ) || defined( __amd64__ ) || \
84  defined( __x86_64 ) || defined( __x86_64__ ) )
85  _mm_pause();
86 #elif defined(__PPC64__)
87  asm volatile( "or 27, 27, 27" ::: "memory" );
88 #endif
89  ret = freeList.try_remove();
90  }
91  ++numAlloc;
92  return ret;
93  }
94 
95  /** Return an element to the memory pool */
96  inline void free(void *ptr)
97  {
98  // TODO: Make sure this is in one of our arenas
99  freeList.insert(ptr);
100 // #ifdef __SST_DEBUG_EVENT_TRACKING__
101 // *((uint64_t*)ptr) = 0xFFFFFFFFFFFFFFFF;
102 // #endif
103  ++numFree;
104  }
105 
106  /**
107  Approximates the current memory usage of the mempool. Some
108  overheads are not taken into account.
109  */
110  uint64_t getBytesMemUsed() {
111  uint64_t bytes_in_arenas = arenas.size() * arenaSize;
112  uint64_t bytes_in_free_list = freeList.size() * sizeof(void*);
113  return bytes_in_arenas + bytes_in_free_list;
114  }
115 
116  uint64_t getUndeletedEntries() {
117  return numAlloc - numFree;
118  }
119 
120  /** Counter: Number of times elements have been allocated */
121  std::atomic<uint64_t> numAlloc;
122  /** Counter: Number times elements have been freed */
123  std::atomic<uint64_t> numFree;
124 
125  size_t getArenaSize() const { return arenaSize; }
126  size_t getElementSize() const { return elemSize; }
127 
128  const std::list<uint8_t*>& getArenas() { return arenas; }
129 
130 private:
131 
132  bool allocPool()
133  {
134  /* If already in progress, return */
135  if ( allocating.exchange(1, std::memory_order_acquire) ) {
136  return true;
137  }
138 
139  uint8_t *newPool = (uint8_t*)mmap(nullptr, arenaSize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
140  if ( MAP_FAILED == newPool ) {
141  allocating.store(0, std::memory_order_release);
142  return false;
143  }
144  std::memset(newPool, 0xFF, arenaSize);
145  arenas.push_back(newPool);
146  size_t nelem = arenaSize / elemSize;
147  for ( size_t i = 0 ; i < nelem ; i++ ) {
148  uint64_t* ptr = (uint64_t*)(newPool + (elemSize*i));
149 // #ifdef __SST_DEBUG_EVENT_TRACKING__
150 // *ptr = 0xFFFFFFFFFFFFFFFF;
151 // #endif
152  freeList.insert(ptr);
153  // freeList.push_back(newPool + (elemSize*i));
154  }
155  allocating.store(0, std::memory_order_release);
156  return true;
157  }
158 
159  size_t elemSize;
160  size_t arenaSize;
161 
162  std::atomic<unsigned int> allocating;
163  FreeList<ThreadSafe::Spinlock> freeList;
164  std::list<uint8_t*> arenas;
165 
166 };
167 
168 }
169 }
170 
171 #endif
MemPool(size_t elementSize, size_t initialSize=(2<< 20))
Create a new Memory Pool.
Definition: mempool.h:61
void free(void *ptr)
Return an element to the memory pool.
Definition: mempool.h:96
void * malloc()
Allocate a new element from the memory pool.
Definition: mempool.h:77
std::atomic< uint64_t > numAlloc
Counter: Number of times elements have been allocated.
Definition: mempool.h:121
uint64_t getBytesMemUsed()
Approximates the current memory usage of the mempool.
Definition: mempool.h:110
std::atomic< uint64_t > numFree
Counter: Number times elements have been freed.
Definition: mempool.h:123
Simple Memory Pool class.
Definition: mempool.h:32