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
This commit is contained in:
Victor Stinner 2012-04-29 02:41:27 +02:00
parent ca6e40f12a
commit ec89539ccc
9 changed files with 729 additions and 133 deletions

View File

@ -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()
.. index::
single: benchmarking
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.
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.
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.
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.
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
.. function:: process_time()
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

View File

@ -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

View File

@ -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); \

View File

@ -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']

View File

@ -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)

View File

@ -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

View File

@ -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).

View File

@ -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,
#endif
CLOCK_MONOTONIC,
CLOCK_REALTIME
};
clockid_t monotonic_clk_ids[] = {
#ifdef CLOCK_MONOTONIC_RAW
CLOCK_MONOTONIC_RAW,
#endif
CLOCK_MONOTONIC
};
clockid_t *clk_ids;
int clk_ids_len;
int ret;
#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
if (strict) {
clk_index = &monotonic_clk_index;
clk_ids = monotonic_clk_ids;
clk_ids_len = Py_ARRAY_LENGTH(monotonic_clk_ids);
}
else {
clk_index = &steady_clk_index;
clk_ids = steady_clk_ids;
clk_ids_len = Py_ARRAY_LENGTH(steady_clk_ids);
}
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);
(*clk_index)++;
if (clk_ids_len <= *clk_index)
(*clk_index) = -1;
}
if (strict) {
if (clock_gettime(clk_id, &tp) != 0) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
return floattime();
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
if (strict) {
PyErr_SetString(PyExc_NotImplementedError,
"no steady clock available on your platform");
return NULL;
info->is_adjusted = 0;
#endif
if (clock_getres(clk_id, &res) == 0)
info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
else
info->resolution = 1e-9;
}
return floattime();
return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
#endif
}
static PyObject *
time_steady(PyObject *self, PyObject *args, PyObject *kwargs)
time_monotonic(PyObject *self, PyObject *unused)
{
static char *kwlist[] = {"strict", NULL};
int strict = 0;
if (!PyArg_ParseTupleAndKeywords(
args, kwargs, "|i:steady", kwlist,
&strict))
return NULL;
return steady_clock(strict);
return pymonotonic(NULL);
}
PyDoc_STRVAR(steady_doc,
"steady(strict=False) -> float\n\
PyDoc_STRVAR(monotonic_doc,
"monotonic() -> float\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.");
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;
}
#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);
}
PyDoc_STRVAR(perf_counter_doc,
"perf_counter() -> float\n\
\n\
Performance counter for benchmarking.");
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;
}
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);
}
#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
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);
}
}
#endif
return floatclock(info);
#endif
}
static PyObject *
time_process_time(PyObject *self, PyObject *unused)
{
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 (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);
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(get_clock_info_doc,
"get_clock_info(name: str) -> dict\n\
\n\
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);
}

View File

@ -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)
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;
#else /* !GETTIMEOFDAY_NO_TZ */
if (gettimeofday(tp, (struct timezone *)NULL) == 0)
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)
{