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 | |
92 | using namespace std; |
93 | |
94 | namespace foundation |
95 | { |
96 | |
97 | // ------------------------------------------------------------------------------------------------ |
98 | // Common code. |
99 | // ------------------------------------------------------------------------------------------------ |
100 | |
101 | void 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 | |
123 | size_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 | |
137 | namespace |
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 | |
184 | size_t System::get_l1_data_cache_size() |
185 | { |
186 | CACHE_DESCRIPTOR cache; |
187 | return get_cache_descriptor(1, cache) ? cache.Size : 0; |
188 | } |
189 | |
190 | size_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 | |
196 | size_t System::get_l2_cache_size() |
197 | { |
198 | CACHE_DESCRIPTOR cache; |
199 | return get_cache_descriptor(2, cache) ? cache.Size : 0; |
200 | } |
201 | |
202 | size_t System::get_l2_cache_line_size() |
203 | { |
204 | CACHE_DESCRIPTOR cache; |
205 | return get_cache_descriptor(2, cache) ? cache.LineSize : 0; |
206 | } |
207 | |
208 | size_t System::get_l3_cache_size() |
209 | { |
210 | CACHE_DESCRIPTOR cache; |
211 | return get_cache_descriptor(3, cache) ? cache.Size : 0; |
212 | } |
213 | |
214 | size_t System::get_l3_cache_line_size() |
215 | { |
216 | CACHE_DESCRIPTOR cache; |
217 | return get_cache_descriptor(3, cache) ? cache.LineSize : 0; |
218 | } |
219 | |
220 | uint64 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 | |
231 | uint64 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 | |
242 | uint64 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 | |
261 | namespace |
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 | |
271 | size_t System::get_l1_data_cache_size() |
272 | { |
273 | return get_system_value("hw.l1dcachesize" ); |
274 | } |
275 | |
276 | size_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 | |
281 | size_t System::get_l2_cache_size() |
282 | { |
283 | return get_system_value("hw.l2cachesize" ); |
284 | } |
285 | |
286 | size_t System::get_l2_cache_line_size() |
287 | { |
288 | return get_l2_cache_size() > 0 ? get_system_value("hw.cachelinesize" ) : 0; |
289 | } |
290 | |
291 | size_t System::get_l3_cache_size() |
292 | { |
293 | return get_system_value("hw.l3cachesize" ); |
294 | } |
295 | |
296 | size_t System::get_l3_cache_line_size() |
297 | { |
298 | return get_l3_cache_size() > 0 ? get_system_value("hw.cachelinesize" ) : 0; |
299 | } |
300 | |
301 | uint64 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 | |
313 | uint64 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 | |
323 | uint64 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 | |
358 | size_t System::get_l1_data_cache_size() |
359 | { |
360 | return sysconf(_SC_LEVEL1_DCACHE_SIZE); |
361 | } |
362 | |
363 | size_t System::get_l1_data_cache_line_size() |
364 | { |
365 | return sysconf(_SC_LEVEL1_DCACHE_LINESIZE); |
366 | } |
367 | |
368 | size_t System::get_l2_cache_size() |
369 | { |
370 | return sysconf(_SC_LEVEL2_CACHE_SIZE); |
371 | } |
372 | |
373 | size_t System::get_l2_cache_line_size() |
374 | { |
375 | return sysconf(_SC_LEVEL2_CACHE_LINESIZE); |
376 | } |
377 | |
378 | size_t System::get_l3_cache_size() |
379 | { |
380 | return sysconf(_SC_LEVEL3_CACHE_SIZE); |
381 | } |
382 | |
383 | size_t System::get_l3_cache_line_size() |
384 | { |
385 | return sysconf(_SC_LEVEL3_CACHE_LINESIZE); |
386 | } |
387 | |
388 | uint64 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 | |
398 | uint64 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 | |
412 | uint64 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 = 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 | |
441 | namespace |
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 | |
738 | size_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 | |
748 | size_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 | |
755 | size_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 | |
762 | size_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 | |
769 | size_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 | |
776 | size_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 | |
783 | uint64 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 | |
791 | uint64 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 |
805 | uint64 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 | |