Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "absl/base/internal/sysinfo.h"
00016
00017 #include "absl/base/attributes.h"
00018
00019 #ifdef _WIN32
00020 #include <shlwapi.h>
00021 #include <windows.h>
00022 #else
00023 #include <fcntl.h>
00024 #include <pthread.h>
00025 #include <sys/stat.h>
00026 #include <sys/types.h>
00027 #include <unistd.h>
00028 #endif
00029
00030 #ifdef __linux__
00031 #include <sys/syscall.h>
00032 #endif
00033
00034 #if defined(__APPLE__) || defined(__FreeBSD__)
00035 #include <sys/sysctl.h>
00036 #endif
00037
00038 #if defined(__myriad2__)
00039 #include <rtems.h>
00040 #endif
00041
00042 #include <string.h>
00043 #include <cassert>
00044 #include <cstdint>
00045 #include <cstdio>
00046 #include <cstdlib>
00047 #include <ctime>
00048 #include <limits>
00049 #include <thread>
00050 #include <utility>
00051 #include <vector>
00052
00053 #include "absl/base/call_once.h"
00054 #include "absl/base/internal/raw_logging.h"
00055 #include "absl/base/internal/spinlock.h"
00056 #include "absl/base/internal/unscaledcycleclock.h"
00057
00058 namespace absl {
00059 namespace base_internal {
00060
00061 static once_flag init_system_info_once;
00062 static int num_cpus = 0;
00063 static double nominal_cpu_frequency = 1.0;
00064
00065 static int GetNumCPUs() {
00066 #if defined(__myriad2__)
00067 return 1;
00068 #else
00069
00070
00071
00072 return std::thread::hardware_concurrency();
00073 #endif
00074 }
00075
00076 #if defined(_WIN32)
00077
00078 static double GetNominalCPUFrequency() {
00079 DWORD data;
00080 DWORD data_size = sizeof(data);
00081 #pragma comment(lib, "shlwapi.lib") // For SHGetValue().
00082 if (SUCCEEDED(
00083 SHGetValueA(HKEY_LOCAL_MACHINE,
00084 "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
00085 "~MHz", nullptr, &data, &data_size))) {
00086 return data * 1e6;
00087 }
00088 return 1.0;
00089 }
00090
00091 #elif defined(CTL_HW) && defined(HW_CPU_FREQ)
00092
00093 static double GetNominalCPUFrequency() {
00094 unsigned freq;
00095 size_t size = sizeof(freq);
00096 int mib[2] = {CTL_HW, HW_CPU_FREQ};
00097 if (sysctl(mib, 2, &freq, &size, nullptr, 0) == 0) {
00098 return static_cast<double>(freq);
00099 }
00100 return 1.0;
00101 }
00102
00103 #else
00104
00105
00106
00107 static bool ReadLongFromFile(const char *file, long *value) {
00108 bool ret = false;
00109 int fd = open(file, O_RDONLY);
00110 if (fd != -1) {
00111 char line[1024];
00112 char *err;
00113 memset(line, '\0', sizeof(line));
00114 int len = read(fd, line, sizeof(line) - 1);
00115 if (len <= 0) {
00116 ret = false;
00117 } else {
00118 const long temp_value = strtol(line, &err, 10);
00119 if (line[0] != '\0' && (*err == '\n' || *err == '\0')) {
00120 *value = temp_value;
00121 ret = true;
00122 }
00123 }
00124 close(fd);
00125 }
00126 return ret;
00127 }
00128
00129 #if defined(ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY)
00130
00131
00132
00133
00134 static int64_t ReadMonotonicClockNanos() {
00135 struct timespec t;
00136 #ifdef CLOCK_MONOTONIC_RAW
00137 int rc = clock_gettime(CLOCK_MONOTONIC_RAW, &t);
00138 #else
00139 int rc = clock_gettime(CLOCK_MONOTONIC, &t);
00140 #endif
00141 if (rc != 0) {
00142 perror("clock_gettime() failed");
00143 abort();
00144 }
00145 return int64_t{t.tv_sec} * 1000000000 + t.tv_nsec;
00146 }
00147
00148 class UnscaledCycleClockWrapperForInitializeFrequency {
00149 public:
00150 static int64_t Now() { return base_internal::UnscaledCycleClock::Now(); }
00151 };
00152
00153 struct TimeTscPair {
00154 int64_t time;
00155 int64_t tsc;
00156 };
00157
00158
00159
00160
00161
00162
00163 static TimeTscPair GetTimeTscPair() {
00164 int64_t best_latency = std::numeric_limits<int64_t>::max();
00165 TimeTscPair best;
00166 for (int i = 0; i < 10; ++i) {
00167 int64_t t0 = ReadMonotonicClockNanos();
00168 int64_t tsc = UnscaledCycleClockWrapperForInitializeFrequency::Now();
00169 int64_t t1 = ReadMonotonicClockNanos();
00170 int64_t latency = t1 - t0;
00171 if (latency < best_latency) {
00172 best_latency = latency;
00173 best.time = t0;
00174 best.tsc = tsc;
00175 }
00176 }
00177 return best;
00178 }
00179
00180
00181
00182 static double MeasureTscFrequencyWithSleep(int sleep_nanoseconds) {
00183 auto t0 = GetTimeTscPair();
00184 struct timespec ts;
00185 ts.tv_sec = 0;
00186 ts.tv_nsec = sleep_nanoseconds;
00187 while (nanosleep(&ts, &ts) != 0 && errno == EINTR) {}
00188 auto t1 = GetTimeTscPair();
00189 double elapsed_ticks = t1.tsc - t0.tsc;
00190 double elapsed_time = (t1.time - t0.time) * 1e-9;
00191 return elapsed_ticks / elapsed_time;
00192 }
00193
00194
00195
00196
00197 static double MeasureTscFrequency() {
00198 double last_measurement = -1.0;
00199 int sleep_nanoseconds = 1000000;
00200 for (int i = 0; i < 8; ++i) {
00201 double measurement = MeasureTscFrequencyWithSleep(sleep_nanoseconds);
00202 if (measurement * 0.99 < last_measurement &&
00203 last_measurement < measurement * 1.01) {
00204
00205
00206 return measurement;
00207 }
00208 last_measurement = measurement;
00209 sleep_nanoseconds *= 2;
00210 }
00211 return last_measurement;
00212 }
00213
00214 #endif // ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY
00215
00216 static double GetNominalCPUFrequency() {
00217 long freq = 0;
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227 if (ReadLongFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", &freq)) {
00228 return freq * 1e3;
00229 }
00230
00231 #if defined(ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY)
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242 return MeasureTscFrequency();
00243 #else
00244
00245
00246
00247
00248 if (ReadLongFromFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq",
00249 &freq)) {
00250 return freq * 1e3;
00251 }
00252
00253 return 1.0;
00254 #endif // !ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY
00255 }
00256
00257 #endif
00258
00259
00260
00261
00262 static void InitializeSystemInfo() {
00263 num_cpus = GetNumCPUs();
00264 nominal_cpu_frequency = GetNominalCPUFrequency();
00265 }
00266
00267 int NumCPUs() {
00268 base_internal::LowLevelCallOnce(&init_system_info_once, InitializeSystemInfo);
00269 return num_cpus;
00270 }
00271
00272 double NominalCPUFrequency() {
00273 base_internal::LowLevelCallOnce(&init_system_info_once, InitializeSystemInfo);
00274 return nominal_cpu_frequency;
00275 }
00276
00277 #if defined(_WIN32)
00278
00279 pid_t GetTID() {
00280 return GetCurrentThreadId();
00281 }
00282
00283 #elif defined(__linux__)
00284
00285 #ifndef SYS_gettid
00286 #define SYS_gettid __NR_gettid
00287 #endif
00288
00289 pid_t GetTID() {
00290 return syscall(SYS_gettid);
00291 }
00292
00293 #elif defined(__akaros__)
00294
00295 pid_t GetTID() {
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312 if (in_vcore_context())
00313 return 0;
00314 return reinterpret_cast<struct pthread_tcb *>(current_uthread)->id;
00315 }
00316
00317 #elif defined(__myriad2__)
00318
00319 pid_t GetTID() {
00320 uint32_t tid;
00321 rtems_task_ident(RTEMS_SELF, 0, &tid);
00322 return tid;
00323 }
00324
00325 #else
00326
00327
00328 static once_flag tid_once;
00329 static pthread_key_t tid_key;
00330 static absl::base_internal::SpinLock tid_lock(
00331 absl::base_internal::kLinkerInitialized);
00332
00333
00334
00335
00336 static std::vector<uint32_t>* tid_array GUARDED_BY(tid_lock) = nullptr;
00337 static constexpr int kBitsPerWord = 32;
00338
00339
00340 static void FreeTID(void *v) {
00341 intptr_t tid = reinterpret_cast<intptr_t>(v);
00342 int word = tid / kBitsPerWord;
00343 uint32_t mask = ~(1u << (tid % kBitsPerWord));
00344 absl::base_internal::SpinLockHolder lock(&tid_lock);
00345 assert(0 <= word && static_cast<size_t>(word) < tid_array->size());
00346 (*tid_array)[word] &= mask;
00347 }
00348
00349 static void InitGetTID() {
00350 if (pthread_key_create(&tid_key, FreeTID) != 0) {
00351
00352 perror("pthread_key_create failed");
00353 abort();
00354 }
00355
00356
00357 absl::base_internal::SpinLockHolder lock(&tid_lock);
00358 tid_array = new std::vector<uint32_t>(1);
00359 (*tid_array)[0] = 1;
00360 }
00361
00362
00363 pid_t GetTID() {
00364 absl::call_once(tid_once, InitGetTID);
00365
00366 intptr_t tid = reinterpret_cast<intptr_t>(pthread_getspecific(tid_key));
00367 if (tid != 0) {
00368 return tid;
00369 }
00370
00371 int bit;
00372 size_t word;
00373 {
00374
00375 absl::base_internal::SpinLockHolder lock(&tid_lock);
00376
00377 word = 0;
00378 while (word < tid_array->size() && ~(*tid_array)[word] == 0) {
00379 ++word;
00380 }
00381 if (word == tid_array->size()) {
00382 tid_array->push_back(0);
00383 }
00384
00385 bit = 0;
00386 while (bit < kBitsPerWord && (((*tid_array)[word] >> bit) & 1) != 0) {
00387 ++bit;
00388 }
00389 tid = (word * kBitsPerWord) + bit;
00390 (*tid_array)[word] |= 1u << bit;
00391 }
00392
00393 if (pthread_setspecific(tid_key, reinterpret_cast<void *>(tid)) != 0) {
00394 perror("pthread_setspecific failed");
00395 abort();
00396 }
00397
00398 return static_cast<pid_t>(tid);
00399 }
00400
00401 #endif
00402
00403 }
00404 }