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