GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing peps.python.org — PEP 418 – Add monotonic time, performance counter, and process time functions | peps.python.org
PEP 418 – Add monotonic time, performance counter, and process time functions
PEP 418 – Add monotonic time, performance counter, and process time functions
Table of Contents
AbstractThis PEP proposes to add RationaleIf a program uses the system time to schedule events or to implement
a timeout, it may fail to run events at the right moment or stop the
timeout too early or too late when the system time is changed manually or
adjusted automatically by NTP. A monotonic clock should be used
instead to not be affected by system time updates:
To measure the performance of a function, Until now, Python did not provide directly a portable
function to measure CPU time. 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. The new
New functions:
Users of new functions:
The Limitations:
Python functionsNew Functionstime.get_clock_info(name)Get information on the specified clock. Supported clock names:
Return a
time.monotonic()Monotonic clock, i.e. cannot go backward. It is not affected by system clock updates. The reference point of the returned value is undefined, so that only the difference between the results of consecutive calls is valid and is a number of seconds. On Windows versions older than Vista, Availability: Windows, Mac OS X, Linux, FreeBSD, OpenBSD, Solaris. Not available on GNU/Hurd. Pseudo-code [2]: if os.name == 'nt': # GetTickCount64() requires Windows Vista, Server 2008 or later if hasattr(_time, 'GetTickCount64'): def monotonic(): return _time.GetTickCount64() * 1e-3 else: def monotonic(): ticks = _time.GetTickCount() if ticks < monotonic.last: # Integer overflow detected monotonic.delta += 2**32 monotonic.last = ticks return (ticks + monotonic.delta) * 1e-3 monotonic.last = 0 monotonic.delta = 0 elif sys.platform == 'darwin': def monotonic(): if monotonic.factor is None: factor = _time.mach_timebase_info() monotonic.factor = timebase[0] / timebase[1] * 1e-9 return _time.mach_absolute_time() * monotonic.factor monotonic.factor = None elif hasattr(time, "clock_gettime") and hasattr(time, "CLOCK_HIGHRES"): def monotonic(): return time.clock_gettime(time.CLOCK_HIGHRES) elif hasattr(time, "clock_gettime") and hasattr(time, "CLOCK_MONOTONIC"): def monotonic(): return time.clock_gettime(time.CLOCK_MONOTONIC) On Windows, time.perf_counter()Performance counter with the highest available resolution to measure a short duration. It does include time elapsed during sleep and is system-wide. The reference point of the returned value is undefined, so that only the difference between the results of consecutive calls is valid and is a number of seconds. It is available on all platforms. Pseudo-code: if os.name == 'nt': def _win_perf_counter(): if _win_perf_counter.frequency is None: _win_perf_counter.frequency = _time.QueryPerformanceFrequency() return _time.QueryPerformanceCounter() / _win_perf_counter.frequency _win_perf_counter.frequency = None def perf_counter(): if perf_counter.use_performance_counter: try: return _win_perf_counter() except OSError: # QueryPerformanceFrequency() fails if the installed # hardware does not support a high-resolution performance # counter perf_counter.use_performance_counter = False if perf_counter.use_monotonic: # The monotonic clock is preferred over the system time try: return time.monotonic() except OSError: perf_counter.use_monotonic = False return time.time() perf_counter.use_performance_counter = (os.name == 'nt') perf_counter.use_monotonic = hasattr(time, 'monotonic') time.process_time()Sum of the system and user CPU time of the current process. It does not include time elapsed during sleep. It is process-wide by definition. The reference point of the returned value is undefined, so that only the difference between the results of consecutive calls is valid. It is available on all platforms. Pseudo-code [2]: if os.name == 'nt': def process_time(): handle = _time.GetCurrentProcess() process_times = _time.GetProcessTimes(handle) return (process_times['UserTime'] + process_times['KernelTime']) * 1e-7 else: try: import resource except ImportError: has_resource = False else: has_resource = True def process_time(): if process_time.clock_id is not None: try: return time.clock_gettime(process_time.clock_id) except OSError: process_time.clock_id = None if process_time.use_getrusage: try: usage = resource.getrusage(resource.RUSAGE_SELF) return usage[0] + usage[1] except OSError: process_time.use_getrusage = False if process_time.use_times: try: times = _time.times() cpu_time = times.tms_utime + times.tms_stime return cpu_time / process_time.ticks_per_seconds except OSError: process_time.use_getrusage = False return _time.clock() if (hasattr(time, 'clock_gettime') and hasattr(time, 'CLOCK_PROF')): process_time.clock_id = time.CLOCK_PROF elif (hasattr(time, 'clock_gettime') and hasattr(time, 'CLOCK_PROCESS_CPUTIME_ID')): process_time.clock_id = time.CLOCK_PROCESS_CPUTIME_ID else: process_time.clock_id = None process_time.use_getrusage = has_resource process_time.use_times = hasattr(_time, 'times') if process_time.use_times: # sysconf("SC_CLK_TCK"), or the HZ constant, or 60 process_time.ticks_per_seconds = _times.ticks_per_seconds Existing Functionstime.time()The system time which is usually the civil time. It is system-wide by definition. It can be set manually by the system administrator or automatically by a NTP daemon. It is available on all platforms and cannot fail. Pseudo-code [2]: if os.name == "nt": def time(): return _time.GetSystemTimeAsFileTime() else: def time(): if hasattr(time, "clock_gettime"): try: return time.clock_gettime(time.CLOCK_REALTIME) except OSError: # CLOCK_REALTIME is not supported (unlikely) pass if hasattr(_time, "gettimeofday"): try: return _time.gettimeofday() except OSError: # gettimeofday() should not fail pass if hasattr(_time, "ftime"): return _time.ftime() else: 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 Pseudo-code [2]: try: import select except ImportError: has_select = False else: has_select = hasattr(select, "select") if has_select: def sleep(seconds): return select.select([], [], [], seconds) elif hasattr(_time, "delay"): def sleep(seconds): milliseconds = int(seconds * 1000) _time.delay(milliseconds) elif os.name == "nt": def sleep(seconds): milliseconds = int(seconds * 1000) win32api.ResetEvent(hInterruptEvent); win32api.WaitForSingleObject(sleep.sigint_event, milliseconds) sleep.sigint_event = win32api.CreateEvent(NULL, TRUE, FALSE, FALSE) # SetEvent(sleep.sigint_event) will be called by the signal handler of SIGINT elif os.name == "os2": def sleep(seconds): milliseconds = int(seconds * 1000) DosSleep(milliseconds) else: def sleep(seconds): seconds = int(seconds) _time.sleep(seconds) Deprecated Functiontime.clock()On Unix, return the current processor time as a floating point number expressed in seconds. It is process-wide by definition. The resolution, and in fact the very definition of the meaning of “processor time”, depends on that of the C function of the same name, but in any case, this is the function to use for benchmarking Python or timing algorithms. On Windows, this function returns wall-clock seconds elapsed since the
first call to this function, as a floating point number, based on the
Win32 function Pseudo-code [2]: if os.name == 'nt': def clock(): try: return _win_perf_counter() except OSError: # QueryPerformanceFrequency() fails if the installed # hardware does not support a high-resolution performance # counter pass return _time.clock() else: clock = _time.clock Alternatives: API designOther names for time.monotonic()
The name “time.try_monotonic()” was also proposed for an older version of time.monotonic() which would fall back to the system time when no monotonic clock was available. Other names for time.perf_counter()
Only expose operating system clocksTo not have to define high-level clocks, which is a difficult task, a simpler approach is to only expose operating system clocks. time.clock_gettime() and related clock identifiers were already added to Python 3.3 for example. time.monotonic(): Fallback to system timeIf no monotonic clock is available, time.monotonic() falls back to the system time. Issues:
Different APIs were proposed to define such function. One function with a flag: time.monotonic(fallback=True)
A keyword argument that gets passed as a constant in the caller is usually poor API. Raising NotImplementedError for a function is something uncommon in Python and should be avoided. One time.monotonic() function, no flagtime.monotonic() returns (time: float, is_monotonic: bool). 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(). Choosing the clock from a list of constraintsThe PEP as proposed offers a few new clocks, but their guarantees are deliberately loose in order to offer useful clocks on different platforms. This inherently embeds policy in the calls, and the caller must thus choose a policy. The “choose a clock” approach suggests an additional API to let callers implement their own policy if necessary by making most platform clocks available and letting the caller pick amongst them. The PEP’s suggested clocks are still expected to be available for the common simple use cases. To do this two facilities are needed: an enumeration of clocks, and metadata on the clocks to enable the user to evaluate their suitability. The primary interface is a function make simple choices easy:
the caller can use
It returns a clock object with a .now() method returning the current time. The clock object is annotated with metadata describing the clock feature set; its .flags field will contain at least all the requested flags. time.get_clock() returns None if no matching clock is found and so calls can be chained using the or operator. Example of a simple policy decision: T = get_clock(MONOTONIC) or get_clock(STEADY) or get_clock() t = T.now() The available clocks always at least include a wrapper for Examples of flags of system clocks:
The clock objects contain other metadata including the clock flags with additional feature flags above those listed above, the name of the underlying OS facility, and clock precisions.
Example partial implementation: clockutils.py. Working around operating system bugs?Should Python ensure that a monotonic clock is truly monotonic by computing the maximum with the clock value and the previous value? Since it’s relatively straightforward to cache the last value returned using a static variable, it might be interesting to use this to make sure that the values returned are indeed monotonic.
Python may only work around a specific known operating system bug: KB274323 contains a code example to workaround the bug (use GetTickCount() to detect QueryPerformanceCounter() leap). Issues with “correcting” non-monotonicities:
Glossary
Hardware clocksList of hardware clocks
Linux clocksourceThere were 4 implementations of the time in the Linux kernel: UTIME (1996), timer wheel (1997), HRT (2001) and hrtimers (2007). The latter is the result of the “high-res-timers” project started by George Anzinger in 2001, with contributions by Thomas Gleixner and Douglas Niehaus. The hrtimers implementation was merged into Linux 2.6.21, released in 2007. hrtimers supports various clock sources. It sets a priority to each source to decide which one will be used. Linux supports the following clock sources:
High-resolution timers are not supported on all hardware architectures. They are at least provided on x86/x86_64, ARM and PowerPC. clock_getres() returns 1 nanosecond for The
Read also the time(7) manual page: “overview of time and timers”. FreeBSD timecounterkern.timecounter.choice lists available hardware clocks with their priority. The sysctl program can be used to change the timecounter. Example: # dmesg | grep Timecounter Timecounter "i8254" frequency 1193182 Hz quality 0 Timecounter "ACPI-safe" frequency 3579545 Hz quality 850 Timecounter "HPET" frequency 100000000 Hz quality 900 Timecounter "TSC" frequency 3411154800 Hz quality 800 Timecounters tick every 10.000 msec # sysctl kern.timecounter.choice kern.timecounter.choice: TSC(800) HPET(900) ACPI-safe(850) i8254(0) dummy(-1000000) # sysctl kern.timecounter.hardware="ACPI-fast" kern.timecounter.hardware: HPET -> ACPI-fast Available clocks:
The commit 222222 (May 2011) decreased ACPI-fast timecounter quality to 900 and increased HPET timecounter quality to 950: “HPET on modern platforms usually have better resolution and lower latency than ACPI timer”. Read Timecounters: Efficient and precise timekeeping in SMP kernels by Poul-Henning Kamp (2002) for the FreeBSD Project. PerformanceReading a hardware clock has a cost. The following table compares the performance of different hardware clocks on Linux 3.3 with Intel Core i7-2600 at 3.40GHz (8 cores). The bench_time.c program was used to fill these tables.
FreeBSD 8.0 in kvm with hardware virtualization:
Each function was called 100,000 times and CLOCK_MONOTONIC was used to get the time before and after. The benchmark was run 5 times, keeping the minimum time. NTP adjustmentNTP has different methods to adjust a clock:
By default, the time is slewed if the offset is less than 128 ms, but stepped otherwise. Slewing is generally desirable (i.e. we should use CLOCK_MONOTONIC, not CLOCK_MONOTONIC_RAW) if one wishes to measure “real” time (and not a time-like object like CPU cycles). This is because the clock on the other end of the NTP connection from you is probably better at keeping time: hopefully that thirty-five thousand dollars of Cesium timekeeping goodness is doing something better than your PC’s $3 quartz crystal, after all. Get more detail in the documentation of the NTP daemon. Operating system time functionsMonotonic Clocks
The “C Resolution” column is the resolution of the underlying C structure. Examples of clock resolution on x86_64:
The “OS Resolution” is the resolution announced by the operating system. The “Python Resolution” is the smallest difference between two calls to the time function computed in Python using the clock_resolution.py program. mach_absolute_timeMac OS X provides a monotonic clock: mach_absolute_time(). It is based on absolute elapsed time since system boot. It is not adjusted and cannot be set. mach_timebase_info() gives a fraction to convert the clock value to a number of nanoseconds. See also the Technical Q&A QA1398. mach_absolute_time() stops during a sleep on a PowerPC CPU, but not on an Intel CPU: Different behaviour of mach_absolute_time() on i386/ppc. CLOCK_MONOTONIC, CLOCK_MONOTONIC_RAW, CLOCK_BOOTTIMECLOCK_MONOTONIC and CLOCK_MONOTONIC_RAW represent monotonic time since
some unspecified starting point. They cannot be set. The resolution
can be read using Documentation: refer to the manual page of your operating system. Examples: CLOCK_MONOTONIC is available at least on the following operating systems:
The following operating systems don’t support CLOCK_MONOTONIC:
On Linux, NTP may adjust the CLOCK_MONOTONIC rate (slewed), but it cannot jump backward. 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. Linux 2.6.39 and glibc 2.14 introduces a new clock: CLOCK_BOOTTIME. CLOCK_BOOTTIME is identical to CLOCK_MONOTONIC, except that it also includes any time spent in suspend. Read also Waking systems from suspend (March, 2011). CLOCK_MONOTONIC stops while the machine is suspended. Linux provides also CLOCK_MONOTONIC_COARSE since Linux 2.6.32. It is similar to CLOCK_MONOTONIC, less precise but faster.
Windows: QueryPerformanceCounterHigh-resolution performance counter. It is monotonic. The frequency of the counter can be read using QueryPerformanceFrequency(). The resolution is 1 / QueryPerformanceFrequency(). It has a much higher resolution, but has lower long term precision than GetTickCount() and timeGetTime() clocks. For example, it will drift compared to the low precision clocks. Documentation: Hardware clocks used by QueryPerformanceCounter:
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. QueryPerformanceCounter() cannot be adjusted: SetSystemTimeAdjustment() only adjusts the system time. Bugs:
Windows: GetTickCount(), GetTickCount64()GetTickCount() and GetTickCount64() are monotonic, cannot fail and are not adjusted by SetSystemTimeAdjustment(). MSDN documentation: GetTickCount(), GetTickCount64(). The resolution can be read using GetSystemTimeAdjustment(). The elapsed time retrieved by GetTickCount() or GetTickCount64() includes time the system spends in sleep or hibernation. GetTickCount64() was added to Windows Vista and Windows Server 2008. It is possible to improve the precision using the undocumented NtSetTimerResolution() function. There are applications using this undocumented function, example: Timer Resolution. WaitForSingleObject() uses the same timer as GetTickCount() with the same precision. Windows: timeGetTimeThe timeGetTime function retrieves the system time, in milliseconds. The system time is the time elapsed since Windows was started. Read the timeGetTime() documentation. The return type of timeGetTime() is a 32-bit unsigned integer. As GetTickCount(), timeGetTime() rolls over after 2^32 milliseconds (49.7 days). The elapsed time retrieved by timeGetTime() includes time the system spends in sleep. The default precision of the timeGetTime function can be five milliseconds or more, depending on the machine. timeBeginPeriod() can be used to increase the precision of timeGetTime() up to 1 millisecond, but it negatively affects power consumption. Calling timeBeginPeriod() also affects the granularity of some other timing calls, such as CreateWaitableTimer(), WaitForSingleObject() and Sleep(). Note timeGetTime() and timeBeginPeriod() are part the Windows multimedia library and so require to link the program against winmm or to dynamically load the library. Solaris: CLOCK_HIGHRESThe Solaris OS has a CLOCK_HIGHRES timer that attempts to use an optimal hardware source, and may give close to nanosecond resolution. CLOCK_HIGHRES is the nonadjustable, high-resolution clock. For timers created with a clockid_t value of CLOCK_HIGHRES, the system will attempt to use an optimal hardware source. The resolution of CLOCK_HIGHRES can be read using Solaris: gethrtimeThe gethrtime() function returns the current high-resolution real time. Time is expressed as nanoseconds since some arbitrary time in the past; it is not correlated in any way to the time of day, and thus is not subject to resetting or drifting by way of adjtime() or settimeofday(). The hires timer is ideally suited to performance measurement tasks, where cheap, accurate interval timing is required. The linearity of gethrtime() is not preserved across a suspend-resume cycle (Bug 4272663). Read the gethrtime() manual page of Solaris 11. On Solaris, gethrtime() is the same as clock_gettime(CLOCK_MONOTONIC). System Time
The “C Resolution” column is the resolution of the underlying C structure. Examples of clock resolution on x86_64:
The “OS Resolution” is the resolution announced by the operating system. The “Python Resolution” is the smallest difference between two calls to the time function computed in Python using the clock_resolution.py program. Windows: GetSystemTimeAsFileTimeThe system time can be read using GetSystemTimeAsFileTime(), ftime() and time(). The resolution of the system time can be read using GetSystemTimeAdjustment(). Read the GetSystemTimeAsFileTime() documentation. The system time can be set using SetSystemTime(). System time on UNIXgettimeofday(), ftime(), time() and clock_gettime(CLOCK_REALTIME) return the system time. The resolution of CLOCK_REALTIME can be read using clock_getres(). The system time can be set using settimeofday() or clock_settime(CLOCK_REALTIME). Linux provides also CLOCK_REALTIME_COARSE since Linux 2.6.32. It is similar to CLOCK_REALTIME, less precise but faster. Alexander Shishkin proposed an API for Linux to be notified when the system clock is changed: timerfd: add TFD_NOTIFY_CLOCK_SET to watch for clock changes (4th version of the API, March 2011). The API is not accepted yet, but CLOCK_BOOTTIME provides a similar feature. Process TimeThe process time cannot be set. It is not monotonic: the clocks stop while the process is idle.
The “C Resolution” column is the resolution of the underlying C structure. Examples of clock resolution on x86_64:
The “OS Resolution” is the resolution announced by the operating system. The “Python Resolution” is the smallest difference between two calls to the time function computed in Python using the clock_resolution.py program. Functions
Python source code includes a portable library to get the process time (CPU time): Tools/pybench/systimes.py. See also the QueryProcessCycleTime() function (sum of the cycle time of all threads) and clock_getcpuclockid(). Thread TimeThe thread time cannot be set. It is not monotonic: the clocks stop while the thread is idle.
The “C Resolution” column is the resolution of the underlying C structure. Examples of clock resolution on x86_64:
The “OS Resolution” is the resolution announced by the operating system. The “Python Resolution” is the smallest difference between two calls to the time function computed in Python using the clock_resolution.py program. Functions
See also the QueryThreadCycleTime() function (cycle time for the specified thread) and pthread_getcpuclockid(). Windows: QueryUnbiasedInterruptTimeGets 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. The elapsed time retrieved by the QueryUnbiasedInterruptTime function includes only time that the system spends in the working state. QueryUnbiasedInterruptTime() is not monotonic. QueryUnbiasedInterruptTime() was introduced in Windows 7. See also QueryIdleProcessorCycleTime() function (cycle time for the idle thread of each processor) SleepSuspend execution of the process for the given number of seconds. Sleep is not affected by system time updates. Sleep is paused during system suspend. For example, if a process sleeps for 60 seconds and the system is suspended for 30 seconds in the middle of the sleep, the sleep duration is 90 seconds in the real time. Sleep can be interrupted by a signal: the function fails with EINTR.
Other functions:
The “C Resolution” column is the resolution of the underlying C structure. Functions
clock_nanosleepclock_nanosleep(clock_id, flags, nanoseconds, remaining): Linux manpage of clock_nanosleep(). If flags is TIMER_ABSTIME, then request is interpreted as an absolute time as measured by the clock, clock_id. If request is less than or equal to the current value of the clock, then clock_nanosleep() returns immediately without suspending the calling thread. POSIX.1 specifies that changing the value of the CLOCK_REALTIME clock via clock_settime(2) shall have no effect on a thread that is blocked on a relative clock_nanosleep(). select()select(nfds, readfds, writefds, exceptfs, timeout). Since Linux 2.6.28, select() uses high-resolution timers to handle the timeout. A process has a “slack” attribute to configure the precision of the timeout, the default slack is 50 microseconds. Before Linux 2.6.28, timeouts for select() were handled by the main timing subsystem at a jiffy-level resolution. Read also High- (but not too high-) resolution timeouts and Timer slack. Other functions
System StandbyThe ACPI power state “S3” is a system standby mode, also called “Suspend to RAM”. RAM remains powered. On Windows, the For Mac OS X, read Registering and unregistering for sleep and wake notifications (Technical Q&A QA1340). FootnotesLinksRelated Python issues:
Libraries exposing monotonic clocks:
Time:
AcceptanceThe PEP was accepted on 2012-04-28 by Guido van Rossum [1]. The PEP implementation has since been committed to the repository. ReferencesCopyrightThis document has been placed in the public domain. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |