1#ifndef MEMORYLOG_HEADER
2#define MEMORYLOG_HEADER
30#ifndef MIN_BYTES_TO_LOG
31#define MIN_BYTES_TO_LOG 0
33#ifndef MAX_ALLOCATIONS_IN_MEMORY
34#define MAX_ALLOCATIONS_IN_MEMORY 100000
38 using TimePoint = std::chrono::time_point<std::chrono::steady_clock>;
56 std::map<void *, size_t> allocations{};
60 std::map<void *, std::string> labels{};
63 std::map<TimePoint, size_t> memory_vs_time{};
66 size_t memory_in_use{0};
67 size_t peak_memory_use{0};
70 size_t min_bytes_to_log = MIN_BYTES_TO_LOG;
71 size_t max_allocations_to_log = MAX_ALLOCATIONS_IN_MEMORY;
72 size_t nallocation{0};
73 bool stop_logging{
false};
77 time_start = std::chrono::steady_clock::now();
79 memory_vs_time[time_start] = 0;
101 if (allocations.find(ptr) == allocations.end()) {
102 std::cout <<
"[MemoryLogging] Warning could not find pointer in allocation list to add label " + name +
106 std::lock_guard<std::mutex> guard(mymutex);
109 void add_label(
void * ptr,
size_t size, std::string name) {
110 if (stop_logging or size < min_bytes_to_log)
115 void add(
void * ptr,
size_t size) {
116 if (stop_logging or size < min_bytes_to_log)
118 std::lock_guard<std::mutex> guard(mymutex);
119 allocations[ptr] = size;
121 memory_in_use += size;
122 if (memory_in_use > peak_memory_use)
123 peak_memory_use = memory_in_use;
125 auto time = std::chrono::steady_clock::now();
126 memory_vs_time[time] = memory_in_use;
130 stop_logging = nallocation >= max_allocations_to_log;
135 memory_vs_time.clear();
140 if (stop_logging or size < min_bytes_to_log)
143#ifdef DEBUG_MEMORYLOG
144 std::string name = labels[ptr];
146 std::cout <<
"===> MemoryLog::remove Freeing[" << name <<
"]" << std::endl;
148 std::lock_guard<std::mutex> guard(mymutex);
149 allocations.erase(ptr);
152 memory_in_use -= size;
154 auto time = std::chrono::steady_clock::now();
155 memory_vs_time[time] = memory_in_use;
161 char ok = stop_logging ? 0 : 1;
163 MPI_Allreduce(MPI_IN_PLACE, &ok, 1, MPI_CHAR, MPI_MIN, MPI_COMM_WORLD);
170 std::cout <<
"Warning: The memory log has stopped working "
171 <<
" due to max number of allocation having been reached\n";
177 double min_sys_vmemory = sysmem.first;
178 double max_sys_vmemory = sysmem.first;
179 double mean_sys_vmemory = sysmem.first;
180 double min_sys_rssmemory = sysmem.second;
181 double max_sys_rssmemory = sysmem.second;
182 double mean_sys_rssmemory = sysmem.second;
185 MPI_Allreduce(MPI_IN_PLACE, &min_sys_vmemory, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
186 MPI_Allreduce(MPI_IN_PLACE, &max_sys_vmemory, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
187 MPI_Allreduce(MPI_IN_PLACE, &mean_sys_vmemory, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
188 MPI_Allreduce(MPI_IN_PLACE, &min_sys_rssmemory, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
189 MPI_Allreduce(MPI_IN_PLACE, &max_sys_rssmemory, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
190 MPI_Allreduce(MPI_IN_PLACE, &mean_sys_rssmemory, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
192 mean_sys_vmemory /=
NTasks;
193 mean_sys_rssmemory /=
NTasks;
195 std::cout <<
"\n#=====================================================\n";
196 std::cout <<
"# MemoryLogging\n";
197 std::cout <<
"#=====================================================\n\n";
198 std::cout <<
"Info about resident set size:\n";
199 std::cout <<
"[Current resident set size]:\n";
200 std::cout <<
"Min over tasks: " << std::setw(15) << min_sys_vmemory / 1.0e6 <<
" MB\n";
201 std::cout <<
"Mean over tasks: " << std::setw(15) << mean_sys_vmemory / 1.0e6 <<
" MB\n";
202 std::cout <<
"Max over tasks: " << std::setw(15) << max_sys_vmemory / 1.0e6 <<
" MB\n";
203 std::cout <<
"[Peak resident set size]:\n";
204 std::cout <<
"Min over tasks: " << std::setw(15) << min_sys_rssmemory / 1.0e6 <<
" MB\n";
205 std::cout <<
"Mean over tasks: " << std::setw(15) << mean_sys_rssmemory / 1.0e6 <<
" MB\n";
206 std::cout <<
"Max over tasks: " << std::setw(15) << max_sys_rssmemory / 1.0e6 <<
" MB\n";
211 std::cout <<
"For info below we are only tracking allocation of standard\n";
212 std::cout <<
"container (Vector) and only allocations larger than " << min_bytes_to_log <<
" bytes\n\n";
215 long long int min_memory = memory_in_use;
216 long long int max_memory = memory_in_use;
217 long long int mean_memory = memory_in_use;
218 long long int peak_memory = peak_memory_use;
220 MPI_Allreduce(MPI_IN_PLACE, &min_memory, 1, MPI_LONG_LONG, MPI_MIN, MPI_COMM_WORLD);
221 MPI_Allreduce(MPI_IN_PLACE, &max_memory, 1, MPI_LONG_LONG, MPI_MAX, MPI_COMM_WORLD);
222 MPI_Allreduce(MPI_IN_PLACE, &mean_memory, 1, MPI_LONG_LONG, MPI_SUM, MPI_COMM_WORLD);
223 MPI_Allreduce(MPI_IN_PLACE, &peak_memory, 1, MPI_LONG_LONG, MPI_MAX, MPI_COMM_WORLD);
227 std::cout <<
"Memory in use (Task 0): " << std::setw(15) << double(memory_in_use) / 1.0e6 <<
" MB\n";
228 std::cout <<
"Min over tasks: " << std::setw(15) << double(min_memory) / 1.0e6 <<
" MB\n";
229 std::cout <<
"Mean over tasks: " << std::setw(15) << double(mean_memory) / 1.0e6 <<
" MB\n";
230 std::cout <<
"Max over tasks: " << std::setw(15) << double(max_memory) / 1.0e6 <<
" MB\n";
232 std::cout <<
"Peak memory use (Task 0): " << std::setw(15) << double(peak_memory_use) / 1.0e6
234 std::cout <<
"Max over tasks: " << std::setw(15) << double(peak_memory) / 1.0e6 <<
" MB\n";
237 if (not allocations.empty()) {
239 std::cout <<
"\nWe have the following things allocated on task 0: \n";
241 for (
auto && a : allocations) {
243 std::string name =
"";
244 if (labels.find(a.first) != labels.end())
245 name = labels[a.first];
246 std::string bytelabel =
" (MB)";
248 std::cout <<
"Address: " << a.first <<
" Size: " << double(a.second) / factor << bytelabel
249 <<
" Label: " << name <<
"\n";
255 if (not memory_vs_time.empty()) {
257 std::cout <<
"Total memory as function of time:\n";
258 for (
auto && m : memory_vs_time) {
260 std::chrono::duration_cast<std::chrono::duration<double>>(m.first - time_start).count();
262 std::cout <<
" Time (sec): " << std::setw(13) << time_in_sec;
263 std::string bytelabel =
" (MB)";
265 std::cout <<
" Memory: " << std::setw(13) << double(m.second) / factor << bytelabel <<
"\n";
270 std::cout <<
"\n#=====================================================\n";
271 std::cout << std::flush;
277 template <
typename T>
286 if (size <= std::numeric_limits<std::size_t>::max() /
sizeof(T)) {
287 if (
auto ptr = std::malloc(size *
sizeof(T))) {
289 return static_cast<T *
>(ptr);
292 std::cout <<
"[LogAllocator] Allocation of " << size <<
" elements of type " <<
typeid(T).name()
293 <<
" of size " <<
sizeof(T) <<
" failed" << std::endl;
294 throw std::bad_alloc();
302 template <
typename T,
typename U>
307 template <
typename T,
typename U>
void free(HaloModel **hm)
Logs all heap allocations in the code that are larger than min_bytes_to_log.
void add(void *ptr, size_t size)
MemoryLog & operator=(const MemoryLog &arg)=delete
MemoryLog & operator=(const MemoryLog &&arg)=delete
MemoryLog(const MemoryLog &arg)=delete
void add_label(void *ptr, std::string name)
void remove(void *ptr, size_t size)
MemoryLog(const MemoryLog &&arg)=delete
void add_label(void *ptr, size_t size, std::string name)
std::pair< double, double > get_system_memory_use()
Fetch the resident set size currently and the peak value so far of it.
bool operator==(const LogAllocator< T > &, const LogAllocator< U > &)
int ThisTask
The MPI task number.
std::chrono::time_point< std::chrono::steady_clock > TimePoint
bool operator!=(const LogAllocator< T > &a, const LogAllocator< U > &b)
int NTasks
Total number of MPI tasks.
Custom allocator that logs allocations.
LogAllocator(const LogAllocator< U > &)
void deallocate(T *ptr, std::size_t size)
T * allocate(std::size_t size)