commit f805d38885a74da57aca066a48c0b012e0015445
parent d034cec7a47fc8676422d69de34055267a45faf2
Author: Jens Stutte <jstutte@mozilla.com>
Date: Tue, 11 Nov 2025 11:25:48 +0000
Bug 1995254 - Always rely on QueryPerformanceCounter for Windows TimeStamp. r=glandium,profiler-reviewers,mstange
QueryPerformanceCounter is considered both reliable and fast since the era of Windows 8, so we can make a huge jump here and move away from GetTickCount always.
This largely simplifies TimeStamp on Windows and brings it on-par in terms of the used data (8 Bytes instead of 24) with all other platforms.
We can eliminate every measured heuristic and rely just on the values given from QueryPerformanceFrequency to determine ticks per second and/or resolution.
For consumers of Now() nothing really changes, as for virtually all "normal" cases they were already seeing QPC values.
But we know at least from bug 1976975 that the old reliability heuristic would strike from time to time in CI and let us fall back to GTC.
There might be other time related weirdnesses in tests coming from that we are not aware of to be related.
Consumers of NowLoRes() will now see the same high resolution (as they do already on MacOS) and practically there is no more reason to use it on Windows.
This guarantees 100% comparability between low- and high-res timestamps, which previously may have led to problems, too.
Note that this will remove support for the MOZ_TIMESTAMP_MODE environment variable, too. There seems to be no usage or documentation of it, it was there probably just for testing purposes.
Differential Revision: https://phabricator.services.mozilla.com/D269724
Diffstat:
11 files changed, 78 insertions(+), 698 deletions(-)
diff --git a/dom/midi/midir_impl/src/lib.rs b/dom/midi/midir_impl/src/lib.rs
@@ -14,18 +14,6 @@ use uuid::Uuid;
* You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate midir;
-#[cfg(target_os = "windows")]
-#[repr(C)]
-#[derive(Clone, Copy)]
-pub struct GeckoTimeStamp {
- gtc: u64,
- qpc: u64,
-
- is_null: u8,
- has_qpc: u8,
-}
-
-#[cfg(not(target_os = "windows"))]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct GeckoTimeStamp {
diff --git a/dom/performance/Performance.cpp b/dom/performance/Performance.cpp
@@ -708,8 +708,8 @@ void Performance::MaybeEmitExternalProfilerMarker(
uint64_t rawStart = startTimeStamp.RawClockMonotonicNanosecondsSinceBoot();
uint64_t rawEnd = endTimeStamp.RawClockMonotonicNanosecondsSinceBoot();
#elif XP_WIN
- uint64_t rawStart = startTimeStamp.RawQueryPerformanceCounterValue().value();
- uint64_t rawEnd = endTimeStamp.RawQueryPerformanceCounterValue().value();
+ uint64_t rawStart = startTimeStamp.RawQueryPerformanceCounterValue();
+ uint64_t rawEnd = endTimeStamp.RawQueryPerformanceCounterValue();
#elif XP_MACOSX
uint64_t rawStart = startTimeStamp.RawMachAbsoluteTimeNanoseconds();
uint64_t rawEnd = endTimeStamp.RawMachAbsoluteTimeNanoseconds();
diff --git a/ipc/glue/IPCMessageUtilsSpecializations.h b/ipc/glue/IPCMessageUtilsSpecializations.h
@@ -27,9 +27,6 @@
#include "mozilla/IntegerRange.h"
#include "mozilla/Maybe.h"
#include "mozilla/TimeStamp.h"
-#ifdef XP_WIN
-# include "mozilla/TimeStamp_windows.h"
-#endif
#include "mozilla/UniquePtr.h"
#include "mozilla/Vector.h"
@@ -435,27 +432,6 @@ struct ParamTraits<mozilla::TimeStamp> {
};
};
-#ifdef XP_WIN
-
-template <>
-struct ParamTraits<mozilla::TimeStampValue> {
- typedef mozilla::TimeStampValue paramType;
- static void Write(MessageWriter* aWriter, const paramType& aParam) {
- WriteParam(aWriter, aParam.mGTC);
- WriteParam(aWriter, aParam.mQPC);
- WriteParam(aWriter, aParam.mIsNull);
- WriteParam(aWriter, aParam.mHasQPC);
- }
- static bool Read(MessageReader* aReader, paramType* aResult) {
- return (ReadParam(aReader, &aResult->mGTC) &&
- ReadParam(aReader, &aResult->mQPC) &&
- ReadParam(aReader, &aResult->mIsNull) &&
- ReadParam(aReader, &aResult->mHasQPC));
- }
-};
-
-#endif
-
template <>
struct ParamTraits<mozilla::dom::ipc::StructuredCloneData> {
typedef mozilla::dom::ipc::StructuredCloneData paramType;
diff --git a/js/src/jit/PerfSpewer.cpp b/js/src/jit/PerfSpewer.cpp
@@ -122,7 +122,7 @@ static uint64_t GetMonotonicTimestamp() {
# ifdef XP_LINUX
return TimeStamp::Now().RawClockMonotonicNanosecondsSinceBoot();
# elif XP_WIN
- return TimeStamp::Now().RawQueryPerformanceCounterValue().value();
+ return TimeStamp::Now().RawQueryPerformanceCounterValue();
# elif XP_DARWIN
return TimeStamp::Now().RawMachAbsoluteTimeNanoseconds();
# else
diff --git a/mozglue/baseprofiler/core/platform.cpp b/mozglue/baseprofiler/core/platform.cpp
@@ -1618,10 +1618,9 @@ static void MaybeWriteRawStartTimeValue(SpliceableJSONWriter& aWriter,
#endif
#ifdef XP_WIN
- Maybe<uint64_t> startTimeQPC = aStartTime.RawQueryPerformanceCounterValue();
- if (startTimeQPC)
- aWriter.DoubleProperty("startTimeAsQueryPerformanceCounterValue",
- static_cast<double>(*startTimeQPC));
+ uint64_t startTimeQPC = aStartTime.RawQueryPerformanceCounterValue();
+ aWriter.DoubleProperty("startTimeAsQueryPerformanceCounterValue",
+ static_cast<double>(startTimeQPC));
#endif
}
diff --git a/mozglue/misc/TimeStamp.h b/mozglue/misc/TimeStamp.h
@@ -21,19 +21,9 @@ template <typename T>
struct ParamTraits;
} // namespace IPC
-#ifdef XP_WIN
-// defines TimeStampValue as a complex value keeping both
-// GetTickCount and QueryPerformanceCounter values
-# include "TimeStamp_windows.h"
-
-# include "mozilla/Maybe.h" // For TimeStamp::RawQueryPerformanceCounterValue
-#endif
-
namespace mozilla {
-#ifndef XP_WIN
-typedef uint64_t TimeStampValue;
-#endif
+using TimeStampValue = uint64_t;
class TimeStamp;
class TimeStampTests;
@@ -53,11 +43,7 @@ class BaseTimeDurationPlatformUtils {
* Instances of this class represent the length of an interval of time.
* Negative durations are allowed, meaning the end is before the start.
*
- * Internally the duration is stored as a int64_t in units of
- * PR_TicksPerSecond() when building with NSPR interval timers, or a
- * system-dependent unit when building with system clocks. The
- * system-dependent unit must be constant, otherwise the semantics of
- * this class would be broken.
+ * Internally the duration is stored as a system-dependent unit.
*
* The ValueCalculator template parameter determines how arithmetic
* operations are performed on the integer count of ticks (mValue).
@@ -355,11 +341,8 @@ typedef BaseTimeDuration<TimeDurationValueCalculator> TimeDuration;
* to a TimeStamp to get a new TimeStamp. You can't do something
* meaningless like add two TimeStamps.
*
- * Internally this is implemented as either a wrapper around
- * - high-resolution, monotonic, system clocks if they exist on this
- * platform
- * - PRIntervalTime otherwise. We detect wraparounds of
- * PRIntervalTime and work around them.
+ * Internally this is implemented as a wrapper around high-resolution,
+ * monotonic, platform-dependent system clocks.
*
* This class is similar to C++11's time_point, however it is
* explicitly nullable and provides an IsNull() method. time_point
@@ -371,20 +354,6 @@ typedef BaseTimeDuration<TimeDurationValueCalculator> TimeDuration;
* Note that, since TimeStamp objects are small, prefer to pass them by value
* unless there is a specific reason not to do so.
*/
-#if defined(XP_WIN)
-// If this static_assert fails then possibly the warning comment below is no
-// longer valid and should be removed.
-static_assert(sizeof(TimeStampValue) > 8);
-#endif
-/*
- * WARNING: On Windows, each TimeStamp is represented internally by two
- * different raw values (one from GTC and one from QPC) and which value gets
- * used for a given operation depends on whether both operands have QPC values
- * or not. This duality of values can lead to some surprising results when
- * mixing TimeStamps with and without QPC values, such as comparisons being
- * non-transitive (ie, a > b > c might not imply a > c). See bug 1829983 for
- * more details/an example.
- */
class TimeStamp {
public:
using DurationType = TimeDuration;
@@ -433,13 +402,21 @@ class TimeStamp {
*
* Now() is trying to ensure the best possible precision on each platform,
* at least one millisecond.
- *
- * NowLoRes() has been introduced to workaround performance problems of
- * QueryPerformanceCounter on the Windows platform. NowLoRes() is giving
- * lower precision, usually 15.6 ms, but with very good performance benefit.
- * Use it for measurements of longer times, like >200ms timeouts.
*/
static TimeStamp Now() { return Now(true); }
+
+ /**
+ * Return a (coarse) timestamp reflecting the current elapsed system time.
+ * NowLoRes() behaves different depending on the OS:
+ *
+ * Windows: NowLoRes() == Now(), uses always QueryPerformanceCounter.
+ * MacOS: NowLoRes() == Now(), uses always mach_absolute_time.
+ * Posix: If the kernel supports CLOCK_MONOTONIC_COARSE use that,
+ * CLOCK_MONOTONIC otherwise.
+ *
+ * Used to promise better performance, which might still be true only for
+ * Posix.
+ */
static TimeStamp NowLoRes() { return Now(false); }
/**
@@ -481,10 +458,8 @@ class TimeStamp {
#endif
#ifdef XP_WIN
- Maybe<uint64_t> RawQueryPerformanceCounterValue() const {
- // mQPC is stored in `mt` i.e. QueryPerformanceCounter * 1000
- // so divide out the 1000
- return mValue.mHasQPC ? Some(mValue.mQPC / 1000ULL) : Nothing();
+ uint64_t RawQueryPerformanceCounterValue() const {
+ return static_cast<uint64_t>(mValue);
}
#endif
diff --git a/mozglue/misc/TimeStamp_windows.cpp b/mozglue/misc/TimeStamp_windows.cpp
@@ -4,548 +4,111 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-// Implement TimeStamp::Now() with QueryPerformanceCounter() controlled with
-// values of GetTickCount64().
-
#include "mozilla/DynamicallyLinkedFunctionPtr.h"
-#include "mozilla/MathAlgorithms.h"
#include "mozilla/TimeStamp.h"
-#include "mozilla/Uptime.h"
-
-#include <stdio.h>
-#include <stdlib.h>
#include <intrin.h>
#include <windows.h>
-// To enable logging define to your favorite logging API
-#define LOG(x)
-
-class AutoCriticalSection {
- public:
- explicit AutoCriticalSection(LPCRITICAL_SECTION aSection)
- : mSection(aSection) {
- ::EnterCriticalSection(mSection);
- }
- ~AutoCriticalSection() { ::LeaveCriticalSection(mSection); }
-
- private:
- LPCRITICAL_SECTION mSection;
-};
-
-// Estimate of the smallest duration of time we can measure.
-static volatile ULONGLONG sResolution;
-static volatile ULONGLONG sResolutionSigDigs;
-static const double kNsPerSecd = 1000000000.0;
-static const LONGLONG kNsPerMillisec = 1000000;
-
-// ----------------------------------------------------------------------------
-// Global constants
-// ----------------------------------------------------------------------------
-
-// Tolerance to failures settings.
+// Historical note: We used to sample both QueryPerformanceCounter (QPC) and
+// GetTickCount (GTC) timestamps in the past, as very early implementations of
+// QPC were buggy. We had heuristics to determine if QPC is unreliable and
+// would have switched to GTC in case, which could cause unexpected time
+// travels between QPC and GPC values when that occured.
//
-// What is the interval we want to have failure free.
-// in [ms]
-static const uint32_t kFailureFreeInterval = 5000;
-// How many failures we are willing to tolerate in the interval.
-static const uint32_t kMaxFailuresPerInterval = 4;
-// What is the threshold to treat fluctuations as actual failures.
-// in [ms]
-static const uint32_t kFailureThreshold = 50;
-
-// If we are not able to get the value of GTC time increment, use this value
-// which is the most usual increment.
-static const DWORD kDefaultTimeIncrement = 156001;
+// Since Windows 8 together with the then modern CPUs, QPC became both reliable
+// and almost as fast as GTC timestamps and provides a much higher resolution.
+// QPC in general exists long enough even on older systems than Windows 8, such
+// that we can just always rely on it, as we do in rust.
// ----------------------------------------------------------------------------
// Global variables, not changing at runtime
// ----------------------------------------------------------------------------
-// Result of QueryPerformanceFrequency
-// We use default of 1 for the case we can't use QueryPerformanceCounter
-// to make mt/ms conversions work despite that.
-static uint64_t sFrequencyPerSec = 1;
-
-namespace mozilla {
-
-MFBT_API uint64_t GetQueryPerformanceFrequencyPerSec() {
- return sFrequencyPerSec;
-}
-
-} // namespace mozilla
-
-// How much we are tolerant to GTC occasional loose of resoltion.
-// This number says how many multiples of the minimal GTC resolution
-// detected on the system are acceptable. This number is empirical.
-static const LONGLONG kGTCTickLeapTolerance = 4;
-
-// Base tolerance (more: "inability of detection" range) threshold is calculated
-// dynamically, and kept in sGTCResolutionThreshold.
-//
-// Schematically, QPC worked "100%" correctly if ((GTC_now - GTC_epoch) -
-// (QPC_now - QPC_epoch)) was in [-sGTCResolutionThreshold,
-// sGTCResolutionThreshold] interval every time we'd compared two time stamps.
-// If not, then we check the overflow behind this basic threshold
-// is in kFailureThreshold. If not, we condider it as a QPC failure. If too
-// many failures in short time are detected, QPC is considered faulty and
-// disabled.
-//
-// Kept in [mt]
-static LONGLONG sGTCResolutionThreshold;
-
-// If QPC is found faulty for two stamps in this interval, we engage
-// the fault detection algorithm. For duration larger then this limit
-// we bypass using durations calculated from QPC when jitter is detected,
-// but don't touch the sUseQPC flag.
-//
-// Value is in [ms].
-static const uint32_t kHardFailureLimit = 2000;
-// Conversion to [mt]
-static LONGLONG sHardFailureLimit;
-
-// Conversion of kFailureFreeInterval and kFailureThreshold to [mt]
-static LONGLONG sFailureFreeInterval;
-static LONGLONG sFailureThreshold;
-
-// ----------------------------------------------------------------------------
-// Systemm status flags
-// ----------------------------------------------------------------------------
-
-// Flag for stable TSC that indicates platform where QPC is stable.
-static bool sHasStableTSC = false;
-
-// ----------------------------------------------------------------------------
-// Global state variables, changing at runtime
-// ----------------------------------------------------------------------------
-
-// Initially true, set to false when QPC is found unstable and never
-// returns back to true since that time.
-static bool volatile sUseQPC = true;
+// Result of QueryPerformanceFrequency, set only once on startup.
+static double sTicksPerSecd;
+static double sTicksPerMsd;
// ----------------------------------------------------------------------------
-// Global lock
+// Useful constants
// ----------------------------------------------------------------------------
-// Thread spin count before entering the full wait state for sTimeStampLock.
-// Inspired by Rob Arnold's work on PRMJ_Now().
-static const DWORD kLockSpinCount = 4096;
-
-// Common mutex (thanks the relative complexity of the logic, this is better
-// then using CMPXCHG8B.)
-// It is protecting the globals bellow.
-static CRITICAL_SECTION sTimeStampLock;
-
-// ----------------------------------------------------------------------------
-// Global lock protected variables
-// ----------------------------------------------------------------------------
-
-// Timestamp in future until QPC must behave correctly.
-// Set to now + kFailureFreeInterval on first QPC failure detection.
-// Set to now + E * kFailureFreeInterval on following errors,
-// where E is number of errors detected during last kFailureFreeInterval
-// milliseconds, calculated simply as:
-// E = (sFaultIntoleranceCheckpoint - now) / kFailureFreeInterval + 1.
-// When E > kMaxFailuresPerInterval -> disable QPC.
-//
-// Kept in [mt]
-static ULONGLONG sFaultIntoleranceCheckpoint = 0;
+static constexpr double kMsPerSecd = 1000.0;
+// Note: Resolution used to be sampled based on a loop of QPC calls.
+// While it is true that on most systems we cannot expect to subsequently
+// sample QPC values as fast as the QPC frequency, we still will get that
+// as resolution of the sampled values, that is we have 1 tick resolution.
+static constexpr LONGLONG kResolution = 1;
namespace mozilla {
-// Result is in [mt]
+// Result is in ticks.
static inline ULONGLONG PerformanceCounter() {
LARGE_INTEGER pc;
- ::QueryPerformanceCounter(&pc);
-
- // QueryPerformanceCounter may slightly jitter (not be 100% monotonic.)
- // This is a simple go-backward protection for such a faulty hardware.
- AutoCriticalSection lock(&sTimeStampLock);
-
- static decltype(LARGE_INTEGER::QuadPart) last;
- if (last > pc.QuadPart) {
- return last * 1000ULL;
- }
- last = pc.QuadPart;
- return pc.QuadPart * 1000ULL;
+ MOZ_ALWAYS_TRUE(::QueryPerformanceCounter(&pc));
+ return pc.QuadPart;
}
-static void InitThresholds() {
- DWORD timeAdjustment = 0, timeIncrement = 0;
- BOOL timeAdjustmentDisabled;
- GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
- &timeAdjustmentDisabled);
-
- LOG(("TimeStamp: timeIncrement=%d [100ns]", timeIncrement));
-
- if (!timeIncrement) {
- timeIncrement = kDefaultTimeIncrement;
- }
-
- // Ceiling to a millisecond
- // Example values: 156001, 210000
- DWORD timeIncrementCeil = timeIncrement;
- // Don't want to round up if already rounded, values will be: 156000, 209999
- timeIncrementCeil -= 1;
- // Convert to ms, values will be: 15, 20
- timeIncrementCeil /= 10000;
- // Round up, values will be: 16, 21
- timeIncrementCeil += 1;
- // Convert back to 100ns, values will be: 160000, 210000
- timeIncrementCeil *= 10000;
-
- // How many milli-ticks has the interval rounded up
- LONGLONG ticksPerGetTickCountResolutionCeiling =
- (int64_t(timeIncrementCeil) * sFrequencyPerSec) / 10000LL;
-
- // GTC may jump by 32 (2*16) ms in two steps, therefor use the ceiling value.
- sGTCResolutionThreshold =
- LONGLONG(kGTCTickLeapTolerance * ticksPerGetTickCountResolutionCeiling);
-
- sHardFailureLimit = ms2mt(kHardFailureLimit);
- sFailureFreeInterval = ms2mt(kFailureFreeInterval);
- sFailureThreshold = ms2mt(kFailureThreshold);
-}
-
-static void InitResolution() {
- // 10 total trials is arbitrary: what we're trying to avoid by
- // looping is getting unlucky and being interrupted by a context
- // switch or signal, or being bitten by paging/cache effects
-
- ULONGLONG minres = ~0ULL;
- if (sUseQPC) {
- int loops = 10;
- do {
- ULONGLONG start = PerformanceCounter();
- ULONGLONG end = PerformanceCounter();
-
- ULONGLONG candidate = (end - start);
- if (candidate < minres) {
- minres = candidate;
- }
- } while (--loops && minres);
-
- if (0 == minres) {
- minres = 1;
- }
- } else {
- // GetTickCount has only ~16ms known resolution
- minres = ms2mt(16);
- }
-
- // Converting minres that is in [mt] to nanosecods, multiplicating
- // the argument to preserve resolution.
- ULONGLONG result = mt2ms(minres * kNsPerMillisec);
- if (0 == result) {
- result = 1;
- }
-
- sResolution = result;
-
- // find the number of significant digits in mResolution, for the
- // sake of ToSecondsSigDigits()
- ULONGLONG sigDigs;
- for (sigDigs = 1; !(sigDigs == result || 10 * sigDigs > result);
- sigDigs *= 10);
-
- sResolutionSigDigs = sigDigs;
-}
-
-// ----------------------------------------------------------------------------
-// TimeStampValue implementation
-// ----------------------------------------------------------------------------
-MFBT_API TimeStampValue& TimeStampValue::operator+=(const int64_t aOther) {
- mGTC += aOther;
- mQPC += aOther;
- return *this;
-}
-
-MFBT_API TimeStampValue& TimeStampValue::operator-=(const int64_t aOther) {
- mGTC -= aOther;
- mQPC -= aOther;
- return *this;
-}
-
-// If the duration is less then two seconds, perform check of QPC stability
-// by comparing both GTC and QPC calculated durations of this and aOther.
-MFBT_API uint64_t TimeStampValue::CheckQPC(const TimeStampValue& aOther) const {
- uint64_t deltaGTC = mGTC - aOther.mGTC;
-
- if (!mHasQPC || !aOther.mHasQPC) { // Both not holding QPC
- return deltaGTC;
- }
-
- uint64_t deltaQPC = mQPC - aOther.mQPC;
-
- if (sHasStableTSC) { // For stable TSC there is no need to check
- return deltaQPC;
- }
-
- // Check QPC is sane before using it.
- int64_t diff = DeprecatedAbs(int64_t(deltaQPC) - int64_t(deltaGTC));
- if (diff <= sGTCResolutionThreshold) {
- return deltaQPC;
- }
-
- // Treat absolutely for calibration purposes
- int64_t duration = DeprecatedAbs(int64_t(deltaGTC));
- int64_t overflow = diff - sGTCResolutionThreshold;
-
- LOG(("TimeStamp: QPC check after %llums with overflow %1.4fms",
- mt2ms(duration), mt2ms_f(overflow)));
-
- if (overflow <= sFailureThreshold) { // We are in the limit, let go.
- return deltaQPC;
- }
-
- // QPC deviates, don't use it, since now this method may only return deltaGTC.
-
- if (!sUseQPC) { // QPC already disabled, no need to run the fault tolerance
- // algorithm.
- return deltaGTC;
- }
-
- LOG(("TimeStamp: QPC jittered over failure threshold"));
-
- if (duration < sHardFailureLimit) {
- // Interval between the two time stamps is very short, consider
- // QPC as unstable and record a failure.
- uint64_t now = ms2mt(GetTickCount64());
-
- AutoCriticalSection lock(&sTimeStampLock);
-
- if (sFaultIntoleranceCheckpoint && sFaultIntoleranceCheckpoint > now) {
- // There's already been an error in the last fault intollerant interval.
- // Time since now to the checkpoint actually holds information on how many
- // failures there were in the failure free interval we have defined.
- uint64_t failureCount =
- (sFaultIntoleranceCheckpoint - now + sFailureFreeInterval - 1) /
- sFailureFreeInterval;
- if (failureCount > kMaxFailuresPerInterval) {
- sUseQPC = false;
- LOG(("TimeStamp: QPC disabled"));
- } else {
- // Move the fault intolerance checkpoint more to the future, prolong it
- // to reflect the number of detected failures.
- ++failureCount;
- sFaultIntoleranceCheckpoint = now + failureCount * sFailureFreeInterval;
- LOG(("TimeStamp: recording %dth QPC failure", failureCount));
- }
- } else {
- // Setup fault intolerance checkpoint in the future for first detected
- // error.
- sFaultIntoleranceCheckpoint = now + sFailureFreeInterval;
- LOG(("TimeStamp: recording 1st QPC failure"));
- }
- }
-
- return deltaGTC;
-}
-
-MFBT_API uint64_t
-TimeStampValue::operator-(const TimeStampValue& aOther) const {
- if (IsNull() && aOther.IsNull()) {
- return uint64_t(0);
- }
-
- return CheckQPC(aOther);
+static void InitConstants() {
+ // Query the frequency from QPC and rely on it for all values.
+ LARGE_INTEGER freq;
+ bool hasQPC = ::QueryPerformanceFrequency(&freq);
+ MOZ_RELEASE_ASSERT(hasQPC);
+ sTicksPerSecd = double(freq.QuadPart);
+ sTicksPerMsd = sTicksPerSecd / kMsPerSecd;
}
-class TimeStampValueTests {
- // Check that nullity is set/not set correctly.
- static_assert(TimeStampValue{0}.IsNull());
- static_assert(!TimeStampValue{1}.IsNull());
-
- // Check that we ignore GTC when both TimeStampValues have QPC. (In each of
- // these tests, looking at GTC would give a different result.)
- static_assert(TimeStampValue{1, 2, true} < TimeStampValue{1, 3, true});
- static_assert(!(TimeStampValue{1, 2, true} == TimeStampValue{1, 3, true}));
-
- static_assert(TimeStampValue{2, 2, true} < TimeStampValue{1, 3, true});
- static_assert(TimeStampValue{2, 2, true} <= TimeStampValue{1, 3, true});
- static_assert(!(TimeStampValue{2, 2, true} > TimeStampValue{1, 3, true}));
-
- static_assert(TimeStampValue{1, 3, true} > TimeStampValue{1, 2, true});
- static_assert(!(TimeStampValue{1, 3, true} == TimeStampValue{1, 2, true}));
-
- static_assert(TimeStampValue{1, 3, true} > TimeStampValue{2, 2, true});
- static_assert(TimeStampValue{1, 3, true} >= TimeStampValue{2, 2, true});
- static_assert(!(TimeStampValue{1, 3, true} < TimeStampValue{2, 2, true}));
-
- static_assert(TimeStampValue{1, 3, true} == TimeStampValue{2, 3, true});
- static_assert(!(TimeStampValue{1, 3, true} < TimeStampValue{2, 3, true}));
-
- static_assert(TimeStampValue{1, 2, true} != TimeStampValue{1, 3, true});
- static_assert(!(TimeStampValue{1, 2, true} == TimeStampValue{1, 3, true}));
-
- // Check that, if either TimeStampValue doesn't have QPC, we only look at the
- // GTC values. These are the same cases as above, except that we accept the
- // opposite results because we turn off QPC on one or both of the
- // TimeStampValue's.
- static_assert(TimeStampValue{1, 2, false} == TimeStampValue{1, 3, true});
- static_assert(TimeStampValue{1, 2, true} == TimeStampValue{1, 3, false});
- static_assert(TimeStampValue{1, 2, false} == TimeStampValue{1, 3, false});
-
- static_assert(TimeStampValue{2, 2, false} > TimeStampValue{1, 3, true});
- static_assert(TimeStampValue{2, 2, true} > TimeStampValue{1, 3, false});
- static_assert(TimeStampValue{2, 2, false} > TimeStampValue{1, 3, false});
-
- static_assert(TimeStampValue{1, 3, false} == TimeStampValue{1, 2, true});
- static_assert(TimeStampValue{1, 3, true} == TimeStampValue{1, 2, false});
- static_assert(TimeStampValue{1, 3, false} == TimeStampValue{1, 2, false});
-
- static_assert(TimeStampValue{1, 3, false} < TimeStampValue{2, 2, true});
- static_assert(TimeStampValue{1, 3, true} < TimeStampValue{2, 2, false});
- static_assert(TimeStampValue{1, 3, false} < TimeStampValue{2, 2, false});
-
- static_assert(TimeStampValue{1, 3, false} < TimeStampValue{2, 3, true});
- static_assert(TimeStampValue{1, 3, true} < TimeStampValue{2, 3, false});
- static_assert(TimeStampValue{1, 3, false} < TimeStampValue{2, 3, false});
-
- static_assert(TimeStampValue{1, 2, false} == TimeStampValue{1, 3, true});
- static_assert(TimeStampValue{1, 2, true} == TimeStampValue{1, 3, false});
- static_assert(TimeStampValue{1, 2, false} == TimeStampValue{1, 3, false});
-};
-
// ----------------------------------------------------------------------------
// TimeDuration and TimeStamp implementation
// ----------------------------------------------------------------------------
MFBT_API double BaseTimeDurationPlatformUtils::ToSeconds(int64_t aTicks) {
- // Converting before arithmetic avoids blocked store forward
- return double(aTicks) / (double(sFrequencyPerSec) * 1000.0);
+ return double(aTicks) / sTicksPerSecd;
}
MFBT_API double BaseTimeDurationPlatformUtils::ToSecondsSigDigits(
int64_t aTicks) {
- // don't report a value < mResolution ...
- LONGLONG resolution = sResolution;
- LONGLONG resolutionSigDigs = sResolutionSigDigs;
- LONGLONG valueSigDigs = resolution * (aTicks / resolution);
- // and chop off insignificant digits
- valueSigDigs = resolutionSigDigs * (valueSigDigs / resolutionSigDigs);
- return double(valueSigDigs) / kNsPerSecd;
+ // As we fix the resolution to 1, all digits are significant and there are
+ // no extra calculations needed. Ensure we do not change this inadvertedly.
+ static_assert(kResolution == 1);
+ return ToSeconds(aTicks);
}
MFBT_API int64_t
BaseTimeDurationPlatformUtils::TicksFromMilliseconds(double aMilliseconds) {
- double result = ms2mt(aMilliseconds);
+ double result = sTicksPerMsd * aMilliseconds;
// NOTE: this MUST be a >= test, because int64_t(double(INT64_MAX))
// overflows and gives INT64_MIN.
if (result >= double(INT64_MAX)) {
return INT64_MAX;
- } else if (result <= double(INT64_MIN)) {
+ }
+ if (result <= double(INT64_MIN)) {
return INT64_MIN;
}
- return result;
+ return (int64_t)result;
}
MFBT_API int64_t BaseTimeDurationPlatformUtils::ResolutionInTicks() {
- return static_cast<int64_t>(sResolution);
-}
-
-static bool HasStableTSC() {
-#if defined(_M_ARM64)
- // AArch64 defines that its system counter run at a constant rate
- // regardless of the current clock frequency of the system. See "The
- // Generic Timer", section D7, in the ARMARM for ARMv8.
- return true;
-#else
- union {
- int regs[4];
- struct {
- int nIds;
- char cpuString[12];
- };
- } cpuInfo;
-
- __cpuid(cpuInfo.regs, 0);
- // Only allow Intel or AMD CPUs for now.
- // The order of the registers is reg[1], reg[3], reg[2]. We just adjust the
- // string so that we can compare in one go.
- if (_strnicmp(cpuInfo.cpuString, "GenuntelineI", sizeof(cpuInfo.cpuString)) &&
- _strnicmp(cpuInfo.cpuString, "AuthcAMDenti", sizeof(cpuInfo.cpuString))) {
- return false;
- }
-
- int regs[4];
-
- // detect if the Advanced Power Management feature is supported
- __cpuid(regs, 0x80000000);
- if ((unsigned int)regs[0] < 0x80000007) {
- // XXX should we return true here? If there is no APM there may be
- // no way how TSC can run out of sync among cores.
- return false;
- }
-
- __cpuid(regs, 0x80000007);
- // if bit 8 is set than TSC will run at a constant rate
- // in all ACPI P-states, C-states and T-states
- return regs[3] & (1 << 8);
-#endif
+ return static_cast<int64_t>(kResolution);
}
+// Note that we init early enough during startup such that we are supposed to
+// not yet have started other threads which could try to use us.
static bool gInitialized = false;
MFBT_API void TimeStamp::Startup() {
if (gInitialized) {
return;
}
-
+ InitConstants();
gInitialized = true;
-
- // Decide which implementation to use for the high-performance timer.
-
- InitializeCriticalSectionAndSpinCount(&sTimeStampLock, kLockSpinCount);
-
- bool forceGTC = false;
- bool forceQPC = false;
-
- char* modevar = getenv("MOZ_TIMESTAMP_MODE");
- if (modevar) {
- if (!strcmp(modevar, "QPC")) {
- forceQPC = true;
- } else if (!strcmp(modevar, "GTC")) {
- forceGTC = true;
- }
- }
-
- LARGE_INTEGER freq;
- sUseQPC = !forceGTC && ::QueryPerformanceFrequency(&freq);
- if (!sUseQPC) {
- // No Performance Counter. Fall back to use GetTickCount64.
- InitResolution();
-
- LOG(("TimeStamp: using GetTickCount64"));
- return;
- }
-
- sHasStableTSC = forceQPC || HasStableTSC();
- LOG(("TimeStamp: HasStableTSC=%d", sHasStableTSC));
-
- sFrequencyPerSec = freq.QuadPart;
- LOG(("TimeStamp: QPC frequency=%llu", sFrequencyPerSec));
-
- InitThresholds();
- InitResolution();
-
- return;
}
-MFBT_API void TimeStamp::Shutdown() { DeleteCriticalSection(&sTimeStampLock); }
-
-TimeStampValue NowInternal(bool aHighResolution) {
- // sUseQPC is volatile
- bool useQPC = (aHighResolution && sUseQPC);
-
- // Both values are in [mt] units.
- ULONGLONG QPC = useQPC ? PerformanceCounter() : uint64_t(0);
- ULONGLONG GTC = ms2mt(GetTickCount64());
- return TimeStampValue(GTC, QPC, useQPC);
-}
+MFBT_API void TimeStamp::Shutdown() {}
MFBT_API TimeStamp TimeStamp::Now(bool aHighResolution) {
- return TimeStamp(NowInternal(aHighResolution));
+ MOZ_ASSERT(gInitialized);
+ return TimeStamp((TimeStampValue)PerformanceCounter());
}
// Computes and returns the process uptime in microseconds.
diff --git a/mozglue/misc/TimeStamp_windows.h b/mozglue/misc/TimeStamp_windows.h
@@ -1,113 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_TimeStamp_windows_h
-#define mozilla_TimeStamp_windows_h
-
-#include "mozilla/Types.h"
-
-namespace mozilla {
-
-/**
- * The [mt] unit:
- *
- * Many values are kept in ticks of the Performance Counter x 1000,
- * further just referred as [mt], meaning milli-ticks.
- *
- * This is needed to preserve maximum precision of the performance frequency
- * representation. GetTickCount64 values in milliseconds are multiplied with
- * frequency per second. Therefore we need to multiply QPC value by 1000 to
- * have the same units to allow simple arithmentic with both QPC and GTC.
- */
-#define ms2mt(x) ((x) * mozilla::GetQueryPerformanceFrequencyPerSec())
-#define mt2ms(x) ((x) / mozilla::GetQueryPerformanceFrequencyPerSec())
-#define mt2ms_f(x) (double(x) / mozilla::GetQueryPerformanceFrequencyPerSec())
-
-MFBT_API uint64_t GetQueryPerformanceFrequencyPerSec();
-
-class TimeStamp;
-class TimeStampValue;
-class TimeStampValueTests;
-class TimeStampTests;
-
-TimeStampValue NowInternal(bool aHighResolution);
-
-class TimeStampValue {
- friend TimeStampValue NowInternal(bool);
- friend bool IsCanonicalTimeStamp(TimeStampValue);
- friend struct IPC::ParamTraits<mozilla::TimeStampValue>;
- friend class TimeStamp;
- friend class TimeStampValueTests;
- friend class TimeStampTests;
-
- // Both QPC and GTC are kept in [mt] units.
- uint64_t mGTC;
- uint64_t mQPC;
-
- bool mIsNull;
- bool mHasQPC;
-
- constexpr MFBT_API TimeStampValue(uint64_t aGTC, uint64_t aQPC, bool aHasQPC)
- : mGTC(aGTC),
- mQPC(aQPC),
- mIsNull(aGTC == 0 && aQPC == 0),
- mHasQPC(aHasQPC) {}
-
- // This constructor should be explicit but it is replacing a constructor that
- // was MOZ_IMPLICIT and there are many locations that are using the automatic
- // conversion.
- constexpr MOZ_IMPLICIT MFBT_API TimeStampValue(uint64_t aGTCAndQPC)
- : TimeStampValue(aGTCAndQPC, aGTCAndQPC, true) {}
-
- MFBT_API uint64_t CheckQPC(const TimeStampValue& aOther) const;
-
- public:
- MFBT_API uint64_t operator-(const TimeStampValue& aOther) const;
-
- TimeStampValue operator+(const int64_t aOther) const {
- return TimeStampValue(mGTC + aOther, mQPC + aOther, mHasQPC);
- }
- TimeStampValue operator-(const int64_t aOther) const {
- return TimeStampValue(mGTC - aOther, mQPC - aOther, mHasQPC);
- }
- MFBT_API TimeStampValue& operator+=(const int64_t aOther);
- MFBT_API TimeStampValue& operator-=(const int64_t aOther);
-
- constexpr bool operator<(const TimeStampValue& aOther) const {
- return mHasQPC && aOther.mHasQPC ? mQPC < aOther.mQPC : mGTC < aOther.mGTC;
- }
- constexpr bool operator>(const TimeStampValue& aOther) const {
- return mHasQPC && aOther.mHasQPC ? mQPC > aOther.mQPC : mGTC > aOther.mGTC;
- }
- constexpr bool operator<=(const TimeStampValue& aOther) const {
- return mHasQPC && aOther.mHasQPC ? mQPC <= aOther.mQPC
- : mGTC <= aOther.mGTC;
- }
- constexpr bool operator>=(const TimeStampValue& aOther) const {
- return mHasQPC && aOther.mHasQPC ? mQPC >= aOther.mQPC
- : mGTC >= aOther.mGTC;
- }
- constexpr bool operator==(const TimeStampValue& aOther) const {
- return mHasQPC && aOther.mHasQPC ? mQPC == aOther.mQPC
- : mGTC == aOther.mGTC;
- }
- constexpr bool operator!=(const TimeStampValue& aOther) const {
- return mHasQPC && aOther.mHasQPC ? mQPC != aOther.mQPC
- : mGTC != aOther.mGTC;
- }
- constexpr bool IsNull() const { return mIsNull; }
-
-#if defined(DEBUG)
- uint64_t GTC() const { return mGTC; }
- uint64_t QPC() const { return mQPC; }
-
- bool HasQPC() const { return mHasQPC; }
-#endif
-};
-
-} // namespace mozilla
-
-#endif /* mozilla_TimeStamp_h */
diff --git a/mozglue/misc/moz.build b/mozglue/misc/moz.build
@@ -38,7 +38,6 @@ if CONFIG["OS_ARCH"] == "WINNT":
"PreXULSkeletonUI.h",
"StackWalk_windows.h",
"StackWalkThread.h",
- "TimeStamp_windows.h",
"WindowsDpiAwareness.h",
]
diff --git a/tools/profiler/core/platform.cpp b/tools/profiler/core/platform.cpp
@@ -3363,10 +3363,9 @@ static void MaybeWriteRawStartTimeValue(SpliceableJSONWriter& aWriter,
#endif
#ifdef XP_WIN
- Maybe<uint64_t> startTimeQPC = aStartTime.RawQueryPerformanceCounterValue();
- if (startTimeQPC)
- aWriter.DoubleProperty("startTimeAsQueryPerformanceCounterValue",
- static_cast<double>(*startTimeQPC));
+ uint64_t startTimeQPC = aStartTime.RawQueryPerformanceCounterValue();
+ aWriter.DoubleProperty("startTimeAsQueryPerformanceCounterValue",
+ static_cast<double>(startTimeQPC));
#endif
}
diff --git a/tools/profiler/public/ETWTools.h b/tools/profiler/public/ETWTools.h
@@ -247,14 +247,8 @@ static inline void CreateDataDescForPayloadNonPOD(
static inline void CreateDataDescForPayloadNonPOD(
PayloadBuffer& aBuffer, EVENT_DATA_DESCRIPTOR& aDescriptor,
const mozilla::TimeStamp& aPayload) {
- if (aPayload.RawQueryPerformanceCounterValue().isNothing()) {
- // This should never happen?
- EventDataDescCreate(&aDescriptor, nullptr, 0);
- return;
- }
-
- CreateDataDescForPayloadPOD(
- aBuffer, aDescriptor, aPayload.RawQueryPerformanceCounterValue().value());
+ CreateDataDescForPayloadPOD(aBuffer, aDescriptor,
+ aPayload.RawQueryPerformanceCounterValue());
}
static inline void CreateDataDescForPayloadNonPOD(
@@ -308,13 +302,13 @@ static inline void StoreBaseEventDataDesc(
const mozilla::MarkerOptions& aOptions) {
if (aOptions.IsTimingUnspecified()) {
aStorage.mStartTime =
- mozilla::TimeStamp::Now().RawQueryPerformanceCounterValue().value();
+ mozilla::TimeStamp::Now().RawQueryPerformanceCounterValue();
aStorage.mPhase = 0;
} else {
aStorage.mStartTime =
- aOptions.Timing().StartTime().RawQueryPerformanceCounterValue().value();
+ aOptions.Timing().StartTime().RawQueryPerformanceCounterValue();
aStorage.mEndTime =
- aOptions.Timing().EndTime().RawQueryPerformanceCounterValue().value();
+ aOptions.Timing().EndTime().RawQueryPerformanceCounterValue();
aStorage.mPhase = uint8_t(aOptions.Timing().MarkerPhase());
}
if (!aOptions.InnerWindowId().IsUnspecified()) {