#if defined(_MSC_VER)
#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
#endif
#include "internal_macros.h"
#ifdef BENCHMARK_OS_WINDOWS
#if !defined(WINVER) || WINVER < 0x0600
#undef WINVER
#define WINVER …
#endif
#include <shlwapi.h>
#undef StrCat
#include <versionhelpers.h>
#include <windows.h>
#include <codecvt>
#else
#include <fcntl.h>
#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT)
#include <sys/resource.h>
#endif
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#if defined BENCHMARK_OS_FREEBSD || defined BENCHMARK_OS_MACOSX || \
defined BENCHMARK_OS_NETBSD || defined BENCHMARK_OS_OPENBSD || \
defined BENCHMARK_OS_DRAGONFLY
#define BENCHMARK_HAS_SYSCTL
#include <sys/sysctl.h>
#endif
#endif
#if defined(BENCHMARK_OS_SOLARIS)
#include <kstat.h>
#include <netdb.h>
#endif
#if defined(BENCHMARK_OS_QNX)
#include <sys/syspage.h>
#endif
#if defined(BENCHMARK_OS_QURT)
#include <qurt.h>
#endif
#if defined(BENCHMARK_HAS_PTHREAD_AFFINITY)
#include <pthread.h>
#endif
#include <algorithm>
#include <array>
#include <bitset>
#include <cerrno>
#include <climits>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <iterator>
#include <limits>
#include <locale>
#include <memory>
#include <random>
#include <sstream>
#include <utility>
#include "benchmark/benchmark.h"
#include "check.h"
#include "cycleclock.h"
#include "internal_macros.h"
#include "log.h"
#include "string_util.h"
#include "timers.h"
namespace benchmark {
namespace {
void PrintImp(std::ostream& out) { … }
template <class First, class... Rest>
void PrintImp(std::ostream& out, First&& f, Rest&&... rest) { … }
template <class... Args>
BENCHMARK_NORETURN void PrintErrorAndDie(Args&&... args) { … }
#ifdef BENCHMARK_HAS_SYSCTL
struct ValueUnion {
union DataT {
int32_t int32_value;
int64_t int64_value;
char bytes[8];
};
using DataPtr = std::unique_ptr<DataT, decltype(&std::free)>;
std::size_t size;
DataPtr buff;
public:
ValueUnion() : size(0), buff(nullptr, &std::free) {}
explicit ValueUnion(std::size_t buff_size)
: size(sizeof(DataT) + buff_size),
buff(::new (std::malloc(size)) DataT(), &std::free) {}
ValueUnion(ValueUnion&& other) = default;
explicit operator bool() const { return bool(buff); }
char* data() const { return buff->bytes; }
std::string GetAsString() const { return std::string(data()); }
int64_t GetAsInteger() const {
if (size == sizeof(buff->int32_value))
return buff->int32_value;
else if (size == sizeof(buff->int64_value))
return buff->int64_value;
BENCHMARK_UNREACHABLE();
}
template <class T, int N>
std::array<T, N> GetAsArray() {
const int arr_size = sizeof(T) * N;
BM_CHECK_LE(arr_size, size);
std::array<T, N> arr;
std::memcpy(arr.data(), data(), arr_size);
return arr;
}
};
ValueUnion GetSysctlImp(std::string const& name) {
#if defined BENCHMARK_OS_OPENBSD
int mib[2];
mib[0] = CTL_HW;
if ((name == "hw.ncpu") || (name == "hw.cpuspeed")) {
ValueUnion buff(sizeof(int));
if (name == "hw.ncpu") {
mib[1] = HW_NCPU;
} else {
mib[1] = HW_CPUSPEED;
}
if (sysctl(mib, 2, buff.data(), &buff.size, nullptr, 0) == -1) {
return ValueUnion();
}
return buff;
}
return ValueUnion();
#else
std::size_t cur_buff_size = 0;
if (sysctlbyname(name.c_str(), nullptr, &cur_buff_size, nullptr, 0) == -1)
return ValueUnion();
ValueUnion buff(cur_buff_size);
if (sysctlbyname(name.c_str(), buff.data(), &buff.size, nullptr, 0) == 0)
return buff;
return ValueUnion();
#endif
}
BENCHMARK_MAYBE_UNUSED
bool GetSysctl(std::string const& name, std::string* out) {
out->clear();
auto buff = GetSysctlImp(name);
if (!buff) return false;
out->assign(buff.data());
return true;
}
template <class Tp,
class = typename std::enable_if<std::is_integral<Tp>::value>::type>
bool GetSysctl(std::string const& name, Tp* out) {
*out = 0;
auto buff = GetSysctlImp(name);
if (!buff) return false;
*out = static_cast<Tp>(buff.GetAsInteger());
return true;
}
template <class Tp, size_t N>
bool GetSysctl(std::string const& name, std::array<Tp, N>* out) {
auto buff = GetSysctlImp(name);
if (!buff) return false;
*out = buff.GetAsArray<Tp, N>();
return true;
}
#endif
template <class ArgT>
bool ReadFromFile(std::string const& fname, ArgT* arg) { … }
CPUInfo::Scaling CpuScaling(int num_cpus) { … }
int CountSetBitsInCPUMap(std::string val) { … }
BENCHMARK_MAYBE_UNUSED
std::vector<CPUInfo::CacheInfo> GetCacheSizesFromKVFS() { … }
#ifdef BENCHMARK_OS_MACOSX
std::vector<CPUInfo::CacheInfo> GetCacheSizesMacOSX() {
std::vector<CPUInfo::CacheInfo> res;
std::array<int, 4> cache_counts{{0, 0, 0, 0}};
GetSysctl("hw.cacheconfig", &cache_counts);
struct {
std::string name;
std::string type;
int level;
int num_sharing;
} cases[] = {{"hw.l1dcachesize", "Data", 1, cache_counts[1]},
{"hw.l1icachesize", "Instruction", 1, cache_counts[1]},
{"hw.l2cachesize", "Unified", 2, cache_counts[2]},
{"hw.l3cachesize", "Unified", 3, cache_counts[3]}};
for (auto& c : cases) {
int val;
if (!GetSysctl(c.name, &val)) continue;
CPUInfo::CacheInfo info;
info.type = c.type;
info.level = c.level;
info.size = val;
info.num_sharing = c.num_sharing;
res.push_back(std::move(info));
}
return res;
}
#elif defined(BENCHMARK_OS_WINDOWS)
std::vector<CPUInfo::CacheInfo> GetCacheSizesWindows() {
std::vector<CPUInfo::CacheInfo> res;
DWORD buffer_size = 0;
using PInfo = SYSTEM_LOGICAL_PROCESSOR_INFORMATION;
using CInfo = CACHE_DESCRIPTOR;
using UPtr = std::unique_ptr<PInfo, decltype(&std::free)>;
GetLogicalProcessorInformation(nullptr, &buffer_size);
UPtr buff(static_cast<PInfo*>(std::malloc(buffer_size)), &std::free);
if (!GetLogicalProcessorInformation(buff.get(), &buffer_size))
PrintErrorAndDie("Failed during call to GetLogicalProcessorInformation: ",
GetLastError());
PInfo* it = buff.get();
PInfo* end = buff.get() + (buffer_size / sizeof(PInfo));
for (; it != end; ++it) {
if (it->Relationship != RelationCache) continue;
using BitSet = std::bitset<sizeof(ULONG_PTR) * CHAR_BIT>;
BitSet b(it->ProcessorMask);
if (!b.test(0)) continue;
const CInfo& cache = it->Cache;
CPUInfo::CacheInfo C;
C.num_sharing = static_cast<int>(b.count());
C.level = cache.Level;
C.size = cache.Size;
C.type = "Unknown";
switch (cache.Type) {
case CacheUnified:
C.type = "Unified";
break;
case CacheInstruction:
C.type = "Instruction";
break;
case CacheData:
C.type = "Data";
break;
case CacheTrace:
C.type = "Trace";
break;
}
res.push_back(C);
}
return res;
}
#elif BENCHMARK_OS_QNX
std::vector<CPUInfo::CacheInfo> GetCacheSizesQNX() {
std::vector<CPUInfo::CacheInfo> res;
struct cacheattr_entry* cache = SYSPAGE_ENTRY(cacheattr);
uint32_t const elsize = SYSPAGE_ELEMENT_SIZE(cacheattr);
int num = SYSPAGE_ENTRY_SIZE(cacheattr) / elsize;
for (int i = 0; i < num; ++i) {
CPUInfo::CacheInfo info;
switch (cache->flags) {
case CACHE_FLAG_INSTR:
info.type = "Instruction";
info.level = 1;
break;
case CACHE_FLAG_DATA:
info.type = "Data";
info.level = 1;
break;
case CACHE_FLAG_UNIFIED:
info.type = "Unified";
info.level = 2;
break;
case CACHE_FLAG_SHARED:
info.type = "Shared";
info.level = 3;
break;
default:
continue;
break;
}
info.size = cache->line_size * cache->num_lines;
info.num_sharing = 0;
res.push_back(std::move(info));
cache = SYSPAGE_ARRAY_ADJ_OFFSET(cacheattr, cache, elsize);
}
return res;
}
#endif
std::vector<CPUInfo::CacheInfo> GetCacheSizes() { … }
std::string GetSystemName() { … }
int GetNumCPUsImpl() { … }
int GetNumCPUs() { … }
class ThreadAffinityGuard final { … };
double GetCPUCyclesPerSecond(CPUInfo::Scaling scaling) { … }
std::vector<double> GetLoadAvg() { … }
}
const CPUInfo& CPUInfo::Get() { … }
CPUInfo::CPUInfo()
: … { … }
const SystemInfo& SystemInfo::Get() { … }
SystemInfo::SystemInfo() : … { … }
}