123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489 |
- // Copyright 2017 The Abseil Authors.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // https://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #include "absl/base/internal/sysinfo.h"
- #include "absl/base/attributes.h"
- #ifdef _WIN32
- #include <windows.h>
- #else
- #include <fcntl.h>
- #include <pthread.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <unistd.h>
- #endif
- #ifdef __linux__
- #include <sys/syscall.h>
- #endif
- #if defined(__APPLE__) || defined(__FreeBSD__)
- #include <sys/sysctl.h>
- #endif
- #ifdef __FreeBSD__
- #include <pthread_np.h>
- #endif
- #ifdef __NetBSD__
- #include <lwp.h>
- #endif
- #if defined(__myriad2__)
- #error #include <rtems.h>
- #endif
- #include <string.h>
- #include <cassert>
- #include <cerrno>
- #include <cstdint>
- #include <cstdio>
- #include <cstdlib>
- #include <ctime>
- #include <limits>
- #include <thread> // NOLINT(build/c++11)
- #include <utility>
- #include <vector>
- #include "absl/base/call_once.h"
- #include "absl/base/config.h"
- #include "absl/base/internal/raw_logging.h"
- #include "absl/base/internal/spinlock.h"
- #include "absl/base/internal/unscaledcycleclock.h"
- #include "absl/base/thread_annotations.h"
- namespace absl {
- ABSL_NAMESPACE_BEGIN
- namespace base_internal {
- namespace {
- #if defined(_WIN32)
- // Returns number of bits set in `bitMask`
- DWORD Win32CountSetBits(ULONG_PTR bitMask) {
- for (DWORD bitSetCount = 0; ; ++bitSetCount) {
- if (bitMask == 0) return bitSetCount;
- bitMask &= bitMask - 1;
- }
- }
- // Returns the number of logical CPUs using GetLogicalProcessorInformation(), or
- // 0 if the number of processors is not available or can not be computed.
- // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformation
- int Win32NumCPUs() {
- #pragma comment(lib, "kernel32.lib")
- using Info = SYSTEM_LOGICAL_PROCESSOR_INFORMATION;
- DWORD info_size = sizeof(Info);
- Info* info(static_cast<Info*>(malloc(info_size)));
- if (info == nullptr) return 0;
- bool success = GetLogicalProcessorInformation(info, &info_size);
- if (!success && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
- free(info);
- info = static_cast<Info*>(malloc(info_size));
- if (info == nullptr) return 0;
- success = GetLogicalProcessorInformation(info, &info_size);
- }
- DWORD logicalProcessorCount = 0;
- if (success) {
- Info* ptr = info;
- DWORD byteOffset = 0;
- while (byteOffset + sizeof(Info) <= info_size) {
- switch (ptr->Relationship) {
- case RelationProcessorCore:
- logicalProcessorCount += Win32CountSetBits(ptr->ProcessorMask);
- break;
- case RelationNumaNode:
- case RelationCache:
- case RelationProcessorPackage:
- // Ignore other entries
- break;
- default:
- // Ignore unknown entries
- break;
- }
- byteOffset += sizeof(Info);
- ptr++;
- }
- }
- free(info);
- return static_cast<int>(logicalProcessorCount);
- }
- #endif
- } // namespace
- static int GetNumCPUs() {
- #if defined(__myriad2__)
- return 1;
- #elif defined(_WIN32)
- const int hardware_concurrency = Win32NumCPUs();
- return hardware_concurrency ? hardware_concurrency : 1;
- #elif defined(_AIX)
- return sysconf(_SC_NPROCESSORS_ONLN);
- #else
- // Other possibilities:
- // - Read /sys/devices/system/cpu/online and use cpumask_parse()
- // - sysconf(_SC_NPROCESSORS_ONLN)
- return static_cast<int>(std::thread::hardware_concurrency());
- #endif
- }
- #if defined(_WIN32)
- static double GetNominalCPUFrequency() {
- #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \
- !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
- // UWP apps don't have access to the registry and currently don't provide an
- // API informing about CPU nominal frequency.
- return 1.0;
- #else
- #pragma comment(lib, "advapi32.lib") // For Reg* functions.
- HKEY key;
- // Use the Reg* functions rather than the SH functions because shlwapi.dll
- // pulls in gdi32.dll which makes process destruction much more costly.
- if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
- "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0,
- KEY_READ, &key) == ERROR_SUCCESS) {
- DWORD type = 0;
- DWORD data = 0;
- DWORD data_size = sizeof(data);
- auto result = RegQueryValueExA(key, "~MHz", nullptr, &type,
- reinterpret_cast<LPBYTE>(&data), &data_size);
- RegCloseKey(key);
- if (result == ERROR_SUCCESS && type == REG_DWORD &&
- data_size == sizeof(data)) {
- return data * 1e6; // Value is MHz.
- }
- }
- return 1.0;
- #endif // WINAPI_PARTITION_APP && !WINAPI_PARTITION_DESKTOP
- }
- #elif defined(CTL_HW) && defined(HW_CPU_FREQ)
- static double GetNominalCPUFrequency() {
- unsigned freq;
- size_t size = sizeof(freq);
- int mib[2] = {CTL_HW, HW_CPU_FREQ};
- if (sysctl(mib, 2, &freq, &size, nullptr, 0) == 0) {
- return static_cast<double>(freq);
- }
- return 1.0;
- }
- #else
- // Helper function for reading a long from a file. Returns true if successful
- // and the memory location pointed to by value is set to the value read.
- static bool ReadLongFromFile(const char *file, long *value) {
- bool ret = false;
- #if defined(_POSIX_C_SOURCE)
- const int file_mode = (O_RDONLY | O_CLOEXEC);
- #else
- const int file_mode = O_RDONLY;
- #endif
- int fd = open(file, file_mode);
- if (fd != -1) {
- char line[1024];
- char *err;
- memset(line, '\0', sizeof(line));
- ssize_t len;
- do {
- len = read(fd, line, sizeof(line) - 1);
- } while (len < 0 && errno == EINTR);
- if (len <= 0) {
- ret = false;
- } else {
- const long temp_value = strtol(line, &err, 10);
- if (line[0] != '\0' && (*err == '\n' || *err == '\0')) {
- *value = temp_value;
- ret = true;
- }
- }
- close(fd);
- }
- return ret;
- }
- #if defined(ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY)
- // Reads a monotonic time source and returns a value in
- // nanoseconds. The returned value uses an arbitrary epoch, not the
- // Unix epoch.
- static int64_t ReadMonotonicClockNanos() {
- struct timespec t;
- #ifdef CLOCK_MONOTONIC_RAW
- int rc = clock_gettime(CLOCK_MONOTONIC_RAW, &t);
- #else
- int rc = clock_gettime(CLOCK_MONOTONIC, &t);
- #endif
- if (rc != 0) {
- ABSL_INTERNAL_LOG(
- FATAL, "clock_gettime() failed: (" + std::to_string(errno) + ")");
- }
- return int64_t{t.tv_sec} * 1000000000 + t.tv_nsec;
- }
- class UnscaledCycleClockWrapperForInitializeFrequency {
- public:
- static int64_t Now() { return base_internal::UnscaledCycleClock::Now(); }
- };
- struct TimeTscPair {
- int64_t time; // From ReadMonotonicClockNanos().
- int64_t tsc; // From UnscaledCycleClock::Now().
- };
- // Returns a pair of values (monotonic kernel time, TSC ticks) that
- // approximately correspond to each other. This is accomplished by
- // doing several reads and picking the reading with the lowest
- // latency. This approach is used to minimize the probability that
- // our thread was preempted between clock reads.
- static TimeTscPair GetTimeTscPair() {
- int64_t best_latency = std::numeric_limits<int64_t>::max();
- TimeTscPair best;
- for (int i = 0; i < 10; ++i) {
- int64_t t0 = ReadMonotonicClockNanos();
- int64_t tsc = UnscaledCycleClockWrapperForInitializeFrequency::Now();
- int64_t t1 = ReadMonotonicClockNanos();
- int64_t latency = t1 - t0;
- if (latency < best_latency) {
- best_latency = latency;
- best.time = t0;
- best.tsc = tsc;
- }
- }
- return best;
- }
- // Measures and returns the TSC frequency by taking a pair of
- // measurements approximately `sleep_nanoseconds` apart.
- static double MeasureTscFrequencyWithSleep(int sleep_nanoseconds) {
- auto t0 = GetTimeTscPair();
- struct timespec ts;
- ts.tv_sec = 0;
- ts.tv_nsec = sleep_nanoseconds;
- while (nanosleep(&ts, &ts) != 0 && errno == EINTR) {}
- auto t1 = GetTimeTscPair();
- double elapsed_ticks = t1.tsc - t0.tsc;
- double elapsed_time = (t1.time - t0.time) * 1e-9;
- return elapsed_ticks / elapsed_time;
- }
- // Measures and returns the TSC frequency by calling
- // MeasureTscFrequencyWithSleep(), doubling the sleep interval until the
- // frequency measurement stabilizes.
- static double MeasureTscFrequency() {
- double last_measurement = -1.0;
- int sleep_nanoseconds = 1000000; // 1 millisecond.
- for (int i = 0; i < 8; ++i) {
- double measurement = MeasureTscFrequencyWithSleep(sleep_nanoseconds);
- if (measurement * 0.99 < last_measurement &&
- last_measurement < measurement * 1.01) {
- // Use the current measurement if it is within 1% of the
- // previous measurement.
- return measurement;
- }
- last_measurement = measurement;
- sleep_nanoseconds *= 2;
- }
- return last_measurement;
- }
- #endif // ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY
- static double GetNominalCPUFrequency() {
- long freq = 0;
- // Google's production kernel has a patch to export the TSC
- // frequency through sysfs. If the kernel is exporting the TSC
- // frequency use that. There are issues where cpuinfo_max_freq
- // cannot be relied on because the BIOS may be exporting an invalid
- // p-state (on x86) or p-states may be used to put the processor in
- // a new mode (turbo mode). Essentially, those frequencies cannot
- // always be relied upon. The same reasons apply to /proc/cpuinfo as
- // well.
- if (ReadLongFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", &freq)) {
- return freq * 1e3; // Value is kHz.
- }
- #if defined(ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY)
- // On these platforms, the TSC frequency is the nominal CPU
- // frequency. But without having the kernel export it directly
- // though /sys/devices/system/cpu/cpu0/tsc_freq_khz, there is no
- // other way to reliably get the TSC frequency, so we have to
- // measure it ourselves. Some CPUs abuse cpuinfo_max_freq by
- // exporting "fake" frequencies for implementing new features. For
- // example, Intel's turbo mode is enabled by exposing a p-state
- // value with a higher frequency than that of the real TSC
- // rate. Because of this, we prefer to measure the TSC rate
- // ourselves on i386 and x86-64.
- return MeasureTscFrequency();
- #else
- // If CPU scaling is in effect, we want to use the *maximum*
- // frequency, not whatever CPU speed some random processor happens
- // to be using now.
- if (ReadLongFromFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq",
- &freq)) {
- return freq * 1e3; // Value is kHz.
- }
- return 1.0;
- #endif // !ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY
- }
- #endif
- ABSL_CONST_INIT static once_flag init_num_cpus_once;
- ABSL_CONST_INIT static int num_cpus = 0;
- // NumCPUs() may be called before main() and before malloc is properly
- // initialized, therefore this must not allocate memory.
- int NumCPUs() {
- base_internal::LowLevelCallOnce(
- &init_num_cpus_once, []() { num_cpus = GetNumCPUs(); });
- return num_cpus;
- }
- // A default frequency of 0.0 might be dangerous if it is used in division.
- ABSL_CONST_INIT static once_flag init_nominal_cpu_frequency_once;
- ABSL_CONST_INIT static double nominal_cpu_frequency = 1.0;
- // NominalCPUFrequency() may be called before main() and before malloc is
- // properly initialized, therefore this must not allocate memory.
- double NominalCPUFrequency() {
- base_internal::LowLevelCallOnce(
- &init_nominal_cpu_frequency_once,
- []() { nominal_cpu_frequency = GetNominalCPUFrequency(); });
- return nominal_cpu_frequency;
- }
- #if defined(_WIN32)
- pid_t GetTID() {
- return pid_t{GetCurrentThreadId()};
- }
- #elif defined(__linux__)
- #ifndef SYS_gettid
- #define SYS_gettid __NR_gettid
- #endif
- pid_t GetTID() {
- return static_cast<pid_t>(syscall(SYS_gettid));
- }
- #elif defined(__akaros__)
- pid_t GetTID() {
- // Akaros has a concept of "vcore context", which is the state the program
- // is forced into when we need to make a user-level scheduling decision, or
- // run a signal handler. This is analogous to the interrupt context that a
- // CPU might enter if it encounters some kind of exception.
- //
- // There is no current thread context in vcore context, but we need to give
- // a reasonable answer if asked for a thread ID (e.g., in a signal handler).
- // Thread 0 always exists, so if we are in vcore context, we return that.
- //
- // Otherwise, we know (since we are using pthreads) that the uthread struct
- // current_uthread is pointing to is the first element of a
- // struct pthread_tcb, so we extract and return the thread ID from that.
- //
- // TODO(dcross): Akaros anticipates moving the thread ID to the uthread
- // structure at some point. We should modify this code to remove the cast
- // when that happens.
- if (in_vcore_context())
- return 0;
- return reinterpret_cast<struct pthread_tcb *>(current_uthread)->id;
- }
- #elif defined(__myriad2__)
- pid_t GetTID() {
- uint32_t tid;
- rtems_task_ident(RTEMS_SELF, 0, &tid);
- return tid;
- }
- #elif defined(__APPLE__)
- pid_t GetTID() {
- uint64_t tid;
- // `nullptr` here implies this thread. This only fails if the specified
- // thread is invalid or the pointer-to-tid is null, so we needn't worry about
- // it.
- pthread_threadid_np(nullptr, &tid);
- return static_cast<pid_t>(tid);
- }
- #elif defined(__FreeBSD__)
- pid_t GetTID() { return static_cast<pid_t>(pthread_getthreadid_np()); }
- #elif defined(__OpenBSD__)
- pid_t GetTID() { return getthrid(); }
- #elif defined(__NetBSD__)
- pid_t GetTID() { return static_cast<pid_t>(_lwp_self()); }
- #elif defined(__native_client__)
- pid_t GetTID() {
- auto* thread = pthread_self();
- static_assert(sizeof(pid_t) == sizeof(thread),
- "In NaCL int expected to be the same size as a pointer");
- return reinterpret_cast<pid_t>(thread);
- }
- #else
- // Fallback implementation of `GetTID` using `pthread_self`.
- pid_t GetTID() {
- // `pthread_t` need not be arithmetic per POSIX; platforms where it isn't
- // should be handled above.
- return static_cast<pid_t>(pthread_self());
- }
- #endif
- // GetCachedTID() caches the thread ID in thread-local storage (which is a
- // userspace construct) to avoid unnecessary system calls. Without this caching,
- // it can take roughly 98ns, while it takes roughly 1ns with this caching.
- pid_t GetCachedTID() {
- #ifdef ABSL_HAVE_THREAD_LOCAL
- static thread_local pid_t thread_id = GetTID();
- return thread_id;
- #else
- return GetTID();
- #endif // ABSL_HAVE_THREAD_LOCAL
- }
- } // namespace base_internal
- ABSL_NAMESPACE_END
- } // namespace absl
|