PEP 418: Add time.perf_counter(), time.process_time(), deprecate time.clock()
Reorder also sections.
This commit is contained in:
parent
288203ec0e
commit
a724856238
306
pep-0418.txt
306
pep-0418.txt
|
@ -1,5 +1,5 @@
|
|||
PEP: 418
|
||||
Title: Add a monotonic time functions
|
||||
Title: Add monotonic time, performance counter and process time functions
|
||||
Version: $Revision$
|
||||
Last-Modified: $Date$
|
||||
Author: Jim Jewett <jimjjewett@gmail.com>, Victor Stinner <victor.stinner@gmail.com>
|
||||
|
@ -13,7 +13,8 @@ Python-Version: 3.3
|
|||
Abstract
|
||||
========
|
||||
|
||||
Add time.monotonic() and time.get_clock_info(name) functions to Python 3.3.
|
||||
Add time.get_clock_info(name), time.monotonic(), time.perf_counter() and
|
||||
time.process_time() functions to Python 3.3.
|
||||
|
||||
|
||||
Rationale
|
||||
|
@ -25,62 +26,74 @@ early or too late when the system clock is set manually or adjusted
|
|||
automatically by NTP. A monotonic clock should be used instead to not be
|
||||
affected by system clock updates.
|
||||
|
||||
Clocks:
|
||||
To measure the performance of a function, time.clock() can be used but it
|
||||
is very different on Windows and on Unix. On Windows, time.clock() includes
|
||||
time elapsed during sleep, whereas it does not on Unix. time.clock() precision
|
||||
is very good on Windows, but very bad on Unix. A new time.perf_counter() should
|
||||
be used instead to always get the most precise performance counter with a
|
||||
portable behaviour.
|
||||
|
||||
* time.time(): system clock
|
||||
* time.monotonic(): monotonic clock
|
||||
To measure CPU time, Python does not provide directly a portable function.
|
||||
time.clock() can be used on Unix, but it has a bad precision.
|
||||
resource.getrusage() can also be used on Unix, but it requires to get fields of
|
||||
a structure and compute the sum of time spend in kernel space and user space.
|
||||
A new time.process_time() function is portable counter, always measure CPU time
|
||||
(don't include time elapsed during sleep), and has the best available
|
||||
precision.
|
||||
|
||||
Each operating system implements clocks and performance counters differently,
|
||||
and it is useful to know exactly which function is used and some properties of
|
||||
the clock like its resolution and its precision. A new time.get_clock_info()
|
||||
function gives access to all available information of each Python time
|
||||
function.
|
||||
|
||||
New functions:
|
||||
|
||||
* time.monotonic(): timeout and scheduling, not affected by system clock
|
||||
updates
|
||||
* time.perf_counter(): benchmarking, most precise clock for short period
|
||||
* time.process_time(): profiling, CPU time of the process
|
||||
|
||||
time.clock() is deprecated by this PEP because it is not portable:
|
||||
time.perf_counter() or time.process_time() should be used instead, depending
|
||||
on your requirements.
|
||||
|
||||
|
||||
Functions
|
||||
=========
|
||||
Python functions
|
||||
================
|
||||
|
||||
To fulfill the use cases, the functions' properties are:
|
||||
New functions
|
||||
-------------
|
||||
|
||||
* time.time(): system clock, "wall clock".
|
||||
* time.monotonic(): monotonic clock
|
||||
* time.get_clock_info(name): get information on the specified time function
|
||||
time.get_clock_info(name)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Get information on the specified clock. Supported clocks:
|
||||
|
||||
time.time()
|
||||
-----------
|
||||
* "clock": time.clock()
|
||||
* "monotonic": time.monotonic()
|
||||
* "perf_counter": time.perf_counter()
|
||||
* "process_time": time.process_time()
|
||||
* "time": 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. It is not monotonic.
|
||||
Return a dictionary with the following keys:
|
||||
|
||||
It is available on all platforms and cannot fail.
|
||||
* Mandatory keys:
|
||||
|
||||
Pseudo-code [#pseudo]_::
|
||||
* "function" (str): name of the underlying operating system function.
|
||||
Examples: "QueryPerformanceCounter()", "clock_gettime(CLOCK_REALTIME)".
|
||||
* "resolution" (float): resolution in seconds of the clock
|
||||
* "is_monotonic" (bool): True if the clock cannot go backward
|
||||
|
||||
if os.name == "nt":
|
||||
def time():
|
||||
return _time.GetSystemTimeAsFileTime()
|
||||
else:
|
||||
def time():
|
||||
if hasattr(time, "clock_gettime"):
|
||||
try:
|
||||
# resolution = 1 nanosecond
|
||||
return time.clock_gettime(time.CLOCK_REALTIME)
|
||||
except OSError:
|
||||
# CLOCK_REALTIME is not supported (unlikely)
|
||||
pass
|
||||
if hasattr(_time, "gettimeofday"):
|
||||
try:
|
||||
# resolution = 1 microsecond
|
||||
return _time.gettimeofday()
|
||||
except OSError:
|
||||
# gettimeofday() should not fail
|
||||
pass
|
||||
if hasattr(_time, "ftime"):
|
||||
# resolution = 1 millisecond
|
||||
return _time.ftime()
|
||||
else:
|
||||
# resolution = 1 second
|
||||
return _time.time()
|
||||
* Optional keys:
|
||||
|
||||
* "precision" (float): precision in seconds of the clock
|
||||
* "is_adjusted" (bool): True if the clock can be adjusted (e.g. by a NTP
|
||||
daemon)
|
||||
|
||||
|
||||
time.monotonic()
|
||||
----------------
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Monotonic clock, cannot go backward. It is not affected by system clock
|
||||
updates. The reference point of the returned value is undefined so only the
|
||||
|
@ -138,8 +151,117 @@ precision than GetTickCount(). It is not reliable and has too many issues.
|
|||
running for more than 49 days.
|
||||
|
||||
|
||||
time.perf_counter()
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Performance counter used for benchmarking. It is monotonic, cannot go backward.
|
||||
It does include time elapsed during sleep. The reference point of the returned
|
||||
value is undefined so only the difference between consecutive calls is valid
|
||||
and is number of seconds.
|
||||
|
||||
Pseudo-code::
|
||||
|
||||
def perf_counter():
|
||||
if perf_counter.use_performance_counter:
|
||||
if perf_counter.perf_frequency is None:
|
||||
try:
|
||||
perf_counter.perf_frequency = float(_time.QueryPerformanceFrequency())
|
||||
except OSError:
|
||||
# QueryPerformanceFrequency() fails if the installed
|
||||
# hardware does not support a high-resolution performance
|
||||
# counter
|
||||
perf_counter.use_performance_counter = False
|
||||
else:
|
||||
return _time.QueryPerformanceCounter() / perf_counter.perf_frequency
|
||||
else:
|
||||
return _time.QueryPerformanceCounter() / perf_counter.perf_frequency
|
||||
if perf_counter.use_monotonic:
|
||||
# Monotonic clock is preferred over system clock
|
||||
try:
|
||||
return time.monotonic()
|
||||
except OSError:
|
||||
perf_counter.use_monotonic = False
|
||||
return time.time()
|
||||
perf_counter.use_performance_counter = (os.name == 'nt')
|
||||
if perf_counter.use_performance_counter:
|
||||
perf_counter.perf_frequency = None
|
||||
perf_counter.use_monotonic = hasattr(time, 'monotonic')
|
||||
|
||||
|
||||
time.process_time()
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Process time used for profiling: some of kernel and user-space CPU
|
||||
time. It does not include time elapsed during sleep. The reference point of
|
||||
the returned value is undefined so only the difference between consecutive
|
||||
calls is valid.
|
||||
|
||||
Pseudo-code::
|
||||
|
||||
if os.name == 'nt':
|
||||
def process_time():
|
||||
handle = win32process.GetCurrentProcess()
|
||||
process_times = win32process.GetProcessTimes(handle)
|
||||
return (process_times['UserTime'] + process_times['KernelTime']) * 1e-7
|
||||
elif (hasattr(time, 'clock_gettime')
|
||||
and hasattr(time, 'CLOCK_PROCESS_CPUTIME_ID')):
|
||||
def process_time():
|
||||
return time.clock_gettime(time.CLOCK_PROCESS_CPUTIME_ID)
|
||||
else:
|
||||
try:
|
||||
import resource
|
||||
except ImportError:
|
||||
def process_time():
|
||||
return _time.clock()
|
||||
else:
|
||||
def process_time():
|
||||
usage = resource.getrusage(resource.RUSAGE_SELF)
|
||||
return usage[0] + usage[1]
|
||||
|
||||
|
||||
Existing functions
|
||||
------------------
|
||||
|
||||
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. It is not monotonic.
|
||||
|
||||
It is available on all platforms and cannot fail.
|
||||
|
||||
Pseudo-code [#pseudo]_::
|
||||
|
||||
if os.name == "nt":
|
||||
def time():
|
||||
return _time.GetSystemTimeAsFileTime()
|
||||
else:
|
||||
def time():
|
||||
if hasattr(time, "clock_gettime"):
|
||||
try:
|
||||
# resolution = 1 nanosecond
|
||||
return time.clock_gettime(time.CLOCK_REALTIME)
|
||||
except OSError:
|
||||
# CLOCK_REALTIME is not supported (unlikely)
|
||||
pass
|
||||
if hasattr(_time, "gettimeofday"):
|
||||
try:
|
||||
# resolution = 1 microsecond
|
||||
return _time.gettimeofday()
|
||||
except OSError:
|
||||
# gettimeofday() should not fail
|
||||
pass
|
||||
if hasattr(_time, "ftime"):
|
||||
# resolution = 1 millisecond
|
||||
return _time.ftime()
|
||||
else:
|
||||
# resolution = 1 second
|
||||
return _time.time()
|
||||
|
||||
|
||||
time.sleep()
|
||||
------------
|
||||
^^^^^^^^^^^^
|
||||
|
||||
Suspend execution for the given number of seconds. The actual suspension time
|
||||
may be less than that requested because any caught signal will terminate the
|
||||
|
@ -184,9 +306,11 @@ Pseudo-code [#pseudo]_::
|
|||
seconds = int(seconds)
|
||||
_time.sleep(seconds)
|
||||
|
||||
Deprecated functions
|
||||
--------------------
|
||||
|
||||
time.clock()
|
||||
------------
|
||||
^^^^^^^^^^^^
|
||||
|
||||
On Unix, return the current processor time as a floating point number expressed
|
||||
in seconds. The precision, and in fact the very definition of the meaning of
|
||||
|
@ -222,30 +346,6 @@ Pseudo-code [#pseudo]_::
|
|||
clock = _time.clock
|
||||
|
||||
|
||||
time.get_clock_info(name)
|
||||
-------------------------
|
||||
|
||||
Get information on the specified clock. Supported clocks:
|
||||
|
||||
* "clock": time.clock()
|
||||
* "monotonic": time.monotonic()
|
||||
* "time": time.time()
|
||||
|
||||
Return a dictionary with the following keys:
|
||||
|
||||
* Mandatory keys:
|
||||
|
||||
* "function" (str): name of the underlying operating system function.
|
||||
Examples: "QueryPerformanceCounter()", "clock_gettime(CLOCK_REALTIME)".
|
||||
* "resolution" (float): resolution in seconds of the clock
|
||||
* "is_monotonic" (bool): True if the clock cannot go backward
|
||||
|
||||
* Optional keys:
|
||||
|
||||
* "precision" (float): precision in seconds of the clock
|
||||
* "is_adjusted" (bool): True if the clock can be adjusted (e.g. by a NTP
|
||||
daemon)
|
||||
|
||||
|
||||
Glossary
|
||||
========
|
||||
|
@ -890,6 +990,7 @@ Name Resolution Include sleep
|
|||
========================= =============== =============
|
||||
GetProcessTimes() 100 ns No
|
||||
CLOCK_PROCESS_CPUTIME_ID 1 ns No
|
||||
getrusage() 1 µs No
|
||||
clock() \- No
|
||||
========================= =============== =============
|
||||
|
||||
|
@ -899,9 +1000,10 @@ Examples of clock precision on x86_64:
|
|||
Name Operating system Precision
|
||||
========================= ================ ===============
|
||||
CLOCK_PROCESS_CPUTIME_ID Linux 3.2 1 ns
|
||||
clock() Linux 3.2 1 µs
|
||||
clock() SunOS 5.11 1 µs
|
||||
getrusage() Linux 3.0 4 ms
|
||||
clock() FreeBSD 8.2 7.8 ms
|
||||
clock() Linux 3.2 10 ms
|
||||
clock() OpenBSD 5.0 10 ms
|
||||
GetProcessTimes() Windows Seven 15.6 ms
|
||||
========================= ================ ===============
|
||||
|
@ -937,6 +1039,10 @@ Resolution:
|
|||
* The clock resolution can be read using clock_getres().
|
||||
* GetProcessTimes(): call GetSystemTimeAdjustment().
|
||||
|
||||
Python source code includes a portable library to get the process time (CPU
|
||||
time): `Tools/pybench/systimes.py
|
||||
<http://hg.python.org/cpython/file/tip/Tools/pybench/systimes.py>`_.
|
||||
|
||||
|
||||
Thread time
|
||||
-----------
|
||||
|
@ -1123,8 +1229,10 @@ Other functions
|
|||
Alternatives: API design
|
||||
========================
|
||||
|
||||
Other names for time.monotonic()
|
||||
--------------------------------
|
||||
Other names for new functions
|
||||
-----------------------------
|
||||
|
||||
time.monotonic():
|
||||
|
||||
* time.counter()
|
||||
* time.seconds()
|
||||
|
@ -1136,6 +1244,12 @@ Other names for time.monotonic()
|
|||
The name "time.try_monotonic()" was also proposed when time.monotonic() was
|
||||
falling back to the system clock when no monotonic clock was available.
|
||||
|
||||
time.perf_counter():
|
||||
|
||||
* time.hires()
|
||||
* time.highres()
|
||||
* time.timer()
|
||||
|
||||
|
||||
Only expose operating system clocks
|
||||
-----------------------------------
|
||||
|
@ -1234,52 +1348,6 @@ Issues of a hacked monotonic function:
|
|||
see the same clock value
|
||||
|
||||
|
||||
Deferred API: time.perf_counter()
|
||||
=================================
|
||||
|
||||
Performance counter used for benchmarking and profiling. The reference point of
|
||||
the returned value is undefined so only the difference between consecutive calls is
|
||||
valid and is number of seconds.
|
||||
|
||||
Pseudo-code::
|
||||
|
||||
def perf_counter():
|
||||
if perf_counter.use_performance_counter:
|
||||
if perf_counter.perf_frequency is None:
|
||||
try:
|
||||
perf_counter.perf_frequency = float(_time.QueryPerformanceFrequency())
|
||||
except OSError:
|
||||
# QueryPerformanceFrequency() fails if the installed
|
||||
# hardware does not support a high-resolution performance
|
||||
# counter
|
||||
perf_counter.use_performance_counter = False
|
||||
else:
|
||||
return _time.QueryPerformanceCounter() / perf_counter.perf_frequency
|
||||
else:
|
||||
return _time.QueryPerformanceCounter() / perf_counter.perf_frequency
|
||||
if perf_counter.use_monotonic:
|
||||
# Monotonic clock is preferred over system clock
|
||||
try:
|
||||
return time.monotonic()
|
||||
except OSError:
|
||||
perf_counter.use_monotonic = False
|
||||
return time.time()
|
||||
perf_counter.use_performance_counter = (os.name == 'nt')
|
||||
if perf_counter.use_performance_counter:
|
||||
perf_counter.perf_frequency = None
|
||||
perf_counter.use_monotonic = hasattr(time, 'monotonic')
|
||||
|
||||
Other names proposed for time.perf_counter():
|
||||
|
||||
* time.hires()
|
||||
* time.highres()
|
||||
* time.timer()
|
||||
|
||||
Python source code includes a portable library to get the process time (CPU
|
||||
time): `Tools/pybench/systimes.py
|
||||
<http://hg.python.org/cpython/file/tip/Tools/pybench/systimes.py>`_.
|
||||
|
||||
|
||||
Footnotes
|
||||
=========
|
||||
|
||||
|
|
Loading…
Reference in New Issue