summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/time.rst85
-rw-r--r--Doc/whatsnew/3.3.rst18
-rw-r--r--Include/pytime.h14
-rw-r--r--Lib/queue.py5
-rw-r--r--Lib/test/test_time.py107
-rw-r--r--Lib/threading.py6
-rw-r--r--Misc/NEWS4
-rw-r--r--Modules/timemodule.c549
-rw-r--r--Python/pytime.c62
9 files changed, 723 insertions, 127 deletions
diff --git a/Doc/library/time.rst b/Doc/library/time.rst
index 70ef114..56ca865 100644
--- a/Doc/library/time.rst
+++ b/Doc/library/time.rst
@@ -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
diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst
index 495243f..3d63127 100644
--- a/Doc/whatsnew/3.3.rst
+++ b/Doc/whatsnew/3.3.rst
@@ -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
diff --git a/Include/pytime.h b/Include/pytime.h
index 221279b..a0cedb2 100644
--- a/Include/pytime.h
+++ b/Include/pytime.h
@@ -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); \
diff --git a/Lib/queue.py b/Lib/queue.py
index 1dc72c4..c3296fe 100644
--- a/Lib/queue.py
+++ b/Lib/queue.py
@@ -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']
diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py
index fb2489c..f00660b 100644
--- a/Lib/test/test_time.py
+++ b/Lib/test/test_time.py
@@ -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)
diff --git a/Lib/threading.py b/Lib/threading.py
index 8c2cee9..6c34d49 100644
--- a/Lib/threading.py
+++ b/Lib/threading.py
@@ -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
diff --git a/Misc/NEWS b/Misc/NEWS
index 98081c7..995ec9e 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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).
diff --git a/Modules/timemodule.c b/Modules/timemodule.c
index 771db83..1e843b9 100644
--- a/Modules/timemodule.c
+++ b/Modules/timemodule.c
@@ -4,9 +4,17 @@
#include <ctype.h>
+#ifdef HAVE_SYS_TIMES_H
+#include <sys/times.h>
+#endif
+
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
-#endif /* HAVE_SYS_TYPES_H */
+#endif
+
+#if defined(HAVE_SYS_RESOURCE_H)
+#include <sys/resource.h>
+#endif
#ifdef QUICKWIN
#include <io.h>
@@ -45,12 +53,16 @@
/* Forward declarations */
static int floatsleep(double);
-static PyObject* floattime(void);
+static PyObject* floattime(_Py_clock_info_t *info);
+
+#ifdef MS_WINDOWS
+static OSVERSIONINFOEX winver;
+#endif
static PyObject *
time_time(PyObject *self, PyObject *unused)
{
- return floattime();
+ return floattime(NULL);
}
PyDoc_STRVAR(time_doc,
@@ -70,7 +82,7 @@ Fractions of a second may be present if the system clock provides them.");
#endif
static PyObject *
-pyclock(void)
+floatclock(_Py_clock_info_t *info)
{
clock_t value;
value = clock();
@@ -80,15 +92,22 @@ pyclock(void)
"or its value cannot be represented");
return NULL;
}
+ if (info) {
+ info->implementation = "clock()";
+ info->resolution = 1.0 / (double)CLOCKS_PER_SEC;
+ info->is_monotonic = 1;
+ info->is_adjusted = 0;
+ }
return PyFloat_FromDouble((double)value / CLOCKS_PER_SEC);
}
#endif /* HAVE_CLOCK */
#if defined(MS_WINDOWS) && !defined(__BORLANDC__)
+#define WIN32_PERF_COUNTER
/* Win32 has better clock replacement; we have our own version, due to Mark
Hammond and Tim Peters */
-static PyObject *
-win32_clock(int fallback)
+static int
+win_perf_counter(_Py_clock_info_t *info, PyObject **result)
{
static LONGLONG cpu_frequency = 0;
static LONGLONG ctrStart;
@@ -102,28 +121,41 @@ win32_clock(int fallback)
if (!QueryPerformanceFrequency(&freq) || freq.QuadPart == 0) {
/* Unlikely to happen - this works on all intel
machines at least! Revert to clock() */
- if (fallback)
- return pyclock();
- else
- return PyErr_SetFromWindowsErr(0);
+ *result = NULL;
+ return -1;
}
cpu_frequency = freq.QuadPart;
}
QueryPerformanceCounter(&now);
diff = (double)(now.QuadPart - ctrStart);
- return PyFloat_FromDouble(diff / (double)cpu_frequency);
+ if (info) {
+ info->implementation = "QueryPerformanceCounter()";
+ info->resolution = 1.0 / (double)cpu_frequency;
+ info->is_monotonic = 1;
+ info->is_adjusted = 0;
+ }
+ *result = PyFloat_FromDouble(diff / (double)cpu_frequency);
+ return 0;
}
#endif
-#if (defined(MS_WINDOWS) && !defined(__BORLANDC__)) || defined(HAVE_CLOCK)
+#if defined(WIN32_PERF_COUNTER) || defined(HAVE_CLOCK)
+#define PYCLOCK
+static PyObject*
+pyclock(_Py_clock_info_t *info)
+{
+#ifdef WIN32_PERF_COUNTER
+ PyObject *res;
+ if (win_perf_counter(info, &res) == 0)
+ return res;
+#endif
+ return floatclock(info);
+}
+
static PyObject *
time_clock(PyObject *self, PyObject *unused)
{
-#if defined(MS_WINDOWS) && !defined(__BORLANDC__)
- return win32_clock(1);
-#else
- return pyclock();
-#endif
+ return pyclock(NULL);
}
PyDoc_STRVAR(clock_doc,
@@ -150,7 +182,6 @@ time_clock_gettime(PyObject *self, PyObject *args)
PyErr_SetFromErrno(PyExc_IOError);
return NULL;
}
-
return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
}
@@ -787,11 +818,74 @@ the local timezone used by methods such as localtime, but this behaviour\n\
should not be relied on.");
#endif /* HAVE_WORKING_TZSET */
+#if defined(MS_WINDOWS) || defined(__APPLE__) \
+ || (defined(HAVE_CLOCK_GETTIME) \
+ && (defined(CLOCK_HIGHRES) || defined(CLOCK_MONOTONIC)))
+#define PYMONOTONIC
+#endif
+
+#ifdef PYMONOTONIC
static PyObject*
-steady_clock(int strict)
+pymonotonic(_Py_clock_info_t *info)
{
-#if defined(MS_WINDOWS) && !defined(__BORLANDC__)
- return win32_clock(!strict);
+#if defined(MS_WINDOWS)
+ static ULONGLONG (*GetTickCount64) (void) = NULL;
+ static ULONGLONG (CALLBACK *Py_GetTickCount64)(void);
+ static int has_getickcount64 = -1;
+ double result;
+
+ if (has_getickcount64 == -1) {
+ /* GetTickCount64() was added to Windows Vista */
+ if (winver.dwMajorVersion >= 6) {
+ HINSTANCE hKernel32;
+ hKernel32 = GetModuleHandleW(L"KERNEL32");
+ *(FARPROC*)&Py_GetTickCount64 = GetProcAddress(hKernel32,
+ "GetTickCount64");
+ has_getickcount64 = (Py_GetTickCount64 != NULL);
+ }
+ else
+ has_getickcount64 = 0;
+ }
+
+ if (has_getickcount64) {
+ ULONGLONG ticks;
+ ticks = Py_GetTickCount64();
+ result = (double)ticks * 1e-3;
+ }
+ else {
+ static DWORD last_ticks = 0;
+ static DWORD n_overflow = 0;
+ DWORD ticks;
+
+ ticks = GetTickCount();
+ if (ticks < last_ticks)
+ n_overflow++;
+ last_ticks = ticks;
+
+ result = ldexp(n_overflow, 32);
+ result += ticks;
+ result *= 1e-3;
+ }
+
+ if (info) {
+ DWORD timeAdjustment, timeIncrement;
+ BOOL isTimeAdjustmentDisabled, ok;
+ if (has_getickcount64)
+ info->implementation = "GetTickCount64()";
+ else
+ info->implementation = "GetTickCount()";
+ info->is_monotonic = 1;
+ ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
+ &isTimeAdjustmentDisabled);
+ if (!ok) {
+ PyErr_SetFromWindowsErr(0);
+ return NULL;
+ }
+ info->resolution = timeIncrement * 1e-7;
+ info->is_adjusted = 0;
+ }
+ return PyFloat_FromDouble(result);
+
#elif defined(__APPLE__)
static mach_timebase_info_data_t timebase;
uint64_t time;
@@ -805,88 +899,338 @@ steady_clock(int strict)
time = mach_absolute_time();
secs = (double)time * timebase.numer / timebase.denom * 1e-9;
-
+ if (info) {
+ info->implementation = "mach_absolute_time()";
+ info->resolution = (double)timebase.numer / timebase.denom * 1e-9;
+ info->is_monotonic = 1;
+ info->is_adjusted = 0;
+ }
return PyFloat_FromDouble(secs);
-#elif defined(HAVE_CLOCK_GETTIME)
- static int steady_clk_index = 0;
- static int monotonic_clk_index = 0;
- int *clk_index;
- clockid_t steady_clk_ids[] = {
-#ifdef CLOCK_MONOTONIC_RAW
- CLOCK_MONOTONIC_RAW,
+
+#elif defined(HAVE_CLOCK_GETTIME) && (defined(CLOCK_HIGHRES) || defined(CLOCK_MONOTONIC))
+ struct timespec tp;
+#ifdef CLOCK_HIGHRES
+ const clockid_t clk_id = CLOCK_HIGHRES;
+ const char *function = "clock_gettime(CLOCK_HIGHRES)";
+#else
+ const clockid_t clk_id = CLOCK_MONOTONIC;
+ const char *function = "clock_gettime(CLOCK_MONOTONIC)";
#endif
- CLOCK_MONOTONIC,
- CLOCK_REALTIME
- };
- clockid_t monotonic_clk_ids[] = {
-#ifdef CLOCK_MONOTONIC_RAW
- CLOCK_MONOTONIC_RAW,
+
+ if (clock_gettime(clk_id, &tp) != 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+
+ if (info) {
+ struct timespec res;
+ info->is_monotonic = 1;
+ info->implementation = function;
+#if (defined(linux) || defined(__linux) || defined(__linux__)) \
+ && !defined(CLOCK_HIGHRES)
+ /* CLOCK_MONOTONIC is adjusted on Linux */
+ info->is_adjusted = 1;
+#else
+ info->is_adjusted = 0;
#endif
- CLOCK_MONOTONIC
- };
- clockid_t *clk_ids;
- int clk_ids_len;
- int ret;
- struct timespec tp;
+ if (clock_getres(clk_id, &res) == 0)
+ info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
+ else
+ info->resolution = 1e-9;
+ }
+ return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
+#endif
+}
- if (strict) {
- clk_index = &monotonic_clk_index;
- clk_ids = monotonic_clk_ids;
- clk_ids_len = Py_ARRAY_LENGTH(monotonic_clk_ids);
+static PyObject *
+time_monotonic(PyObject *self, PyObject *unused)
+{
+ return pymonotonic(NULL);
+}
+
+PyDoc_STRVAR(monotonic_doc,
+"monotonic() -> float\n\
+\n\
+Monotonic clock, cannot go backward.");
+#endif /* PYMONOTONIC */
+
+static PyObject*
+perf_counter(_Py_clock_info_t *info)
+{
+#if defined(WIN32_PERF_COUNTER) || defined(PYMONOTONIC)
+ PyObject *res;
+#endif
+#if defined(WIN32_PERF_COUNTER)
+ static int use_perf_counter = 1;
+#endif
+#ifdef PYMONOTONIC
+ static int use_monotonic = 1;
+#endif
+
+#ifdef WIN32_PERF_COUNTER
+ if (use_perf_counter) {
+ if (win_perf_counter(info, &res) == 0)
+ return res;
+ use_perf_counter = 0;
}
- else {
- clk_index = &steady_clk_index;
- clk_ids = steady_clk_ids;
- clk_ids_len = Py_ARRAY_LENGTH(steady_clk_ids);
+#endif
+
+#ifdef PYMONOTONIC
+ if (use_monotonic) {
+ res = pymonotonic(info);
+ if (res != NULL)
+ return res;
+ use_monotonic = 0;
+ PyErr_Clear();
}
+#endif
+
+ return floattime(info);
+}
+
+static PyObject *
+time_perf_counter(PyObject *self, PyObject *unused)
+{
+ return perf_counter(NULL);
+}
- while (0 <= *clk_index) {
- clockid_t clk_id = clk_ids[*clk_index];
- ret = clock_gettime(clk_id, &tp);
- if (ret == 0)
- return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
+PyDoc_STRVAR(perf_counter_doc,
+"perf_counter() -> float\n\
+\n\
+Performance counter for benchmarking.");
- (*clk_index)++;
- if (clk_ids_len <= *clk_index)
- (*clk_index) = -1;
+static PyObject*
+py_process_time(_Py_clock_info_t *info)
+{
+#if defined(MS_WINDOWS)
+ HANDLE process;
+ FILETIME creation_time, exit_time, kernel_time, user_time;
+ ULARGE_INTEGER large;
+ double total;
+ BOOL ok;
+
+ process = GetCurrentProcess();
+ ok = GetProcessTimes(process, &creation_time, &exit_time, &kernel_time, &user_time);
+ if (!ok)
+ return PyErr_SetFromWindowsErr(0);
+
+ large.u.LowPart = kernel_time.dwLowDateTime;
+ large.u.HighPart = kernel_time.dwHighDateTime;
+ total = (double)large.QuadPart;
+ large.u.LowPart = user_time.dwLowDateTime;
+ large.u.HighPart = user_time.dwHighDateTime;
+ total += (double)large.QuadPart;
+ if (info) {
+ info->implementation = "GetProcessTimes()";
+ info->resolution = 1e-7;
+ info->is_monotonic = 1;
+ info->is_adjusted = 0;
}
- if (strict) {
- PyErr_SetFromErrno(PyExc_OSError);
- return NULL;
+ return PyFloat_FromDouble(total * 1e-7);
+#else
+
+#if defined(HAVE_SYS_RESOURCE_H)
+ struct rusage ru;
+#endif
+#ifdef HAVE_TIMES
+ struct tms t;
+ static long ticks_per_second = -1;
+#endif
+
+#if defined(HAVE_CLOCK_GETTIME) \
+ && (defined(CLOCK_PROCESS_CPUTIME_ID) || defined(CLOCK_PROF))
+ struct timespec tp;
+#ifdef CLOCK_PROF
+ const clockid_t clk_id = CLOCK_PROF;
+ const char *function = "clock_gettime(CLOCK_PROF)";
+#else
+ const clockid_t clk_id = CLOCK_PROCESS_CPUTIME_ID;
+ const char *function = "clock_gettime(CLOCK_PROCESS_CPUTIME_ID)";
+#endif
+
+ if (clock_gettime(clk_id, &tp) == 0) {
+ if (info) {
+ struct timespec res;
+ info->implementation = function;
+ info->is_monotonic = 1;
+ info->is_adjusted = 0;
+ if (clock_getres(clk_id, &res) == 0)
+ info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
+ else
+ info->resolution = 1e-9;
+ }
+ return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
}
- return floattime();
+#endif
+
+#if defined(HAVE_SYS_RESOURCE_H)
+ if (getrusage(RUSAGE_SELF, &ru) == 0) {
+ double total;
+ total = ru.ru_utime.tv_sec + ru.ru_utime.tv_usec * 1e-6;
+ total += ru.ru_stime.tv_sec + ru.ru_stime.tv_usec * 1e-6;
+ if (info) {
+ info->implementation = "getrusage(RUSAGE_SELF)";
+ info->is_monotonic = 1;
+ info->is_adjusted = 0;
+ info->resolution = 1e-6;
+ }
+ return PyFloat_FromDouble(total);
+ }
+#endif
+
+#ifdef HAVE_TIMES
+ if (times(&t) != (clock_t)-1) {
+ double total;
+
+ if (ticks_per_second == -1) {
+#if defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK)
+ ticks_per_second = sysconf(_SC_CLK_TCK);
+ if (ticks_per_second < 1)
+ ticks_per_second = -1;
+#elif defined(HZ)
+ ticks_per_second = HZ;
#else
- if (strict) {
- PyErr_SetString(PyExc_NotImplementedError,
- "no steady clock available on your platform");
- return NULL;
+ ticks_per_second = 60; /* magic fallback value; may be bogus */
+#endif
+ }
+
+ if (ticks_per_second != -1) {
+ total = (double)t.tms_utime / ticks_per_second;
+ total += (double)t.tms_stime / ticks_per_second;
+ if (info) {
+ info->implementation = "times()";
+ info->is_monotonic = 1;
+ info->is_adjusted = 0;
+ info->resolution = 1.0 / ticks_per_second;
+ }
+ return PyFloat_FromDouble(total);
+ }
}
- return floattime();
+#endif
+
+ return floatclock(info);
#endif
}
static PyObject *
-time_steady(PyObject *self, PyObject *args, PyObject *kwargs)
+time_process_time(PyObject *self, PyObject *unused)
{
- static char *kwlist[] = {"strict", NULL};
- int strict = 0;
+ return py_process_time(NULL);
+}
+
+PyDoc_STRVAR(process_time_doc,
+"process_time() -> float\n\
+\n\
+Process time for profiling: sum of the kernel and user-space CPU time.");
+
+
+static PyTypeObject ClockInfoType;
+
+PyDoc_STRVAR(ClockInfo_docstring,
+ "Clock information");
+
+static PyStructSequence_Field ClockInfo_fields[] = {
+ {"implementation", "name of the underlying C function "
+ "used to get the clock value"},
+ {"is_monotonic", "True if the clock cannot go backward, False otherwise"},
+ {"is_adjusted", "True if the clock can be adjusted "
+ "(e.g. by a NTP daemon), False otherwise"},
+ {"resolution", "resolution of the clock in seconds"},
+ {NULL, NULL}
+};
+
+static PyStructSequence_Desc ClockInfo_desc = {
+ "time.clock_info",
+ ClockInfo_docstring,
+ ClockInfo_fields,
+ 4,
+};
+
+static PyObject *
+time_get_clock_info(PyObject *self, PyObject *args)
+{
+ char *name;
+ PyObject *obj;
+ _Py_clock_info_t info;
+ PyObject *result;
+
+ if (!PyArg_ParseTuple(args, "s:get_clock_info", &name))
+ return NULL;
+
+#ifdef Py_DEBUG
+ info.implementation = NULL;
+ info.is_monotonic = -1;
+ info.is_adjusted = -1;
+ info.resolution = -1.0;
+#else
+ info.implementation = "";
+ info.is_monotonic = 0;
+ info.is_adjusted = 0;
+ info.resolution = 1.0;
+#endif
- if (!PyArg_ParseTupleAndKeywords(
- args, kwargs, "|i:steady", kwlist,
- &strict))
+ if (strcmp(name, "time") == 0)
+ obj = floattime(&info);
+#ifdef PYCLOCK
+ else if (strcmp(name, "clock") == 0)
+ obj = pyclock(&info);
+#endif
+#ifdef PYMONOTONIC
+ else if (strcmp(name, "monotonic") == 0)
+ obj = pymonotonic(&info);
+#endif
+ else if (strcmp(name, "perf_counter") == 0)
+ obj = perf_counter(&info);
+ else if (strcmp(name, "process_time") == 0)
+ obj = py_process_time(&info);
+ else {
+ PyErr_SetString(PyExc_ValueError, "unknown clock");
+ return NULL;
+ }
+ if (obj == NULL)
return NULL;
+ Py_DECREF(obj);
- return steady_clock(strict);
+ result = PyStructSequence_New(&ClockInfoType);
+ if (result == NULL)
+ return NULL;
+
+ assert(info.implementation != NULL);
+ obj = PyUnicode_FromString(info.implementation);
+ if (obj == NULL)
+ goto error;
+ PyStructSequence_SET_ITEM(result, 0, obj);
+
+ assert(info.is_monotonic != -1);
+ obj = PyBool_FromLong(info.is_monotonic);
+ if (obj == NULL)
+ goto error;
+ PyStructSequence_SET_ITEM(result, 1, obj);
+
+ assert(info.is_adjusted != -1);
+ obj = PyBool_FromLong(info.is_adjusted);
+ if (obj == NULL)
+ goto error;
+ PyStructSequence_SET_ITEM(result, 2, obj);
+
+ assert(info.resolution > 0.0);
+ assert(info.resolution <= 1.0);
+ obj = PyFloat_FromDouble(info.resolution);
+ if (obj == NULL)
+ goto error;
+ PyStructSequence_SET_ITEM(result, 3, obj);
+
+ return result;
+
+error:
+ Py_DECREF(result);
+ return NULL;
}
-PyDoc_STRVAR(steady_doc,
-"steady(strict=False) -> float\n\
+PyDoc_STRVAR(get_clock_info_doc,
+"get_clock_info(name: str) -> dict\n\
\n\
-Return the current time as a floating point number expressed in seconds.\n\
-This clock advances at a steady rate relative to real time and it may not\n\
-be adjusted. The reference point of the returned value is undefined so only\n\
-the difference of consecutive calls is valid.");
-
+Get information of the specified clock.");
static void
PyInit_timezone(PyObject *m) {
@@ -977,10 +1321,8 @@ PyInit_timezone(PyObject *m) {
#endif /* __CYGWIN__ */
#endif /* !HAVE_TZNAME || __GLIBC__ || __CYGWIN__*/
-#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_GETRES)
-#ifdef CLOCK_REALTIME
+#if defined(HAVE_CLOCK_GETTIME)
PyModule_AddIntMacro(m, CLOCK_REALTIME);
-#endif
#ifdef CLOCK_MONOTONIC
PyModule_AddIntMacro(m, CLOCK_MONOTONIC);
#endif
@@ -1002,7 +1344,7 @@ PyInit_timezone(PyObject *m) {
static PyMethodDef time_methods[] = {
{"time", time_time, METH_NOARGS, time_doc},
-#if (defined(MS_WINDOWS) && !defined(__BORLANDC__)) || defined(HAVE_CLOCK)
+#ifdef PYCLOCK
{"clock", time_clock, METH_NOARGS, clock_doc},
#endif
#ifdef HAVE_CLOCK_GETTIME
@@ -1018,8 +1360,6 @@ static PyMethodDef time_methods[] = {
#ifdef HAVE_MKTIME
{"mktime", time_mktime, METH_O, mktime_doc},
#endif
- {"steady", (PyCFunction)time_steady, METH_VARARGS|METH_KEYWORDS,
- steady_doc},
#ifdef HAVE_STRFTIME
{"strftime", time_strftime, METH_VARARGS, strftime_doc},
#endif
@@ -1027,6 +1367,12 @@ static PyMethodDef time_methods[] = {
#ifdef HAVE_WORKING_TZSET
{"tzset", time_tzset, METH_NOARGS, tzset_doc},
#endif
+#ifdef PYMONOTONIC
+ {"monotonic", time_monotonic, METH_NOARGS, monotonic_doc},
+#endif
+ {"process_time", time_process_time, METH_NOARGS, process_time_doc},
+ {"perf_counter", time_perf_counter, METH_NOARGS, perf_counter_doc},
+ {"get_clock_info", time_get_clock_info, METH_VARARGS, get_clock_info_doc},
{NULL, NULL} /* sentinel */
};
@@ -1104,6 +1450,20 @@ PyInit_time(void)
if (!initialized) {
PyStructSequence_InitType(&StructTimeType,
&struct_time_type_desc);
+
+ /* initialize ClockInfoType */
+ PyStructSequence_InitType(&ClockInfoType, &ClockInfo_desc);
+ Py_INCREF(&ClockInfoType);
+ PyModule_AddObject(m, "clock_info", (PyObject*)&ClockInfoType);
+
+#ifdef MS_WINDOWS
+ winver.dwOSVersionInfoSize = sizeof(winver);
+ if (!GetVersionEx((OSVERSIONINFO*)&winver)) {
+ Py_DECREF(m);
+ PyErr_SetFromWindowsErr(0);
+ return NULL;
+ }
+#endif
}
Py_INCREF(&StructTimeType);
PyModule_AddObject(m, "struct_time", (PyObject*) &StructTimeType);
@@ -1112,7 +1472,7 @@ PyInit_time(void)
}
static PyObject*
-floattime(void)
+floattime(_Py_clock_info_t *info)
{
_PyTime_timeval t;
#ifdef HAVE_CLOCK_GETTIME
@@ -1123,10 +1483,21 @@ floattime(void)
because it would require to link Python to the rt (real-time)
library, at least on Linux */
ret = clock_gettime(CLOCK_REALTIME, &tp);
- if (ret == 0)
+ if (ret == 0) {
+ if (info) {
+ struct timespec res;
+ info->implementation = "clock_gettime(CLOCK_REALTIME)";
+ info->is_monotonic = 0;
+ info->is_adjusted = 1;
+ if (clock_getres(CLOCK_REALTIME, &res) == 0)
+ info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
+ else
+ info->resolution = 1e-9;
+ }
return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
+ }
#endif
- _PyTime_gettimeofday(&t);
+ _PyTime_gettimeofday_info(&t, info);
return PyFloat_FromDouble((double)t.tv_sec + t.tv_usec * 1e-6);
}
diff --git a/Python/pytime.c b/Python/pytime.c
index db3f683..ddd3088 100644
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -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)
{