Commit 189098f9 authored by gejun's avatar gejun

Revert time.cpp and use monotonic-time to implement cpuwide_time outside baidu

parent 8aabf87f
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
#endif #endif
#include <string.h> // memmem #include <string.h> // memmem
#undef _GNU_SOURCE #undef _GNU_SOURCE
#include <ctype.h>
#include "butil/time.h" #include "butil/time.h"
...@@ -82,7 +81,8 @@ int64_t monotonic_time_ns() { ...@@ -82,7 +81,8 @@ int64_t monotonic_time_ns() {
namespace detail { namespace detail {
static int64_t read_cpu_current_frequency(char* buf, ssize_t n) { // read_cpu_frequency() is modified from source code of glibc.
int64_t read_cpu_frequency(bool* invariant_tsc) {
/* We read the information from the /proc filesystem. It contains at /* We read the information from the /proc filesystem. It contains at
least one line like least one line like
cpu MHz : 497.840237 cpu MHz : 497.840237
...@@ -90,148 +90,54 @@ static int64_t read_cpu_current_frequency(char* buf, ssize_t n) { ...@@ -90,148 +90,54 @@ static int64_t read_cpu_current_frequency(char* buf, ssize_t n) {
cpu MHz : 497.841 cpu MHz : 497.841
We search for this line and convert the number in an integer. */ We search for this line and convert the number in an integer. */
int64_t result = 0;
char *mhz = static_cast<char*>(memmem(buf, n, "cpu MHz", 7));
if (mhz != NULL) {
char *endp = buf + n;
int seen_decpoint = 0;
int ndigits = 0;
/* Search for the beginning of the string. */
while (mhz < endp && (*mhz < '0' || *mhz > '9') && *mhz != '\n') {
++mhz;
}
while (mhz < endp && *mhz != '\n') {
if (*mhz >= '0' && *mhz <= '9') {
result *= 10;
result += *mhz - '0';
if (seen_decpoint)
++ndigits;
} else if (*mhz == '.') {
seen_decpoint = 1;
}
++mhz;
}
/* Compensate for missing digits at the end. */
while (ndigits++ < 6) {
result *= 10;
}
}
return result;
}
#if defined(__x86_64__) || defined(__i386__)
#if defined(__pic__) && defined(__i386__)
static void __cpuid(uint32_t reg[4], uint32_t code) {
__asm__ volatile (
"mov %%ebx, %%edi\n"
"cpuid\n"
"xchg %%edi, %%ebx\n"
: "=a"(reg[0]), "=D"(reg[1]), "=c"(reg[2]), "=d"(reg[3])
: "a"(code)
);
}
#else
static void __cpuid(uint32_t reg[4], uint32_t code) {
__asm__ volatile (
"cpuid \n\t"
: "=a"(reg[0]), "=b"(reg[1]), "=c"(reg[2]), "=d"(reg[3])
: "a"(code)
);
}
#endif
#endif
static int64_t read_cpu_frequency_by_cpuid() {
int64_t result = 0;
#if defined(__x86_64__) || defined(__i386__)
uint32_t reg[4];
__cpuid(reg, 0);
if (reg[0] >= 0x16 && reg[1] == 0x756e6547 && reg[2] == 0x6c65746e && reg[3] == 0x49656e69) {
//Intel CPU only
__cpuid(reg, 0x16);
return static_cast<uint64_t>(reg[0]) * 1000000UL;
}
#endif
return result;
}
static int64_t read_cpu_frequency_from_brand_string() {
int64_t result = 0;
#if defined(__x86_64__) || defined(__i386__)
union {
char brand[48];
uint32_t reg[12];
} buf;
__cpuid(buf.reg, 0x80000000);
if (buf.reg[0] < 0x80000004) {
return 0;
}
__cpuid(buf.reg, 0x80000002);
__cpuid(buf.reg+4, 0x80000003);
__cpuid(buf.reg+8, 0x80000004);
//Get something like: Intel(R) Xeon(R) CPU E5-2620 v2 @ 2.10GHz
char* end = buf.brand + sizeof(buf.brand);
char* p = buf.brand;
while (p != end && *p != '@') {
if (*p++ == '\n') {
return 0;
}
}
while (p != end && !isdigit(*p)) {
p++;
}
//expect x.xxGhz
//FSB may be 0.10GHz or 0.133...GHz
if (end - p < 7 || p[1] != '.'
|| !isdigit(p[2]) || !isdigit(p[3]) || p[4] != 'G') {
return 0;
}
result = (p[0]-'0') * 10 + (p[2]-'0');
int64_t last = p[3] - '0';
if (last == 7) {
last = 6;
}
for (int i = 0; i < 8; i++) {
result = result * 10 + last;
}
#endif
return result;
}
// read_cpu_frequency() is modified from source code of glibc.
int64_t read_cpu_frequency(bool* invariant_tsc) {
const int fd = open("/proc/cpuinfo", O_RDONLY); const int fd = open("/proc/cpuinfo", O_RDONLY);
if (fd < 0) { if (fd < 0) {
return 0; return 0;
} }
int64_t result = 0;
char buf[4096]; // should be enough char buf[4096]; // should be enough
const ssize_t n = read(fd, buf, sizeof(buf)); const ssize_t n = read(fd, buf, sizeof(buf));
close (fd); if (n > 0) {
if (n <= 0) { char *mhz = static_cast<char*>(memmem(buf, n, "cpu MHz", 7));
return 0;
} if (mhz != NULL) {
char *endp = buf + n;
int seen_decpoint = 0;
int ndigits = 0;
if (invariant_tsc) { /* Search for the beginning of the string. */
char* flags_pos = static_cast<char*>(memmem(buf, n, "flags", 5)); while (mhz < endp && (*mhz < '0' || *mhz > '9') && *mhz != '\n') {
if (flags_pos ++mhz;
&& memmem(flags_pos, buf + n - flags_pos, "constant_tsc", 12)
&& memmem(flags_pos, buf + n - flags_pos, "nonstop_tsc", 11)) {
int64_t result = read_cpu_frequency_by_cpuid();
if (result <= 0) {
result = read_cpu_frequency_from_brand_string();
} }
if (result > 0) { while (mhz < endp && *mhz != '\n') {
*invariant_tsc = true; if (*mhz >= '0' && *mhz <= '9') {
return result; result *= 10;
result += *mhz - '0';
if (seen_decpoint)
++ndigits;
} else if (*mhz == '.') {
seen_decpoint = 1;
}
++mhz;
} }
/* Compensate for missing digits at the end. */
while (ndigits++ < 6) {
result *= 10;
}
}
if (invariant_tsc) {
char* flags_pos = static_cast<char*>(memmem(buf, n, "flags", 5));
*invariant_tsc =
(flags_pos &&
memmem(flags_pos, buf + n - flags_pos, "constant_tsc", 12) &&
memmem(flags_pos, buf + n - flags_pos, "nonstop_tsc", 11));
} }
//current frequency is usually not invariant
*invariant_tsc = false;
} }
return read_cpu_current_frequency(buf, n); close (fd);
return result;
} }
// Return value must be >= 0 // Return value must be >= 0
......
...@@ -236,6 +236,17 @@ extern int64_t invariant_cpu_freq; ...@@ -236,6 +236,17 @@ extern int64_t invariant_cpu_freq;
// note: Inlining shortens time cost per-call for 15ns in a loop of many // note: Inlining shortens time cost per-call for 15ns in a loop of many
// calls to this function. // calls to this function.
inline int64_t cpuwide_time_ns() { inline int64_t cpuwide_time_ns() {
#if !defined(BAIDU_INTERNAL)
// nearly impossible to get the correct invariant cpu frequency on
// different CPU and machines. CPU-ID rarely works and frequencies
// in "model name" and "cpu Mhz" are both unreliable.
// Since clock_gettime() in newer glibc/kernel is much faster(~30ns)
// which is closer to the previous impl. of cpuwide_time(~10ns), we
// simply use the monotonic time to get rid of all related issues.
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
return now.tv_sec * 1000000000L + now.tv_nsec;
#else
int64_t cpu_freq = detail::invariant_cpu_freq; int64_t cpu_freq = detail::invariant_cpu_freq;
if (cpu_freq > 0) { if (cpu_freq > 0) {
const uint64_t tsc = detail::clock_cycles(); const uint64_t tsc = detail::clock_cycles();
...@@ -253,6 +264,7 @@ inline int64_t cpuwide_time_ns() { ...@@ -253,6 +264,7 @@ inline int64_t cpuwide_time_ns() {
detail::invariant_cpu_freq = detail::read_invariant_cpu_frequency(); detail::invariant_cpu_freq = detail::read_invariant_cpu_frequency();
return cpuwide_time_ns(); return cpuwide_time_ns();
} }
#endif // defined(BAIDU_INTERNAL)
} }
inline int64_t cpuwide_time_us() { inline int64_t cpuwide_time_us() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment