17 #ifdef BENCHMARK_OS_WINDOWS
19 #undef StrCat // Don't let StrCat in string_util.h be renamed to lstrcatA
20 #include <versionhelpers.h>
25 #ifndef BENCHMARK_OS_FUCHSIA
26 #include <sys/resource.h>
29 #include <sys/types.h>
31 #if defined BENCHMARK_OS_FREEBSD || defined BENCHMARK_OS_MACOSX || \
32 defined BENCHMARK_OS_NETBSD || defined BENCHMARK_OS_OPENBSD || \
33 defined BENCHMARK_OS_DRAGONFLY
34 #define BENCHMARK_HAS_SYSCTL
35 #include <sys/sysctl.h>
38 #if defined(BENCHMARK_OS_SOLARIS)
41 #if defined(BENCHMARK_OS_QNX)
42 #include <sys/syspage.h>
73 void PrintImp(std::ostream&
out) {
out << std::endl; }
75 template <
class First,
class... Rest>
76 void PrintImp(std::ostream&
out, First&& f, Rest&&... rest) {
77 out << std::forward<First>(f);
78 PrintImp(
out, std::forward<Rest>(rest)...);
81 template <
class...
Args>
83 PrintImp(std::cerr, std::forward<Args>(
args)...);
84 std::exit(EXIT_FAILURE);
87 #ifdef BENCHMARK_HAS_SYSCTL
98 using DataPtr = std::unique_ptr<DataT, decltype(&std::free)>;
105 ValueUnion() : Size(0), Buff(nullptr, &
std::free) {}
107 explicit ValueUnion(
size_t BuffSize)
108 : Size(sizeof(DataT) + BuffSize),
109 Buff(::
new (
std::malloc(Size)) DataT(), &
std::free) {}
111 ValueUnion(ValueUnion&& other) =
default;
113 explicit operator bool()
const {
return bool(Buff); }
115 char*
data()
const {
return Buff->bytes; }
120 if (Size ==
sizeof(Buff->uint32_value))
121 return static_cast<int32_t>(Buff->uint32_value);
122 else if (Size ==
sizeof(Buff->uint64_value))
123 return static_cast<int64_t>(Buff->uint64_value);
128 if (Size ==
sizeof(Buff->uint32_value))
129 return Buff->uint32_value;
130 else if (Size ==
sizeof(Buff->uint64_value))
131 return Buff->uint64_value;
135 template <
class T,
int N>
136 std::array<T, N> GetAsArray() {
137 const int ArrSize =
sizeof(
T) *
N;
139 std::array<T, N> Arr;
146 #if defined BENCHMARK_OS_OPENBSD
150 if ((Name ==
"hw.ncpu") || (Name ==
"hw.cpuspeed")){
151 ValueUnion buff(
sizeof(
int));
153 if (Name ==
"hw.ncpu") {
156 mib[1] = HW_CPUSPEED;
159 if (sysctl(mib, 2, buff.data(), &buff.Size,
nullptr, 0) == -1) {
166 size_t CurBuffSize = 0;
167 if (sysctlbyname(Name.c_str(),
nullptr, &CurBuffSize,
nullptr, 0) == -1)
170 ValueUnion buff(CurBuffSize);
171 if (sysctlbyname(Name.c_str(), buff.data(), &buff.Size,
nullptr, 0) == 0)
180 auto Buff = GetSysctlImp(Name);
181 if (!Buff)
return false;
182 Out->assign(Buff.data());
190 auto Buff = GetSysctlImp(Name);
191 if (!Buff)
return false;
192 *Out =
static_cast<Tp
>(Buff.GetAsUnsigned());
196 template <
class Tp,
size_t N>
197 bool GetSysctl(
std::string const& Name, std::array<Tp, N>* Out) {
198 auto Buff = GetSysctlImp(Name);
199 if (!Buff)
return false;
200 *Out = Buff.GetAsArray<Tp,
N>();
205 template <
class ArgT>
208 std::ifstream
f(fname.c_str());
209 if (!
f.is_open())
return false;
216 if (
num_cpus <= 0)
return CPUInfo::Scaling::UNKNOWN;
217 #if defined(BENCHMARK_OS_QNX)
218 return CPUInfo::Scaling::UNKNOWN;
219 #elif !defined(BENCHMARK_OS_WINDOWS)
224 for (
int cpu = 0; cpu <
num_cpus; ++cpu) {
226 StrCat(
"/sys/devices/system/cpu/cpu", cpu,
"/cpufreq/scaling_governor");
227 if (ReadFromFile(governor_file, &res) && res !=
"performance")
return CPUInfo::Scaling::ENABLED;
229 return CPUInfo::Scaling::DISABLED;
231 return CPUInfo::Scaling::UNKNOWN;
239 CPUMask Mask(benchmark::stoul(Part,
nullptr, 16));
240 return static_cast<int>(Mask.count());
244 while ((
Pos = Val.find(
',')) != std::string::npos) {
245 total += CountBits(Val.substr(0,
Pos));
246 Val = Val.substr(
Pos + 1);
249 total += CountBits(Val);
255 std::vector<CPUInfo::CacheInfo> GetCacheSizesFromKVFS() {
256 std::vector<CPUInfo::CacheInfo> res;
257 std::string dir =
"/sys/devices/system/cpu/cpu0/cache/";
260 CPUInfo::CacheInfo info;
263 if (!
f.is_open())
break;
267 PrintErrorAndDie(
"Failed while reading file '", FPath,
"size'");
272 "Invalid cache size format: failed to read size suffix");
273 else if (f &&
suffix !=
"K")
274 PrintErrorAndDie(
"Invalid cache size format: Expected bytes ",
suffix);
278 if (!ReadFromFile(
StrCat(FPath,
"type"), &info.type))
279 PrintErrorAndDie(
"Failed to read from file ", FPath,
"type");
280 if (!ReadFromFile(
StrCat(FPath,
"level"), &info.level))
281 PrintErrorAndDie(
"Failed to read from file ", FPath,
"level");
283 if (!ReadFromFile(
StrCat(FPath,
"shared_cpu_map"), &map_str))
284 PrintErrorAndDie(
"Failed to read from file ", FPath,
"shared_cpu_map");
285 info.num_sharing = CountSetBitsInCPUMap(map_str);
292 #ifdef BENCHMARK_OS_MACOSX
293 std::vector<CPUInfo::CacheInfo> GetCacheSizesMacOSX() {
294 std::vector<CPUInfo::CacheInfo> res;
295 std::array<uint64_t, 4> CacheCounts{{0, 0, 0, 0}};
296 GetSysctl(
"hw.cacheconfig", &CacheCounts);
303 } Cases[] = {{
"hw.l1dcachesize",
"Data", 1, CacheCounts[1]},
304 {
"hw.l1icachesize",
"Instruction", 1, CacheCounts[1]},
305 {
"hw.l2cachesize",
"Unified", 2, CacheCounts[2]},
306 {
"hw.l3cachesize",
"Unified", 3, CacheCounts[3]}};
307 for (
auto& C : Cases) {
309 if (!GetSysctl(
C.name, &val))
continue;
310 CPUInfo::CacheInfo info;
312 info.level =
C.level;
314 info.num_sharing =
static_cast<int>(
C.num_sharing);
319 #elif defined(BENCHMARK_OS_WINDOWS)
320 std::vector<CPUInfo::CacheInfo> GetCacheSizesWindows() {
321 std::vector<CPUInfo::CacheInfo> res;
323 using PInfo = SYSTEM_LOGICAL_PROCESSOR_INFORMATION;
324 using CInfo = CACHE_DESCRIPTOR;
326 using UPtr = std::unique_ptr<PInfo, decltype(&std::free)>;
327 GetLogicalProcessorInformation(
nullptr, &
buffer_size);
328 UPtr buff((PInfo*)malloc(
buffer_size), &std::free);
329 if (!GetLogicalProcessorInformation(buff.get(), &
buffer_size))
330 PrintErrorAndDie(
"Failed during call to GetLogicalProcessorInformation: ",
333 PInfo*
it = buff.get();
337 if (
it->Relationship != RelationCache)
continue;
338 using BitSet = std::bitset<
sizeof(ULONG_PTR) * CHAR_BIT>;
339 BitSet B(
it->ProcessorMask);
341 if (!B.test(0))
continue;
342 CInfo* Cache = &
it->Cache;
343 CPUInfo::CacheInfo
C;
344 C.num_sharing =
static_cast<int>(B.count());
345 C.level = Cache->Level;
346 C.size = Cache->Size;
347 switch (Cache->Type) {
351 case CacheInstruction:
352 C.type =
"Instruction";
368 #elif BENCHMARK_OS_QNX
369 std::vector<CPUInfo::CacheInfo> GetCacheSizesQNX() {
370 std::vector<CPUInfo::CacheInfo> res;
371 struct cacheattr_entry *cache = SYSPAGE_ENTRY(cacheattr);
372 uint32_t const elsize = SYSPAGE_ELEMENT_SIZE(cacheattr);
373 int num = SYSPAGE_ENTRY_SIZE(cacheattr) / elsize ;
374 for(
int i = 0;
i <
num; ++
i ) {
375 CPUInfo::CacheInfo info;
376 switch (cache->flags){
377 case CACHE_FLAG_INSTR :
378 info.type =
"Instruction";
381 case CACHE_FLAG_DATA :
385 case CACHE_FLAG_UNIFIED :
386 info.type =
"Unified";
389 case CACHE_FLAG_SHARED :
390 info.type =
"Shared";
397 info.size = cache->line_size * cache->num_lines;
398 info.num_sharing = 0;
400 cache = SYSPAGE_ARRAY_ADJ_OFFSET(cacheattr, cache, elsize);
406 std::vector<CPUInfo::CacheInfo> GetCacheSizes() {
407 #ifdef BENCHMARK_OS_MACOSX
408 return GetCacheSizesMacOSX();
409 #elif defined(BENCHMARK_OS_WINDOWS)
410 return GetCacheSizesWindows();
411 #elif defined(BENCHMARK_OS_QNX)
412 return GetCacheSizesQNX();
414 return GetCacheSizesFromKVFS();
419 #if defined(BENCHMARK_OS_WINDOWS)
421 const unsigned COUNT = MAX_COMPUTERNAME_LENGTH+1;
422 TCHAR hostname[
COUNT] = {
'\0'};
423 DWORD DWCOUNT =
COUNT;
424 if (!GetComputerName(hostname, &DWCOUNT))
430 using convert_type = std::codecvt_utf8<wchar_t>;
431 std::wstring_convert<convert_type, wchar_t>
converter;
436 #else // defined(BENCHMARK_OS_WINDOWS)
437 #ifndef HOST_NAME_MAX
438 #ifdef BENCHMARK_HAS_SYSCTL // BSD/Mac Doesnt have HOST_NAME_MAX defined
439 #define HOST_NAME_MAX 64
440 #elif defined(BENCHMARK_OS_NACL)
441 #define HOST_NAME_MAX 64
442 #elif defined(BENCHMARK_OS_QNX)
443 #define HOST_NAME_MAX 154
444 #elif defined(BENCHMARK_OS_RTEMS)
445 #define HOST_NAME_MAX 256
447 #pragma message("HOST_NAME_MAX not defined. using 64")
448 #define HOST_NAME_MAX 64
450 #endif // def HOST_NAME_MAX
455 #endif // Catch-all POSIX block.
459 #ifdef BENCHMARK_HAS_SYSCTL
461 if (GetSysctl(
"hw.ncpu", &NumCPU))
return NumCPU;
462 fprintf(
stderr,
"Err: %s\n", strerror(errno));
463 std::exit(EXIT_FAILURE);
464 #elif defined(BENCHMARK_OS_WINDOWS)
469 GetSystemInfo(&sysinfo);
470 return sysinfo.dwNumberOfProcessors;
473 #elif defined(BENCHMARK_OS_SOLARIS)
475 int NumCPU = sysconf(_SC_NPROCESSORS_ONLN);
478 "sysconf(_SC_NPROCESSORS_ONLN) failed with error: %s\n",
482 #elif defined(BENCHMARK_OS_QNX)
483 return static_cast<int>(_syspage_ptr->num_cpu);
487 std::ifstream
f(
"/proc/cpuinfo");
489 std::cerr <<
"failed to open /proc/cpuinfo\n";
494 while (std::getline(
f, ln)) {
495 if (ln.empty())
continue;
496 size_t SplitIdx = ln.find(
':');
498 #if defined(__s390__)
501 if (SplitIdx != std::string::npos)
value = ln.substr(
Key.size()+1,SplitIdx-
Key.size()-1);
503 if (SplitIdx != std::string::npos)
value = ln.substr(SplitIdx + 1);
505 if (ln.size() >=
Key.size() && ln.compare(0,
Key.size(),
Key) == 0) {
507 if (!
value.empty()) {
508 int CurID = benchmark::stoi(
value);
514 std::cerr <<
"Failure reading /proc/cpuinfo\n";
518 std::cerr <<
"Failed to read to end of /proc/cpuinfo\n";
525 "CPU ID assignments in /proc/cpuinfo seem messed up."
526 " This is usually caused by a bad BIOS.\n");
538 #if defined BENCHMARK_OS_LINUX || defined BENCHMARK_OS_CYGWIN
547 if (ReadFromFile(
"/sys/devices/system/cpu/cpu0/tsc_freq_khz", &freq)
551 || (scaling == CPUInfo::Scaling::DISABLED &&
552 ReadFromFile(
"/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq",
557 || ReadFromFile(
"/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq",
561 return freq * 1000.0;
564 const double error_value = -1;
565 double bogo_clock = error_value;
567 std::ifstream
f(
"/proc/cpuinfo");
569 std::cerr <<
"failed to open /proc/cpuinfo\n";
574 if (
Key.size() >
Value.size())
return false;
575 auto Cmp = [&](
char X,
char Y) {
576 return std::tolower(
X) == std::tolower(Y);
578 return std::equal(
Key.begin(),
Key.end(),
Value.begin(), Cmp);
582 while (std::getline(
f, ln)) {
583 if (ln.empty())
continue;
584 size_t SplitIdx = ln.find(
':');
586 if (SplitIdx != std::string::npos)
value = ln.substr(SplitIdx + 1);
590 if (startsWithKey(ln,
"cpu MHz")) {
591 if (!
value.empty()) {
592 double cycles_per_second = benchmark::stod(
value) * 1000000.0;
593 if (cycles_per_second > 0)
return cycles_per_second;
595 }
else if (startsWithKey(ln,
"bogomips")) {
596 if (!
value.empty()) {
597 bogo_clock = benchmark::stod(
value) * 1000000.0;
598 if (bogo_clock < 0.0) bogo_clock = error_value;
603 std::cerr <<
"Failure reading /proc/cpuinfo\n";
607 std::cerr <<
"Failed to read to end of /proc/cpuinfo\n";
614 if (bogo_clock >= 0.0)
return bogo_clock;
616 #elif defined BENCHMARK_HAS_SYSCTL
617 constexpr
auto* FreqStr =
618 #if defined(BENCHMARK_OS_FREEBSD) || defined(BENCHMARK_OS_NETBSD)
620 #elif defined BENCHMARK_OS_OPENBSD
622 #elif defined BENCHMARK_OS_DRAGONFLY
627 unsigned long long hz = 0;
628 #if defined BENCHMARK_OS_OPENBSD
629 if (GetSysctl(FreqStr, &hz))
return hz * 1000000;
631 if (GetSysctl(FreqStr, &hz))
return hz;
633 fprintf(
stderr,
"Unable to determine clock rate from sysctl: %s: %s\n",
634 FreqStr, strerror(errno));
636 #elif defined BENCHMARK_OS_WINDOWS
639 DWORD
data, data_size =
sizeof(
data);
640 if (IsWindowsXPOrGreater() &&
642 SHGetValueA(HKEY_LOCAL_MACHINE,
643 "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
644 "~MHz",
nullptr, &
data, &data_size)))
647 #elif defined (BENCHMARK_OS_SOLARIS)
648 kstat_ctl_t *kc = kstat_open();
650 std::cerr <<
"failed to open /dev/kstat\n";
653 kstat_t *ksp = kstat_lookup(kc, (
char*)
"cpu_info", -1, (
char*)
"cpu_info0");
655 std::cerr <<
"failed to lookup in /dev/kstat\n";
658 if (kstat_read(kc, ksp, NULL) < 0) {
659 std::cerr <<
"failed to read from /dev/kstat\n";
663 (kstat_named_t*)kstat_data_lookup(ksp, (
char*)
"current_clock_Hz");
665 std::cerr <<
"failed to lookup data in /dev/kstat\n";
668 if (knp->data_type != KSTAT_DATA_UINT64) {
669 std::cerr <<
"current_clock_Hz is of unexpected data type: "
670 << knp->data_type <<
"\n";
673 double clock_hz = knp->value.ui64;
676 #elif defined (BENCHMARK_OS_QNX)
677 return static_cast<double>((
int64_t)(SYSPAGE_ENTRY(cpuinfo)->speed) *
681 const int estimate_time_ms = 1000;
687 std::vector<double> GetLoadAvg() {
688 #if (defined BENCHMARK_OS_FREEBSD || defined(BENCHMARK_OS_LINUX) || \
689 defined BENCHMARK_OS_MACOSX || defined BENCHMARK_OS_NETBSD || \
690 defined BENCHMARK_OS_OPENBSD || defined BENCHMARK_OS_DRAGONFLY) && \
691 !defined(__ANDROID__)
692 constexpr
int kMaxSamples = 3;
693 std::vector<double> res(kMaxSamples, 0.0);
694 const int nelem = getloadavg(res.data(), kMaxSamples);
716 cycles_per_second(GetCPUCyclesPerSecond(scaling)),
717 caches(GetCacheSizes()),
718 load_avg(GetLoadAvg()) {}