Rewrite the PEP 418
* Rename time.steady() to time.monotonic(), again * time.monotonic() does not fallback to the system clock anymore * Drop time.perf_counter() function
This commit is contained in:
parent
f44aa119d8
commit
acaec37446
213
pep-0418.txt
213
pep-0418.txt
|
@ -1,5 +1,5 @@
|
|||
PEP: 418
|
||||
Title: Add steady and high-resolution time functions
|
||||
Title: Add a monotonic time functions
|
||||
Version: $Revision$
|
||||
Last-Modified: $Date$
|
||||
Author: Victor Stinner <victor.stinner@gmail.com>
|
||||
|
@ -13,8 +13,7 @@ Python-Version: 3.3
|
|||
Abstract
|
||||
========
|
||||
|
||||
Add time.steady(), time.perf_counter(), time.get_clock_info(name) functions to
|
||||
Python 3.3.
|
||||
Add time.monotonic() and time.get_clock_info(name) functions to Python 3.3.
|
||||
|
||||
|
||||
Rationale
|
||||
|
@ -25,8 +24,9 @@ Use cases:
|
|||
* Display the current time to a human (e.g. display a calendar or draw
|
||||
a wall clock): use system clock, i.e. time.time() or
|
||||
datetime.datetime.now().
|
||||
* Benchmark, profiling: time.perf_counter().
|
||||
* Event scheduler, timeout: time.steady().
|
||||
* Event scheduler, timeout: time.monotonic().
|
||||
* Benchmark, profiling: time.clock() on Windows, time.monotonic(),
|
||||
or fallback to time.time()
|
||||
|
||||
|
||||
Functions
|
||||
|
@ -35,8 +35,7 @@ Functions
|
|||
To fulfill the use cases, the functions' properties are:
|
||||
|
||||
* time.time(): system clock, "wall clock".
|
||||
* time.perf_counter(): clock with the best accuracy.
|
||||
* time.steady(): steady clock, should be monotonic
|
||||
* time.monotonic(): monotonic clock
|
||||
* time.get_clock_info(name): get information on the specified time function
|
||||
|
||||
|
||||
|
@ -78,129 +77,75 @@ Pseudo-code [#pseudo]_::
|
|||
return _time.time()
|
||||
|
||||
|
||||
time.steady()
|
||||
-------------
|
||||
time.monotonic()
|
||||
----------------
|
||||
|
||||
Steady clock. Use a monotonic clock, or falls back to the system clock. 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.
|
||||
|
||||
Use time.get_clock_info('steady')['is_monotonic'] to check if the clock
|
||||
monotonic or not.
|
||||
Monotonic clock, 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.
|
||||
|
||||
The elapsed time may or may not include time the system spends in
|
||||
sleep or hibernation; this depends on the operating system.
|
||||
|
||||
Availability: Windows, Mac OS X, Unix.
|
||||
|
||||
Pseudo-code [#pseudo]_::
|
||||
|
||||
if os.name == 'nt':
|
||||
# GetTickCount64() requires Windows Vista, Server 2008 or later
|
||||
if hasattr(time, '_GetTickCount64'):
|
||||
def steady():
|
||||
def monotonic():
|
||||
return _time.GetTickCount64() * 1e-3
|
||||
else:
|
||||
def steady():
|
||||
def monotonic():
|
||||
ticks = _time.GetTickCount()
|
||||
if ticks < steady.last:
|
||||
if ticks < monotonic.last:
|
||||
# Integer overflow detected
|
||||
steady.delta += 2**32
|
||||
steady.last = ticks
|
||||
return (ticks + steady.delta) * 1e-3
|
||||
steady.last = 0
|
||||
steady.delta = 0
|
||||
monotonic.delta += 2**32
|
||||
monotonic.last = ticks
|
||||
return (ticks + monotonic.delta) * 1e-3
|
||||
monotonic.last = 0
|
||||
monotonic.delta = 0
|
||||
|
||||
elif os.name == 'mac':
|
||||
def steady():
|
||||
if steady.factor is None:
|
||||
def monotonic():
|
||||
if monotonic.factor is None:
|
||||
factor = _time.mach_timebase_info()
|
||||
steady.factor = timebase[0] / timebase[1]
|
||||
return _time.mach_absolute_time() * steady.factor
|
||||
steady.factor = None
|
||||
monotonic.factor = timebase[0] / timebase[1]
|
||||
return _time.mach_absolute_time() * monotonic.factor
|
||||
monotonic.factor = None
|
||||
|
||||
elif os.name.startswith('sunos'):
|
||||
def steady():
|
||||
if steady.use_clock_highres:
|
||||
elif hasattr(time, "clock_gettime"):
|
||||
def monotonic():
|
||||
if monotonic.use_clock_highres:
|
||||
try:
|
||||
time.clock_gettime(time.CLOCK_HIGHRES)
|
||||
except OSError:
|
||||
steady.use_clock_highres = False
|
||||
if steady.use_gethrtime:
|
||||
try:
|
||||
return time.gethrtime()
|
||||
except OSError:
|
||||
steady.use_gethrtime = False
|
||||
return time.time()
|
||||
steady.use_clock_highres = (hasattr(time, 'clock_gettime')
|
||||
monotonic.use_clock_highres = False
|
||||
return time.clock_gettime(time.CLOCK_MONOTONIC)
|
||||
monotonic.use_clock_highres = (hasattr(time, 'clock_gettime')
|
||||
and hasattr(time, 'CLOCK_HIGHRES'))
|
||||
steady.use_gethrtime = True
|
||||
|
||||
elif hasattr(time, "clock_gettime"):
|
||||
def steady():
|
||||
while steady.clocks:
|
||||
try:
|
||||
clk_id = steady.clocks[0]
|
||||
return time.clock_gettime(clk_id)
|
||||
except OSError:
|
||||
del steady.clocks[0]
|
||||
return time.time()
|
||||
steady.clocks = []
|
||||
if hasattr(time, 'CLOCK_HIGHRES'):
|
||||
steady.clocks.append(time.CLOCK_HIGHRES)
|
||||
steady.clocks.append(time.CLOCK_MONOTONIC)
|
||||
|
||||
else:
|
||||
def steady():
|
||||
return time.time()
|
||||
|
||||
On Windows, QueryPerformanceCounter() is not used even though it has a
|
||||
better resolution than GetTickCount(). It is not reliable and has too
|
||||
many issues.
|
||||
On Windows, QueryPerformanceCounter() is not used even though it has a better
|
||||
accuracy than GetTickCount(). It is not reliable and has too many issues.
|
||||
|
||||
.. note::
|
||||
|
||||
time.steady() detects GetTickCount() integer overflow (32 bits,
|
||||
roll-over after 49.7 days): it increases a delta by 2\ :sup:`32`
|
||||
each time than an overflow is detected. The delta is stored in the
|
||||
process-local state and so the value of time.steady() may be
|
||||
different in two Python processes.
|
||||
time.monotonic() detects GetTickCount() integer overflow (32 bits, roll-over
|
||||
after 49.7 days): it increases a delta by 2\ :sup:`32` each time than an
|
||||
overflow is detected. The delta is stored in the process-local state and so
|
||||
the value of time.monotonic() may be different in two Python processes
|
||||
running for more than 49 days.
|
||||
|
||||
|
||||
time.perf_counter()
|
||||
-------------------
|
||||
|
||||
Clock with the best available resolution.
|
||||
|
||||
It is available on all platforms and cannot fail.
|
||||
|
||||
Pseudo-code::
|
||||
|
||||
def perf_counter():
|
||||
if perf_counter.use_performance_counter:
|
||||
try:
|
||||
return _time.QueryPerformanceCounter()
|
||||
except OSError:
|
||||
# QueryPerformanceFrequency() may fail, if the installed
|
||||
# hardware does not support a high-resolution performance
|
||||
# counter for example
|
||||
perf_counter.use_performance_counter = False
|
||||
if perf_counter.use_steady:
|
||||
# Monotonic clock is preferred over system clock
|
||||
try:
|
||||
return time.steady()
|
||||
except OSError:
|
||||
perf_counter.use_steady = False
|
||||
return time.time()
|
||||
perf_counter.use_performance_counter = (os.name == 'nt')
|
||||
perf_counter.use_steady = hasattr(time, 'steady')
|
||||
|
||||
time.get_clock_info(name)
|
||||
-------------------------
|
||||
|
||||
Get information on the specified clock. Supported clocks:
|
||||
|
||||
* "clock": time.clock()
|
||||
* "perf_counter": time.perf_counter()
|
||||
* "steady": time.steady()
|
||||
* "monotonic": time.monotonic()
|
||||
* "time": time.time()
|
||||
|
||||
Return a dictionary with the following keys:
|
||||
|
@ -264,8 +209,8 @@ Hardware clocks
|
|||
32,768 Hz
|
||||
|
||||
|
||||
NTP adjusted
|
||||
============
|
||||
NTP adjustment
|
||||
==============
|
||||
|
||||
NTP has diffent methods to adjust a clock:
|
||||
|
||||
|
@ -655,10 +600,6 @@ clock_getres(). It looks like Linux does not implement clock_getres() and
|
|||
always return 1 nanosecond. For GetProcessTimes(), the accuracy is read using
|
||||
GetSystemTimeAdjustment().
|
||||
|
||||
Python source code includes a portable library to get the process time:
|
||||
`Tools/pybench/systimes.py
|
||||
<http://hg.python.org/cpython/file/tip/Tools/pybench/systimes.py>`_.
|
||||
|
||||
|
||||
Functions
|
||||
^^^^^^^^^
|
||||
|
@ -793,27 +734,17 @@ Read also the `time(7) manual page
|
|||
Alternatives: API design
|
||||
========================
|
||||
|
||||
Other names for new functions
|
||||
-----------------------------
|
||||
Other names for time.monotonic()
|
||||
--------------------------------
|
||||
|
||||
time.perf_counter():
|
||||
|
||||
* time.highres()
|
||||
* time.hires(): "hires" can be read as "to hire" as in "he hires a car
|
||||
to go on holiday", rather than a "HIgh-RESolution clock".
|
||||
* time.timer(): "it would be too easy to confuse with (or misspell as)
|
||||
time.time()"
|
||||
|
||||
time.steady():
|
||||
|
||||
* time.monotonic(): QueryPerformanceCounter() is monotonic but it is not used
|
||||
by time.steady() because it is not steady, and it is surprising to have to
|
||||
check for time.get_clock_info('monotonic')['is_monotonic'].
|
||||
* time.try_monotonic(): it is a clear and obvious solution for the
|
||||
use-case of "I prefer the monotonic clock, if it is available,
|
||||
otherwise I'll take my chances with a best-effect clock."
|
||||
* time.steady()
|
||||
* time.wallclock(): it is not the system time aka the "wall clock", but
|
||||
a monotonic clock with an unspecified starting point
|
||||
* time.seconds()
|
||||
* time.counter()
|
||||
|
||||
The name "time.try_monotonic()" was also proposed when time.monotonic() was
|
||||
falling back to the system clock when no monotonic clock was available.
|
||||
|
||||
|
||||
Only expose operating system clocks
|
||||
|
@ -824,14 +755,18 @@ approach is to only expose operating system clocks. time.clock_gettime() and
|
|||
related clock identifiers were already added to Python 3.3 for example.
|
||||
|
||||
|
||||
Don't fallback on system clock
|
||||
------------------------------
|
||||
Fallback to system clock
|
||||
------------------------
|
||||
|
||||
time.monotonic() is always a monotonic clock and is only available if the
|
||||
operating system provides a monotonic clock.
|
||||
If no monotonic clock is available, time.monotonic() falls back to the system
|
||||
clock.
|
||||
|
||||
time.perf_counter() is only available if the operating system provides a clock
|
||||
with a high resolution (e.g. at least a microsecond or better).
|
||||
Issues:
|
||||
|
||||
* It is hard to define correctly such function in the documentation: is it
|
||||
monotonic? is it steady? is it adjusted?
|
||||
* Some user want to decide what to do when no monotonic clock is available:
|
||||
use another clock, display an error, or do something else
|
||||
|
||||
|
||||
One function choosing the clock from a list of constrains
|
||||
|
@ -846,7 +781,8 @@ One function choosing the clock from a list of constrains
|
|||
time.get_clock() returns None if the clock is found and so calls can be chained
|
||||
using the or operator. Example::
|
||||
|
||||
func = time.get_clock(time.MONOTONIC) or time.get_clock(time.STEADY) or time.time()
|
||||
get_time = time.get_clock(time.MONOTONIC) or time.get_clock(time.STEADY) or time.time()
|
||||
t = get_time()
|
||||
|
||||
Example of flags of system clocks:
|
||||
|
||||
|
@ -854,7 +790,7 @@ Example of flags of system clocks:
|
|||
* GetTickCount: MONOTONIC | STEADY
|
||||
* CLOCK_MONOTONIC: MONOTONIC | STEADY (or only MONOTONIC on Linux)
|
||||
* CLOCK_MONOTONIC_RAW: MONOTONIC | STEADY
|
||||
* gettimeofday(): (none)
|
||||
* gettimeofday(): (no flag)
|
||||
|
||||
|
||||
One function with a flag: time.monotonic(fallback=True)
|
||||
|
@ -908,6 +844,29 @@ Issues of a hacked monotonic function:
|
|||
see the same clock value
|
||||
|
||||
|
||||
Deferred API: time.perf_counter()
|
||||
=================================
|
||||
|
||||
Python does not provide a portable "performance counter" clock for benchmarking
|
||||
or profiling. Each tool has to implement its own heuristic to decide which
|
||||
clock is the best depending on the OS and on which counters are available.
|
||||
|
||||
A previous version of the PEP proposed a time.perf_counter() function using
|
||||
QueryPerformanceCounter() on Windows, time.monotonic(), or falls back to the
|
||||
system time. This function was not well defined and the idea is deferred.
|
||||
|
||||
Proposed names for such function:
|
||||
|
||||
* time.hires()
|
||||
* time.highres()
|
||||
* time.perf_counter()
|
||||
* time.timer()
|
||||
|
||||
Python source code includes a portable library to get the process time:
|
||||
`Tools/pybench/systimes.py
|
||||
<http://hg.python.org/cpython/file/tip/Tools/pybench/systimes.py>`_.
|
||||
|
||||
|
||||
Footnotes
|
||||
=========
|
||||
|
||||
|
|
Loading…
Reference in New Issue