ergo
AllocatorManager.h
Go to the documentation of this file.
1 /* Ergo, version 3.7, a program for linear scaling electronic structure
2  * calculations.
3  * Copyright (C) 2018 Elias Rudberg, Emanuel H. Rubensson, Pawel Salek,
4  * and Anastasia Kruchinina.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  *
19  * Primary academic reference:
20  * Ergo: An open-source program for linear-scaling electronic structure
21  * calculations,
22  * Elias Rudberg, Emanuel H. Rubensson, Pawel Salek, and Anastasia
23  * Kruchinina,
24  * SoftwareX 7, 107 (2018),
25  * <http://dx.doi.org/10.1016/j.softx.2018.03.005>
26  *
27  * For further information about Ergo, see <http://www.ergoscf.org>.
28  */
29 
40 #ifndef MAT_ALLOCATORMANAGER_HEADER
41 #define MAT_ALLOCATORMANAGER_HEADER
42 
43 #include <stdexcept>
44 #include <list>
45 #include <string>
46 #include <sstream>
47 #include <iomanip> /* For setprecision */
48 #include <iostream>
49 #include <cstdlib>
50 #include "Allocator.h"
51 
52 namespace mat {
53 
54 template<class Treal>
56 {
57  public:
58  void init(size_t noOfRealsPerBuffer_,
59  size_t noOfBuffers_) {
60  if(noOfRealsPerBuffer != 0) {
61  // This means that the AllocatorManager has already been initialized.
62  // We allow this if the parameters are the same.
63  if(noOfRealsPerBuffer_ != noOfRealsPerBuffer || noOfBuffers_ != noOfBuffers)
64  throw std::runtime_error("Error in AllocatorManager: "
65  "attempt to re-initialize with different parameters.");
66  }
67  if(noOfRealsPerBuffer_ <= 0 || noOfBuffers_ <= 0)
68  throw std::runtime_error("Error in AllocatorManager: bad input to init().");
69  noOfRealsPerBuffer = noOfRealsPerBuffer_;
70  noOfBuffers = noOfBuffers_;
71  }
72  static AllocatorManager & instance();
73  Treal* alloc(size_t n) {
74  if(n != noOfRealsPerBuffer)
75  return new Treal[n];
76  pthread_mutex_lock(&mutex);
77  // Go through list to see if there is any free space.
78  typename std::list< Allocator<Treal>* >::iterator it = list.begin();
79  while(it != list.end()) {
80  if(!(*it)->isFull()) {
81  // OK, found allocator that is not full. Use it.
82  Treal* ptr = (*it)->alloc();
83  pthread_mutex_unlock(&mutex);
84  return ptr;
85  }
86  it++;
87  }
88  // We did not find any non-full existing allocator. Need to add a new one.
89  /* ELIAS NOTE 2016-06-30: Important to catch exceptions here so
90  that we unlock the mutex in case allocation fails, otherwise
91  program may hang later when trying to lock the mutex e.g. to
92  delete something after catching the exception elsewhere in the
93  code. This happened with some test runs on Triolith and it was
94  very annoying, took a long time to figure out why the runs
95  hanged. */
96  Allocator<Treal>* newAllocator;
97  try {
98  newAllocator = new Allocator<Treal>(noOfRealsPerBuffer,
99  noOfBuffers);
100  }
101  catch (const std::bad_alloc & e) {
102  size_t noOfBytesPerAllocator = noOfBuffers * noOfRealsPerBuffer * sizeof(Treal);
103  size_t totNoOfBytesAllocated = list.size() * noOfBytesPerAllocator;
104  std::cerr << "Error in AllocatorManager::alloc(): std::bad_alloc exception caught. Usage before error: list.size() = " << list.size()
105  << " --> " << (double)totNoOfBytesAllocated/1000000000 << " GB used. peakListSize = " << peakListSize << std::endl;
106  pthread_mutex_unlock(&mutex);
107  throw e;
108  }
109  catch(...) {
110  std::cerr << "Error in AllocatorManager::alloc(): exception caught (but not std::bad_alloc!?!)." << std::endl;
111  pthread_mutex_unlock(&mutex);
112  throw std::runtime_error("Error in AllocatorManager::alloc(): exception caught (but not std::bad_alloc!?!).");
113  }
114  list.push_back(newAllocator);
115  if(list.size() > peakListSize)
116  peakListSize = list.size();
117  Treal* ptr = newAllocator->alloc();
118  pthread_mutex_unlock(&mutex);
119  return ptr;
120  }
121  void free(Treal* ptr) {
122  pthread_mutex_lock(&mutex);
123  // Go through list to see if this ptr belongs to any allocator.
124  typename std::list< Allocator<Treal>* >::iterator it = list.begin();
125  while(it != list.end()) {
126  if((*it)->ownsPtr(ptr)) {
127  (*it)->free(ptr);
128  // Now check if allocator is empty; in that case we want to remove it.
129  if((*it)->isEmpty()) {
130  delete *it;
131  list.erase(it);
132  }
133  pthread_mutex_unlock(&mutex);
134  return;
135  }
136  it++;
137  }
138  delete [] ptr;
139  pthread_mutex_unlock(&mutex);
140  }
141  std::string getStatistics() {
142 
143  if(noOfRealsPerBuffer == 0) return "AllocatorManager is not initialized.";
144 
145  size_t noOfBytesPerAllocator = noOfBuffers * noOfRealsPerBuffer * sizeof(Treal);
146  size_t totNoOfBytesAllocated = list.size() * noOfBytesPerAllocator;
147  size_t peakNoOfBytesAllocated = peakListSize * noOfBytesPerAllocator;
148  size_t totNoOfBytesUsed = 0;
149  // Go through list to compute totNoOfBytesUsed
150  typename std::list< Allocator<Treal>* >::iterator it = list.begin();
151  while(it != list.end()) {
152  totNoOfBytesUsed += (size_t)((*it)->getNoOfOccupiedSlots()) * noOfRealsPerBuffer * sizeof(Treal);
153  it++;
154  }
155  std::stringstream ss;
156  ss << "AllocatorManager statistics: ";
157  ss << std::setprecision(3)
158  << " noOfRealsPerBuffer: " << noOfRealsPerBuffer
159  << " noOfBuffers: " << noOfBuffers
160  << " list.size(): " << list.size()
161  << ". "
162  << "Allocated: " << (double)totNoOfBytesAllocated / 1e9 << " GB, "
163  << "Used: " << (double)totNoOfBytesUsed / 1e9 << " GB, "
164  << "Peak alloc: " << (double)peakNoOfBytesAllocated/ 1e9 << " GB.";
165  return ss.str();
166  }
167  private:
169  pthread_mutex_init(&mutex, NULL);
170  }
172  if(!list.empty()) {
173  std::cerr << "Error in AllocatorManager destructor: not empty." << std::endl;
174  abort();
175  }
176  // Go through list to free any allocators that are left.
177  typename std::list< Allocator<Treal>* >::iterator it = list.begin();
178  while(it != list.end()) {
179  delete *it;
180  it++;
181  }
182  }
183  std::list< Allocator<Treal>* > list;
185  size_t noOfBuffers;
186  pthread_mutex_t mutex;
187  size_t peakListSize;
188 }; // end class AllocatorManager
189 
190 } /* end namespace mat */
191 
192 #endif
void init(size_t noOfRealsPerBuffer_, size_t noOfBuffers_)
Definition: AllocatorManager.h:58
AllocatorManager()
Definition: AllocatorManager.h:168
std::string getStatistics()
Definition: AllocatorManager.h:141
Treal * alloc()
Definition: Allocator.h:73
Definition: AllocatorManager.h:55
static AllocatorManager & instance()
pthread_mutex_t mutex
Definition: AllocatorManager.h:186
Code for Allocator class used for memory allocation/deallocation in matrix library.
Definition: allocate.cc:39
void free(Treal *ptr)
Definition: AllocatorManager.h:121
Definition: Allocator.h:51
std::list< Allocator< Treal > *> list
Definition: AllocatorManager.h:183
size_t noOfBuffers
Definition: AllocatorManager.h:185
~AllocatorManager()
Definition: AllocatorManager.h:171
size_t noOfRealsPerBuffer
Definition: AllocatorManager.h:184
Treal * alloc(size_t n)
Definition: AllocatorManager.h:73
size_t peakListSize
Definition: AllocatorManager.h:187