From 3df5c68487df9d1d20ab0cd06e7942a4f96d40a4 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 16 Nov 2020 13:21:45 +0100 Subject: [PATCH] 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. --- Doc/library/time.rst | 43 ++++++++++++++++--- .../2020-11-14-14-34-32.bpo-37205.iDbHrw.rst | 5 +++ Python/pytime.c | 16 +------ 3 files changed, 43 insertions(+), 21 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-11-14-14-34-32.bpo-37205.iDbHrw.rst diff --git a/Doc/library/time.rst b/Doc/library/time.rst index cff6320b526..143f84b565f 100644 --- a/Doc/library/time.rst +++ b/Doc/library/time.rst @@ -166,6 +166,9 @@ Functions 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*. + Use :func:`clock_gettime_ns` to avoid the precision loss caused by the + :class:`float` type. + .. availability:: Unix. .. versionadded:: 3.3 @@ -185,6 +188,9 @@ Functions Set the time of the specified clock *clk_id*. Currently, :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. .. versionadded:: 3.3 @@ -273,10 +279,17 @@ Functions The reference point of the returned value is undefined, so that only the 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 + .. versionchanged:: 3.5 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 @@ -295,8 +308,14 @@ Functions point of the returned value is undefined, so that only the difference between 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 + .. versionchanged:: 3.10 + On Windows, the function is now system-wide. + .. function:: perf_counter_ns() -> int 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 of consecutive calls is valid. + Use :func:`process_time_ns` to avoid the precision loss caused by the + :class:`float` type. + .. versionadded:: 3.3 .. function:: process_time_ns() -> int @@ -581,6 +603,17 @@ Functions :class:`struct_time` object is returned, from which the components 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 @@ -595,6 +628,9 @@ Functions returned value is undefined, so that only the difference between the results 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 ``CLOCK_THREAD_CPUTIME_ID``. @@ -608,13 +644,6 @@ Functions .. 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() Reset the time conversion rules used by the library routines. The environment diff --git a/Misc/NEWS.d/next/Library/2020-11-14-14-34-32.bpo-37205.iDbHrw.rst b/Misc/NEWS.d/next/Library/2020-11-14-14-34-32.bpo-37205.iDbHrw.rst new file mode 100644 index 00000000000..5cf325ec0c1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-11-14-14-34-32.bpo-37205.iDbHrw.rst @@ -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. diff --git a/Python/pytime.c b/Python/pytime.c index 89d63e08042..179bced1ac6 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -6,7 +6,7 @@ #if defined(__APPLE__) #include /* mach_absolute_time(), mach_timebase_info() */ -#if defined(__APPLE__) && defined(__has_builtin) +#if defined(__APPLE__) && defined(__has_builtin) # if __has_builtin(__builtin_available) # define HAVE_CLOCK_GETTIME_RUNTIME __builtin_available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) # endif @@ -730,7 +730,7 @@ pygettimeofday(_PyTime_t *tp, _Py_clock_info_t *info, int raise) } #ifdef HAVE_CLOCK_GETTIME_RUNTIME - } else { + } else { #endif #endif @@ -822,7 +822,6 @@ pymonotonic(_PyTime_t *tp, _Py_clock_info_t *info, int raise) #elif defined(__APPLE__) static mach_timebase_info_data_t timebase; - static uint64_t t0 = 0; uint64_t ticks; 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"); return -1; } - - t0 = mach_absolute_time(); } if (info) { @@ -871,9 +868,6 @@ pymonotonic(_PyTime_t *tp, _Py_clock_info_t *info, int raise) } 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, (_PyTime_t)timebase.numer, (_PyTime_t)timebase.denom); @@ -960,7 +954,6 @@ static int win_perf_counter(_PyTime_t *tp, _Py_clock_info_t *info) { static LONGLONG frequency = 0; - static LONGLONG t0 = 0; LARGE_INTEGER now; LONGLONG ticksll; _PyTime_t ticks; @@ -1000,7 +993,6 @@ win_perf_counter(_PyTime_t *tp, _Py_clock_info_t *info) } QueryPerformanceCounter(&now); - t0 = now.QuadPart; } if (info) { @@ -1013,10 +1005,6 @@ win_perf_counter(_PyTime_t *tp, _Py_clock_info_t *info) QueryPerformanceCounter(&now); 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, both types are signed */ Py_BUILD_ASSERT(sizeof(ticksll) <= sizeof(ticks));