2012-03-26 19:12:03 -04:00
|
|
|
PEP: 418
|
2012-03-27 13:27:28 -04:00
|
|
|
Title: Add monotonic and high-resolution time functions
|
2012-03-26 19:12:03 -04:00
|
|
|
Version: $Revision$
|
|
|
|
Last-Modified: $Date$
|
|
|
|
Author: Victor Stinner <victor.stinner@gmail.com>
|
|
|
|
Status: Draft
|
|
|
|
Type: Standards Track
|
|
|
|
Content-Type: text/x-rst
|
|
|
|
Created: 26-March-2012
|
|
|
|
Python-Version: 3.3
|
|
|
|
|
|
|
|
|
|
|
|
Abstract
|
|
|
|
========
|
|
|
|
|
2012-03-27 13:27:28 -04:00
|
|
|
Add time.monotonic(), time.hires() and time.try_monotonic() functions to Python
|
|
|
|
3.3.
|
2012-03-26 19:12:03 -04:00
|
|
|
|
|
|
|
|
|
|
|
Rationale
|
|
|
|
=========
|
|
|
|
|
|
|
|
Use cases:
|
|
|
|
|
2012-03-27 13:27:28 -04:00
|
|
|
* Display the current time to a human (e.g. display a calendar or draw a wall
|
|
|
|
clock): use system clock. time.time() or datetime.datetime.now()
|
2012-03-26 19:12:03 -04:00
|
|
|
* Implement a timeout: use monotonic clock, or fallback to the clock with
|
2012-03-27 13:27:28 -04:00
|
|
|
the highest resolution. time.try_monotonic()
|
|
|
|
* Benchmark and profiling: time.hires(), with a manual fallback to time.time()
|
2012-03-26 19:22:04 -04:00
|
|
|
* Event scheduler: time.monotonic()
|
2012-03-26 19:12:03 -04:00
|
|
|
|
2012-03-27 13:27:28 -04:00
|
|
|
time.try_monotonic() tries to use a monotonic clock, but it falls back to a
|
2012-03-26 19:12:03 -04:00
|
|
|
non-monotonic clock if no monotonic clock is available or if reading a
|
|
|
|
monotonic clock failed.
|
|
|
|
|
|
|
|
The pybench benchmark tool supports the following clocks: time.clock(),
|
|
|
|
time.time() and systimes.processtime(). By default, it uses time.clock() on
|
|
|
|
Windows, and time.time() otherwise. It is possible to choose the clock using
|
|
|
|
the --timer command line option.
|
|
|
|
|
|
|
|
Which clock?
|
|
|
|
|
|
|
|
* System time: pthread_mutex_timedlock(), pthread_cond_wait() (CLOCK_REALTIME)
|
|
|
|
* Monotonic: clock_nanosleep() (CLOCK_MONOTONIC)
|
|
|
|
|
|
|
|
Timeouts:
|
|
|
|
|
|
|
|
* threading.Lock.wait()
|
|
|
|
* select.select()
|
|
|
|
* time.sleep()
|
|
|
|
* subprocess.Popen.communicate()
|
|
|
|
* etc.
|
|
|
|
|
|
|
|
|
2012-03-26 19:30:38 -04:00
|
|
|
Functions
|
|
|
|
=========
|
|
|
|
|
2012-03-27 13:27:28 -04:00
|
|
|
time.time()
|
2012-03-26 19:30:38 -04:00
|
|
|
-----------
|
|
|
|
|
2012-03-27 13:27:28 -04:00
|
|
|
The system time is the "wall clock". It can be set manually by the system
|
|
|
|
administrator or automatically by a NTP daemon. It can jump backward and
|
|
|
|
forward, and is not monotonic.
|
|
|
|
|
|
|
|
* Windows: GetSystemTimeAsFileTime()
|
|
|
|
* clock_gettime(CLOCK_REALTIME), gettimeofday(), ftime() or time()
|
|
|
|
|
|
|
|
It is avaialble on all platforms and cannot fail.
|
|
|
|
|
|
|
|
|
|
|
|
time.monotonic()
|
|
|
|
----------------
|
|
|
|
|
|
|
|
Monotonic clock advancing at a monotonic rate relative to real time. It
|
|
|
|
cannot go backward. Its rate may be adjusted by NTP. The reference point of the
|
|
|
|
returned value is undefined so only the difference of consecutive calls is
|
|
|
|
valid.
|
|
|
|
|
|
|
|
It is not avaialble on all platforms and raise an OSError on error.
|
|
|
|
The monotonic clock may stop while the system is suspended.
|
|
|
|
|
|
|
|
Pseudo-code::
|
|
|
|
|
|
|
|
if os.name == 'nt':
|
|
|
|
if hasattr(time, '_GetTickCount64'):
|
|
|
|
monotonic = _time.GetTickCount64
|
|
|
|
else:
|
|
|
|
def monotonic():
|
|
|
|
ticks = _time.GetTickCount()
|
|
|
|
if ticks < monotonic.last:
|
|
|
|
# Integer overflow detected
|
|
|
|
monotonic.delta += 2**32
|
|
|
|
monotonic.last = ticks
|
|
|
|
return ticks + monotonic.delta
|
|
|
|
monotonic.last = 0
|
|
|
|
monotonic.delta = 0
|
|
|
|
if os.name == 'mac':
|
|
|
|
def monotonic():
|
|
|
|
if monotonic.factor is None:
|
|
|
|
factor = _time.mach_timebase_info()
|
|
|
|
monotonic.factor = timebase[0] / timebase[1]
|
|
|
|
return _time.mach_absolute_time() * monotonic.factor
|
|
|
|
monotonic.factor = None
|
|
|
|
elif hasattr(time, "clock_gettime") and hasattr(time, "CLOCK_MONOTONIC"):
|
|
|
|
def monotonic():
|
|
|
|
if monotonic.use_monotonic_raw:
|
|
|
|
try:
|
|
|
|
return time.clock_gettime(time.CLOCK_MONOTONIC_RAW)
|
|
|
|
except OSError:
|
|
|
|
monotonic.use_monotonic_raw = False
|
|
|
|
return time.clock_gettime(time.CLOCK_MONOTONIC)
|
|
|
|
monotonic.use_monotonic_raw = hasattr(time, "CLOCK_MONOTONIC_RAW")
|
|
|
|
|
|
|
|
time.hires()
|
|
|
|
------------
|
|
|
|
|
|
|
|
High-resolution clock. It has an unspecified starting point and may be
|
|
|
|
adjusted. The clock starting point changes when the system is resumed after
|
|
|
|
being suspended.
|
|
|
|
|
|
|
|
Pseudo-code::
|
|
|
|
|
|
|
|
if os.name == 'nt':
|
|
|
|
def hires():
|
|
|
|
return _time.QueryPerformanceCounter()
|
|
|
|
elif hasattr(time, "clock_gettime") and hasattr(time, "CLOCK_REALTIME"):
|
|
|
|
def hires():
|
|
|
|
return time.clock_gettime(time.CLOCK_REALTIME)
|
|
|
|
|
|
|
|
It is not available on all platforms and may raise an OSError.
|
|
|
|
|
|
|
|
|
|
|
|
time.try_monotonic()
|
|
|
|
--------------------
|
|
|
|
|
2012-03-26 19:30:38 -04:00
|
|
|
This clock advances at a steady rate relative to real time. It may be adjusted.
|
|
|
|
The reference point of the returned value is undefined so only the difference
|
|
|
|
of consecutive calls is valid.
|
|
|
|
|
2012-03-27 13:27:28 -04:00
|
|
|
Pseudo-code::
|
|
|
|
|
|
|
|
def try_monotonic():
|
|
|
|
if try_monotonic.use_monotonic:
|
|
|
|
try:
|
|
|
|
return time.monotonic()
|
|
|
|
except (AttributeError, OSError):
|
|
|
|
try_monotonic.use_monotonic = False
|
|
|
|
if try_monotonic.use_hires:
|
|
|
|
try:
|
|
|
|
return time.hires()
|
|
|
|
except (AttributeError, OSError):
|
|
|
|
try_monotonic.use_hires = False
|
|
|
|
return time.time()
|
|
|
|
try_monotonic.use_monotonic = True
|
|
|
|
try_monotonic.use_hires = True
|
|
|
|
|
|
|
|
|
2012-03-26 19:30:38 -04:00
|
|
|
If available, a monotonic clock is used. The function falls back to another
|
|
|
|
clock if the monotonic clock failed or is not available.
|
|
|
|
|
|
|
|
This function cannot fail.
|
|
|
|
|
|
|
|
|
2012-03-26 19:12:03 -04:00
|
|
|
Clocks
|
|
|
|
======
|
|
|
|
|
|
|
|
Monotonic
|
|
|
|
---------
|
|
|
|
|
|
|
|
* mach_absolute_time(): Mac OS X provides a monotonic clock:
|
|
|
|
mach_absolute_time(). mach_timebase_info() provides a fraction to convert it
|
|
|
|
to a number of nanoseconds. According to the documentation,
|
|
|
|
mach_timebase_info() is always equals to one and does never fail, even if
|
|
|
|
the function may fail according to its prototype.
|
|
|
|
* clock_gettime(CLOCK_MONOTONIC): Clock that cannot be set and represents
|
|
|
|
monotonic time since some unspecified starting point.
|
|
|
|
* clock_gettime(CLOCK_MONOTONIC_RAW), since Linux 2.6.28; Linux-specific:
|
|
|
|
Similar to CLOCK_MONOTONIC, but provides access to a raw hardware-based time
|
|
|
|
that is not subject to NTP adjustments.
|
|
|
|
* Windows: GetTickCount(), GetTickCount64().
|
|
|
|
|
|
|
|
CLOCK_MONOTONIC and CLOCK_MONOTONIC_RAW clocks cannot be set.
|
|
|
|
|
|
|
|
On Linux, NTP may adjust CLOCK_MONOTONIC rate, but not jump backward. If
|
|
|
|
available, CLOCK_MONOTONIC_RAW should be used instead of CLOCK_MONOTONIC to
|
|
|
|
avoid the NTP adjustement. CLOCK_MONOTONIC stops while the machine is
|
|
|
|
suspended.
|
|
|
|
|
|
|
|
Resolution:
|
|
|
|
|
|
|
|
* mach_absolute_time(): 1 nanosecond
|
|
|
|
* CLOCK_MONOTONIC, CLOCK_MONOTONIC_RAW: be read using clock_getres().
|
|
|
|
1 nanosecond on Linux for example.
|
|
|
|
* GetTickCount(), GetTickCount64(): 1 millisecond
|
|
|
|
|
|
|
|
May fail?
|
|
|
|
|
|
|
|
* mach_absolute_time() cannot fail. According to the documentation,
|
|
|
|
mach_timebase_info() does never fail, even if the function has a return
|
|
|
|
value.
|
|
|
|
* clock_gettime() can fail if the system does not support the specified clock,
|
|
|
|
whereas the standard C library supports it. For example, CLOCK_MONOTONIC_RAW
|
|
|
|
requires a kernel version 2.6.28 or later.
|
|
|
|
* GetTickCount() and GetTickCount64() cannot fail
|
|
|
|
|
|
|
|
Note: clock_gettime() requires to link the program with the realtime ("rt") library.
|
|
|
|
|
|
|
|
Note: GetTickCount64() was added to Windows Vista and Windows Server 2008.
|
|
|
|
|
|
|
|
|
|
|
|
System time
|
|
|
|
-----------
|
|
|
|
|
|
|
|
System time on Windows
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
|
|
The system time can be read using GetSystemTimeAsFileTime(), ftime() and
|
|
|
|
time().
|
|
|
|
|
|
|
|
The system time resolution can be read using GetSystemTimeAdjustment(). The
|
|
|
|
accurary is usually between 0.5 millisecond and 15 milliseconds. Resolution:
|
|
|
|
|
|
|
|
* GetSystemTimeAsFileTime(): 100 nanoseconds
|
|
|
|
* ftime(): 1 millisecond
|
|
|
|
* time(): 1 second
|
|
|
|
|
|
|
|
The system time can be set using SetSystemTime().
|
|
|
|
|
|
|
|
System time on UNIX
|
|
|
|
^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
|
|
gettimeofday(), ftime(), time() and clock_settime(CLOCK_REALTIME) return the
|
|
|
|
system clock.
|
|
|
|
|
|
|
|
Resolution:
|
|
|
|
|
|
|
|
* clock_settime(): clock_getres(CLOCK_REALTIME), 1 nanosecond on Linux
|
|
|
|
* gettimeofday(): 1 microsecond
|
|
|
|
* ftime(): 1 millisecond
|
|
|
|
* time(): 1 second
|
|
|
|
|
|
|
|
The system time can be set using settimeofday() or clock_settime(CLOCK_REALTIME).
|
|
|
|
|
|
|
|
|
|
|
|
Process and thread time
|
|
|
|
-----------------------
|
|
|
|
|
|
|
|
The process and thread time cannot be set. They are not monotonic: the clocks
|
|
|
|
stop while the process/thread is idle.
|
|
|
|
|
|
|
|
Process
|
|
|
|
^^^^^^^
|
|
|
|
|
|
|
|
* Windows: GetProcessTimes()
|
|
|
|
* clock_gettime(CLOCK_PROCESS_CPUTIME_ID): High-resolution per-process timer from the CPU.
|
|
|
|
* clock():
|
|
|
|
|
|
|
|
* Windows: The elapsed wall-clock time since the start of the process
|
|
|
|
(elapsed time in seconds times CLOCKS_PER_SEC). It can fail.
|
|
|
|
* UNIX: returns an approximation of processor time used by the program.
|
|
|
|
|
|
|
|
* times()
|
|
|
|
* getrusage(): ru_utime and ru_stime fields
|
|
|
|
|
|
|
|
Resolution:
|
|
|
|
|
|
|
|
* clock() rate is CLOCKS_PER_SEC. It was called CLK_TCK in Microsoft C before
|
|
|
|
6.0. On Linux 3, clock() has a resolution of 1 microsecond
|
|
|
|
* The clock resolution can be read using clock_getres().
|
|
|
|
clock_getres(CLOCK_REALTIME) is 1 nanosecond on Linux
|
|
|
|
* GetProcessTimes(): call GetSystemTimeAdjustment()
|
|
|
|
|
|
|
|
Thread
|
|
|
|
^^^^^^
|
|
|
|
|
|
|
|
* Windows: GetThreadTimes()
|
|
|
|
* clock_gettime(CLOCK_THREAD_CPUTIME_ID): Thread-specific CPU-time clock.
|
|
|
|
|
|
|
|
Resolution:
|
|
|
|
|
|
|
|
* CLOCK_THREAD_CPUTIME_ID: call clock_getres(). 1 nanosecond on Linux.
|
|
|
|
* GetThreadTimes(): call GetSystemTimeAdjustment()
|
|
|
|
|
|
|
|
See also pthread_getcpuclockid().
|
|
|
|
|
|
|
|
|
|
|
|
QueryPerformanceCounter
|
|
|
|
-----------------------
|
|
|
|
|
|
|
|
Windows provides a high-resolution performance counter:
|
|
|
|
QueryPerformanceCounter(). Its frequency can be retrieved using
|
|
|
|
QueryPerformanceFrequency().
|
|
|
|
|
|
|
|
On Windows XP, QueryPerformanceFrequency() is the processor frequency and
|
|
|
|
QueryPerformanceCounter() is the TSC of the current processor. Windows XP
|
|
|
|
had a bug (see `KB896256 <http://support.microsoft.com/?id=896256>`_): on a
|
|
|
|
multiprocessor computer, QueryPerformanceCounter() returned a different value
|
|
|
|
for each processor.
|
|
|
|
|
|
|
|
QueryPerformanceCounter() is not monotonic.
|
|
|
|
|
|
|
|
QueryPerformanceFrequency() fails if the installed hardware does not support a
|
|
|
|
high-resolution performance counter.
|
|
|
|
|
|
|
|
QueryPerformanceFrequency() should only be called once: the frequency will not
|
|
|
|
change while the system is running.
|
|
|
|
|
|
|
|
|
|
|
|
QueryUnbiasedInterruptTime
|
|
|
|
--------------------------
|
|
|
|
|
|
|
|
Gets the current unbiased interrupt time from the biased interrupt time and the
|
|
|
|
current sleep bias amount. This time is not affected by power management sleep
|
|
|
|
transitions.
|
|
|
|
|
|
|
|
Is it monotonic?
|
|
|
|
|
|
|
|
QueryUnbiasedInterruptTime() was introduced in Windows 7.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
API design
|
|
|
|
==========
|
|
|
|
|
2012-03-27 13:27:28 -04:00
|
|
|
Two functions: time.monotonic(), time.try_monotonic()
|
|
|
|
-----------------------------------------------------
|
2012-03-26 19:12:03 -04:00
|
|
|
|
2012-03-27 13:27:28 -04:00
|
|
|
* time.try_monotonic() falls back to another clock if no monotonic clock is not
|
2012-03-26 19:12:03 -04:00
|
|
|
available or does not work, but it does never fail.
|
|
|
|
* time.monotonic() is not always available and may raise OSError.
|
|
|
|
|
2012-03-27 13:27:28 -04:00
|
|
|
One function with a flag: time.try_monotonic(strict=False)
|
|
|
|
----------------------------------------------------------
|
2012-03-26 19:12:03 -04:00
|
|
|
|
2012-03-27 13:27:28 -04:00
|
|
|
* time.try_monotonic(strict=False) falls back to another clock if no monotonic clock
|
2012-03-26 19:12:03 -04:00
|
|
|
is not available or does not work, but it does never fail.
|
2012-03-27 13:27:28 -04:00
|
|
|
* time.try_monotonic(strict=True) raises OSError if monotonic clock fails or
|
2012-03-26 19:12:03 -04:00
|
|
|
NotImplementedError if the system does not provide a monotonic clock
|
|
|
|
|
|
|
|
"A keyword argument that gets passed as a constant in the caller is usually
|
|
|
|
poor API."
|
|
|
|
|
2012-03-26 19:30:38 -04:00
|
|
|
Raising NotImplementedError for a function is something uncommon in Python and
|
|
|
|
should be avoided.
|
|
|
|
|
2012-03-26 19:12:03 -04:00
|
|
|
One function, no flag
|
|
|
|
---------------------
|
|
|
|
|
2012-03-27 13:27:28 -04:00
|
|
|
time.try_monotonic() returns (time: float, is_monotonic: bool).
|
2012-03-26 19:12:03 -04:00
|
|
|
|
2012-03-27 13:27:28 -04:00
|
|
|
An alternative is to use a function attribute: time.try_monotonic.monotonic. The
|
|
|
|
attribute value would be None before the first call to time.try_monotonic().
|
2012-03-26 19:12:03 -04:00
|
|
|
|
|
|
|
|
2012-03-26 19:30:38 -04:00
|
|
|
Working around operating system bugs?
|
|
|
|
=====================================
|
2012-03-26 19:12:03 -04:00
|
|
|
|
|
|
|
Should Python ensure manually that a monotonic clock is truly monotonic by
|
|
|
|
computing the maximum with the clock value and the previous value?
|
|
|
|
|
|
|
|
* Virtual machines provide less reliable clocks.
|
|
|
|
* QueryPerformanceCounter() had a bug in 2006 on multiprocessor computers
|
|
|
|
|
|
|
|
|
|
|
|
Links
|
|
|
|
=====
|
|
|
|
|
2012-03-26 20:19:00 -04:00
|
|
|
* `Issue #12822: NewGIL should use CLOCK_MONOTONIC if possible.
|
|
|
|
<http://bugs.python.org/issue12822>`_
|
|
|
|
* `Issue #14222: Use time.steady() to implement timeout
|
|
|
|
<http://bugs.python.org/issue14222>`_
|
|
|
|
* `Issue #14397: Use GetTickCount/GetTickCount64 instead of QueryPerformanceCounter for monotonic clock
|
|
|
|
<http://bugs.python.org/issue14397>`_
|
|
|
|
* `python-monotonic-time
|
|
|
|
<http://code.google.com/p/python-monotonic-time/>`_
|
|
|
|
(`github <https://github.com/gavinbeatty/python-monotonic-time>`_)
|
|
|
|
* `Qt library: QElapsedTimer
|
|
|
|
<http://qt-project.org/doc/qt-4.8/qelapsedtimer.html>`_
|
2012-03-26 19:12:03 -04:00
|
|
|
* `Windows: Game Timing and Multicore Processors
|
|
|
|
<http://msdn.microsoft.com/en-us/library/ee417693.aspx>`_
|
|
|
|
|