1
2//
3// This source file is part of appleseed.
4// Visit http://appleseedhq.net/ for additional information and resources.
5//
6// This software is released under the MIT license.
7//
8// Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
9// Copyright (c) 2014-2017 Francois Beaune, The appleseedhq Organization
10//
11// Permission is hereby granted, free of charge, to any person obtaining a copy
12// of this software and associated documentation files (the "Software"), to deal
13// in the Software without restriction, including without limitation the rights
14// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15// copies of the Software, and to permit persons to whom the Software is
16// furnished to do so, subject to the following conditions:
17//
18// The above copyright notice and this permission notice shall be included in
19// all copies or substantial portions of the Software.
20//
21// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27// THE SOFTWARE.
28//
29
30// Interface header.
31#include "system.h"
32
33// appleseed.foundation headers.
34#include "foundation/platform/thread.h"
35#include "foundation/utility/log.h"
36#include "foundation/utility/string.h"
37
38// Standard headers.
39#include <string>
40
41// Windows.
42#if defined _WIN32
43
44 // appleseed.foundation headers.
45 #include "foundation/platform/windows.h"
46
47 // Standard headers.
48 #include <cassert>
49 #include <cstdlib>
50
51 // Platform headers.
52 #include "psapi.h"
53
54// OS X.
55#elif defined __APPLE__
56
57 // Platform headers.
58 #include <mach/mach.h>
59 #include <mach/task.h>
60 #include <mach/task_info.h>
61 #include <sys/mount.h>
62 #include <sys/param.h>
63 #include <sys/sysctl.h>
64 #include <sys/types.h>
65
66// Linux.
67#elif defined __linux__
68
69 // Standard headers.
70 #include <cstdio>
71
72 // Platform headers.
73 #include <sys/sysinfo.h>
74 #include <sys/types.h>
75 #include <unistd.h>
76
77// FreeBSD.
78#elif defined __FreeBSD__
79
80 #include <sys/types.h>
81 #include <sys/resource.h>
82 #include <sys/sysctl.h>
83 #include <unistd.h>
84
85// Other platforms.
86#else
87
88 #error Unsupported platform.
89
90#endif
91
92using namespace std;
93
94namespace foundation
95{
96
97// ------------------------------------------------------------------------------------------------
98// Common code.
99// ------------------------------------------------------------------------------------------------
100
101void System::print_information(Logger& logger)
102{
103 LOG_INFO(
104 logger,
105 "system information:\n"
106 " logical cores %s\n"
107 " L1 data cache size %s, line size %s\n"
108 " L2 cache size %s, line size %s\n"
109 " L3 cache size %s, line size %s\n"
110 " physical memory size %s\n"
111 " virtual memory size %s",
112 pretty_uint(get_logical_cpu_core_count()).c_str(),
113 pretty_size(get_l1_data_cache_size()).c_str(),
114 pretty_size(get_l1_data_cache_line_size()).c_str(),
115 pretty_size(get_l2_cache_size()).c_str(),
116 pretty_size(get_l2_cache_line_size()).c_str(),
117 pretty_size(get_l3_cache_size()).c_str(),
118 pretty_size(get_l3_cache_line_size()).c_str(),
119 pretty_size(get_total_physical_memory_size()).c_str(),
120 pretty_size(get_total_virtual_memory_size()).c_str());
121}
122
123size_t System::get_logical_cpu_core_count()
124{
125 const size_t concurrency =
126 static_cast<size_t>(boost::thread::hardware_concurrency());
127
128 return concurrency > 1 ? concurrency : 1;
129}
130
131// ------------------------------------------------------------------------------------------------
132// Windows.
133// ------------------------------------------------------------------------------------------------
134
135#if defined _WIN32
136
137namespace
138{
139 // This code is based on a code snippet by Nick Strupat (http://stackoverflow.com/a/4049562).
140 bool get_cache_descriptor(const size_t level, CACHE_DESCRIPTOR& result)
141 {
142 assert(level >= 1 && level <= 3);
143
144 DWORD buffer_size = 0;
145 BOOL success = GetLogicalProcessorInformation(0, &buffer_size);
146 if (success == TRUE || GetLastError() != ERROR_INSUFFICIENT_BUFFER)
147 return false;
148
149 SYSTEM_LOGICAL_PROCESSOR_INFORMATION* buffer =
150 (SYSTEM_LOGICAL_PROCESSOR_INFORMATION*)malloc(buffer_size);
151 if (buffer == 0)
152 return false;
153
154 success = GetLogicalProcessorInformation(buffer, &buffer_size);
155 if (success == FALSE)
156 {
157 free(buffer);
158 return false;
159 }
160
161 bool found = false;
162
163 for (size_t i = 0; i < buffer_size / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); ++i)
164 {
165 if (buffer[i].Relationship == RelationCache)
166 {
167 const CACHE_DESCRIPTOR& cache = buffer[i].Cache;
168
169 if (cache.Level == level && (cache.Type == CacheData || cache.Type == CacheUnified))
170 {
171 found = true;
172 result = cache;
173 break;
174 }
175 }
176 }
177
178 free(buffer);
179
180 return found;
181 }
182}
183
184size_t System::get_l1_data_cache_size()
185{
186 CACHE_DESCRIPTOR cache;
187 return get_cache_descriptor(1, cache) ? cache.Size : 0;
188}
189
190size_t System::get_l1_data_cache_line_size()
191{
192 CACHE_DESCRIPTOR cache;
193 return get_cache_descriptor(1, cache) ? cache.LineSize : 0;
194}
195
196size_t System::get_l2_cache_size()
197{
198 CACHE_DESCRIPTOR cache;
199 return get_cache_descriptor(2, cache) ? cache.Size : 0;
200}
201
202size_t System::get_l2_cache_line_size()
203{
204 CACHE_DESCRIPTOR cache;
205 return get_cache_descriptor(2, cache) ? cache.LineSize : 0;
206}
207
208size_t System::get_l3_cache_size()
209{
210 CACHE_DESCRIPTOR cache;
211 return get_cache_descriptor(3, cache) ? cache.Size : 0;
212}
213
214size_t System::get_l3_cache_line_size()
215{
216 CACHE_DESCRIPTOR cache;
217 return get_cache_descriptor(3, cache) ? cache.LineSize : 0;
218}
219
220uint64 System::get_total_physical_memory_size()
221{
222 // Reference: http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
223
224 MEMORYSTATUSEX mem_info;
225 mem_info.dwLength = sizeof(mem_info);
226 GlobalMemoryStatusEx(&mem_info);
227
228 return static_cast<uint64>(mem_info.ullTotalPhys);
229}
230
231uint64 System::get_total_virtual_memory_size()
232{
233 // Reference: http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
234
235 MEMORYSTATUSEX mem_info;
236 mem_info.dwLength = sizeof(mem_info);
237 GlobalMemoryStatusEx(&mem_info);
238
239 return static_cast<uint64>(mem_info.ullTotalPageFile);
240}
241
242uint64 System::get_process_virtual_memory_size()
243{
244 // Reference: http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
245
246 PROCESS_MEMORY_COUNTERS_EX pmc;
247 GetProcessMemoryInfo(
248 GetCurrentProcess(),
249 reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmc),
250 sizeof(pmc));
251
252 return pmc.PrivateUsage;
253}
254
255// ------------------------------------------------------------------------------------------------
256// OS X.
257// ------------------------------------------------------------------------------------------------
258
259#elif defined __APPLE__
260
261namespace
262{
263 size_t get_system_value(const char* name)
264 {
265 size_t value = 0;
266 size_t value_size = sizeof(value);
267 return sysctlbyname(name, &value, &value_size, 0, 0) == 0 ? value : 0;
268 }
269}
270
271size_t System::get_l1_data_cache_size()
272{
273 return get_system_value("hw.l1dcachesize");
274}
275
276size_t System::get_l1_data_cache_line_size()
277{
278 return get_l1_data_cache_size() > 0 ? get_system_value("hw.cachelinesize") : 0;
279}
280
281size_t System::get_l2_cache_size()
282{
283 return get_system_value("hw.l2cachesize");
284}
285
286size_t System::get_l2_cache_line_size()
287{
288 return get_l2_cache_size() > 0 ? get_system_value("hw.cachelinesize") : 0;
289}
290
291size_t System::get_l3_cache_size()
292{
293 return get_system_value("hw.l3cachesize");
294}
295
296size_t System::get_l3_cache_line_size()
297{
298 return get_l3_cache_size() > 0 ? get_system_value("hw.cachelinesize") : 0;
299}
300
301uint64 System::get_total_physical_memory_size()
302{
303 // Reference: http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
304
305 int name[2] = { CTL_HW, HW_MEMSIZE };
306 uint64_t result;
307 size_t result_length = sizeof(uint64_t);
308 sysctl(name, 2, &result, &result_length, 0, 0);
309
310 return static_cast<uint64>(result);
311}
312
313uint64 System::get_total_virtual_memory_size()
314{
315 // Reference: http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
316
317 struct statfs stats;
318 if (statfs("/", &stats) == 0)
319 return static_cast<uint64>(stats.f_bsize) * stats.f_bfree;
320 else return 0;
321}
322
323uint64 System::get_process_virtual_memory_size()
324{
325 // Reference: http://nadeausoftware.com/articles/2012/07/c_c_tip_how_get_process_resident_set_size_physical_memory_use
326
327#ifdef MACH_TASK_BASIC_INFO
328 struct mach_task_basic_info info;
329 mach_msg_type_number_t info_count = MACH_TASK_BASIC_INFO_COUNT;
330
331 if (task_info(
332 mach_task_self(),
333 MACH_TASK_BASIC_INFO,
334 (task_info_t)&info,
335 &info_count) != KERN_SUCCESS)
336 return 0;
337#else
338 struct task_basic_info info;
339 mach_msg_type_number_t info_count = TASK_BASIC_INFO_COUNT;
340
341 if (task_info(
342 mach_task_self(),
343 TASK_BASIC_INFO,
344 (task_info_t)&info,
345 &info_count) != KERN_SUCCESS)
346 return 0;
347#endif
348
349 return info.resident_size;
350}
351
352// ------------------------------------------------------------------------------------------------
353// Linux.
354// ------------------------------------------------------------------------------------------------
355
356#elif defined __linux__
357
358size_t System::get_l1_data_cache_size()
359{
360 return sysconf(_SC_LEVEL1_DCACHE_SIZE);
361}
362
363size_t System::get_l1_data_cache_line_size()
364{
365 return sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
366}
367
368size_t System::get_l2_cache_size()
369{
370 return sysconf(_SC_LEVEL2_CACHE_SIZE);
371}
372
373size_t System::get_l2_cache_line_size()
374{
375 return sysconf(_SC_LEVEL2_CACHE_LINESIZE);
376}
377
378size_t System::get_l3_cache_size()
379{
380 return sysconf(_SC_LEVEL3_CACHE_SIZE);
381}
382
383size_t System::get_l3_cache_line_size()
384{
385 return sysconf(_SC_LEVEL3_CACHE_LINESIZE);
386}
387
388uint64 System::get_total_physical_memory_size()
389{
390 // Reference: http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
391
392 struct sysinfo mem_info;
393 sysinfo(&mem_info);
394
395 return static_cast<uint64>(mem_info.totalram) * mem_info.mem_unit;
396}
397
398uint64 System::get_total_virtual_memory_size()
399{
400 // Reference: http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
401
402 struct sysinfo mem_info;
403 sysinfo(&mem_info);
404
405 uint64 result = mem_info.totalram;
406 result += mem_info.totalswap;
407 result *= mem_info.mem_unit;
408
409 return result;
410}
411
412uint64 System::get_process_virtual_memory_size()
413{
414 // Reference: http://nadeausoftware.com/articles/2012/07/c_c_tip_how_get_process_resident_set_size_physical_memory_use
415
416 FILE* fp = fopen("/proc/self/statm", "r");
417 if (fp == 0)
418 return 0;
419
420 long rss = 0;
421 if (fscanf(fp, "%*s%ld", &rss) != 1)
422 {
423 fclose(fp);
424 return 0;
425 }
426
427 fclose(fp);
428
429 return static_cast<uint64>(rss) * sysconf(_SC_PAGESIZE);
430}
431
432// ------------------------------------------------------------------------------------------------
433// FreeBSD.
434// ------------------------------------------------------------------------------------------------
435
436#elif defined __FreeBSD__ && (__x86_64__ || __i386__)
437
438// FreeBSD does not provide an API to query CPU cache information, so we'd
439// have to ask for it by ourselves...
440
441namespace
442{
443 // Since we only want to know cache size and line size, structure below
444 // is very simplistic; initially all caches have zero size (i.e. absent).
445 struct TrivialX86Cache
446 {
447 size_t size;
448 size_t linesize;
449 } x86_caches[3];
450
451 enum { L1D, L2, L3 };
452 enum { eax, ebx, ecx, edx };
453
454 // %ebx may be used to point to GOT (Global Offset Table) for PIC (Position
455 // Independent Code) on 32-bit x86, so it must be preserved. Normally
456 // compilers handle this implicitly because %ebx is also callee saved, but
457 // GCC before 5.0 forbids any use of %ebx with PIC, so it must be performed
458 // explicitly. Unfortunately, we need a separate implementation for x86-64
459 // to preserve %rbx because 32-bit operations would set the upper 32 bits
460 // to zero.
461 inline void cpuid(uint32_t* regs)
462 {
463 asm(
464#if __x86_64__
465 "movq %%rbx, %q1\n\t"
466 "cpuid\n\t"
467 "xchgq %%rbx, %q1"
468#else
469 "movl %%ebx, %1\n\t"
470 "cpuid\n\t"
471 "xchgl %%ebx, %1"
472#endif
473 : "+a" (regs[eax]),
474 "=r" (regs[ebx]),
475 "+c" (regs[ecx]),
476 "=d" (regs[edx]));
477 }
478
479#define BIT(n) (1ul << (n))
480#define BITMASK(h, l) ((BIT(h) | (BIT(h) - 1)) & ~(BIT(l) - 1))
481#define BITFIELD(x, h, l) (((x) & BITMASK(h, l)) >> l)
482
483 // For modern CPUs, we use Deterministic Cache Parameters (Function 04h)
484 // to obtain cache information.
485 void get_cache_info_deterministic(TrivialX86Cache* caches)
486 {
487 uint32_t regs[4];
488
489 // Cycle up to ten possible caches to be extra sure.
490 for (size_t i = 0; i < 10; i++)
491 {
492 regs[eax] = 4;
493 regs[ecx] = i;
494 cpuid(regs);
495
496 const unsigned type = BITFIELD(regs[eax], 4, 0);
497 if (type == 0) break; // no more caches, we're done.
498 if (type == 2) continue; // ignore instruction caches.
499
500 const unsigned level = BITFIELD(regs[eax], 7, 5);
501 const unsigned linesize = BITFIELD(regs[ebx], 11, 0) + 1;
502 const unsigned sets = BITFIELD(regs[ecx], 31, 0) + 1;
503 const unsigned associativity = BITFIELD(regs[ebx], 31, 22) + 1;
504
505 assert(level > 0);
506
507 caches[level - 1].size = linesize * sets * associativity;
508 caches[level - 1].linesize = linesize;
509 }
510 }
511
512 // On older CPUs we might have to rely on Cache Descriptors (Function 02h)
513 // and Intel documentation (table of the known values).
514 void get_cache_info_from_table(TrivialX86Cache* caches)
515 {
516 uint32_t regs[4];
517 int no_higher_level_cache = 0;
518
519 regs[eax] = 2;
520 cpuid(regs);
521 // Doing only one call is technically wrong, but all CPUs up to Core i7
522 // require a single call. Since this is a fallback code path for really
523 // old CPUs anyways (modern ones will provide Function 4), we should be
524 // safe, but let's add an assert() on the lower 8 bits just in case.
525 assert((regs[eax] & 0xFF) == 1);
526
527 for (size_t i = 1; i < 4 * 4; i++)
528 {
529 // Check descriptor validity for every octet: if bit 31 is set,
530 // skip to the next one.
531 if (i % 4 == 0 && (regs[i / 4] >> 31) == 1)
532 {
533 i += 4;
534 continue;
535 }
536
537 // Descriptor decode values from the Intel manual, Table 2-7.
538 switch ((regs[i / 4] >> (i % 4) * 8) & 0xFF)
539 {
540 case 0x0A:
541 caches[L1D].size = 8;
542 caches[L1D].linesize = 32;
543 break;
544 case 0x0C:
545 caches[L1D].size = 16;
546 caches[L1D].linesize = 32;
547 break;
548 case 0x0D:
549 case 0x60:
550 case 0x67:
551 caches[L1D].size = 16;
552 caches[L1D].linesize = 64;
553 break;
554 case 0x21:
555 case 0x3C:
556 case 0x7A:
557 caches[L2].size = 256;
558 caches[L2].linesize = 64;
559 break;
560 case 0x22:
561 case 0xD0:
562 caches[L3].size = 512;
563 caches[L3].linesize = 64;
564 break;
565 case 0x23:
566 case 0xD1:
567 case 0xD6:
568 caches[L3].size = 1024;
569 caches[L3].linesize = 64;
570 break;
571 case 0x25:
572 case 0xD2:
573 case 0xD7:
574 case 0xE2:
575 caches[L3].size = 2048;
576 caches[L3].linesize = 64;
577 break;
578 case 0x29:
579 case 0x46:
580 case 0xD8:
581 case 0xE3:
582 caches[L3].size = 4096;
583 caches[L3].linesize = 64;
584 break;
585 case 0x2C:
586 caches[L1D].size = 32;
587 caches[L1D].linesize = 64;
588 break;
589 case 0x39:
590 case 0x3B:
591 case 0x79:
592 caches[L2].size = 128;
593 caches[L2].linesize = 64;
594 break;
595 case 0x3A:
596 caches[L2].size = 192;
597 caches[L2].linesize = 64;
598 break;
599 case 0x3D:
600 caches[L2].size = 384;
601 caches[L2].linesize = 64;
602 break;
603 case 0x3E:
604 case 0x7B:
605 case 0x7F:
606 case 0x86:
607 caches[L2].size = 512;
608 caches[L2].linesize = 64;
609 break;
610 case 0x40:
611 no_higher_level_cache = 1;
612 break;
613 case 0x41:
614 caches[L2].size = 128;
615 caches[L2].linesize = 32;
616 break;
617 case 0x42:
618 case 0x82:
619 caches[L2].size = 256;
620 caches[L2].linesize = 32;
621 break;
622 case 0x43:
623 case 0x83:
624 caches[L2].size = 512;
625 caches[L2].linesize = 32;
626 break;
627 case 0x44:
628 caches[L2].size = 1024;
629 caches[L2].linesize = 32;
630 break;
631 case 0x45:
632 caches[L2].size = 2048;
633 caches[L2].linesize = 32;
634 break;
635 case 0x47:
636 case 0x4B:
637 case 0xE4:
638 caches[L3].size = 8192;
639 caches[L3].linesize = 64;
640 break;
641 case 0x48:
642 caches[L2].size = 3072;
643 caches[L2].linesize = 64;
644 break;
645 case 0x49:
646 // todo: check for Intel Xeon processor MP, Family 0Fh,
647 // Model 06h, because 0x49 means L3 cache (4MB, 16-way,
648 // 64-byte linesize) for this CPU.
649 caches[L2].size = 4096;
650 caches[L2].linesize = 64;
651 break;
652 case 0x4A:
653 case 0xDE:
654 caches[L3].size = 6 * 1024;
655 caches[L3].linesize = 64;
656 break;
657 case 0x4C:
658 case 0xEA:
659 caches[L3].size = 12 * 1024;
660 caches[L3].linesize = 64;
661 break;
662 case 0x4D:
663 caches[L3].size = 16 * 1024;
664 caches[L3].linesize = 64;
665 break;
666 case 0x4E:
667 caches[L2].size = 6 * 1024;
668 caches[L2].linesize = 64;
669 break;
670 case 0x66:
671 caches[L1D].size = 8;
672 caches[L1D].linesize = 64;
673 break;
674 case 0x68:
675 caches[L1D].size = 32;
676 caches[L1D].linesize = 64;
677 break;
678 case 0x78:
679 case 0x7C:
680 caches[L2].size = 1024;
681 caches[L2].linesize = 64;
682 break;
683 case 0x7D:
684 caches[L2].size = 2048;
685 caches[L2].linesize = 64;
686 break;
687 case 0x84:
688 caches[L2].size = 1024;
689 caches[L2].linesize = 32;
690 break;
691 case 0x85:
692 caches[L2].size = 2048;
693 caches[L2].linesize = 32;
694 break;
695 case 0x87:
696 caches[L2].size = 1024;
697 caches[L2].linesize = 64;
698 break;
699 case 0xDC:
700 caches[L3].size = 1536;
701 caches[L3].linesize = 64;
702 break;
703 case 0xDD:
704 caches[L3].size = 3 * 1024;
705 caches[L3].linesize = 64;
706 break;
707 case 0xEB:
708 caches[L3].size = 18 * 1024;
709 caches[L3].linesize = 64;
710 break;
711 case 0xEC:
712 caches[L3].size = 24 * 1024;
713 caches[L3].linesize = 64;
714 break;
715 }
716 }
717
718 // Convert Kbytes to bytes.
719 caches[L1D].size *= 1024;
720 caches[L2].size *= 1024;
721 caches[L3].size *= 1024;
722 }
723
724 void x86_get_cache_basic_info(TrivialX86Cache* caches)
725 {
726 uint32_t regs[4];
727
728 // Assume that all Intel CPUs support CPUID instruction.
729 regs[eax] = 0;
730 cpuid(regs);
731 if (regs[eax] >= 4)
732 get_cache_info_deterministic(caches);
733 else if (regs[eax] >= 2)
734 get_cache_info_from_table(caches);
735 }
736}
737
738size_t System::get_l1_data_cache_size()
739{
740 // Here and below we'd check for L1D cache size: if it's initialized,
741 // it means that x86_get_cache_basic_info() was already called and we
742 // don't have to do it again.
743 if (!x86_caches[L1D].size)
744 x86_get_cache_basic_info(x86_caches);
745 return x86_caches[L1D].size;
746}
747
748size_t System::get_l1_data_cache_line_size()
749{
750 if (!x86_caches[L1D].size)
751 x86_get_cache_basic_info(x86_caches);
752 return x86_caches[L1D].linesize;
753}
754
755size_t System::get_l2_cache_size()
756{
757 if (!x86_caches[L1D].size)
758 x86_get_cache_basic_info(x86_caches);
759 return x86_caches[L2].size;
760}
761
762size_t System::get_l2_cache_line_size()
763{
764 if (!x86_caches[L1D].size)
765 x86_get_cache_basic_info(x86_caches);
766 return x86_caches[L2].linesize;
767}
768
769size_t System::get_l3_cache_size()
770{
771 if (!x86_caches[L1D].size)
772 x86_get_cache_basic_info(x86_caches);
773 return x86_caches[L3].size;
774}
775
776size_t System::get_l3_cache_line_size()
777{
778 if (!x86_caches[L1D].size)
779 x86_get_cache_basic_info(x86_caches);
780 return x86_caches[L3].linesize;
781}
782
783uint64 System::get_total_physical_memory_size()
784{
785 const long pagesize = sysconf(_SC_PAGESIZE);
786 const long numpages = sysconf(_SC_PHYS_PAGES);
787
788 return static_cast<uint64>(pagesize) * numpages;
789}
790
791uint64 System::get_total_virtual_memory_size()
792{
793 quad_t swap;
794 size_t len = sizeof(swap);
795
796 const int result = sysctlbyname("vm.swap_total", &swap, &len, 0x0, 0);
797 assert(result == 0);
798
799 return get_total_physical_memory_size() + swap;
800}
801
802// curproc->p_stats->p_ru is updated on statclock tick and may be not very
803// granular (if called early in program's life, it can even yield zeros).
804// Reference: https://lists.freebsd.org/pipermail/freebsd-stable/2006-March/023262.html
805uint64 System::get_process_virtual_memory_size()
806{
807 struct rusage ru;
808
809 const int result = getrusage(RUSAGE_SELF, &ru);
810 assert(result == 0);
811
812 return static_cast<uint64>(ru.ru_maxrss) * 1024;
813}
814
815#endif
816
817} // namespace foundation
818