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),
allocation_granularity_(page_size_),
perf_counter_frequency_(0),
nano_seconds_per_period_(0),
GetSystemTimePreciseAsFileTime_(NULL) {
SYSTEM_INFO sinfo;
@ -87,6 +88,10 @@ WinEnvIO::WinEnvIO(Env* hosted_env)
ret = QueryPerformanceFrequency(&qpf);
assert(ret == TRUE);
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");
@ -955,22 +960,30 @@ uint64_t WinEnvIO::NowMicros() {
return li.QuadPart;
}
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() {
if (nano_seconds_per_period_ != 0) {
// all std::chrono clocks on windows have the same resolution that is only
// good enough for microseconds but not nanoseconds
// On Windows 8 and Windows 2012 Server
// GetSystemTimePreciseAsFileTime(&current_time) can be used
LARGE_INTEGER li;
QueryPerformanceCounter(&li);
// Convert to nanoseconds first to avoid loss of precision
// and divide by frequency
li.QuadPart *= std::nano::den;
li.QuadPart /= perf_counter_frequency_;
// Convert performance counter to nanoseconds by precomputed ratio.
// Directly multiply nano::den with li.QuadPart causes overflow.
// Only do this when nano::den is divisible by perf_counter_frequency_,
// 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 s;

View File

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