fix NowNanos overflow (#5062)

Summary:
The original implementation of WinEnvIO::NowNanos() has a constant data overflow by:
li.QuadPart *= std::nano::den;
As a result, the api provides a incorrect result.
e.g.:
li.QuadPart=13477844301545
std::nano::den=1e9

The fix uses pre-computed nano_seconds_per_period_ to present the nano seconds per performance counter period, in the case if nano::den is divisible by perf_counter_frequency_. Otherwise it falls back to use high_resolution_clock.
siying ajkr
Pull Request resolved: https://github.com/facebook/rocksdb/pull/5062

Differential Revision: D14426842

Pulled By: anand1976

fbshipit-source-id: 127f1daf423dd4b30edd0dcf8ea0466f468bec12
This commit is contained in:
Burton Li 2019-03-21 15:10:38 -07:00 committed by Facebook Github Bot
parent c84fad7a19
commit 88d85b6820
2 changed files with 26 additions and 12 deletions

View File

@ -73,6 +73,7 @@ WinEnvIO::WinEnvIO(Env* hosted_env)
page_size_(4 * 1024), page_size_(4 * 1024),
allocation_granularity_(page_size_), allocation_granularity_(page_size_),
perf_counter_frequency_(0), perf_counter_frequency_(0),
nano_seconds_per_period_(0),
GetSystemTimePreciseAsFileTime_(NULL) { GetSystemTimePreciseAsFileTime_(NULL) {
SYSTEM_INFO sinfo; SYSTEM_INFO sinfo;
@ -87,6 +88,10 @@ WinEnvIO::WinEnvIO(Env* hosted_env)
ret = QueryPerformanceFrequency(&qpf); ret = QueryPerformanceFrequency(&qpf);
assert(ret == TRUE); assert(ret == TRUE);
perf_counter_frequency_ = qpf.QuadPart; perf_counter_frequency_ = qpf.QuadPart;
if (std::nano::den % perf_counter_frequency_ == 0) {
nano_seconds_per_period_ = std::nano::den / perf_counter_frequency_;
}
} }
HMODULE module = GetModuleHandle("kernel32.dll"); HMODULE module = GetModuleHandle("kernel32.dll");
@ -955,21 +960,29 @@ uint64_t WinEnvIO::NowMicros() {
return li.QuadPart; return li.QuadPart;
} }
using namespace std::chrono; using namespace std::chrono;
return duration_cast<microseconds>(system_clock::now().time_since_epoch()).count(); return duration_cast<microseconds>(
high_resolution_clock::now().time_since_epoch()).count();
} }
uint64_t WinEnvIO::NowNanos() { uint64_t WinEnvIO::NowNanos() {
// all std::chrono clocks on windows have the same resolution that is only if (nano_seconds_per_period_ != 0) {
// good enough for microseconds but not nanoseconds // all std::chrono clocks on windows have the same resolution that is only
// On Windows 8 and Windows 2012 Server // good enough for microseconds but not nanoseconds
// GetSystemTimePreciseAsFileTime(&current_time) can be used // On Windows 8 and Windows 2012 Server
LARGE_INTEGER li; // GetSystemTimePreciseAsFileTime(&current_time) can be used
QueryPerformanceCounter(&li); LARGE_INTEGER li;
// Convert to nanoseconds first to avoid loss of precision QueryPerformanceCounter(&li);
// and divide by frequency // Convert performance counter to nanoseconds by precomputed ratio.
li.QuadPart *= std::nano::den; // Directly multiply nano::den with li.QuadPart causes overflow.
li.QuadPart /= perf_counter_frequency_; // Only do this when nano::den is divisible by perf_counter_frequency_,
return li.QuadPart; // which most likely is the case in reality. If it's not, fall back to
// high_resolution_clock, which may be less precise under old compilers.
li.QuadPart *= nano_seconds_per_period_;
return li.QuadPart;
}
using namespace std::chrono;
return duration_cast<nanoseconds>(
high_resolution_clock::now().time_since_epoch()).count();
} }
Status WinEnvIO::GetHostName(char* name, uint64_t len) { Status WinEnvIO::GetHostName(char* name, uint64_t len) {

View File

@ -198,6 +198,7 @@ private:
size_t page_size_; size_t page_size_;
size_t allocation_granularity_; size_t allocation_granularity_;
uint64_t perf_counter_frequency_; uint64_t perf_counter_frequency_;
uint64_t nano_seconds_per_period_;
FnGetSystemTimePreciseAsFileTime GetSystemTimePreciseAsFileTime_; FnGetSystemTimePreciseAsFileTime GetSystemTimePreciseAsFileTime_;
}; };