Mantid
Loading...
Searching...
No Matches
Memory.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
9#include "MantidKernel/DllConfig.h"
10#include "MantidKernel/Logger.h"
11
12#include <cstdio>
13#include <sstream>
14
15#ifdef __linux__
16#include <fstream>
17#include <malloc.h>
18#include <sys/resource.h>
19#include <unistd.h>
20#endif
21#ifdef __APPLE__
22#include <mach/mach.h>
23#include <mach/mach_host.h>
24#include <mach/task.h>
25#include <malloc/malloc.h>
26#include <sys/sysctl.h>
27#endif
28#ifdef _WIN32
29#include <Psapi.h>
30#include <windows.h>
31#endif
32
33using std::size_t;
34using std::string;
35
36namespace Mantid::Kernel {
37namespace {
39Logger g_log("Memory");
40} // namespace
41
43template <typename TYPE> string memToString(const TYPE mem_in_kiB) {
44 std::stringstream buffer;
45 if (mem_in_kiB < static_cast<TYPE>(1024))
46 buffer << mem_in_kiB << " kB";
47 else if (mem_in_kiB < static_cast<TYPE>(100 * 1024 * 1024))
48 buffer << (mem_in_kiB / static_cast<TYPE>(1024)) << " MB";
49 else
50 buffer << (mem_in_kiB / static_cast<TYPE>(1024 * 1024)) << " GB";
51 return buffer.str();
52}
53
54// -------------------- functions for getting the memory associated with the
55// process
62void process_mem_usage(size_t &vm_usage, size_t &resident_set) {
63 vm_usage = 0;
64 resident_set = 0;
65
66#ifdef __linux__
67 // Adapted from
68 // http://stackoverflow.com/questions/669438/how-to-get-memory-usage-at-run-time-in-c
69 using std::ifstream;
70 using std::ios_base;
71
72 // 'file' stat seems to give the most reliable results
73 ifstream stat_stream("/proc/self/stat", ios_base::in);
74
75 // dummy vars for leading entries in stat that we don't care about
76 string pid, comm, state, ppid, pgrp, session, tty_nr;
77 string tpgid, flags, minflt, cminflt, majflt, cmajflt;
78 string utime, stime, cutime, cstime, priority, nice;
79 string O, itrealvalue, starttime;
80
81 // the two fields we want
82 unsigned long vsize; // according to man this is %lu
83 long rss; // according to man this is %ld
84
85 stat_stream >> pid >> comm >> state >> ppid >> pgrp >> session >> tty_nr >> tpgid >> flags >> minflt >> cminflt >>
86 majflt >> cmajflt >> utime >> stime >> cutime >> cstime >> priority >> nice >> O >> itrealvalue >> starttime >>
87 vsize >> rss; // don't care about the rest
88
89 long page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024; // in case x86-64 is configured to use 2MB pages
90 vm_usage = static_cast<size_t>(vsize / static_cast<long double>(1024.0));
91 resident_set = static_cast<size_t>(rss * page_size_kb);
92#elif __APPLE__
93 // Adapted from http://blog.kuriositaet.de/?p=257. No official apple docs
94 // could be found
95 // task_t task = MACH_PORT_NULL;
96 struct task_basic_info t_info;
97 mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
98
99 if (KERN_SUCCESS !=
100 task_info(mach_task_self(), TASK_BASIC_INFO, reinterpret_cast<task_info_t>(&t_info), &t_info_count)) {
101 return;
102 }
103 // Need to find out the system page size for next part
104 vm_size_t pageSize;
105 mach_port_t port = mach_host_self();
106 host_page_size(port, &pageSize);
107 resident_set = static_cast<size_t>(t_info.resident_size * pageSize);
108 vm_usage = static_cast<size_t>(t_info.virtual_size * pageSize / 1024L);
109#elif _WIN32
110 // Adapted from
111 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682050%28v=vs.85%29.aspx
112 DWORD pid = GetCurrentProcessId();
113 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
114 if (NULL == hProcess)
115 return;
116 PROCESS_MEMORY_COUNTERS pmc;
117 if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) {
118 vm_usage = pmc.PagefileUsage / 1024;
119 resident_set = pmc.WorkingSetSize / 1024;
120 }
121 CloseHandle(hProcess);
122#endif
123}
124
125// ----------------------- functions associated with getting the memory of the
126// system
127
128#ifdef __linux__
136bool read_mem_info(size_t &sys_avail, size_t &sys_total) {
137 std::ifstream file("/proc/meminfo");
138 string line;
139 int values_found(0);
140 // Need to set this to zero
141 sys_avail = 0;
142 while (getline(file, line)) {
143 std::istringstream is(line);
144 string tag;
145 long value(0);
146 is >> tag >> value;
147 if (!is)
148 return false;
149 if (tag == "MemTotal:") {
150 ++values_found;
151 sys_total = value;
152 } else if (tag == "MemFree:") {
153 ++values_found;
154 sys_avail += value;
155 } else if (tag == "Cached:") {
156 ++values_found;
157 sys_avail += value;
158 } else if (tag == "Buffers:") {
159 ++values_found;
160 sys_avail += value;
161 } else
162 continue;
163 if (values_found == 4) {
164 file.close();
165 return true;
166 }
167 }
168 file.close();
169 return false;
170}
171#endif
172
173#ifdef _WIN32
174namespace { // Anonymous namespace
175
176MEMORYSTATUSEX
177memStatus;
178} // namespace
179#endif
180
187void MemoryStats::process_mem_system(size_t &sys_avail, size_t &sys_total) {
188 sys_avail = 0;
189 sys_total = 0;
190#ifdef __linux__
191 /*
192 * Taken from API/MemoryManager.cpp_LINUX
193 *
194 * As usual things are more complex on Linux. I think we need to
195 * take into account the value of Cached as well since, especially
196 * if the system has been running for a long time, MemFree will seem
197 * a lot smaller than it should be. To be completely correct we also
198 * need to add the value of the "Buffers" as well.
199 *
200 * The only way I can see as to get acces to the Cached value is
201 * from the /proc/meminfo file so if this is not successful I'll
202 * fall back to using the sysconf method and forget the cache
203 * RJT(18/2/10) : Should we be using sysinfo() here?
204 */
205 if (!read_mem_info(sys_avail, sys_total)) {
206 long int totPages = sysconf(_SC_PHYS_PAGES);
207 long int avPages = sysconf(_SC_AVPHYS_PAGES);
208 long int pageSize = sysconf(_SC_PAGESIZE);
209 if (totPages < 0)
210 totPages = 0;
211 if (avPages < 0)
212 totPages = 0;
213 if (pageSize < 1)
214 pageSize = 1;
215 // Commented out the next line as the value was being written by the one
216 // after
217 // sys_avail = avPages / 1024 * pageSize;
218 sys_avail = totPages / 1024 * pageSize;
219 }
220// Can get the info on the memory that we've already obtained but aren't using
221// right now
222#ifdef __GLIBC_MINOR__
223#if __GLIBC_MINOR__ >= 33 // mallinfo2 available since glibc 2.33
224 auto info = mallinfo2();
225#else
226 auto info = mallinfo();
227#endif
228#else
229 auto info = mallinfo();
230#endif
231 // Now add in reserved but unused memory as reported by malloc
232 const size_t unusedReserved = info.fordblks / 1024;
233 g_log.debug() << "Linux - Adding reserved but unused memory of " << unusedReserved << " KB\n";
234 sys_avail += unusedReserved;
235
236#elif __APPLE__
237 // Get the total RAM of the system
238 uint64_t totalmem;
239 size_t len = sizeof(totalmem);
240 // Gives system memory in bytes
241 int err = sysctlbyname("hw.memsize", &totalmem, &len, nullptr, 0);
242 if (err)
243 g_log.warning("Unable to obtain memory of system");
244 sys_total = totalmem / 1024;
245
246 mach_port_t port = mach_host_self();
247 // Need to find out the system page size for next part
248 vm_size_t pageSize;
249 host_page_size(port, &pageSize);
250
251 // Now get the amount of free memory (=free+inactive memory)
252 vm_statistics vmStats;
253 mach_msg_type_number_t count;
254 count = sizeof(vm_statistics) / sizeof(natural_t);
255 err = host_statistics(port, HOST_VM_INFO, reinterpret_cast<host_info_t>(&vmStats), &count);
256 if (err)
257 g_log.warning("Unable to obtain memory statistics for this Mac.");
258 sys_avail = pageSize * (vmStats.free_count + vmStats.inactive_count) / 1024;
259
260 // Now add in reserved but unused memory as reported by malloc
261 const size_t unusedReserved = mstats().bytes_free / 1024;
262 g_log.debug() << "Mac - Adding reserved but unused memory of " << unusedReserved << " KB\n";
263 sys_avail += unusedReserved;
264#elif _WIN32
265 GlobalMemoryStatusEx(&memStatus);
266 if (memStatus.ullTotalPhys < memStatus.ullTotalVirtual) {
267 sys_avail = static_cast<size_t>(memStatus.ullAvailPhys / 1024);
268 sys_total = static_cast<size_t>(memStatus.ullTotalPhys / 1024);
269 } else // All virtual memory will be physical, but a process cannot have more
270 // than TotalVirtual.
271 {
272 sys_avail = static_cast<size_t>(memStatus.ullAvailVirtual / 1024);
273 sys_total = static_cast<size_t>(memStatus.ullTotalVirtual / 1024);
274 }
275#endif
276
277 g_log.debug() << "Memory: " << sys_avail << " (free), " << sys_total << " (total).\n";
278}
279
289 static bool initialized(false);
290 if (initialized)
291 return;
292#ifdef __linux__
293 /* The line below tells malloc to use a different memory allocation system
294 * call (mmap) to the 'usual'
295 * one (sbrk) for requests above the threshold of the second argument (in
296 * bytes). The effect of this
297 * is that, for the current threshold value of 8*4096, storage for workspaces
298 * having 4096 or greater
299 * bins per spectrum will be allocated using mmap.
300 * This should have the effect that memory is returned to the kernel as soon
301 * as a workspace is deleted, preventing things going to managed workspaces
302 * when they shouldn't. This will also hopefully reduce memory fragmentation.
303 * Potential downsides to look out for are whether this memory allocation
304 * technique makes things
305 * noticeably slower and whether it wastes memory (mmap allocates in blocks of
306 * the system page size.
307 */
308 mallopt(M_MMAP_THRESHOLD, 8 * 4096);
309#elif _WIN32
310 Logger memOptLogger("MemoryOptions");
311 // Try to enable the Low Fragmentation Heap for all heaps
312 // Bit of a brute force approach, but don't know which heap workspace data
313 // ends up on
314 HANDLE hHeaps[1025];
315 // Get the number of heaps
316 const DWORD numHeap = GetProcessHeaps(1024, hHeaps);
317 memOptLogger.debug() << "Number of heaps: " << numHeap << "\n"; // GetProcessHeaps(0, NULL) << "\n";
318 ULONG ulEnableLFH = 2; // 2 = Low Fragmentation Heap
319 for (DWORD i = 0; i < numHeap; i++) {
320 if (!HeapSetInformation(hHeaps[i], HeapCompatibilityInformation, &ulEnableLFH, sizeof(ulEnableLFH))) {
321 memOptLogger.debug() << "Failed to enable the LFH for heap " << i << "\n";
322 }
323 }
324#endif
325 initialized = true;
326}
327
328// ------------------ The actual class ----------------------------------------
329
334MemoryStats::MemoryStats(const MemoryStatsIgnore ignore) : vm_usage(0), res_usage(0), total_memory(0), avail_memory(0) {
335
336#ifdef _WIN32
337 memStatus.dwLength = sizeof(MEMORYSTATUSEX);
338#endif
339
340 this->ignoreFields(ignore);
341 this->update();
342}
343
350 std::lock_guard<std::mutex> lock(MemoryStats::mutexMemory);
351 // get what is used by the process
352 if (this->ignore != MEMORY_STATS_IGNORE_PROCESS) {
354 }
355
356 // get the system information
357 if (this->ignore != MEMORY_STATS_IGNORE_SYSTEM) {
359 }
360}
361
366void MemoryStats::ignoreFields(const MemoryStatsIgnore ignore) { this->ignore = ignore; }
367
372string MemoryStats::vmUsageStr() const { return memToString(this->vm_usage); }
373
378string MemoryStats::resUsageStr() const { return memToString(this->res_usage); }
379
384string MemoryStats::totalMemStr() const { return memToString(this->total_memory); }
385
390string MemoryStats::availMemStr() const { return memToString(this->avail_memory); }
391
397size_t MemoryStats::totalMem() const { return this->total_memory; }
398
404size_t MemoryStats::availMem() const { return this->avail_memory; }
405
410size_t MemoryStats::residentMem() const { return this->res_usage; }
411
418size_t MemoryStats::virtualMem() const { return this->vm_usage; }
419
432#ifdef _WIN32
433 MEMORY_BASIC_INFORMATION info; // Windows structure
434 char *addr = NULL;
435 size_t unusedReserved = 0; // total reserved space
436 DWORDLONG size = 0;
437 GlobalMemoryStatusEx(&memStatus);
438 DWORDLONG GB2 = memStatus.ullTotalVirtual; // Maximum memory available to the process
439
440 // Loop over all virtual memory to find out the status of every block.
441 do {
442 VirtualQuery(addr, &info, sizeof(MEMORY_BASIC_INFORMATION));
443
444 // Count up the total size of reserved but unused blocks
445 if (info.State == MEM_RESERVE)
446 unusedReserved += info.RegionSize;
447
448 addr += info.RegionSize; // Move up to the starting address for the next call
449 size += info.RegionSize;
450 } while (size < GB2);
451
452 // Convert from bytes to KB
453 unusedReserved /= 1024;
454
455 return unusedReserved;
456#else
457 return 0;
458#endif
459}
460
466 return 100. * static_cast<double>(this->avail_memory) / static_cast<double>(this->total_memory);
467}
468
470std::ostream &operator<<(std::ostream &out, const MemoryStats &stats) {
471 if (stats.ignore != MEMORY_STATS_IGNORE_PROCESS) {
472 out << "virtual[" << stats.vmUsageStr() << "] ";
473 out << "resident[" << stats.resUsageStr() << "] ";
474 }
475 if (stats.ignore != MEMORY_STATS_IGNORE_SYSTEM) {
476 out << "available[" << stats.availMemStr() << "] ";
477 out << "total[" << stats.totalMemStr() << "] ";
478 }
479 return out;
480}
481
491#if defined(_WIN32)
492 /* Windows -------------------------------------------------- */
493 PROCESS_MEMORY_COUNTERS info;
494 GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
495 return (size_t)info.PeakWorkingSetSize;
496
497#elif (defined(_AIX) || defined(__TOS__AIX__)) || \
498 (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__)))
499 /* AIX and Solaris ------------------------------------------ */
500 struct psinfo psinfo;
501 int fd = -1;
502 if ((fd = open("/proc/self/psinfo", O_RDONLY)) == -1)
503 return (size_t)0L; /* Can't open? */
504 if (read(fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) {
505 close(fd);
506 return (size_t)0L; /* Can't read? */
507 }
508 close(fd);
509 return (size_t)(psinfo.pr_rssize * 1024L);
510
511#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
512 /* BSD, Linux, and OSX -------------------------------------- */
513 struct rusage rusage;
514 getrusage(RUSAGE_SELF, &rusage);
515#if defined(__APPLE__) && defined(__MACH__)
516 return static_cast<size_t>(rusage.ru_maxrss);
517#else
518 return (size_t)(rusage.ru_maxrss * 1024L);
519#endif
520
521#else
522 /* Unknown OS ----------------------------------------------- */
523 return (size_t)0L; /* Unsupported. */
524#endif
525}
526
535#if defined(_WIN32)
536 /* Windows -------------------------------------------------- */
537 PROCESS_MEMORY_COUNTERS info;
538 GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
539 return (size_t)info.WorkingSetSize;
540
541#elif defined(__APPLE__) && defined(__MACH__)
542 /* OSX ------------------------------------------------------ */
543 struct mach_task_basic_info info;
544 mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT;
545 if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, reinterpret_cast<task_info_t>(&info), &infoCount) !=
546 KERN_SUCCESS)
547 return static_cast<size_t>(0L); /* Can't access? */
548 return static_cast<size_t>(info.resident_size);
549
550#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
551 /* Linux ---------------------------------------------------- */
552 long rss = 0L;
553 FILE *fp = nullptr;
554 if ((fp = fopen("/proc/self/statm", "r")) == nullptr)
555 return (size_t)0L; /* Can't open? */
556 if (fscanf(fp, "%*s%20ld", &rss) != 1) {
557 fclose(fp);
558 return (size_t)0L; /* Can't read? */
559 }
560 fclose(fp);
561 return (size_t)rss * (size_t)sysconf(_SC_PAGESIZE);
562
563#else
564 /* AIX, BSD, Solaris, and Unknown OS ------------------------ */
565 return (size_t)0L; /* Unsupported. */
566#endif
567}
568
573std::string MemoryStats::checkAvailableMemory(std::size_t const requestedMemoryBytes) const {
574 if (!memoryCheckEnabled())
575 return "";
576
577 std::string errorString;
578 const auto availableMemory = this->availMem() * 1024; // Convert from KiB to bytes
579 if (requestedMemoryBytes > availableMemory) {
580 double constexpr bytesToGB = 1e9;
581 double requestedGB = static_cast<double>(requestedMemoryBytes) / bytesToGB;
582 double availableGB = static_cast<double>(availableMemory) / bytesToGB;
583 errorString = "Requested " + std::to_string(requestedGB) + " GB of memory, but only " +
584 std::to_string(availableGB) + " GB is available.";
585 }
586 return errorString;
587}
588
596 return ConfigService::Instance().getValue<bool>("algorithms.memorycheck").value_or(true);
597}
598
599// -------------------------- concrete instantiations
600template MANTID_KERNEL_DLL string memToString<uint32_t>(const uint32_t);
601template MANTID_KERNEL_DLL string memToString<uint64_t>(const uint64_t);
602// To initialize the static class variable.
603std::mutex MemoryStats::mutexMemory;
604
605} // namespace Mantid::Kernel
double value
The value of the point.
Definition FitMW.cpp:51
int count
counter
Definition Matrix.cpp:37
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition Logger.h:51
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
This class is responsible for memory statistics.
Definition Memory.h:28
std::size_t avail_memory
Available memory of system in kiB.
Definition Memory.h:54
std::string resUsageStr() const
Returns the resident memory used by the current process.
Definition Memory.cpp:378
std::string checkAvailableMemory(std::size_t const requestedMemoryBytes) const
Check if there is enough space in memory to hold the requested amount of memory.
Definition Memory.cpp:573
std::string availMemStr() const
Returns the available memory of the system as a string.
Definition Memory.cpp:390
static std::mutex mutexMemory
Mutex to avoid simultaneous access to memory resources.
Definition Memory.h:57
double getFreeRatio() const
The ratio of available to total system memory as a number between 0-100.
Definition Memory.cpp:465
std::string vmUsageStr() const
Returns the virtual memory usage as a string.
Definition Memory.cpp:372
std::size_t reservedMem() const
Returns the reserved memory that has not been factored into the available memory calculation.
Definition Memory.cpp:431
static bool memoryCheckEnabled()
Whether checkAvailableMemory compares a request against the available memory at all.
Definition Memory.cpp:595
void ignoreFields(const MemoryStatsIgnore)
Set the fields to ignore.
Definition Memory.cpp:366
void update()
Update the structure with current information, taking into account what is to be ignored.
Definition Memory.cpp:349
std::size_t res_usage
Resident memory usage by process in kiB.
Definition Memory.h:52
std::size_t getCurrentRSS() const
Definition Memory.cpp:534
MemoryStatsIgnore ignore
What fields to ignore.
Definition Memory.h:50
std::size_t getPeakRSS() const
Definition Memory.cpp:490
std::size_t vm_usage
Virtual memory usage by process in kiB.
Definition Memory.h:51
std::size_t totalMem() const
Returns the total memory of the system.
Definition Memory.cpp:397
MemoryStats(const MemoryStatsIgnore ignore=MEMORY_STATS_IGNORE_NONE)
Constructor.
Definition Memory.cpp:334
std::string totalMemStr() const
Returns the total memory of the system as a string.
Definition Memory.cpp:384
std::size_t availMem() const
Returns the available memory of the system in kiB.
Definition Memory.cpp:404
std::size_t residentMem() const
Returns the memory usage of the current process in kiB.
Definition Memory.cpp:410
void process_mem_system(size_t &sys_avail, size_t &sys_total)
Attempts to read the system memory statistics.
Definition Memory.cpp:187
std::size_t total_memory
Total physical memory of system in kiB.
Definition Memory.h:53
std::size_t virtualMem() const
Returns the virtual memory usage of the current process in kiB.
Definition Memory.cpp:418
MANTID_KERNEL_DLL void initAllocatorOptions()
Initialize platform-dependent options for memory management.
Definition Memory.cpp:288
template MANTID_KERNEL_DLL string memToString< uint64_t >(const uint64_t)
MANTID_KERNEL_DLL std::ostream & operator<<(std::ostream &, CPUTimer &)
Convenience function to provide for easier debug printing.
Definition CPUTimer.cpp:86
void process_mem_usage(size_t &vm_usage, size_t &resident_set)
Attempts to read the system-dependent data for a process' virtual memory size and resident set size,...
Definition Memory.cpp:62
MemoryStatsIgnore
Enmuerate the ignored memory fields.
Definition Memory.h:19
@ MEMORY_STATS_IGNORE_SYSTEM
Definition Memory.h:19
@ MEMORY_STATS_IGNORE_PROCESS
Definition Memory.h:19
template MANTID_KERNEL_DLL string memToString< uint32_t >(const uint32_t)
std::string memToString(const TYPE mem_in_kiB)
Convert a (number) for memory in kiB to a string with proper units.
Definition Memory.cpp:43
std::string to_string(const wide_integer< Bits, Signed > &n)