PEP 418: merge time.try_monotonic() and time.hires() into time.hires()
Cleanup the PEP.
This commit is contained in:
parent
280378cb3a
commit
02b9a5bcb3
217
pep-0418.txt
217
pep-0418.txt
|
@ -13,8 +13,7 @@ Python-Version: 3.3
|
|||
Abstract
|
||||
========
|
||||
|
||||
Add time.monotonic(), time.hires() and time.try_monotonic() functions to Python
|
||||
3.3.
|
||||
Add time.monotonic() and time.hires() functions to Python 3.3.
|
||||
|
||||
|
||||
Rationale
|
||||
|
@ -24,33 +23,9 @@ Use cases:
|
|||
|
||||
* 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()
|
||||
* Implement a timeout: use monotonic clock, or fallback to the clock with
|
||||
the highest resolution. time.try_monotonic()
|
||||
* Benchmark and profiling: time.hires(), with a manual fallback to time.time()
|
||||
* Benchmark, profiling, timeout: time.hires()
|
||||
* Event scheduler: time.monotonic()
|
||||
|
||||
time.try_monotonic() tries to use a monotonic clock, but it falls back to a
|
||||
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.
|
||||
|
||||
|
||||
Functions
|
||||
=========
|
||||
|
@ -60,7 +35,7 @@ time.time()
|
|||
|
||||
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.
|
||||
forward. It is not monotonic.
|
||||
|
||||
It is available on all platforms and cannot fail.
|
||||
|
||||
|
@ -96,12 +71,12 @@ Pseudo-code [#pseudo]_: ::
|
|||
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.
|
||||
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 available on all platforms and may raise an OSError.
|
||||
|
||||
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 [#pseudo]_: ::
|
||||
|
@ -157,53 +132,21 @@ Pseudo-code [#pseudo]_: ::
|
|||
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.
|
||||
High-resolution clock: use a monotonic clock if available, or fallback to the
|
||||
system time.
|
||||
|
||||
It is available on all platforms and cannot fail.
|
||||
|
||||
Pseudo-code [#pseudo]_: ::
|
||||
|
||||
if os.name == 'nt':
|
||||
def hires():
|
||||
return _time.QueryPerformanceCounter()
|
||||
elif hasattr(time, "monotonic"):
|
||||
hires = time.monotonic
|
||||
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()
|
||||
--------------------
|
||||
|
||||
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.
|
||||
|
||||
Pseudo-code::
|
||||
|
||||
def try_monotonic():
|
||||
if try_monotonic.use_monotonic:
|
||||
def hires():
|
||||
if hires.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
|
||||
except OSError:
|
||||
hires.use_monotonic = False
|
||||
return time.time()
|
||||
try_monotonic.use_monotonic = True
|
||||
try_monotonic.use_hires = True
|
||||
|
||||
|
||||
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.
|
||||
hires.use_monotonic = hasattr(time, 'monotonic')
|
||||
|
||||
|
||||
Clocks
|
||||
|
@ -212,54 +155,75 @@ 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().
|
||||
mach_absolute_time
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
CLOCK_MONOTONIC and CLOCK_MONOTONIC_RAW clocks cannot be set.
|
||||
Mac OS X provides a monotonic clock: mach_absolute_time(). mach_timebase_info()
|
||||
provides a fraction to convert the clock value 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.
|
||||
|
||||
mach_absolute_time() has a resolution of 1 nanosecond.
|
||||
|
||||
CLOCK_MONOTONIC, CLOCK_MONOTONIC_RAW
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
CLOCK_MONOTONIC and CLOCK_MONOTONIC_RAW represents monotonic time since some
|
||||
unspecified starting point. They cannot be set.
|
||||
|
||||
CLOCK_MONOTONIC_RAW is specific to Linux. It is similar to CLOCK_MONOTONIC, but
|
||||
provides access to a raw hardware-based time that is not subject to NTP
|
||||
adjustments. CLOCK_MONOTONIC_RAW requires Linux 2.6.28 or later.
|
||||
|
||||
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.
|
||||
avoid the NTP adjustement.
|
||||
|
||||
Resolution:
|
||||
CLOCK_MONOTONIC stops while the machine is suspended.
|
||||
|
||||
* 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
|
||||
clock_gettime() fails 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.
|
||||
|
||||
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
|
||||
clock_getres() gives the clock resolution. It is 1 nanosecond on Linux.
|
||||
|
||||
.. note::
|
||||
clock_gettime() requires to link the program with the realtime ("rt") library.
|
||||
|
||||
.. note::
|
||||
GetTickCount64() was added to Windows Vista and Windows Server 2008.
|
||||
QueryPerformanceCounter
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
High-resolution performance counter. It is monotonic.
|
||||
QueryPerformanceFrequency() gives its frequency.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
QueryPerformanceFrequency() should only be called once: the frequency will not
|
||||
change while the system is running. It fails if the installed hardware does not
|
||||
support a high-resolution performance counter.
|
||||
|
||||
|
||||
GetTickCount(), GetTickCount64()
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
GetTickCount() and GetTickCount64() are monotonic and cannot fail.
|
||||
|
||||
GetTickCount64() was added to Windows Vista and Windows Server 2008.
|
||||
|
||||
The clock resolution is 1 millisecond.
|
||||
|
||||
|
||||
System time
|
||||
-----------
|
||||
|
||||
System time on Windows
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
Windows: GetSystemTimeAsFileTime
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The system time can be read using GetSystemTimeAsFileTime(), ftime() and
|
||||
time().
|
||||
|
@ -276,12 +240,12 @@ The system time can be set using SetSystemTime().
|
|||
System time on UNIX
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
gettimeofday(), ftime(), time() and clock_settime(CLOCK_REALTIME) return the
|
||||
gettimeofday(), ftime(), time() and clock_gettime(CLOCK_REALTIME) return the
|
||||
system clock.
|
||||
|
||||
Resolution:
|
||||
|
||||
* clock_settime(): clock_getres(CLOCK_REALTIME), 1 nanosecond on Linux
|
||||
* clock_gettime(): clock_getres(CLOCK_REALTIME), 1 nanosecond on Linux
|
||||
* gettimeofday(): 1 microsecond
|
||||
* ftime(): 1 millisecond
|
||||
* time(): 1 second
|
||||
|
@ -299,7 +263,8 @@ Process
|
|||
^^^^^^^
|
||||
|
||||
* Windows: GetProcessTimes()
|
||||
* clock_gettime(CLOCK_PROCESS_CPUTIME_ID): High-resolution per-process timer from the CPU.
|
||||
* 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
|
||||
|
@ -331,28 +296,6 @@ Resolution:
|
|||
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 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
|
||||
--------------------------
|
||||
|
||||
|
@ -369,12 +312,12 @@ QueryUnbiasedInterruptTime() was introduced in Windows 7.
|
|||
Alternatives: API design
|
||||
========================
|
||||
|
||||
One function with a flag: time.try_monotonic(strict=False)
|
||||
One function with a flag: time.monotonic(strict=False)
|
||||
----------------------------------------------------------
|
||||
|
||||
* time.try_monotonic(strict=False) falls back to another clock if no monotonic clock
|
||||
* time.monotonic(strict=False) falls back to another clock if no monotonic clock
|
||||
is not available or does not work, but it does never fail.
|
||||
* time.try_monotonic(strict=True) raises OSError if monotonic clock fails or
|
||||
* time.monotonic(strict=True) raises OSError if monotonic clock fails or
|
||||
NotImplementedError if the system does not provide a monotonic clock
|
||||
|
||||
"A keyword argument that gets passed as a constant in the caller is usually
|
||||
|
@ -387,10 +330,10 @@ should be avoided.
|
|||
One function, no flag
|
||||
---------------------
|
||||
|
||||
time.try_monotonic() returns (time: float, is_monotonic: bool).
|
||||
time.monotonic() returns (time: float, is_monotonic: bool).
|
||||
|
||||
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().
|
||||
An alternative is to use a function attribute: time.monotonic.is_monotonic. The
|
||||
attribute value would be None before the first call to time.monotonic().
|
||||
|
||||
|
||||
Working around operating system bugs?
|
||||
|
|
Loading…
Reference in New Issue