Fix wording and spelling

This commit is contained in:
Antoine Pitrou 2017-10-22 11:32:40 +02:00
parent 4d9406ca17
commit 114408971f
1 changed files with 89 additions and 86 deletions

View File

@ -13,15 +13,15 @@ Python-Version: 3.7
Abstract Abstract
======== ========
Add six new "nanosecond" variant of existing functions to the ``time`` Add six new "nanosecond" variants of existing functions to the ``time``
module: ``clock_gettime_ns()``, ``clock_settime_ns()``, module: ``clock_gettime_ns()``, ``clock_settime_ns()``,
``monotonic_ns()``, ``perf_counter_ns()``, ``process_time_ns()`` and ``monotonic_ns()``, ``perf_counter_ns()``, ``process_time_ns()`` and
``time_ns()``. Similar to the existing functions without the ``_ns`` ``time_ns()``. While similar to the existing functions without the
suffix, they have nanosecond resolution: use a number of nanoseconds as ``_ns`` suffix, they provide nanosecond resolution: they return a number of
a Python int. nanoseconds as a Python ``int``.
The ``time.time_ns()`` resolution measured in Python is 3 times better The ``time.time_ns()`` resolution is 3 times better than the ``time.time()``
than the ``time.time()`` resolution on Linux and Windows. resolution on Linux and Windows.
Rationale Rationale
@ -31,15 +31,15 @@ Float type limited to 104 days
------------------------------ ------------------------------
The clocks resolution of desktop and latop computers is getting closer The clocks resolution of desktop and latop computers is getting closer
to nanosecond resolution. More and more clocks have a frequency in MHz, to nanosecond resolution. More and more clocks have a frequency in MHz,
up to GHz for the CPU TSC clock. up to GHz for the CPU TSC clock.
The Python ``time.time()`` function returns the current time as a The Python ``time.time()`` function returns the current time as a
floatting point number which is usually a 64-bit binary floatting number floating-point number which is usually a 64-bit binary floating-point
(in the IEEE 754 format). number (in the IEEE 754 format).
The problem is that the float type starts to lose nanoseconds after 104 The problem is that the ``float`` type starts to lose nanoseconds after 104
days. Conversion from nanoseconds (``int``) to seconds (``float``) and days. Converting from nanoseconds (``int``) to seconds (``float``) and
then back to nanoseconds (``int``) to check if conversions lose then back to nanoseconds (``int``) to check if conversions lose
precision:: precision::
@ -53,7 +53,8 @@ precision::
104 days, 5:59:59.254741 104 days, 5:59:59.254741
``time.time()`` returns seconds elapsed since the UNIX epoch: January ``time.time()`` returns seconds elapsed since the UNIX epoch: January
1st, 1970. This function loses precision since May 1970 (47 years ago):: 1st, 1970. This function hasn't had nanosecond precision since May 1970
(47 years ago)::
>>> import datetime >>> import datetime
>>> unix_epoch = datetime.datetime(1970, 1, 1) >>> unix_epoch = datetime.datetime(1970, 1, 1)
@ -75,7 +76,7 @@ The PEP was rejected for different reasons:
Python. Python.
* It was not clear if hardware clocks really had a resolution of 1 * It was not clear if hardware clocks really had a resolution of 1
nanosecond, especially at the Python level. nanosecond, or if that made sense at the Python level.
* The ``decimal.Decimal`` type is uncommon in Python and so requires * The ``decimal.Decimal`` type is uncommon in Python and so requires
to adapt code to handle it. to adapt code to handle it.
@ -84,32 +85,32 @@ The PEP was rejected for different reasons:
Issues caused by precision loss Issues caused by precision loss
------------------------------- -------------------------------
Example 1: measure time delta in long running process Example 1: measure time delta in long-running process
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A server is running for longer than 104 days. A clock is read before and A server is running for longer than 104 days. A clock is read before and
after running a function to measure its performance to detect after running a function to measure its performance to detect
performance issues at runtime. Such benchmark only lose precision performance issues at runtime. Such benchmark only loses precision
because of the float type used by clocks, not because of the clock because of the float type used by clocks, not because of the clock
resolution. resolution.
On Python microbenchmarks, it is common to see function calls taking On Python microbenchmarks, it is common to see function calls taking
less than 100 ns. A difference of a single nanosecond becomes less than 100 ns. A difference of a few nanoseconds might become
significant. significant.
Example 2: compare time with different resolution Example 2: compare times with different resolution
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Two programs "A" and "B" are runing on the same system and use the system Two programs "A" and "B" are running on the same system and use the system
clock. The program A reads the system clock with nanosecond resolution clock. The program A reads the system clock with nanosecond resolution
and writes the timestamp with nanosecond resolution. The program B reads and writes a timestamp with nanosecond resolution. The program B reads
the timestamp with nanosecond resolution, but compares it to the system the timestamp with nanosecond resolution, but compares it to the system
clock read with a worse resolution. To simplify the example, let's say clock read with a worse resolution. To simplify the example, let's say
that it reads the clock with second resolution. If that case, there is a that B reads the clock with second resolution. If that case, there is a
window of 1 second while the program B can see the timestamp written by A window of 1 second while the program B can see the timestamp written by A
as "in the future". as "in the future".
Nowadays, more and more databases and filesystems support storing time Nowadays, more and more databases and filesystems support storing times
with nanosecond resolution. with nanosecond resolution.
.. note:: .. note::
@ -164,15 +165,15 @@ This PEP adds six new functions to the ``time`` module:
* ``time.time_ns()`` * ``time.time_ns()``
These functions are similar to the version without the ``_ns`` suffix, These functions are similar to the version without the ``_ns`` suffix,
but use nanoseconds as Python ``int``. but return a number of nanoseconds as a Python ``int``.
For example, ``time.monotonic_ns() == int(time.monotonic() * 1e9)`` if For example, ``time.monotonic_ns() == int(time.monotonic() * 1e9)`` if
``monotonic()`` value is small enough to not lose precision. ``monotonic()`` value is small enough to not lose precision.
These functions are needed because they handle large timestamps, like These functions are needed because they may return "large" timestamps,
time.time() which uses the UNIX epoch as reference, and so their version like ``time.time()`` which uses the UNIX epoch as reference, and so their
without the ``_ns`` suffix are likely to lose precision at the ``float``-returning variants are likely to lose precision at the nanosecond
nanosecond resolution. resolution.
Unchanged functions Unchanged functions
------------------- -------------------
@ -180,13 +181,13 @@ Unchanged functions
Since the ``time.clock()`` function was deprecated in Python 3.3, no Since the ``time.clock()`` function was deprecated in Python 3.3, no
``time.clock_ns()`` is added. ``time.clock_ns()`` is added.
Python has other functions handling time. No nanosecond variant was Python has other time-returning functions. No nanosecond variant is
proposed because their internal resolution is greater or equal to 1 us, proposed for these other functions, either because their internal
or because their maximum value is a small enough to not lose precision. resolution is greater or equal to 1 us, or because their maximum value
For example, the maximum value of ``clock_getres()`` should be 1 is small enough to not lose precision. For example, the maximum value of
second. ``time.clock_getres()`` should be 1 second.
Example of unchanged functions: Examples of unchanged functions:
* ``os`` module: ``sched_rr_get_interval()``, ``times()``, ``wait3()`` * ``os`` module: ``sched_rr_get_interval()``, ``times()``, ``wait3()``
and ``wait4()`` and ``wait4()``
@ -200,8 +201,8 @@ Example of unchanged functions:
See also the `Annex: Clocks Resolution in Python`_. See also the `Annex: Clocks Resolution in Python`_.
A new nanosecond flavor of these functions may be added later if an A new nanosecond-returning flavor of these functions may be added later
operating system adds a new function providing better resolution. if an operating system exposes new functions providing better resolution.
Alternatives and discussion Alternatives and discussion
@ -210,47 +211,48 @@ Alternatives and discussion
Sub-nanosecond resolution Sub-nanosecond resolution
------------------------- -------------------------
``time.time_ns()`` API is not "future-proof": if clocks resolutions ``time.time_ns()`` API is not theoretically future-proof: if clock
increase, new Python functions may be needed. resolutions continue to increase below the nanosecond level, new Python
functions may be needed.
In practive, the resolution of 1 nanosecond is currently enough for all In practive, the 1 nanosecond resolution is currently enough for all
structures used by all operating systems functions. structures returned by all common operating systems functions.
Hardware clock with a resolution better than 1 nanosecond already Hardware clocks with a resolution better than 1 nanosecond already
exists. For example, the frequency of a CPU TSC clock is the CPU base exist. For example, the frequency of a CPU TSC clock is the CPU base
frequency: the resolution is around 0.3 ns for a CPU running at 3 frequency: the resolution is around 0.3 ns for a CPU running at 3
GHz. Users who have access to such hardware and really need GHz. Users who have access to such hardware and really need
sub-nanosecond resolution can easily extend Python for their needs. sub-nanosecond resolution can however extend Python for their needs.
Such rare use case don't justify to design the Python standard library Such a rare use case doesn't justify to design the Python standard library
to support sub-nanosecond resolution. to support sub-nanosecond resolution.
For the CPython implementation, nanosecond resolution is convenient: the For the CPython implementation, nanosecond resolution is convenient: the
standard and well supported ``int64_t`` type can be used to store time. standard and well supported ``int64_t`` type can be used to store a
It supports a time delta between -292 years and 292 years. Using the nanosecond-precise timestamp. It supports a timespan of -292 years
UNIX epoch as reference, this type supports time since year 1677 to year to +292 years. Using the UNIX epoch as reference, it therefore supports
2262:: representing times since year 1677 to year 2262::
>>> 1970 - 2 ** 63 / (10 ** 9 * 3600 * 24 * 365.25) >>> 1970 - 2 ** 63 / (10 ** 9 * 3600 * 24 * 365.25)
1677.728976954687 1677.728976954687
>>> 1970 + 2 ** 63 / (10 ** 9 * 3600 * 24 * 365.25) >>> 1970 + 2 ** 63 / (10 ** 9 * 3600 * 24 * 365.25)
2262.271023045313 2262.271023045313
Modify time.time() result type Modifying time.time() result type
------------------------------ ---------------------------------
It was proposed to modify ``time.time()`` to return a different float It was proposed to modify ``time.time()`` to return a different number
type with better precision. type with better precision.
The PEP 410 proposed to use ``decimal.Decimal`` which already exists and The PEP 410 proposed to return ``decimal.Decimal`` which already exists and
supports arbitray precision, but it was rejected. Apart supports arbitray precision, but it was rejected. Apart from
``decimal.Decimal``, no portable ``float`` type with better precision is ``decimal.Decimal``, no portable real number type with better precision
currently available in Python. is currently available in Python.
Changing the builtin Python ``float`` type is out of the scope of this Changing the built-in Python ``float`` type is out of the scope of this
PEP. PEP.
Moreover, changing existing functions to return a new type introduces a Moreover, changing existing functions to return a new type introduces a
risk of breaking the backward compatibility even the new type is risk of breaking the backward compatibility even if the new type is
designed carefully. designed carefully.
@ -259,24 +261,24 @@ Different types
Many ideas of new types were proposed to support larger or arbitrary Many ideas of new types were proposed to support larger or arbitrary
precision: fractions, structures or 2-tuple using integers, precision: fractions, structures or 2-tuple using integers,
fixed-precision floating point number, etc. fixed-point number, etc.
See also the PEP 410 for a previous long discussion on other types. See also the PEP 410 for a previous long discussion on other types.
Adding a new type requires more effort to support it, than reusing Adding a new type requires more effort to support it, than reusing
the existing ``int`` type. The standard library, third party code and the existing ``int`` type. The standard library, third party code and
applications would have to be modified to support it. applications would have to be modified to support it.
The Python ``int`` type is well known, well supported, ease to The Python ``int`` type is well known, well supported, easy to
manipulate, and supports all arithmetic operations like: manipulate, and supports all arithmetic operations such as
``dt = t2 - t1``. ``dt = t2 - t1``.
Moreover, using nanoseconds as integer is not new in Python, it's Moreover, taking/returning an integer number of nanoseconds is not a
already used for ``os.stat_result`` and new concept in Python, as witnessed by ``os.stat_result`` and
``os.utime(ns=(atime_ns, mtime_ns))``. ``os.utime(ns=(atime_ns, mtime_ns))``.
.. note:: .. note::
If the Python ``float`` type becomes larger (ex: decimal128 or If the Python ``float`` type becomes larger (e.g. decimal128 or
float128), the ``time.time()`` precision will increase as well. float128), the ``time.time()`` precision will increase as well.
Different API Different API
@ -291,13 +293,14 @@ resolution. If each Python module uses a different resolution, it can
become difficult to handle different resolutions, instead of just become difficult to handle different resolutions, instead of just
seconds (``time.time()`` returning ``float``) and nanoseconds seconds (``time.time()`` returning ``float``) and nanoseconds
(``time.time_ns()`` returning ``int``). Moreover, as written above, (``time.time_ns()`` returning ``int``). Moreover, as written above,
there is no need for resolution better than 1 nanosecond in practive in there is no need for resolution better than 1 nanosecond in practice in
the Python standard library. the Python standard library.
New time_ns module A new module
------------------ ------------
Add a new ``time_ns`` module which contains the six new functions: It was proposed to add a new ``time_ns`` module containing the following
functions:
* ``time_ns.clock_gettime(clock_id)`` * ``time_ns.clock_gettime(clock_id)``
* ``time_ns.clock_settime(clock_id, time: int)`` * ``time_ns.clock_settime(clock_id, time: int)``
@ -306,25 +309,25 @@ Add a new ``time_ns`` module which contains the six new functions:
* ``time_ns.process_time()`` * ``time_ns.process_time()``
* ``time_ns.time()`` * ``time_ns.time()``
The first question is if the ``time_ns`` should expose exactly the same The first question is whether the ``time_ns`` module should expose exactly
API (constants, functions, etc.) than the ``time`` module. It can be the same API (constants, functions, etc.) as the ``time`` module. It can be
painful to maintain two flavors of the ``time`` module. How users use painful to maintain two flavors of the ``time`` module. How are users use
suppose to make a choice between these two modules? supposed to make a choice between these two modules?
If tomorrow, other nanosecond variant are needed in the ``os`` module, If tomorrow, other nanosecond variants are needed in the ``os`` module,
will we have to add a new ``os_ns`` module as well? There are functions will we have to add a new ``os_ns`` module as well? There are functions
related to time in many modules: ``time``, ``os``, ``signal``, related to time in many modules: ``time``, ``os``, ``signal``,
``resource``, ``select``, etc. ``resource``, ``select``, etc.
Another idea is to add a ``time.ns`` submodule or a nested-namespace to Another idea is to add a ``time.ns`` submodule or a nested-namespace to
get the ``time.ns.time()`` syntax. get the ``time.ns.time()`` syntax, but it suffers from the same issues.
Annex: Clocks Resolution in Python Annex: Clocks Resolution in Python
================================== ==================================
This annex contains the resolution of clocks measured in Python, and not This annex contains the resolution of clocks as measured in Python, and
the resolution announced by the operating system or the resolution of not the resolution announced by the operating system or the resolution of
the internal structure used by the operating system. the internal structure used by the operating system.
Script Script
@ -413,16 +416,16 @@ Analysis
The resolution of ``time.time_ns()`` is much better than The resolution of ``time.time_ns()`` is much better than
``time.time()``: **84 ns (2.8x better) vs 239 ns on Linux and 318 us ``time.time()``: **84 ns (2.8x better) vs 239 ns on Linux and 318 us
(2.8x better) vs 894 us on Windows**. The ``time.time()`` resolution will (2.8x better) vs 894 us on Windows**. The ``time.time()`` resolution will
becomes larger (worse) next years since every day adds only become larger (worse) as years pass since every day adds
864,00,000,000,000 nanoseconds to the system clock which increases the 86,400,000,000,000 nanoseconds to the system clock, which increases the
precision loss. precision loss.
The difference between ``time.perf_counter()``, ``time.monotonic The difference between ``time.perf_counter()``, ``time.monotonic()``,
clock()``, ``time.process_time()`` and their nanosecond variant is ``time.process_time()`` and their respective nanosecond variants is
not visible in this quick script since the script runs less than 1 not visible in this quick script since the script runs for less than 1
minute, and the uptime of the computer used to run the script was minute, and the uptime of the computer used to run the script was
smaller than 1 week. A significant difference should be seen with an smaller than 1 week. A significant difference may be seen if uptime
uptime of at least 104 days. reaches 104 days or more.
``resource.getrusage()`` and ``times()`` have a resolution greater or ``resource.getrusage()`` and ``times()`` have a resolution greater or
equal to 1 microsecond, and so don't need a variant with nanosecond equal to 1 microsecond, and so don't need a variant with nanosecond