Kaydet (Commit) ec89539c authored tarafından Victor Stinner's avatar Victor Stinner

Issue #14428, #14397: Implement the PEP 418

 * Rename time.steady() to time.monotonic()
 * On Windows, time.monotonic() uses GetTickCount/GetTickCount64() instead of
   QueryPerformanceCounter()
 * time.monotonic() uses CLOCK_HIGHRES if available
 * Add time.get_clock_info(), time.perf_counter() and time.process_time()
   functions
üst ca6e40f1
...@@ -155,6 +155,30 @@ The module defines the following functions and data items: ...@@ -155,6 +155,30 @@ The module defines the following functions and data items:
.. versionadded:: 3.3 .. versionadded:: 3.3
.. class:: clock_info
Clock information object created by :func:`get_clock_info`.
.. attribute:: implementation
name of the underlying C function used to get the clock value
.. attribute:: is_monotonic
``True`` if the clock cannot go backward, ``False`` otherwise
.. attribute:: is_adjusted
``True`` if the clock can be adjusted (e.g. by a NTP daemon),
``False`` otherwise
.. attribute:: resolution
Resolution of the clock in seconds (:class:`float`)
.. versionadded:: 3.3
.. function:: clock_settime(clk_id, time) .. function:: clock_settime(clk_id, time)
Set the time of the specified clock *clk_id*. Set the time of the specified clock *clk_id*.
...@@ -236,6 +260,22 @@ The module defines the following functions and data items: ...@@ -236,6 +260,22 @@ The module defines the following functions and data items:
Nonzero if a DST timezone is defined. Nonzero if a DST timezone is defined.
.. function:: get_clock_info(name)
Get information on the specified clock as a :class:`clock_info` object.
Supported clock names:
* ``'clock'``: :func:`time.clock`
* ``'monotonic'``: :func:`time.monotonic`
* ``'perf_counter'``: :func:`time.perf_counter`
* ``'process_time'``: :func:`time.process_time`
* ``'time'``: :func:`time.time`
.. versionadded:: 3.3
.. function:: gmtime([secs]) .. function:: gmtime([secs])
Convert a time expressed in seconds since the epoch to a :class:`struct_time` in Convert a time expressed in seconds since the epoch to a :class:`struct_time` in
...@@ -265,20 +305,43 @@ The module defines the following functions and data items: ...@@ -265,20 +305,43 @@ The module defines the following functions and data items:
The earliest date for which it can generate a time is platform-dependent. The earliest date for which it can generate a time is platform-dependent.
.. function:: steady(strict=False) .. function:: monotonic()
Monotonic clock, i.e. cannot go backward. It is not affected by system
clock updates. The reference point of the returned value is undefined, so
that only the difference between the results of consecutive calls is valid
and is a number of seconds.
On Windows versions older than Vista, :func:`monotonic` detects
:c:func:`GetTickCount` integer overflow (32 bits, roll-over after 49.7
days). It increases an internal epoch (reference time by) 2\ :sup:`32` each
time that an overflow is detected. The epoch is stored in the process-local
state and so the value of :func:`monotonic` may be different in two Python
processes running for more than 49 days. On more recent versions of Windows
and on other operating systems, :func:`monotonic` is system-wide.
Availability: Windows, Mac OS X, Linux, FreeBSD, OpenBSD, Solaris.
.. versionadded:: 3.3
.. function:: perf_counter()
Performance counter with the highest available resolution to measure a short
duration. It does include time elapsed during sleep and is system-wide.
The reference point of the returned value is undefined, so that only the
difference between the results of consecutive calls is valid and is a number
of seconds.
.. versionadded:: 3.3
.. index::
single: benchmarking
Return the current time as a floating point number expressed in seconds. .. function:: process_time()
This clock advances at a steady rate relative to real time and it may not be
adjusted. The reference point of the returned value is undefined so only the
difference of consecutive calls is valid.
If available, a monotonic clock is used. By default, Sum of the system and user CPU time of the current process. It does not
the function falls back to another clock if the monotonic clock failed or is include time elapsed during sleep. It is process-wide by definition. The
not available. If *strict* is True, raise an :exc:`OSError` on error or reference point of the returned value is undefined, so that only the
:exc:`NotImplementedError` if no monotonic clock is available. difference between the results of consecutive calls is valid.
.. versionadded:: 3.3 .. versionadded:: 3.3
......
...@@ -1059,13 +1059,21 @@ sys ...@@ -1059,13 +1059,21 @@ sys
time time
---- ----
The :mod:`time` module has new functions: The :pep:`418` added new functions to the :mod:`time` module:
* :func:`~time.clock_getres` and :func:`~time.clock_gettime` functions and * :func:`~time.get_clock_info`: Get information on a clock.
``CLOCK_xxx`` constants. * :func:`~time.monotonic`: Monotonic clock (cannot go backward), not affected
* :func:`~time.steady`. by system clock updates.
* :func:`~time.perf_counter`: Performance counter with the highest available
resolution to measure a short duration.
* :func:`~time.process_time`: Sum of the system and user CPU time of the
current process.
(Contributed by Victor Stinner in :issue:`10278`) Other new functions:
* :func:`~time.clock_getres`, :func:`~time.clock_gettime` and
:func:`~time.clock_settime` functions with ``CLOCK_xxx`` constants.
(Contributed by Victor Stinner in :issue:`10278`)
types types
......
...@@ -22,11 +22,25 @@ typedef struct { ...@@ -22,11 +22,25 @@ typedef struct {
} _PyTime_timeval; } _PyTime_timeval;
#endif #endif
/* Structure used by time.get_clock_info() */
typedef struct {
const char *implementation;
int is_monotonic;
int is_adjusted;
double resolution;
} _Py_clock_info_t;
/* Similar to POSIX gettimeofday but cannot fail. If system gettimeofday /* Similar to POSIX gettimeofday but cannot fail. If system gettimeofday
* fails or is not available, fall back to lower resolution clocks. * fails or is not available, fall back to lower resolution clocks.
*/ */
PyAPI_FUNC(void) _PyTime_gettimeofday(_PyTime_timeval *tp); PyAPI_FUNC(void) _PyTime_gettimeofday(_PyTime_timeval *tp);
/* Similar to _PyTime_gettimeofday() but retrieve also information on the
* clock used to get the current time. */
PyAPI_FUNC(void) _PyTime_gettimeofday_info(
_PyTime_timeval *tp,
_Py_clock_info_t *info);
#define _PyTime_ADD_SECONDS(tv, interval) \ #define _PyTime_ADD_SECONDS(tv, interval) \
do { \ do { \
tv.tv_usec += (long) (((long) interval - interval) * 1000000); \ tv.tv_usec += (long) (((long) interval - interval) * 1000000); \
......
...@@ -6,7 +6,10 @@ except ImportError: ...@@ -6,7 +6,10 @@ except ImportError:
import dummy_threading as threading import dummy_threading as threading
from collections import deque from collections import deque
from heapq import heappush, heappop from heapq import heappush, heappop
from time import steady as time try:
from time import monotonic as time
except ImportError:
from time import time
__all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue'] __all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue']
......
...@@ -5,6 +5,10 @@ import locale ...@@ -5,6 +5,10 @@ import locale
import sysconfig import sysconfig
import sys import sys
import platform import platform
try:
import threading
except ImportError:
threading = None
# Max year is only limited by the size of C int. # Max year is only limited by the size of C int.
SIZEOF_INT = sysconfig.get_config_var('SIZEOF_INT') or 4 SIZEOF_INT = sysconfig.get_config_var('SIZEOF_INT') or 4
...@@ -23,9 +27,20 @@ class TimeTestCase(unittest.TestCase): ...@@ -23,9 +27,20 @@ class TimeTestCase(unittest.TestCase):
time.timezone time.timezone
time.tzname time.tzname
def test_time(self):
time.time()
info = time.get_clock_info('time')
self.assertEqual(info.is_monotonic, False)
if sys.platform != 'win32':
self.assertEqual(info.is_adjusted, True)
def test_clock(self): def test_clock(self):
time.clock() time.clock()
info = time.get_clock_info('clock')
self.assertEqual(info.is_monotonic, True)
self.assertEqual(info.is_adjusted, False)
@unittest.skipUnless(hasattr(time, 'clock_gettime'), @unittest.skipUnless(hasattr(time, 'clock_gettime'),
'need time.clock_gettime()') 'need time.clock_gettime()')
def test_clock_realtime(self): def test_clock_realtime(self):
...@@ -56,7 +71,9 @@ class TimeTestCase(unittest.TestCase): ...@@ -56,7 +71,9 @@ class TimeTestCase(unittest.TestCase):
except PermissionError: except PermissionError:
pass pass
self.assertRaises(OSError, time.clock_settime, time.CLOCK_MONOTONIC, 0) if hasattr(time, 'CLOCK_MONOTONIC'):
self.assertRaises(OSError,
time.clock_settime, time.CLOCK_MONOTONIC, 0)
def test_conversions(self): def test_conversions(self):
self.assertEqual(time.ctime(self.t), self.assertEqual(time.ctime(self.t),
...@@ -342,23 +359,69 @@ class TimeTestCase(unittest.TestCase): ...@@ -342,23 +359,69 @@ class TimeTestCase(unittest.TestCase):
pass pass
self.assertEqual(time.strftime('%Z', tt), tzname) self.assertEqual(time.strftime('%Z', tt), tzname)
def test_steady(self): @unittest.skipUnless(hasattr(time, 'monotonic'),
t1 = time.steady() 'need time.monotonic')
def test_monotonic(self):
t1 = time.monotonic()
time.sleep(0.1) time.sleep(0.1)
t2 = time.steady() t2 = time.monotonic()
dt = t2 - t1 dt = t2 - t1
# may fail if the system clock was changed
self.assertGreater(t2, t1) self.assertGreater(t2, t1)
self.assertAlmostEqual(dt, 0.1, delta=0.2) self.assertAlmostEqual(dt, 0.1, delta=0.2)
def test_steady_strict(self): info = time.get_clock_info('monotonic')
self.assertEqual(info.is_monotonic, True)
if sys.platform == 'linux':
self.assertEqual(info.is_adjusted, True)
else:
self.assertEqual(info.is_adjusted, False)
def test_perf_counter(self):
time.perf_counter()
def test_process_time(self):
start = time.process_time()
time.sleep(0.1)
stop = time.process_time()
self.assertLess(stop - start, 0.01)
info = time.get_clock_info('process_time')
self.assertEqual(info.is_monotonic, True)
self.assertEqual(info.is_adjusted, False)
@unittest.skipUnless(threading,
'need threading')
def test_process_time_threads(self):
class BusyThread(threading.Thread):
def run(self):
while not self.stop:
pass
thread = BusyThread()
thread.stop = False
t1 = time.process_time()
thread.start()
time.sleep(0.2)
t2 = time.process_time()
thread.stop = True
thread.join()
self.assertGreater(t2 - t1, 0.1)
@unittest.skipUnless(hasattr(time, 'monotonic'),
'need time.monotonic')
@unittest.skipUnless(hasattr(time, 'clock_settime'),
'need time.clock_settime')
def test_monotonic_settime(self):
t1 = time.monotonic()
realtime = time.clock_gettime(time.CLOCK_REALTIME)
# jump backward with an offset of 1 hour
try: try:
t1 = time.steady(strict=True) time.clock_settime(time.CLOCK_REALTIME, realtime - 3600)
except OSError as err: except PermissionError as err:
self.skipTest("the monotonic clock failed: %s" % err) self.skipTest(err)
except NotImplementedError: t2 = time.monotonic()
self.skipTest("no monotonic clock available") time.clock_settime(time.CLOCK_REALTIME, realtime)
t2 = time.steady(strict=True) # monotonic must not be affected by system clock updates
self.assertGreaterEqual(t2, t1) self.assertGreaterEqual(t2, t1)
def test_localtime_failure(self): def test_localtime_failure(self):
...@@ -378,6 +441,26 @@ class TimeTestCase(unittest.TestCase): ...@@ -378,6 +441,26 @@ class TimeTestCase(unittest.TestCase):
self.assertRaises(OSError, time.localtime, invalid_time_t) self.assertRaises(OSError, time.localtime, invalid_time_t)
self.assertRaises(OSError, time.ctime, invalid_time_t) self.assertRaises(OSError, time.ctime, invalid_time_t)
def test_get_clock_info(self):
clocks = ['clock', 'perf_counter', 'process_time', 'time']
if hasattr(time, 'monotonic'):
clocks.append('monotonic')
for name in clocks:
info = time.get_clock_info(name)
#self.assertIsInstance(info, dict)
self.assertIsInstance(info.implementation, str)
self.assertNotEqual(info.implementation, '')
self.assertIsInstance(info.is_monotonic, bool)
self.assertIsInstance(info.resolution, float)
# 0.0 < resolution <= 1.0
self.assertGreater(info.resolution, 0.0)
self.assertLessEqual(info.resolution, 1.0)
self.assertIsInstance(info.is_adjusted, bool)
self.assertRaises(ValueError, time.get_clock_info, 'xxx')
class TestLocale(unittest.TestCase): class TestLocale(unittest.TestCase):
def setUp(self): def setUp(self):
self.oldloc = locale.setlocale(locale.LC_ALL) self.oldloc = locale.setlocale(locale.LC_ALL)
......
...@@ -3,7 +3,11 @@ ...@@ -3,7 +3,11 @@
import sys as _sys import sys as _sys
import _thread import _thread
from time import steady as _time, sleep as _sleep from time import sleep as _sleep
try:
from time import monotonic as _time
except ImportError:
from time import time as _time
from traceback import format_exc as _format_exc from traceback import format_exc as _format_exc
from _weakrefset import WeakSet from _weakrefset import WeakSet
......
...@@ -81,6 +81,10 @@ Core and Builtins ...@@ -81,6 +81,10 @@ Core and Builtins
Library Library
------- -------
- Issue #14428: Implement the PEP 418. Add time.get_clock_info(),
time.perf_counter() and time.process_time() functions, and rename
time.steady() to time.monotonic().
- Issue #14646: importlib.util.module_for_loader() now sets __loader__ and - Issue #14646: importlib.util.module_for_loader() now sets __loader__ and
__package__ (when possible). __package__ (when possible).
......
This diff is collapsed.
...@@ -18,8 +18,8 @@ ...@@ -18,8 +18,8 @@
extern int ftime(struct timeb *); extern int ftime(struct timeb *);
#endif #endif
void static void
_PyTime_gettimeofday(_PyTime_timeval *tp) pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info)
{ {
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
FILETIME system_time; FILETIME system_time;
...@@ -35,6 +35,20 @@ _PyTime_gettimeofday(_PyTime_timeval *tp) ...@@ -35,6 +35,20 @@ _PyTime_gettimeofday(_PyTime_timeval *tp)
microseconds = large.QuadPart / 10 - 11644473600000000; microseconds = large.QuadPart / 10 - 11644473600000000;
tp->tv_sec = microseconds / 1000000; tp->tv_sec = microseconds / 1000000;
tp->tv_usec = microseconds % 1000000; tp->tv_usec = microseconds % 1000000;
if (info) {
DWORD timeAdjustment, timeIncrement;
BOOL isTimeAdjustmentDisabled;
info->implementation = "GetSystemTimeAsFileTime()";
info->is_monotonic = 0;
(void) GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
&isTimeAdjustmentDisabled);
info->resolution = timeIncrement * 1e-7;
if (isTimeAdjustmentDisabled)
info->is_adjusted = 0;
else
info->is_adjusted = 1;
}
#else #else
/* There are three ways to get the time: /* There are three ways to get the time:
(1) gettimeofday() -- resolution in microseconds (1) gettimeofday() -- resolution in microseconds
...@@ -46,14 +60,22 @@ _PyTime_gettimeofday(_PyTime_timeval *tp) ...@@ -46,14 +60,22 @@ _PyTime_gettimeofday(_PyTime_timeval *tp)
Note: clock resolution does not imply clock accuracy! */ Note: clock resolution does not imply clock accuracy! */
#ifdef HAVE_GETTIMEOFDAY #ifdef HAVE_GETTIMEOFDAY
int err;
#ifdef GETTIMEOFDAY_NO_TZ #ifdef GETTIMEOFDAY_NO_TZ
if (gettimeofday(tp) == 0) err = gettimeofday(tp);
return; #else
#else /* !GETTIMEOFDAY_NO_TZ */ err = gettimeofday(tp, (struct timezone *)NULL);
if (gettimeofday(tp, (struct timezone *)NULL) == 0) #endif
if (err == 0) {
if (info) {
info->implementation = "gettimeofday()";
info->resolution = 1e-6;
info->is_monotonic = 0;
info->is_adjusted = 1;
}
return; return;
#endif /* !GETTIMEOFDAY_NO_TZ */ }
#endif /* !HAVE_GETTIMEOFDAY */ #endif /* HAVE_GETTIMEOFDAY */
#if defined(HAVE_FTIME) #if defined(HAVE_FTIME)
{ {
...@@ -61,15 +83,39 @@ _PyTime_gettimeofday(_PyTime_timeval *tp) ...@@ -61,15 +83,39 @@ _PyTime_gettimeofday(_PyTime_timeval *tp)
ftime(&t); ftime(&t);
tp->tv_sec = t.time; tp->tv_sec = t.time;
tp->tv_usec = t.millitm * 1000; tp->tv_usec = t.millitm * 1000;
if (info) {
info->implementation = "ftime()";
info->resolution = 1e-3;
info->is_monotonic = 0;
info->is_adjusted = 1;
}
} }
#else /* !HAVE_FTIME */ #else /* !HAVE_FTIME */
tp->tv_sec = time(NULL); tp->tv_sec = time(NULL);
tp->tv_usec = 0; tp->tv_usec = 0;
if (info) {
info->implementation = "time()";
info->resolution = 1.0;
info->is_monotonic = 0;
info->is_adjusted = 1;
}
#endif /* !HAVE_FTIME */ #endif /* !HAVE_FTIME */
#endif /* MS_WINDOWS */ #endif /* MS_WINDOWS */
} }
void
_PyTime_gettimeofday(_PyTime_timeval *tp)
{
pygettimeofday(tp, NULL);
}
void
_PyTime_gettimeofday_info(_PyTime_timeval *tp, _Py_clock_info_t *info)
{
pygettimeofday(tp, info);
}
static void static void
error_time_t_overflow(void) error_time_t_overflow(void)
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment