forked from python/peps
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpep-0418.txt
1653 lines (1300 loc) · 64.3 KB
/
pep-0418.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
PEP: 418
Title: Add monotonic time, performance counter, and process time functions
Version: $Revision$
Last-Modified: $Date$
Author: Cameron Simpson <[email protected]>, Jim Jewett <[email protected]>, Stephen J. Turnbull <[email protected]>, Victor Stinner <[email protected]>
Status: Final
Type: Standards Track
Content-Type: text/x-rst
Created: 26-March-2012
Python-Version: 3.3
Abstract
========
This PEP proposes to add ``time.get_clock_info(name)``,
``time.monotonic()``, ``time.perf_counter()`` and
``time.process_time()`` functions to Python 3.3.
Rationale
=========
If 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:
``time.monotonic()``.
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()`` resolution is very good on Windows, but
very bad on Unix. The new ``time.perf_counter()`` function should be
used instead to always get the most precise performance counter with a
portable behaviour (ex: include time spend during sleep).
Until now, Python did not provide directly a portable
function to measure CPU time. ``time.clock()`` can be used on Unix,
but it has bad
resolution. ``resource.getrusage()`` or ``os.times()`` can also be
used on Unix, but they require to compute the sum of time
spent in kernel space and user space. The new ``time.process_time()``
function acts as a portable counter that always measures CPU time
(excluding time elapsed during sleep) and has the best available
resolution.
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
``time.get_clock_info()`` function gives access to all available
information about 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
Users of new functions:
* time.monotonic(): concurrent.futures, multiprocessing, queue, subprocess,
telnet and threading modules to implement timeout
* time.perf_counter(): trace and timeit modules, pybench program
* time.process_time(): profile module
* time.get_clock_info(): pybench program to display information about the
timer like the resolution
The ``time.clock()`` function is deprecated because it is not
portable: it behaves differently depending on the operating system.
``time.perf_counter()`` or ``time.process_time()`` should be used
instead, depending on your requirements. ``time.clock()`` is marked as
deprecated but is not planned for removal.
Limitations:
* The behaviour of clocks after a system suspend is not defined in the
documentation of new functions. The behaviour depends on the
operating system: see the `Monotonic Clocks`_ section below. Some
recent operating systems provide two clocks, one including time
elapsed during system suspend, one not including this time. Most
operating systems only provide one kind of clock.
* time.monotonic() and time.perf_counter() may or may not be adjusted.
For example, ``CLOCK_MONOTONIC`` is slewed on Linux, whereas
``GetTickCount()`` is not adjusted on Windows.
``time.get_clock_info('monotonic')['adjustable']`` can be used to check
if the monotonic clock is adjustable or not.
* No time.thread_time() function is proposed by this PEP because it is
not needed by Python standard library nor a common asked feature.
Such function would only be available on Windows and Linux. On
Linux, it is possible to use
``time.clock_gettime(CLOCK_THREAD_CPUTIME_ID)``. On Windows, ctypes or
another module can be used to call the ``GetThreadTimes()``
function.
Python functions
================
New Functions
-------------
time.get_clock_info(name)
^^^^^^^^^^^^^^^^^^^^^^^^^
Get information on the specified clock. Supported clock names:
* ``"clock"``: ``time.clock()``
* ``"monotonic"``: ``time.monotonic()``
* ``"perf_counter"``: ``time.perf_counter()``
* ``"process_time"``: ``time.process_time()``
* ``"time"``: ``time.time()``
Return a ``time.clock_info`` object which has the following attributes:
* ``implementation`` (str): name of the underlying operating system
function. Examples: ``"QueryPerformanceCounter()"``,
``"clock_gettime(CLOCK_REALTIME)"``.
* ``monotonic`` (bool): True if the clock cannot go backward.
* ``adjustable`` (bool): ``True`` if the clock can be changed automatically
(e.g. by a NTP daemon) or manually by the system administrator, ``False``
otherwise
* ``resolution`` (float): resolution in seconds of the clock.
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, ``time.monotonic()`` detects
``GetTickCount()`` integer overflow (32 bits, roll-over after 49.7
days). It increases an internal epoch (reference time by) 2\
:sup:`32` each time that an overflow is detected. The epoch 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. On more recent versions of
Windows and on other operating systems, ``time.monotonic()`` is
system-wide.
Availability: Windows, Mac OS X, Linux, FreeBSD, OpenBSD, Solaris.
Not available on GNU/Hurd.
Pseudo-code [#pseudo]_::
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, ``QueryPerformanceCounter()`` is not used even though it
has a better resolution than ``GetTickCount()``. It is not reliable
and has too many issues.
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 [#pseudo]_::
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 Functions
------------------
time.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 [#pseudo]_::
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 ``time.sleep()`` following execution of that
signal's catching routine. Also, the suspension time may be longer
than requested by an arbitrary amount because of the scheduling of
other activity in the system.
Pseudo-code [#pseudo]_::
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 Function
-------------------
time.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 ``QueryPerformanceCounter()``. The resolution is
typically better than one microsecond. It is system-wide.
Pseudo-code [#pseudo]_::
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 design
========================
Other names for time.monotonic()
--------------------------------
* time.counter()
* time.metronomic()
* time.seconds()
* time.steady(): "steady" is ambiguous: it means different things to
different people. For example, on Linux, CLOCK_MONOTONIC is
adjusted. If we uses the real time as the reference clock, we may
say that CLOCK_MONOTONIC is steady. But CLOCK_MONOTONIC gets
suspended on system suspend, whereas real time includes any time
spent in suspend.
* time.timeout_clock()
* time.wallclock(): time.monotonic() is not the system time aka the
"wall clock", but a monotonic clock with an unspecified starting
point.
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()
-----------------------------------
* time.high_precision()
* time.highres()
* time.hires()
* time.performance_counter()
* time.timer()
Only expose operating system clocks
-----------------------------------
To 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 time
-----------------------------------------
If no monotonic clock is available, time.monotonic() falls back to the
system time.
Issues:
* It is hard to define such a function correctly in the documentation:
is it monotonic? Is it steady? Is it adjusted?
* Some users want to decide what to do when no monotonic clock is
available: use another clock, display an error, or do something
else.
Different APIs were proposed to define such function.
One function with a flag: time.monotonic(fallback=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* time.monotonic(fallback=True) falls back to the system time if no
monotonic clock is available or if the monotonic clock failed.
* time.monotonic(fallback=False) raises OSError if monotonic clock
fails and NotImplementedError if the system does not provide a
monotonic clock
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 flag
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
time.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 constraints
---------------------------------------------
The 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 ``time.get_clock(*flags)`` with some combination of flags.
This includes at least:
* time.MONOTONIC: clock cannot go backward
* time.STEADY: clock rate is steady
* time.ADJUSTED: clock may be adjusted, for example by NTP
* time.HIGHRES: clock with the highest resolution
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 ``time.time()``,
so a final call with no flags can always be used to obtain a working clock.
Examples of flags of system clocks:
* QueryPerformanceCounter: MONOTONIC | HIGHRES
* GetTickCount: MONOTONIC | STEADY
* CLOCK_MONOTONIC: MONOTONIC | STEADY (or only MONOTONIC on Linux)
* CLOCK_MONOTONIC_RAW: MONOTONIC | STEADY
* gettimeofday(): (no flag)
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.
``time.get_clock()`` still chooses a single clock; an enumeration
facility is also required.
The most obvious method is to offer ``time.get_clocks()`` with the
same signature as ``time.get_clock()``, but returning a sequence
of all clocks matching the requested flags.
Requesting no flags would thus enumerate all available clocks,
allowing the caller to make an arbitrary choice amongst them based
on their metadata.
Example partial implementation:
`clockutils.py <http://hg.python.org/peps/file/tip/pep-0418/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.
* Virtual machines provide less reliable clocks.
* QueryPerformanceCounter() has known bugs (only one is not fixed yet)
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:
* if the clock is accidentally set forward by an hour and then back
again, you wouldn't have a useful clock for an hour
* the cache is not shared between processes so different processes
wouldn't see the same clock value
Glossary
========
:Accuracy:
The amount of deviation of measurements by a given instrument from
true values. See also `Accuracy and precision
<http://en.wikipedia.org/wiki/Accuracy_and_precision>`_.
Inaccuracy in clocks may be caused by lack of precision, drift, or an
incorrect initial setting of the clock (e.g., timing of threads is
inherently inaccurate because perfect synchronization in resetting
counters is quite difficult).
:Adjusted:
Resetting a clock to the correct time. This may be done either
with a <Step> or by <Slewing>.
:Civil Time:
Time of day; external to the system. 10:45:13am is a Civil time;
45 seconds is not. Provided by existing function
``time.localtime()`` and ``time.gmtime()``. Not changed by this
PEP.
:Clock:
An instrument for measuring time. Different clocks have different
characteristics; for example, a clock with nanosecond
<precision> may start to <drift> after a few minutes, while a less
precise clock remained accurate for days. This PEP is primarily
concerned with clocks which use a unit of seconds.
:Counter:
A clock which increments each time a certain event occurs. A
counter is strictly monotonic, but not a monotonic clock. It can
be used to generate a unique (and ordered) timestamp, but these
timestamps cannot be mapped to <civil time>; tick creation may well
be bursty, with several advances in the same millisecond followed
by several days without any advance.
:CPU Time:
A measure of how much CPU effort has been spent on a certain task.
CPU seconds are often normalized (so that a variable number can
occur in the same actual second). CPU seconds can be important
when profiling, but they do not map directly to user response time,
nor are they directly comparable to (real time) seconds.
:Drift:
The accumulated error against "true" time, as defined externally to
the system. Drift may be due to imprecision, or to a difference
between the average rate at which clock time advances and that of
real time.
:Epoch:
The reference point of a clock. For clocks providing <civil time>,
this is often midnight as the day (and year) rolled over to January
1, 1970. For a <clock_monotonic> clock, the epoch may be undefined
(represented as None).
:Latency:
Delay. By the time a clock call returns, the <real time> has
advanced, possibly by more than the precision of the clock.
:Monotonic:
The characteristics expected of a monotonic clock in practice.
Moving in at most one direction; for clocks, that direction is
forward. The <clock> should also be <steady>, and should be
convertible to a unit of seconds. The tradeoffs often include lack
of a defined <epoch> or mapping to <Civil Time>.
:Precision:
The amount of deviation among measurements of the same physical
value by a single instrument. Imprecision in clocks may be caused by
a fluctuation of the rate at which clock time advances relative to
real time, including clock adjustment by slewing.
:Process Time:
Time elapsed since the process began. It is typically measured in
<CPU time> rather than <real time>, and typically does not advance
while the process is suspended.
:Real Time:
Time in the real world. This differs from <Civil time> in that it
is not <adjusted>, but they should otherwise advance in lockstep.
It is not related to the "real time" of "Real Time [Operating]
Systems". It is sometimes called "wall clock time" to avoid that
ambiguity; unfortunately, that introduces different ambiguities.
:Resolution:
The smallest difference between two physical values that results
in a different measurement by a given instrument.
:Slew:
A slight change to a clock's speed, usually intended to correct
<drift> with respect to an external authority.
:Stability:
Persistence of accuracy. A measure of expected <drift>.
:Steady:
A clock with high <stability> and relatively high <accuracy> and
<precision>. In practice, it is often used to indicate a
<clock_monotonic> clock, but places greater emphasis on the
consistency of the duration between subsequent ticks.
:Step:
An instantaneous change in the represented time. Instead of
speeding or slowing the clock (<slew>), a single offset is
permanently added.
:System Time:
Time as represented by the Operating System.
:Thread Time:
Time elapsed since the thread began. It is typically measured in
<CPU time> rather than <real time>, and typically does not advance
while the thread is idle.
:Wallclock:
What the clock on the wall says. This is typically used as a
synonym for <real time>; unfortunately, wall time is itself
ambiguous.
Hardware clocks
===============
List of hardware clocks
-----------------------
* HPET: A High Precision Event Timer (HPET) chip consists of a 64-bit
up-counter (main counter) counting at least at 10 MHz and a set of
up to 256 comparators (at least 3). Each HPET can have up to 32
timers. HPET can cause around 3 seconds of drift per day.
* TSC (Time Stamp Counter): Historically, the TSC increased with every
internal processor clock cycle, but now the rate is usually constant
(even if the processor changes frequency) and usually equals the
maximum processor frequency. Multiple cores have different TSC
values. Hibernation of system will reset TSC value. The RDTSC
instruction can be used to read this counter. CPU frequency scaling
for power saving.
* ACPI Power Management Timer: ACPI 24-bit timer with a frequency of
3.5 MHz (3,579,545 Hz).
* Cyclone: The Cyclone timer uses a 32-bit counter on IBM Extended
X-Architecture (EXA) chipsets which include computers that use the
IBM "Summit" series chipsets (ex: x440). This is available in IA32
and IA64 architectures.
* PIT (programmable interrupt timer): Intel 8253/8254 chipsets with a
configurable frequency in range 18.2 Hz - 1.2 MHz. It uses a 16-bit
counter.
* RTC (Real-time clock). Most RTCs use a crystal oscillator with a
frequency of 32,768 Hz.
Linux clocksource
-----------------
There 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:
* tsc
* hpet
* pit
* pmtmr: ACPI Power Management Timer
* cyclone
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 ``CLOCK_REALTIME`` and
``CLOCK_MONOTONIC`` regardless of underlying clock source. Read `Re:
clock_getres() and real resolution
<http://lkml.org/lkml/2012/2/9/100>`_ from Thomas Gleixner (9 Feb
2012) for an explanation.
The ``/sys/devices/system/clocksource/clocksource0`` directory
contains two useful files:
* ``available_clocksource``: list of available clock sources
* ``current_clocksource``: clock source currently used. It is
possible to change the current clocksource by writing the name of a
clocksource into this file.
``/proc/timer_list`` contains the list of all hardware timers.
Read also the `time(7) manual page
<http://www.kernel.org/doc/man-pages/online/pages/man7/time.7.html>`_:
"overview of time and timers".
FreeBSD timecounter
-------------------
kern.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:
* "TSC": Time Stamp Counter of the processor
* "HPET": High Precision Event Timer
* "ACPI-fast": ACPI Power Management timer (fast mode)
* "ACPI-safe": ACPI Power Management timer (safe mode)
* "i8254": PIT with Intel 8254 chipset
The `commit 222222
<http://svnweb.freebsd.org/base?view=revision&revision=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
<http://phk.freebsd.dk/pubs/timecounter.pdf>`_ by Poul-Henning Kamp
(2002) for the FreeBSD Project.
Performance
-----------
Reading 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
<http://hg.python.org/peps/file/tip/pep-0418/bench_time.c>`_ program
was used to fill these tables.
======================== ====== ======= ======
Function TSC ACPI PM HPET
======================== ====== ======= ======
time() 2 ns 2 ns 2 ns
CLOCK_REALTIME_COARSE 10 ns 10 ns 10 ns
CLOCK_MONOTONIC_COARSE 12 ns 13 ns 12 ns
CLOCK_THREAD_CPUTIME_ID 134 ns 135 ns 135 ns
CLOCK_PROCESS_CPUTIME_ID 127 ns 129 ns 129 ns
clock() 146 ns 146 ns 143 ns
gettimeofday() 23 ns 726 ns 637 ns
CLOCK_MONOTONIC_RAW 31 ns 716 ns 607 ns
CLOCK_REALTIME 27 ns 707 ns 629 ns
CLOCK_MONOTONIC 27 ns 723 ns 635 ns
======================== ====== ======= ======
FreeBSD 8.0 in kvm with hardware virtualization:
======================== ====== ========= ======= =======
Function TSC ACPI-Safe HPET i8254
======================== ====== ========= ======= =======
time() 191 ns 188 ns 189 ns 188 ns
CLOCK_SECOND 187 ns 184 ns 187 ns 183 ns
CLOCK_REALTIME_FAST 189 ns 180 ns 187 ns 190 ns
CLOCK_UPTIME_FAST 191 ns 185 ns 186 ns 196 ns
CLOCK_MONOTONIC_FAST 188 ns 187 ns 188 ns 189 ns
CLOCK_THREAD_CPUTIME_ID 208 ns 206 ns 207 ns 220 ns
CLOCK_VIRTUAL 280 ns 279 ns 283 ns 296 ns
CLOCK_PROF 289 ns 280 ns 282 ns 286 ns
clock() 342 ns 340 ns 337 ns 344 ns
CLOCK_UPTIME_PRECISE 197 ns 10380 ns 4402 ns 4097 ns
CLOCK_REALTIME 196 ns 10376 ns 4337 ns 4054 ns
CLOCK_MONOTONIC_PRECISE 198 ns 10493 ns 4413 ns 3958 ns
CLOCK_UPTIME 197 ns 10523 ns 4458 ns 4058 ns
gettimeofday() 202 ns 10524 ns 4186 ns 3962 ns
CLOCK_REALTIME_PRECISE 197 ns 10599 ns 4394 ns 4060 ns
CLOCK_MONOTONIC 201 ns 10766 ns 4498 ns 3943 ns
======================== ====== ========= ======= =======
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 adjustment
==============
NTP has different methods to adjust a clock:
* "slewing": change the clock frequency to be slightly faster or
slower (which is done with ``adjtime()``). Since the slew rate is
limited to 0.5 millisecond per second, each second of adjustment requires an
amortization interval of 2000 seconds. Thus, an adjustment of many
seconds can take hours or days to amortize.
* "stepping": jump by a large amount in a single discrete step (which
is done with ``settimeofday()``)
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
<http://doc.ntp.org/4.1.2/ntpd.htm>`_.
Operating system time functions
===============================
Monotonic Clocks
----------------
========================= ============ =============== ============= ===============
Name C Resolution Adjusted Include Sleep Include Suspend
========================= ============ =============== ============= ===============
gethrtime() 1 ns No Yes Yes
CLOCK_HIGHRES 1 ns No Yes Yes
CLOCK_MONOTONIC 1 ns Slewed on Linux Yes No
CLOCK_MONOTONIC_COARSE 1 ns Slewed on Linux Yes No
CLOCK_MONOTONIC_RAW 1 ns No Yes No
CLOCK_BOOTTIME 1 ns ? Yes Yes
CLOCK_UPTIME 1 ns No Yes ?
mach_absolute_time() 1 ns No Yes No
QueryPerformanceCounter() \- No Yes ?
GetTickCount[64]() 1 ms No Yes Yes
timeGetTime() 1 ms No Yes ?
========================= ============ =============== ============= ===============
The "C Resolution" column is the resolution of the underlying C
structure.
Examples of clock resolution on x86_64:
========================= ================ ============= =================
Name Operating system OS Resolution Python Resolution
========================= ================ ============= =================
QueryPerformanceCounter Windows Seven 10 ns 10 ns
CLOCK_HIGHRES SunOS 5.11 2 ns 265 ns
CLOCK_MONOTONIC Linux 3.0 1 ns 322 ns
CLOCK_MONOTONIC_RAW Linux 3.3 1 ns 628 ns
CLOCK_BOOTTIME Linux 3.3 1 ns 628 ns
mach_absolute_time() Mac OS 10.6 1 ns 3 µs
CLOCK_MONOTONIC FreeBSD 8.2 11 ns 5 µs
CLOCK_MONOTONIC OpenBSD 5.0 10 ms 5 µs
CLOCK_UPTIME FreeBSD 8.2 11 ns 6 µs
CLOCK_MONOTONIC_COARSE Linux 3.3 1 ms 1 ms
CLOCK_MONOTONIC_COARSE Linux 3.0 4 ms 4 ms
GetTickCount64() Windows Seven 16 ms 15 ms
========================= ================ ============= =================
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
<http://hg.python.org/peps/file/tip/pep-0418/clock_resolution.py>`_
program.
mach_absolute_time
^^^^^^^^^^^^^^^^^^
Mac 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
<https://developer.apple.com/library/mac/#qa/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
<http://lists.apple.com/archives/PerfOptimization-dev/2006/Jul/msg00024.html>`_.
CLOCK_MONOTONIC, CLOCK_MONOTONIC_RAW, CLOCK_BOOTTIME
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
CLOCK_MONOTONIC and CLOCK_MONOTONIC_RAW represent monotonic time since
some unspecified starting point. They cannot be set. The resolution
can be read using ``clock_getres()``.
Documentation: refer to the manual page of your operating system.
Examples:
* `FreeBSD clock_gettime() manual page
<http://www.freebsd.org/cgi/man.cgi?query=clock_gettime>`_
* `Linux clock_gettime() manual page
<http://linux.die.net/man/3/clock_gettime>`_
CLOCK_MONOTONIC is available at least on the following operating
systems:
* DragonFly BSD, FreeBSD >= 5.0, OpenBSD, NetBSD
* Linux
* Solaris
The following operating systems don't support CLOCK_MONOTONIC:
* GNU/Hurd (see `open issues/ clock_gettime
<http://www.gnu.org/software/hurd/open_issues/clock_gettime.html>`_)
* Mac OS X
* Windows
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.