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:
parent
c84fad7a19
commit
88d85b6820
@ -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,21 +960,29 @@ 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() {
|
||||
// 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(¤t_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_;
|
||||
return li.QuadPart;
|
||||
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(¤t_time) can be used
|
||||
LARGE_INTEGER li;
|
||||
QueryPerformanceCounter(&li);
|
||||
// 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) {
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user