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