bpo-37205: time.perf_counter() and time.monotonic() are system-wide (GH-23284)

time.perf_counter() on Windows and time.monotonic() on macOS are now
system-wide. Previously, they used an offset computed at startup to
reduce the precision loss caused by the float type. Use
time.perf_counter_ns() and time.monotonic_ns() added in Python 3.7 to
avoid this precision loss.
This commit is contained in:
Victor Stinner 2020-11-16 13:21:45 +01:00 committed by GitHub
parent aa01011003
commit 3df5c68487
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 43 additions and 21 deletions

View File

@ -166,6 +166,9 @@ Functions
Return the time of the specified clock *clk_id*. Refer to Return the time of the specified clock *clk_id*. Refer to
:ref:`time-clock-id-constants` for a list of accepted values for *clk_id*. :ref:`time-clock-id-constants` for a list of accepted values for *clk_id*.
Use :func:`clock_gettime_ns` to avoid the precision loss caused by the
:class:`float` type.
.. availability:: Unix. .. availability:: Unix.
.. versionadded:: 3.3 .. versionadded:: 3.3
@ -185,6 +188,9 @@ Functions
Set the time of the specified clock *clk_id*. Currently, Set the time of the specified clock *clk_id*. Currently,
:data:`CLOCK_REALTIME` is the only accepted value for *clk_id*. :data:`CLOCK_REALTIME` is the only accepted value for *clk_id*.
Use :func:`clock_settime_ns` to avoid the precision loss caused by the
:class:`float` type.
.. availability:: Unix. .. availability:: Unix.
.. versionadded:: 3.3 .. versionadded:: 3.3
@ -273,10 +279,17 @@ Functions
The reference point of the returned value is undefined, so that only the The reference point of the returned value is undefined, so that only the
difference between the results of consecutive calls is valid. difference between the results of consecutive calls is valid.
Use :func:`monotonic_ns` to avoid the precision loss caused by the
:class:`float` type.
.. versionadded:: 3.3 .. versionadded:: 3.3
.. versionchanged:: 3.5 .. versionchanged:: 3.5
The function is now always available and always system-wide. The function is now always available and always system-wide.
.. versionchanged:: 3.10
On macOS, the function is now system-wide.
.. function:: monotonic_ns() -> int .. function:: monotonic_ns() -> int
@ -295,8 +308,14 @@ Functions
point of the returned value is undefined, so that only the difference between point of the returned value is undefined, so that only the difference between
the results of consecutive calls is valid. the results of consecutive calls is valid.
Use :func:`perf_counter_ns` to avoid the precision loss caused by the
:class:`float` type.
.. versionadded:: 3.3 .. versionadded:: 3.3
.. versionchanged:: 3.10
On Windows, the function is now system-wide.
.. function:: perf_counter_ns() -> int .. function:: perf_counter_ns() -> int
Similar to :func:`perf_counter`, but return time as nanoseconds. Similar to :func:`perf_counter`, but return time as nanoseconds.
@ -317,6 +336,9 @@ Functions
returned value is undefined, so that only the difference between the results returned value is undefined, so that only the difference between the results
of consecutive calls is valid. of consecutive calls is valid.
Use :func:`process_time_ns` to avoid the precision loss caused by the
:class:`float` type.
.. versionadded:: 3.3 .. versionadded:: 3.3
.. function:: process_time_ns() -> int .. function:: process_time_ns() -> int
@ -581,6 +603,17 @@ Functions
:class:`struct_time` object is returned, from which the components :class:`struct_time` object is returned, from which the components
of the calendar date may be accessed as attributes. of the calendar date may be accessed as attributes.
Use :func:`time_ns` to avoid the precision loss caused by the :class:`float`
type.
.. function:: time_ns() -> int
Similar to :func:`~time.time` but returns time as an integer number of nanoseconds
since the epoch_.
.. versionadded:: 3.7
.. function:: thread_time() -> float .. function:: thread_time() -> float
@ -595,6 +628,9 @@ Functions
returned value is undefined, so that only the difference between the results returned value is undefined, so that only the difference between the results
of consecutive calls in the same thread is valid. of consecutive calls in the same thread is valid.
Use :func:`thread_time_ns` to avoid the precision loss caused by the
:class:`float` type.
.. availability:: Windows, Linux, Unix systems supporting .. availability:: Windows, Linux, Unix systems supporting
``CLOCK_THREAD_CPUTIME_ID``. ``CLOCK_THREAD_CPUTIME_ID``.
@ -608,13 +644,6 @@ Functions
.. versionadded:: 3.7 .. versionadded:: 3.7
.. function:: time_ns() -> int
Similar to :func:`~time.time` but returns time as an integer number of nanoseconds
since the epoch_.
.. versionadded:: 3.7
.. function:: tzset() .. function:: tzset()
Reset the time conversion rules used by the library routines. The environment Reset the time conversion rules used by the library routines. The environment

View File

@ -0,0 +1,5 @@
:func:`time.perf_counter()` on Windows and :func:`time.monotonic()` on macOS
are now system-wide. Previously, they used an offset computed at startup to
reduce the precision loss caused by the float type. Use
:func:`time.perf_counter_ns()` and :func:`time.monotonic_ns()` added in Python
3.7 to avoid this precision loss.

View File

@ -6,7 +6,7 @@
#if defined(__APPLE__) #if defined(__APPLE__)
#include <mach/mach_time.h> /* mach_absolute_time(), mach_timebase_info() */ #include <mach/mach_time.h> /* mach_absolute_time(), mach_timebase_info() */
#if defined(__APPLE__) && defined(__has_builtin) #if defined(__APPLE__) && defined(__has_builtin)
# if __has_builtin(__builtin_available) # if __has_builtin(__builtin_available)
# define HAVE_CLOCK_GETTIME_RUNTIME __builtin_available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) # define HAVE_CLOCK_GETTIME_RUNTIME __builtin_available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
# endif # endif
@ -730,7 +730,7 @@ pygettimeofday(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
} }
#ifdef HAVE_CLOCK_GETTIME_RUNTIME #ifdef HAVE_CLOCK_GETTIME_RUNTIME
} else { } else {
#endif #endif
#endif #endif
@ -822,7 +822,6 @@ pymonotonic(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
#elif defined(__APPLE__) #elif defined(__APPLE__)
static mach_timebase_info_data_t timebase; static mach_timebase_info_data_t timebase;
static uint64_t t0 = 0;
uint64_t ticks; uint64_t ticks;
if (timebase.denom == 0) { if (timebase.denom == 0) {
@ -859,8 +858,6 @@ pymonotonic(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
"mach_timebase_info is too large"); "mach_timebase_info is too large");
return -1; return -1;
} }
t0 = mach_absolute_time();
} }
if (info) { if (info) {
@ -871,9 +868,6 @@ pymonotonic(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
} }
ticks = mach_absolute_time(); ticks = mach_absolute_time();
/* Use a "time zero" to reduce precision loss when converting time
to floatting point number, as in time.monotonic(). */
ticks -= t0;
*tp = _PyTime_MulDiv(ticks, *tp = _PyTime_MulDiv(ticks,
(_PyTime_t)timebase.numer, (_PyTime_t)timebase.numer,
(_PyTime_t)timebase.denom); (_PyTime_t)timebase.denom);
@ -960,7 +954,6 @@ static int
win_perf_counter(_PyTime_t *tp, _Py_clock_info_t *info) win_perf_counter(_PyTime_t *tp, _Py_clock_info_t *info)
{ {
static LONGLONG frequency = 0; static LONGLONG frequency = 0;
static LONGLONG t0 = 0;
LARGE_INTEGER now; LARGE_INTEGER now;
LONGLONG ticksll; LONGLONG ticksll;
_PyTime_t ticks; _PyTime_t ticks;
@ -1000,7 +993,6 @@ win_perf_counter(_PyTime_t *tp, _Py_clock_info_t *info)
} }
QueryPerformanceCounter(&now); QueryPerformanceCounter(&now);
t0 = now.QuadPart;
} }
if (info) { if (info) {
@ -1013,10 +1005,6 @@ win_perf_counter(_PyTime_t *tp, _Py_clock_info_t *info)
QueryPerformanceCounter(&now); QueryPerformanceCounter(&now);
ticksll = now.QuadPart; ticksll = now.QuadPart;
/* Use a "time zero" to reduce precision loss when converting time
to floatting point number, as in time.perf_counter(). */
ticksll -= t0;
/* Make sure that casting LONGLONG to _PyTime_t cannot overflow, /* Make sure that casting LONGLONG to _PyTime_t cannot overflow,
both types are signed */ both types are signed */
Py_BUILD_ASSERT(sizeof(ticksll) <= sizeof(ticks)); Py_BUILD_ASSERT(sizeof(ticksll) <= sizeof(ticks));