PEP 418: Add fallback=True argument to time.monotonic()

* time.monotonic() doesn't use QPC anymore
 * Rewrite also time.monotonic() and time.highres() definitions.
This commit is contained in:
Victor Stinner 2012-03-31 00:50:05 +02:00
parent 2e69f4914f
commit 8e9a3c50ca
1 changed files with 53 additions and 36 deletions

View File

@ -13,7 +13,7 @@ Python-Version: 3.3
Abstract Abstract
======== ========
Add time.monotonic() and time.highres() functions to Python 3.3. Add time.monotonic(fallback=True) and time.highres() functions to Python 3.3.
Rationale Rationale
@ -24,12 +24,19 @@ Use cases:
* Display the current time to a human (e.g. display a calendar or draw a wall * 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() clock): use system clock. time.time() or datetime.datetime.now()
* Benchmark, profiling, timeout: time.highres() * Benchmark, profiling, timeout: time.highres()
* Event scheduler: time.monotonic() * Event scheduler: time.monotonic(), or time.monotonic(fallback=False)
Functions Functions
========= =========
* time.time(): system clock, "wall clock"
* time.highres(): clock with the best accuracy
* time.monotonic(fallback=True): monotonic clock. If no monotonic clock is
available, falls back to system clock by default, or raises an OSError if
*fallback* is False. time.monotonic(fallback=True) cannot go backward.
time.time() time.time()
----------- -----------
@ -68,12 +75,12 @@ Pseudo-code [#pseudo]_: ::
return _time.time() return _time.time()
time.monotonic() time.monotonic(fallback=True)
---------------- -----------------------------
Clock advancing at a monotonic rate relative to real time. It cannot go Clock that cannot go backward, its rate is as steady as possible. Its rate may
backward. Its rate may be adjusted by NTP. The reference point of the returned be adjusted by NTP. The reference point of the returned value is undefined so
value is undefined so only the difference of consecutive calls is valid. only the difference of consecutive calls is valid.
It is not available on all platforms and may raise an OSError. It is not It is not available on all platforms and may raise an OSError. It is not
available on GNU/Hurd for example. available on GNU/Hurd for example.
@ -85,34 +92,21 @@ Pseudo-code [#pseudo]_: ::
if os.name == 'nt': if os.name == 'nt':
# GetTickCount64() requires Windows Vista, Server 2008 or later # GetTickCount64() requires Windows Vista, Server 2008 or later
if hasattr(time, '_GetTickCount64'): if hasattr(time, '_GetTickCount64'):
_get_tick_count = _time.GetTickCount64 def monotonic(fallback=True):
return _time.GetTickCount64()
else: else:
def _get_tick_count(): def monotonic(fallback=True):
ticks = _time.GetTickCount() ticks = _time.GetTickCount()
if ticks < _get_tick_count.last: if ticks < monotonic.last:
# Integer overflow detected # Integer overflow detected
_get_tick_count.delta += 2**32 monotonic.delta += 2**32
_get_tick_count.last = ticks monotonic.last = ticks
return ticks + _get_tick_count.delta return ticks + monotonic.delta
_get_tick_count.last = 0 monotonic.last = 0
_get_tick_count.delta = 0 monotonic.delta = 0
def monotonic():
if monotonic.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
monotonic.use_performance_counter = False
# Fallback to GetTickCount/GetTickCount64 which has
# a lower resolution
return _get_tick_count()
monotonic.use_performance_counter = True
elif os.name == 'mac': elif os.name == 'mac':
def monotonic(): def monotonic(fallback=True):
if monotonic.factor is None: if monotonic.factor is None:
factor = _time.mach_timebase_info() factor = _time.mach_timebase_info()
monotonic.factor = timebase[0] / timebase[1] monotonic.factor = timebase[0] / timebase[1]
@ -120,31 +114,45 @@ Pseudo-code [#pseudo]_: ::
monotonic.factor = None monotonic.factor = None
elif os.name.startswith('sunos'): elif os.name.startswith('sunos'):
def monotonic(): def monotonic(fallback=True):
if monotonic.use_clock_highres: if monotonic.use_clock_highres:
try: try:
time.clock_gettime(time.CLOCK_HIGHRES) time.clock_gettime(time.CLOCK_HIGHRES)
except OSError: except OSError:
monotonic.use_clock_highres = False monotonic.use_clock_highres = False
return time.gethrtime() if monotonic.use_gethrtime:
try:
return time.gethrtime()
except OSError:
if not fallback:
raise
monotonic.use_gethrtime = False
return time.time()
monotonic.use_clock_highres = (hasattr(time, 'clock_gettime') monotonic.use_clock_highres = (hasattr(time, 'clock_gettime')
and hasattr(time, 'CLOCK_HIGHRES')) and hasattr(time, 'CLOCK_HIGHRES'))
monotonic.use_gethrtime = True
elif hasattr(time, "clock_gettime"): elif hasattr(time, "clock_gettime"):
def monotonic(): def monotonic(fallback=True):
while monotonic.clocks: while monotonic.clocks:
try: try:
clk_id = monotonic.clocks[0] clk_id = monotonic.clocks[0]
return time.clock_gettime(clk_id) return time.clock_gettime(clk_id)
except OSError: except OSError:
# CLOCK_MONOTONIC_RAW requires a Linux kernel >= 2.6.28 # CLOCK_MONOTONIC_RAW requires a Linux kernel >= 2.6.28
if len(monotonic.clocks) == 1 and not fallback:
raise
del monotonic.clocks[0] del monotonic.clocks[0]
return time.clock_gettime(time.CLOCK_MONOTONIC) return time.time()
monotonic.clocks = [] monotonic.clocks = []
if hasattr(time, 'CLOCK_MONOTONIC_RAW'): if hasattr(time, 'CLOCK_MONOTONIC_RAW'):
monotonic.clocks.append(time.CLOCK_MONOTONIC_RAW) monotonic.clocks.append(time.CLOCK_MONOTONIC_RAW)
if hasattr(time, 'CLOCK_HIGHRES'): if hasattr(time, 'CLOCK_HIGHRES'):
monotonic.clocks.append(time.CLOCK_HIGHRES) monotonic.clocks.append(time.CLOCK_HIGHRES)
monotonic.clocks.append(time.CLOCK_MONOTONIC)
On Windows, QueryPerformanceCounter() is not used even if it has a better
resolution than GetTickCount(). It is not reliable and has too much issues.
.. note:: .. note::
@ -157,20 +165,29 @@ Pseudo-code [#pseudo]_: ::
time.highres() time.highres()
-------------- --------------
High-resolution clock: use a monotonic clock if available, or fallback to the Clock with the best available resolution.
system time.
It is available on all platforms and cannot fail. It is available on all platforms and cannot fail.
Pseudo-code:: Pseudo-code::
def highres(): def highres():
if monotonic.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
monotonic.use_performance_counter = False
if highres.use_monotonic: if highres.use_monotonic:
# Monotonic clock is preferred over system clock
try: try:
return time.monotonic() return time.monotonic()
except OSError: except OSError:
highres.use_monotonic = False highres.use_monotonic = False
return time.time() return time.time()
highres.use_performance_counter = (os.name == 'nt')
highres.use_monotonic = hasattr(time, 'monotonic') highres.use_monotonic = hasattr(time, 'monotonic')