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:
.. 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)
Set the time of the specified clock *clk_id*.
......@@ -236,6 +260,22 @@ The module defines the following functions and data items:
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])
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:
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.
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.
.. function:: process_time()
If available, a monotonic clock is used. By default,
the function falls back to another clock if the monotonic clock failed or is
not available. If *strict* is True, raise an :exc:`OSError` on error or
:exc:`NotImplementedError` if no monotonic clock is available.
Sum of the system and user CPU time of the current process. It does not
include time elapsed during sleep. It is process-wide by definition. The
reference point of the returned value is undefined, so that only the
difference between the results of consecutive calls is valid.
.. versionadded:: 3.3
......
......@@ -1059,13 +1059,21 @@ sys
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
``CLOCK_xxx`` constants.
* :func:`~time.steady`.
* :func:`~time.get_clock_info`: Get information on a clock.
* :func:`~time.monotonic`: Monotonic clock (cannot go backward), not affected
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
......
......@@ -22,11 +22,25 @@ typedef struct {
} _PyTime_timeval;
#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
* fails or is not available, fall back to lower resolution clocks.
*/
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) \
do { \
tv.tv_usec += (long) (((long) interval - interval) * 1000000); \
......
......@@ -6,7 +6,10 @@ except ImportError:
import dummy_threading as threading
from collections import deque
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']
......
......@@ -5,6 +5,10 @@ import locale
import sysconfig
import sys
import platform
try:
import threading
except ImportError:
threading = None
# Max year is only limited by the size of C int.
SIZEOF_INT = sysconfig.get_config_var('SIZEOF_INT') or 4
......@@ -23,9 +27,20 @@ class TimeTestCase(unittest.TestCase):
time.timezone
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):
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'),
'need time.clock_gettime()')
def test_clock_realtime(self):
......@@ -56,7 +71,9 @@ class TimeTestCase(unittest.TestCase):
except PermissionError:
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):
self.assertEqual(time.ctime(self.t),
......@@ -342,23 +359,69 @@ class TimeTestCase(unittest.TestCase):
pass
self.assertEqual(time.strftime('%Z', tt), tzname)
def test_steady(self):
t1 = time.steady()
@unittest.skipUnless(hasattr(time, 'monotonic'),
'need time.monotonic')
def test_monotonic(self):
t1 = time.monotonic()
time.sleep(0.1)
t2 = time.steady()
t2 = time.monotonic()
dt = t2 - t1
# may fail if the system clock was changed
self.assertGreater(t2, t1)
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:
t1 = time.steady(strict=True)
except OSError as err:
self.skipTest("the monotonic clock failed: %s" % err)
except NotImplementedError:
self.skipTest("no monotonic clock available")
t2 = time.steady(strict=True)
time.clock_settime(time.CLOCK_REALTIME, realtime - 3600)
except PermissionError as err:
self.skipTest(err)
t2 = time.monotonic()
time.clock_settime(time.CLOCK_REALTIME, realtime)
# monotonic must not be affected by system clock updates
self.assertGreaterEqual(t2, t1)
def test_localtime_failure(self):
......@@ -378,6 +441,26 @@ class TimeTestCase(unittest.TestCase):
self.assertRaises(OSError, time.localtime, 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):
def setUp(self):
self.oldloc = locale.setlocale(locale.LC_ALL)
......
......@@ -3,7 +3,11 @@
import sys as _sys
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 _weakrefset import WeakSet
......
......@@ -81,6 +81,10 @@ Core and Builtins
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
__package__ (when possible).
......
This diff is collapsed.
......@@ -18,8 +18,8 @@
extern int ftime(struct timeb *);
#endif
void
_PyTime_gettimeofday(_PyTime_timeval *tp)
static void
pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info)
{
#ifdef MS_WINDOWS
FILETIME system_time;
......@@ -35,6 +35,20 @@ _PyTime_gettimeofday(_PyTime_timeval *tp)
microseconds = large.QuadPart / 10 - 11644473600000000;
tp->tv_sec = 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
/* There are three ways to get the time:
(1) gettimeofday() -- resolution in microseconds
......@@ -46,14 +60,22 @@ _PyTime_gettimeofday(_PyTime_timeval *tp)
Note: clock resolution does not imply clock accuracy! */
#ifdef HAVE_GETTIMEOFDAY
int err;
#ifdef GETTIMEOFDAY_NO_TZ
if (gettimeofday(tp) == 0)
return;
#else /* !GETTIMEOFDAY_NO_TZ */
if (gettimeofday(tp, (struct timezone *)NULL) == 0)
err = gettimeofday(tp);
#else
err = gettimeofday(tp, (struct timezone *)NULL);
#endif
if (err == 0) {
if (info) {
info->implementation = "gettimeofday()";
info->resolution = 1e-6;
info->is_monotonic = 0;
info->is_adjusted = 1;
}
return;
#endif /* !GETTIMEOFDAY_NO_TZ */
#endif /* !HAVE_GETTIMEOFDAY */
}
#endif /* HAVE_GETTIMEOFDAY */
#if defined(HAVE_FTIME)
{
......@@ -61,15 +83,39 @@ _PyTime_gettimeofday(_PyTime_timeval *tp)
ftime(&t);
tp->tv_sec = t.time;
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 */
tp->tv_sec = time(NULL);
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 /* 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
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