summaryrefslogtreecommitdiffstats
path: root/Python
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@python.org>2024-03-14 15:42:41 (GMT)
committerGitHub <noreply@github.com>2024-03-14 15:42:41 (GMT)
commit846ad5a26ac0ff988a3fceec8f8e830f68bdf48a (patch)
tree59187e5155d80521daf817214a4b80bb139c23df /Python
parent415cd06d724762f23b42f1ab36867b8114714684 (diff)
downloadcpython-846ad5a26ac0ff988a3fceec8f8e830f68bdf48a.zip
cpython-846ad5a26ac0ff988a3fceec8f8e830f68bdf48a.tar.gz
cpython-846ad5a26ac0ff988a3fceec8f8e830f68bdf48a.tar.bz2
gh-88494: Use QueryPerformanceCounter() for time.monotonic() (#116781)
On Windows, time.monotonic() now uses the QueryPerformanceCounter() clock to have a resolution better than 1 us, instead of the gGetTickCount64() clock which has a resolution of 15.6 ms.
Diffstat (limited to 'Python')
-rw-r--r--Python/pytime.c211
1 files changed, 73 insertions, 138 deletions
diff --git a/Python/pytime.c b/Python/pytime.c
index 70d92ca..45be6a3 100644
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -1027,9 +1027,76 @@ _PyTime_TimeWithInfo(PyTime_t *t, _Py_clock_info_t *info)
}
+#ifdef MS_WINDOWS
+static int
+py_win_perf_counter_frequency(_PyTimeFraction *base, int raise_exc)
+{
+ LARGE_INTEGER freq;
+ // Since Windows XP, the function cannot fail.
+ (void)QueryPerformanceFrequency(&freq);
+ LONGLONG frequency = freq.QuadPart;
+
+ // Since Windows XP, frequency cannot be zero.
+ assert(frequency >= 1);
+
+ Py_BUILD_ASSERT(sizeof(PyTime_t) == sizeof(frequency));
+ PyTime_t denom = (PyTime_t)frequency;
+
+ // Known QueryPerformanceFrequency() values:
+ //
+ // * 10,000,000 (10 MHz): 100 ns resolution
+ // * 3,579,545 Hz (3.6 MHz): 279 ns resolution
+ if (_PyTimeFraction_Set(base, SEC_TO_NS, denom) < 0) {
+ if (raise_exc) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "invalid QueryPerformanceFrequency");
+ }
+ return -1;
+ }
+ return 0;
+}
+
+
+// N.B. If raise_exc=0, this may be called without the GIL.
+static int
+py_get_win_perf_counter(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
+{
+ assert(info == NULL || raise_exc);
+
+ static _PyTimeFraction base = {0, 0};
+ if (base.denom == 0) {
+ if (py_win_perf_counter_frequency(&base, raise_exc) < 0) {
+ return -1;
+ }
+ }
+
+ if (info) {
+ info->implementation = "QueryPerformanceCounter()";
+ info->resolution = _PyTimeFraction_Resolution(&base);
+ info->monotonic = 1;
+ info->adjustable = 0;
+ }
+
+ LARGE_INTEGER now;
+ QueryPerformanceCounter(&now);
+ LONGLONG ticksll = now.QuadPart;
+
+ /* Make sure that casting LONGLONG to PyTime_t cannot overflow,
+ both types are signed */
+ PyTime_t ticks;
+ static_assert(sizeof(ticksll) <= sizeof(ticks),
+ "LONGLONG is larger than PyTime_t");
+ ticks = (PyTime_t)ticksll;
+
+ *tp = _PyTimeFraction_Mul(ticks, &base);
+ return 0;
+}
+#endif // MS_WINDOWS
+
+
#ifdef __APPLE__
static int
-py_mach_timebase_info(_PyTimeFraction *base, int raise)
+py_mach_timebase_info(_PyTimeFraction *base, int raise_exc)
{
mach_timebase_info_data_t timebase;
// According to the Technical Q&A QA1398, mach_timebase_info() cannot
@@ -1051,7 +1118,7 @@ py_mach_timebase_info(_PyTimeFraction *base, int raise)
// * (1000000000, 33333335) on PowerPC: ~30 ns
// * (1000000000, 25000000) on PowerPC: 40 ns
if (_PyTimeFraction_Set(base, numer, denom) < 0) {
- if (raise) {
+ if (raise_exc) {
PyErr_SetString(PyExc_RuntimeError,
"invalid mach_timebase_info");
}
@@ -1069,42 +1136,9 @@ py_get_monotonic_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
assert(info == NULL || raise_exc);
#if defined(MS_WINDOWS)
- ULONGLONG ticks = GetTickCount64();
- static_assert(sizeof(ticks) <= sizeof(PyTime_t),
- "ULONGLONG is larger than PyTime_t");
- PyTime_t t;
- if (ticks <= (ULONGLONG)PyTime_MAX) {
- t = (PyTime_t)ticks;
- }
- else {
- // GetTickCount64() maximum is larger than PyTime_t maximum:
- // ULONGLONG is unsigned, whereas PyTime_t is signed.
- t = PyTime_MAX;
- }
-
- int res = pytime_mul(&t, MS_TO_NS);
- *tp = t;
-
- if (raise_exc && res < 0) {
- pytime_overflow();
+ if (py_get_win_perf_counter(tp, info, raise_exc) < 0) {
return -1;
}
-
- if (info) {
- DWORD timeAdjustment, timeIncrement;
- BOOL isTimeAdjustmentDisabled, ok;
- info->implementation = "GetTickCount64()";
- info->monotonic = 1;
- ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
- &isTimeAdjustmentDisabled);
- if (!ok) {
- PyErr_SetFromWindowsErr(0);
- return -1;
- }
- info->resolution = timeIncrement * 1e-7;
- info->adjustable = 0;
- }
-
#elif defined(__APPLE__)
static _PyTimeFraction base = {0, 0};
if (base.denom == 0) {
@@ -1190,8 +1224,7 @@ _PyTime_MonotonicUnchecked(void)
{
PyTime_t t;
if (py_get_monotonic_clock(&t, NULL, 0) < 0) {
- // If mach_timebase_info(), clock_gettime() or gethrtime() fails:
- // silently ignore the failure and return 0.
+ // Ignore silently the error and return 0.
t = 0;
}
return t;
@@ -1216,122 +1249,24 @@ _PyTime_MonotonicWithInfo(PyTime_t *tp, _Py_clock_info_t *info)
}
-#ifdef MS_WINDOWS
-static int
-py_win_perf_counter_frequency(_PyTimeFraction *base, int raise)
-{
- LONGLONG frequency;
-
- LARGE_INTEGER freq;
- // Since Windows XP, the function cannot fail.
- (void)QueryPerformanceFrequency(&freq);
- frequency = freq.QuadPart;
-
- // Since Windows XP, frequency cannot be zero.
- assert(frequency >= 1);
-
- Py_BUILD_ASSERT(sizeof(PyTime_t) == sizeof(frequency));
- PyTime_t denom = (PyTime_t)frequency;
-
- // Known QueryPerformanceFrequency() values:
- //
- // * 10,000,000 (10 MHz): 100 ns resolution
- // * 3,579,545 Hz (3.6 MHz): 279 ns resolution
- if (_PyTimeFraction_Set(base, SEC_TO_NS, denom) < 0) {
- if (raise) {
- PyErr_SetString(PyExc_RuntimeError,
- "invalid QueryPerformanceFrequency");
- }
- return -1;
- }
- return 0;
-}
-
-
-// N.B. If raise_exc=0, this may be called without the GIL.
-static int
-py_get_win_perf_counter(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
-{
- assert(info == NULL || raise_exc);
-
- static _PyTimeFraction base = {0, 0};
- if (base.denom == 0) {
- if (py_win_perf_counter_frequency(&base, raise_exc) < 0) {
- return -1;
- }
- }
-
- if (info) {
- info->implementation = "QueryPerformanceCounter()";
- info->resolution = _PyTimeFraction_Resolution(&base);
- info->monotonic = 1;
- info->adjustable = 0;
- }
-
- LARGE_INTEGER now;
- QueryPerformanceCounter(&now);
- LONGLONG ticksll = now.QuadPart;
-
- /* Make sure that casting LONGLONG to PyTime_t cannot overflow,
- both types are signed */
- PyTime_t ticks;
- static_assert(sizeof(ticksll) <= sizeof(ticks),
- "LONGLONG is larger than PyTime_t");
- ticks = (PyTime_t)ticksll;
-
- PyTime_t ns = _PyTimeFraction_Mul(ticks, &base);
- *tp = ns;
- return 0;
-}
-#endif // MS_WINDOWS
-
-
int
_PyTime_PerfCounterWithInfo(PyTime_t *t, _Py_clock_info_t *info)
{
-#ifdef MS_WINDOWS
- return py_get_win_perf_counter(t, info, 1);
-#else
return _PyTime_MonotonicWithInfo(t, info);
-#endif
}
PyTime_t
_PyTime_PerfCounterUnchecked(void)
{
- PyTime_t t;
- int res;
-#ifdef MS_WINDOWS
- res = py_get_win_perf_counter(&t, NULL, 0);
-#else
- res = py_get_monotonic_clock(&t, NULL, 0);
-#endif
- if (res < 0) {
- // If py_win_perf_counter_frequency() or py_get_monotonic_clock()
- // fails: silently ignore the failure and return 0.
- t = 0;
- }
- return t;
+ return _PyTime_MonotonicUnchecked();
}
int
PyTime_PerfCounter(PyTime_t *result)
{
- int res;
-#ifdef MS_WINDOWS
- res = py_get_win_perf_counter(result, NULL, 1);
-#else
- res = py_get_monotonic_clock(result, NULL, 1);
-#endif
- if (res < 0) {
- // If py_win_perf_counter_frequency() or py_get_monotonic_clock()
- // fails: silently ignore the failure and return 0.
- *result = 0;
- return -1;
- }
- return 0;
+ return PyTime_Monotonic(result);
}