| Index: src/base/platform/time.cc
|
| diff --git a/src/base/platform/time.cc b/src/base/platform/time.cc
|
| index b2355a33bde275695d13fe49ceb619ddf12911d5..27e8e968ddf9f3be387e2479de61f331d69cc1fd 100644
|
| --- a/src/base/platform/time.cc
|
| +++ b/src/base/platform/time.cc
|
| @@ -51,7 +51,7 @@ int64_t ComputeThreadTicks() {
|
| // microsecond timebase. Minimum requirement is MONOTONIC_CLOCK to be supported
|
| // on the system. FreeBSD 6 has CLOCK_MONOTONIC but defines
|
| // _POSIX_MONOTONIC_CLOCK to -1.
|
| -inline int64_t ClockNow(clockid_t clk_id) {
|
| +V8_INLINE int64_t ClockNow(clockid_t clk_id) {
|
| #if (defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0) || \
|
| defined(V8_OS_BSD) || defined(V8_OS_ANDROID)
|
| struct timespec ts;
|
| @@ -67,6 +67,24 @@ inline int64_t ClockNow(clockid_t clk_id) {
|
| return 0;
|
| #endif
|
| }
|
| +#elif V8_OS_WIN
|
| +V8_INLINE bool IsQPCReliable() {
|
| + v8::base::CPU cpu;
|
| + // On Athlon X2 CPUs (e.g. model 15) QueryPerformanceCounter is unreliable.
|
| + return strcmp(cpu.vendor(), "AuthenticAMD") == 0 && cpu.family() == 15;
|
| +}
|
| +
|
| +// Returns the current value of the performance counter.
|
| +V8_INLINE uint64_t QPCNowRaw() {
|
| + LARGE_INTEGER perf_counter_now = {};
|
| + // According to the MSDN documentation for QueryPerformanceCounter(), this
|
| + // will never fail on systems that run XP or later.
|
| + // https://msdn.microsoft.com/library/windows/desktop/ms644904.aspx
|
| + BOOL result = ::QueryPerformanceCounter(&perf_counter_now);
|
| + DCHECK(result);
|
| + USE(result);
|
| + return perf_counter_now.QuadPart;
|
| +}
|
| #endif // V8_OS_MACOSX
|
|
|
|
|
| @@ -456,15 +474,12 @@ class HighResolutionTickClock final : public TickClock {
|
| virtual ~HighResolutionTickClock() {}
|
|
|
| int64_t Now() override {
|
| - LARGE_INTEGER now;
|
| - BOOL result = QueryPerformanceCounter(&now);
|
| - DCHECK(result);
|
| - USE(result);
|
| + uint64_t now = QPCNowRaw();
|
|
|
| // Intentionally calculate microseconds in a round about manner to avoid
|
| // overflow and precision issues. Think twice before simplifying!
|
| - int64_t whole_seconds = now.QuadPart / ticks_per_second_;
|
| - int64_t leftover_ticks = now.QuadPart % ticks_per_second_;
|
| + int64_t whole_seconds = now / ticks_per_second_;
|
| + int64_t leftover_ticks = now % ticks_per_second_;
|
| int64_t ticks = (whole_seconds * Time::kMicrosecondsPerSecond) +
|
| ((leftover_ticks * Time::kMicrosecondsPerSecond) / ticks_per_second_);
|
|
|
| @@ -529,10 +544,8 @@ struct CreateHighResTickClockTrait {
|
| return tick_clock.Pointer();
|
| }
|
|
|
| - // On Athlon X2 CPUs (e.g. model 15) the QueryPerformanceCounter
|
| - // is unreliable, fallback to the low-resolution tick clock.
|
| - CPU cpu;
|
| - if (strcmp(cpu.vendor(), "AuthenticAMD") == 0 && cpu.family() == 15) {
|
| + // If QPC not reliable, fallback to low-resolution tick clock.
|
| + if (IsQPCReliable()) {
|
| return tick_clock.Pointer();
|
| }
|
|
|
| @@ -621,11 +634,106 @@ ThreadTicks ThreadTicks::Now() {
|
| #elif(defined(_POSIX_THREAD_CPUTIME) && (_POSIX_THREAD_CPUTIME >= 0)) || \
|
| defined(V8_OS_ANDROID)
|
| return ThreadTicks(ClockNow(CLOCK_THREAD_CPUTIME_ID));
|
| +#elif V8_OS_WIN
|
| + return ThreadTicks::GetForThread(::GetCurrentThread());
|
| #else
|
| UNREACHABLE();
|
| return ThreadTicks();
|
| #endif
|
| }
|
|
|
| +
|
| +#if V8_OS_WIN
|
| +ThreadTicks ThreadTicks::GetForThread(const HANDLE& thread_handle) {
|
| + DCHECK(IsSupported());
|
| +
|
| + // Get the number of TSC ticks used by the current thread.
|
| + ULONG64 thread_cycle_time = 0;
|
| + ::QueryThreadCycleTime(thread_handle, &thread_cycle_time);
|
| +
|
| + // Get the frequency of the TSC.
|
| + double tsc_ticks_per_second = TSCTicksPerSecond();
|
| + if (tsc_ticks_per_second == 0)
|
| + return ThreadTicks();
|
| +
|
| + // Return the CPU time of the current thread.
|
| + double thread_time_seconds = thread_cycle_time / tsc_ticks_per_second;
|
| + return ThreadTicks(
|
| + static_cast<int64_t>(thread_time_seconds * Time::kMicrosecondsPerSecond));
|
| +}
|
| +
|
| +// static
|
| +bool ThreadTicks::IsSupportedWin() {
|
| + static bool is_supported = base::CPU().has_non_stop_time_stamp_counter() &&
|
| + !IsQPCReliable();
|
| + return is_supported;
|
| +}
|
| +
|
| +// static
|
| +void ThreadTicks::WaitUntilInitializedWin() {
|
| + while (TSCTicksPerSecond() == 0)
|
| + ::Sleep(10);
|
| +}
|
| +
|
| +double ThreadTicks::TSCTicksPerSecond() {
|
| + DCHECK(IsSupported());
|
| +
|
| + // The value returned by QueryPerformanceFrequency() cannot be used as the TSC
|
| + // frequency, because there is no guarantee that the TSC frequency is equal to
|
| + // the performance counter frequency.
|
| +
|
| + // The TSC frequency is cached in a static variable because it takes some time
|
| + // to compute it.
|
| + static double tsc_ticks_per_second = 0;
|
| + if (tsc_ticks_per_second != 0)
|
| + return tsc_ticks_per_second;
|
| +
|
| + // Increase the thread priority to reduces the chances of having a context
|
| + // switch during a reading of the TSC and the performance counter.
|
| + int previous_priority = ::GetThreadPriority(::GetCurrentThread());
|
| + ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
|
| +
|
| + // The first time that this function is called, make an initial reading of the
|
| + // TSC and the performance counter.
|
| + static const uint64_t tsc_initial = __rdtsc();
|
| + static const uint64_t perf_counter_initial = QPCNowRaw();
|
| +
|
| + // Make a another reading of the TSC and the performance counter every time
|
| + // that this function is called.
|
| + uint64_t tsc_now = __rdtsc();
|
| + uint64_t perf_counter_now = QPCNowRaw();
|
| +
|
| + // Reset the thread priority.
|
| + ::SetThreadPriority(::GetCurrentThread(), previous_priority);
|
| +
|
| + // Make sure that at least 50 ms elapsed between the 2 readings. The first
|
| + // time that this function is called, we don't expect this to be the case.
|
| + // Note: The longer the elapsed time between the 2 readings is, the more
|
| + // accurate the computed TSC frequency will be. The 50 ms value was
|
| + // chosen because local benchmarks show that it allows us to get a
|
| + // stddev of less than 1 tick/us between multiple runs.
|
| + // Note: According to the MSDN documentation for QueryPerformanceFrequency(),
|
| + // this will never fail on systems that run XP or later.
|
| + // https://msdn.microsoft.com/library/windows/desktop/ms644905.aspx
|
| + LARGE_INTEGER perf_counter_frequency = {};
|
| + ::QueryPerformanceFrequency(&perf_counter_frequency);
|
| + DCHECK_GE(perf_counter_now, perf_counter_initial);
|
| + uint64_t perf_counter_ticks = perf_counter_now - perf_counter_initial;
|
| + double elapsed_time_seconds =
|
| + perf_counter_ticks / static_cast<double>(perf_counter_frequency.QuadPart);
|
| +
|
| + const double kMinimumEvaluationPeriodSeconds = 0.05;
|
| + if (elapsed_time_seconds < kMinimumEvaluationPeriodSeconds)
|
| + return 0;
|
| +
|
| + // Compute the frequency of the TSC.
|
| + DCHECK_GE(tsc_now, tsc_initial);
|
| + uint64_t tsc_ticks = tsc_now - tsc_initial;
|
| + tsc_ticks_per_second = tsc_ticks / elapsed_time_seconds;
|
| +
|
| + return tsc_ticks_per_second;
|
| +}
|
| +#endif // V8_OS_WIN
|
| +
|
| } // namespace base
|
| } // namespace v8
|
|
|