CINXE.COM

PEP 564 – Add new time functions with nanosecond resolution | peps.python.org

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="color-scheme" content="light dark"> <title>PEP 564 – Add new time functions with nanosecond resolution | peps.python.org</title> <link rel="shortcut icon" href="../_static/py.png"> <link rel="canonical" href="https://peps.python.org/pep-0564/"> <link rel="stylesheet" href="../_static/style.css" type="text/css"> <link rel="stylesheet" href="../_static/mq.css" type="text/css"> <link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light"> <link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark"> <link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss"> <meta property="og:title" content='PEP 564 – Add new time functions with nanosecond resolution | peps.python.org'> <meta property="og:description" content="Add six new “nanosecond” variants of existing functions to the time module: clock_gettime_ns(), clock_settime_ns(), monotonic_ns(), perf_counter_ns(), process_time_ns() and time_ns(). While similar to the existing functions without the _ns suffix, they..."> <meta property="og:type" content="website"> <meta property="og:url" content="https://peps.python.org/pep-0564/"> <meta property="og:site_name" content="Python Enhancement Proposals (PEPs)"> <meta property="og:image" content="https://peps.python.org/_static/og-image.png"> <meta property="og:image:alt" content="Python PEPs"> <meta property="og:image:width" content="200"> <meta property="og:image:height" content="200"> <meta name="description" content="Add six new “nanosecond” variants of existing functions to the time module: clock_gettime_ns(), clock_settime_ns(), monotonic_ns(), perf_counter_ns(), process_time_ns() and time_ns(). While similar to the existing functions without the _ns suffix, they..."> <meta name="theme-color" content="#3776ab"> </head> <body> <svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> <symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all"> <title>Following system colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="9"></circle> <path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path> </svg> </symbol> <symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all"> <title>Selected dark colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path> </svg> </symbol> <symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all"> <title>Selected light colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="5"></circle> <line x1="12" y1="1" x2="12" y2="3"></line> <line x1="12" y1="21" x2="12" y2="23"></line> <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line> <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line> <line x1="1" y1="12" x2="3" y2="12"></line> <line x1="21" y1="12" x2="23" y2="12"></line> <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line> <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line> </svg> </symbol> </svg> <script> document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto" </script> <section id="pep-page-section"> <header> <h1>Python Enhancement Proposals</h1> <ul class="breadcrumbs"> <li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> &raquo; </li> <li><a href="../pep-0000/">PEP Index</a> &raquo; </li> <li>PEP 564</li> </ul> <button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())"> <svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg> <svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg> <svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg> <span class="visually-hidden">Toggle light / dark / auto colour theme</span> </button> </header> <article> <section id="pep-content"> <h1 class="page-title">PEP 564 – Add new time functions with nanosecond resolution</h1> <dl class="rfc2822 field-list simple"> <dt class="field-odd">Author<span class="colon">:</span></dt> <dd class="field-odd">Victor Stinner &lt;vstinner&#32;&#97;t&#32;python.org&gt;</dd> <dt class="field-even">Status<span class="colon">:</span></dt> <dd class="field-even"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd> <dt class="field-odd">Type<span class="colon">:</span></dt> <dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd> <dt class="field-even">Created<span class="colon">:</span></dt> <dd class="field-even">16-Oct-2017</dd> <dt class="field-odd">Python-Version<span class="colon">:</span></dt> <dd class="field-odd">3.7</dd> <dt class="field-even">Resolution<span class="colon">:</span></dt> <dd class="field-even"><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2017-October/150046.html">Python-Dev message</a></dd> </dl> <hr class="docutils" /> <section id="contents"> <details><summary>Table of Contents</summary><ul class="simple"> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#rationale">Rationale</a><ul> <li><a class="reference internal" href="#float-type-limited-to-104-days">Float type limited to 104 days</a></li> <li><a class="reference internal" href="#previous-rejected-pep">Previous rejected PEP</a></li> <li><a class="reference internal" href="#issues-caused-by-precision-loss">Issues caused by precision loss</a><ul> <li><a class="reference internal" href="#example-1-measure-time-delta-in-long-running-process">Example 1: measure time delta in long-running process</a></li> <li><a class="reference internal" href="#example-2-compare-times-with-different-resolution">Example 2: compare times with different resolution</a></li> </ul> </li> <li><a class="reference internal" href="#cpython-enhancements-of-the-last-5-years">CPython enhancements of the last 5 years</a></li> <li><a class="reference internal" href="#existing-python-apis-using-nanoseconds-as-int">Existing Python APIs using nanoseconds as int</a></li> </ul> </li> <li><a class="reference internal" href="#changes">Changes</a><ul> <li><a class="reference internal" href="#new-functions">New functions</a></li> <li><a class="reference internal" href="#unchanged-functions">Unchanged functions</a></li> </ul> </li> <li><a class="reference internal" href="#alternatives-and-discussion">Alternatives and discussion</a><ul> <li><a class="reference internal" href="#sub-nanosecond-resolution">Sub-nanosecond resolution</a></li> <li><a class="reference internal" href="#modifying-time-time-result-type">Modifying time.time() result type</a></li> <li><a class="reference internal" href="#different-types">Different types</a></li> <li><a class="reference internal" href="#different-api">Different API</a></li> <li><a class="reference internal" href="#a-new-module">A new module</a></li> </ul> </li> <li><a class="reference internal" href="#annex-clocks-resolution-in-python">Annex: Clocks Resolution in Python</a><ul> <li><a class="reference internal" href="#script">Script</a></li> <li><a class="reference internal" href="#linux">Linux</a></li> <li><a class="reference internal" href="#windows">Windows</a></li> <li><a class="reference internal" href="#analysis">Analysis</a></li> </ul> </li> <li><a class="reference internal" href="#links">Links</a></li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> </details></section> <section id="abstract"> <h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2> <p>Add six new “nanosecond” variants of existing functions to the <code class="docutils literal notranslate"><span class="pre">time</span></code> module: <code class="docutils literal notranslate"><span class="pre">clock_gettime_ns()</span></code>, <code class="docutils literal notranslate"><span class="pre">clock_settime_ns()</span></code>, <code class="docutils literal notranslate"><span class="pre">monotonic_ns()</span></code>, <code class="docutils literal notranslate"><span class="pre">perf_counter_ns()</span></code>, <code class="docutils literal notranslate"><span class="pre">process_time_ns()</span></code> and <code class="docutils literal notranslate"><span class="pre">time_ns()</span></code>. While similar to the existing functions without the <code class="docutils literal notranslate"><span class="pre">_ns</span></code> suffix, they provide nanosecond resolution: they return a number of nanoseconds as a Python <code class="docutils literal notranslate"><span class="pre">int</span></code>.</p> <p>The <code class="docutils literal notranslate"><span class="pre">time.time_ns()</span></code> resolution is 3 times better than the <code class="docutils literal notranslate"><span class="pre">time.time()</span></code> resolution on Linux and Windows.</p> </section> <section id="rationale"> <h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2> <section id="float-type-limited-to-104-days"> <h3><a class="toc-backref" href="#float-type-limited-to-104-days" role="doc-backlink">Float type limited to 104 days</a></h3> <p>The clocks resolution of desktop and laptop computers is getting closer to nanosecond resolution. More and more clocks have a frequency in MHz, up to GHz for the CPU TSC clock.</p> <p>The Python <code class="docutils literal notranslate"><span class="pre">time.time()</span></code> function returns the current time as a floating-point number which is usually a 64-bit binary floating-point number (in the IEEE 754 format).</p> <p>The problem is that the <code class="docutils literal notranslate"><span class="pre">float</span></code> type starts to lose nanoseconds after 104 days. Converting from nanoseconds (<code class="docutils literal notranslate"><span class="pre">int</span></code>) to seconds (<code class="docutils literal notranslate"><span class="pre">float</span></code>) and then back to nanoseconds (<code class="docutils literal notranslate"><span class="pre">int</span></code>) to check if conversions lose precision:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># no precision loss</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">**</span> <span class="mi">52</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="nb">int</span><span class="p">(</span><span class="nb">float</span><span class="p">(</span><span class="n">x</span> <span class="o">*</span> <span class="mf">1e-9</span><span class="p">)</span> <span class="o">*</span> <span class="mf">1e9</span><span class="p">)</span> <span class="o">-</span> <span class="n">x</span> <span class="mi">0</span> <span class="c1"># precision loss! (1 nanosecond)</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">**</span> <span class="mi">53</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="nb">int</span><span class="p">(</span><span class="nb">float</span><span class="p">(</span><span class="n">x</span> <span class="o">*</span> <span class="mf">1e-9</span><span class="p">)</span> <span class="o">*</span> <span class="mf">1e9</span><span class="p">)</span> <span class="o">-</span> <span class="n">x</span> <span class="o">-</span><span class="mi">1</span> <span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span><span class="p">(</span><span class="n">datetime</span><span class="o">.</span><span class="n">timedelta</span><span class="p">(</span><span class="n">seconds</span><span class="o">=</span><span class="mi">2</span> <span class="o">**</span> <span class="mi">53</span> <span class="o">/</span> <span class="mf">1e9</span><span class="p">))</span> <span class="mi">104</span> <span class="n">days</span><span class="p">,</span> <span class="mi">5</span><span class="p">:</span><span class="mi">59</span><span class="p">:</span><span class="mf">59.254741</span> </pre></div> </div> <p><code class="docutils literal notranslate"><span class="pre">time.time()</span></code> returns seconds elapsed since the UNIX epoch: January 1st, 1970. This function hasn’t had nanosecond precision since May 1970 (47 years ago):</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">datetime</span> <span class="gp">&gt;&gt;&gt; </span><span class="n">unix_epoch</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">(</span><span class="mi">1970</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="gp">&gt;&gt;&gt; </span><span class="nb">print</span><span class="p">(</span><span class="n">unix_epoch</span> <span class="o">+</span> <span class="n">datetime</span><span class="o">.</span><span class="n">timedelta</span><span class="p">(</span><span class="n">seconds</span><span class="o">=</span><span class="mi">2</span><span class="o">**</span><span class="mi">53</span> <span class="o">/</span> <span class="mf">1e9</span><span class="p">))</span> <span class="go">1970-04-15 05:59:59.254741</span> </pre></div> </div> </section> <section id="previous-rejected-pep"> <h3><a class="toc-backref" href="#previous-rejected-pep" role="doc-backlink">Previous rejected PEP</a></h3> <p>Five years ago, the <a class="pep reference internal" href="../pep-0410/" title="PEP 410 – Use decimal.Decimal type for timestamps">PEP 410</a> proposed a large and complex change in all Python functions returning time to support nanosecond resolution using the <code class="docutils literal notranslate"><span class="pre">decimal.Decimal</span></code> type.</p> <p>The PEP was rejected for different reasons:</p> <ul class="simple"> <li>The idea of adding a new optional parameter to change the result type was rejected. It’s an uncommon (and bad?) programming practice in Python.</li> <li>It was not clear if hardware clocks really had a resolution of 1 nanosecond, or if that made sense at the Python level.</li> <li>The <code class="docutils literal notranslate"><span class="pre">decimal.Decimal</span></code> type is uncommon in Python and so requires to adapt code to handle it.</li> </ul> </section> <section id="issues-caused-by-precision-loss"> <h3><a class="toc-backref" href="#issues-caused-by-precision-loss" role="doc-backlink">Issues caused by precision loss</a></h3> <section id="example-1-measure-time-delta-in-long-running-process"> <h4><a class="toc-backref" href="#example-1-measure-time-delta-in-long-running-process" role="doc-backlink">Example 1: measure time delta in long-running process</a></h4> <p>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 performance issues at runtime. Such benchmark only loses precision because of the float type used by clocks, not because of the clock resolution.</p> <p>On Python microbenchmarks, it is common to see function calls taking less than 100 ns. A difference of a few nanoseconds might become significant.</p> </section> <section id="example-2-compare-times-with-different-resolution"> <h4><a class="toc-backref" href="#example-2-compare-times-with-different-resolution" role="doc-backlink">Example 2: compare times with different resolution</a></h4> <p>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 and writes a timestamp with nanosecond resolution. The program B reads the timestamp with nanosecond resolution, but compares it to the system clock read with a worse resolution. To simplify the example, let’s say 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 as “in the future”.</p> <p>Nowadays, more and more databases and filesystems support storing times with nanosecond resolution.</p> <div class="admonition note"> <p class="admonition-title">Note</p> <p>This issue was already fixed for file modification time by adding the <code class="docutils literal notranslate"><span class="pre">st_mtime_ns</span></code> field to the <code class="docutils literal notranslate"><span class="pre">os.stat()</span></code> result, and by accepting nanoseconds in <code class="docutils literal notranslate"><span class="pre">os.utime()</span></code>. This PEP proposes to generalize the fix.</p> </div> </section> </section> <section id="cpython-enhancements-of-the-last-5-years"> <h3><a class="toc-backref" href="#cpython-enhancements-of-the-last-5-years" role="doc-backlink">CPython enhancements of the last 5 years</a></h3> <p>Since the <a class="pep reference internal" href="../pep-0410/" title="PEP 410 – Use decimal.Decimal type for timestamps">PEP 410</a> was rejected:</p> <ul class="simple"> <li>The <code class="docutils literal notranslate"><span class="pre">os.stat_result</span></code> structure got 3 new fields for timestamps as nanoseconds (Python <code class="docutils literal notranslate"><span class="pre">int</span></code>): <code class="docutils literal notranslate"><span class="pre">st_atime_ns</span></code>, <code class="docutils literal notranslate"><span class="pre">st_ctime_ns</span></code> and <code class="docutils literal notranslate"><span class="pre">st_mtime_ns</span></code>.</li> <li>The <a class="pep reference internal" href="../pep-0418/" title="PEP 418 – Add monotonic time, performance counter, and process time functions">PEP 418</a> was accepted, Python 3.3 got 3 new clocks: <code class="docutils literal notranslate"><span class="pre">time.monotonic()</span></code>, <code class="docutils literal notranslate"><span class="pre">time.perf_counter()</span></code> and <code class="docutils literal notranslate"><span class="pre">time.process_time()</span></code>.</li> <li>The CPython private “pytime” C API handling time now uses a new <code class="docutils literal notranslate"><span class="pre">_PyTime_t</span></code> type: simple 64-bit signed integer (C <code class="docutils literal notranslate"><span class="pre">int64_t</span></code>). The <code class="docutils literal notranslate"><span class="pre">_PyTime_t</span></code> unit is an implementation detail and not part of the API. The unit is currently <code class="docutils literal notranslate"><span class="pre">1</span> <span class="pre">nanosecond</span></code>.</li> </ul> </section> <section id="existing-python-apis-using-nanoseconds-as-int"> <h3><a class="toc-backref" href="#existing-python-apis-using-nanoseconds-as-int" role="doc-backlink">Existing Python APIs using nanoseconds as int</a></h3> <p>The <code class="docutils literal notranslate"><span class="pre">os.stat_result</span></code> structure has 3 fields for timestamps as nanoseconds (<code class="docutils literal notranslate"><span class="pre">int</span></code>): <code class="docutils literal notranslate"><span class="pre">st_atime_ns</span></code>, <code class="docutils literal notranslate"><span class="pre">st_ctime_ns</span></code> and <code class="docutils literal notranslate"><span class="pre">st_mtime_ns</span></code>.</p> <p>The <code class="docutils literal notranslate"><span class="pre">ns</span></code> parameter of the <code class="docutils literal notranslate"><span class="pre">os.utime()</span></code> function accepts a <code class="docutils literal notranslate"><span class="pre">(atime_ns:</span> <span class="pre">int,</span> <span class="pre">mtime_ns:</span> <span class="pre">int)</span></code> tuple: nanoseconds.</p> </section> </section> <section id="changes"> <h2><a class="toc-backref" href="#changes" role="doc-backlink">Changes</a></h2> <section id="new-functions"> <h3><a class="toc-backref" href="#new-functions" role="doc-backlink">New functions</a></h3> <p>This PEP adds six new functions to the <code class="docutils literal notranslate"><span class="pre">time</span></code> module:</p> <ul class="simple"> <li><code class="docutils literal notranslate"><span class="pre">time.clock_gettime_ns(clock_id)</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">time.clock_settime_ns(clock_id,</span> <span class="pre">time:</span> <span class="pre">int)</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">time.monotonic_ns()</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">time.perf_counter_ns()</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">time.process_time_ns()</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">time.time_ns()</span></code></li> </ul> <p>These functions are similar to the version without the <code class="docutils literal notranslate"><span class="pre">_ns</span></code> suffix, but return a number of nanoseconds as a Python <code class="docutils literal notranslate"><span class="pre">int</span></code>.</p> <p>For example, <code class="docutils literal notranslate"><span class="pre">time.monotonic_ns()</span> <span class="pre">==</span> <span class="pre">int(time.monotonic()</span> <span class="pre">*</span> <span class="pre">1e9)</span></code> if <code class="docutils literal notranslate"><span class="pre">monotonic()</span></code> value is small enough to not lose precision.</p> <p>These functions are needed because they may return “large” timestamps, like <code class="docutils literal notranslate"><span class="pre">time.time()</span></code> which uses the UNIX epoch as reference, and so their <code class="docutils literal notranslate"><span class="pre">float</span></code>-returning variants are likely to lose precision at the nanosecond resolution.</p> </section> <section id="unchanged-functions"> <h3><a class="toc-backref" href="#unchanged-functions" role="doc-backlink">Unchanged functions</a></h3> <p>Since the <code class="docutils literal notranslate"><span class="pre">time.clock()</span></code> function was deprecated in Python 3.3, no <code class="docutils literal notranslate"><span class="pre">time.clock_ns()</span></code> is added.</p> <p>Python has other time-returning functions. No nanosecond variant is proposed for these other functions, either because their internal resolution is greater or equal to 1 us, or because their maximum value is small enough to not lose precision. For example, the maximum value of <code class="docutils literal notranslate"><span class="pre">time.clock_getres()</span></code> should be 1 second.</p> <p>Examples of unchanged functions:</p> <ul class="simple"> <li><code class="docutils literal notranslate"><span class="pre">os</span></code> module: <code class="docutils literal notranslate"><span class="pre">sched_rr_get_interval()</span></code>, <code class="docutils literal notranslate"><span class="pre">times()</span></code>, <code class="docutils literal notranslate"><span class="pre">wait3()</span></code> and <code class="docutils literal notranslate"><span class="pre">wait4()</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">resource</span></code> module: <code class="docutils literal notranslate"><span class="pre">ru_utime</span></code> and <code class="docutils literal notranslate"><span class="pre">ru_stime</span></code> fields of <code class="docutils literal notranslate"><span class="pre">getrusage()</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">signal</span></code> module: <code class="docutils literal notranslate"><span class="pre">getitimer()</span></code>, <code class="docutils literal notranslate"><span class="pre">setitimer()</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">time</span></code> module: <code class="docutils literal notranslate"><span class="pre">clock_getres()</span></code></li> </ul> <p>See also the <a class="reference internal" href="#annex-clocks-resolution-in-python">Annex: Clocks Resolution in Python</a>.</p> <p>A new nanosecond-returning flavor of these functions may be added later if an operating system exposes new functions providing better resolution.</p> </section> </section> <section id="alternatives-and-discussion"> <h2><a class="toc-backref" href="#alternatives-and-discussion" role="doc-backlink">Alternatives and discussion</a></h2> <section id="sub-nanosecond-resolution"> <h3><a class="toc-backref" href="#sub-nanosecond-resolution" role="doc-backlink">Sub-nanosecond resolution</a></h3> <p><code class="docutils literal notranslate"><span class="pre">time.time_ns()</span></code> API is not theoretically future-proof: if clock resolutions continue to increase below the nanosecond level, new Python functions may be needed.</p> <p>In practice, the 1 nanosecond resolution is currently enough for all structures returned by all common operating systems functions.</p> <p>Hardware clocks with a resolution better than 1 nanosecond already 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 GHz. Users who have access to such hardware and really need sub-nanosecond resolution can however extend Python for their needs. Such a rare use case doesn’t justify to design the Python standard library to support sub-nanosecond resolution.</p> <p>For the CPython implementation, nanosecond resolution is convenient: the standard and well supported <code class="docutils literal notranslate"><span class="pre">int64_t</span></code> type can be used to store a nanosecond-precise timestamp. It supports a timespan of -292 years to +292 years. Using the UNIX epoch as reference, it therefore supports representing times since year 1677 to year 2262:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="mi">1970</span> <span class="o">-</span> <span class="mi">2</span> <span class="o">**</span> <span class="mi">63</span> <span class="o">/</span> <span class="p">(</span><span class="mi">10</span> <span class="o">**</span> <span class="mi">9</span> <span class="o">*</span> <span class="mi">3600</span> <span class="o">*</span> <span class="mi">24</span> <span class="o">*</span> <span class="mf">365.25</span><span class="p">)</span> <span class="go">1677.728976954687</span> <span class="gp">&gt;&gt;&gt; </span><span class="mi">1970</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">**</span> <span class="mi">63</span> <span class="o">/</span> <span class="p">(</span><span class="mi">10</span> <span class="o">**</span> <span class="mi">9</span> <span class="o">*</span> <span class="mi">3600</span> <span class="o">*</span> <span class="mi">24</span> <span class="o">*</span> <span class="mf">365.25</span><span class="p">)</span> <span class="go">2262.271023045313</span> </pre></div> </div> </section> <section id="modifying-time-time-result-type"> <h3><a class="toc-backref" href="#modifying-time-time-result-type" role="doc-backlink">Modifying time.time() result type</a></h3> <p>It was proposed to modify <code class="docutils literal notranslate"><span class="pre">time.time()</span></code> to return a different number type with better precision.</p> <p>The <a class="pep reference internal" href="../pep-0410/" title="PEP 410 – Use decimal.Decimal type for timestamps">PEP 410</a> proposed to return <code class="docutils literal notranslate"><span class="pre">decimal.Decimal</span></code> which already exists and supports arbitrary precision, but it was rejected. Apart from <code class="docutils literal notranslate"><span class="pre">decimal.Decimal</span></code>, no portable real number type with better precision is currently available in Python.</p> <p>Changing the built-in Python <code class="docutils literal notranslate"><span class="pre">float</span></code> type is out of the scope of this PEP.</p> <p>Moreover, changing existing functions to return a new type introduces a risk of breaking the backward compatibility even if the new type is designed carefully.</p> </section> <section id="different-types"> <h3><a class="toc-backref" href="#different-types" role="doc-backlink">Different types</a></h3> <p>Many ideas of new types were proposed to support larger or arbitrary precision: fractions, structures or 2-tuple using integers, fixed-point number, etc.</p> <p>See also the <a class="pep reference internal" href="../pep-0410/" title="PEP 410 – Use decimal.Decimal type for timestamps">PEP 410</a> for a previous long discussion on other types.</p> <p>Adding a new type requires more effort to support it, than reusing the existing <code class="docutils literal notranslate"><span class="pre">int</span></code> type. The standard library, third party code and applications would have to be modified to support it.</p> <p>The Python <code class="docutils literal notranslate"><span class="pre">int</span></code> type is well known, well supported, easy to manipulate, and supports all arithmetic operations such as <code class="docutils literal notranslate"><span class="pre">dt</span> <span class="pre">=</span> <span class="pre">t2</span> <span class="pre">-</span> <span class="pre">t1</span></code>.</p> <p>Moreover, taking/returning an integer number of nanoseconds is not a new concept in Python, as witnessed by <code class="docutils literal notranslate"><span class="pre">os.stat_result</span></code> and <code class="docutils literal notranslate"><span class="pre">os.utime(ns=(atime_ns,</span> <span class="pre">mtime_ns))</span></code>.</p> <div class="admonition note"> <p class="admonition-title">Note</p> <p>If the Python <code class="docutils literal notranslate"><span class="pre">float</span></code> type becomes larger (e.g. decimal128 or float128), the <code class="docutils literal notranslate"><span class="pre">time.time()</span></code> precision will increase as well.</p> </div> </section> <section id="different-api"> <h3><a class="toc-backref" href="#different-api" role="doc-backlink">Different API</a></h3> <p>The <code class="docutils literal notranslate"><span class="pre">time.time(ns=False)</span></code> API was proposed to avoid adding new functions. It’s an uncommon (and bad?) programming practice in Python to change the result type depending on a parameter.</p> <p>Different options were proposed to allow the user to choose the time resolution. If each Python module uses a different resolution, it can become difficult to handle different resolutions, instead of just seconds (<code class="docutils literal notranslate"><span class="pre">time.time()</span></code> returning <code class="docutils literal notranslate"><span class="pre">float</span></code>) and nanoseconds (<code class="docutils literal notranslate"><span class="pre">time.time_ns()</span></code> returning <code class="docutils literal notranslate"><span class="pre">int</span></code>). Moreover, as written above, there is no need for resolution better than 1 nanosecond in practice in the Python standard library.</p> </section> <section id="a-new-module"> <h3><a class="toc-backref" href="#a-new-module" role="doc-backlink">A new module</a></h3> <p>It was proposed to add a new <code class="docutils literal notranslate"><span class="pre">time_ns</span></code> module containing the following functions:</p> <ul class="simple"> <li><code class="docutils literal notranslate"><span class="pre">time_ns.clock_gettime(clock_id)</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">time_ns.clock_settime(clock_id,</span> <span class="pre">time:</span> <span class="pre">int)</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">time_ns.monotonic()</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">time_ns.perf_counter()</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">time_ns.process_time()</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">time_ns.time()</span></code></li> </ul> <p>The first question is whether the <code class="docutils literal notranslate"><span class="pre">time_ns</span></code> module should expose exactly the same API (constants, functions, etc.) as the <code class="docutils literal notranslate"><span class="pre">time</span></code> module. It can be painful to maintain two flavors of the <code class="docutils literal notranslate"><span class="pre">time</span></code> module. How are users use supposed to make a choice between these two modules?</p> <p>If tomorrow, other nanosecond variants are needed in the <code class="docutils literal notranslate"><span class="pre">os</span></code> module, will we have to add a new <code class="docutils literal notranslate"><span class="pre">os_ns</span></code> module as well? There are functions related to time in many modules: <code class="docutils literal notranslate"><span class="pre">time</span></code>, <code class="docutils literal notranslate"><span class="pre">os</span></code>, <code class="docutils literal notranslate"><span class="pre">signal</span></code>, <code class="docutils literal notranslate"><span class="pre">resource</span></code>, <code class="docutils literal notranslate"><span class="pre">select</span></code>, etc.</p> <p>Another idea is to add a <code class="docutils literal notranslate"><span class="pre">time.ns</span></code> submodule or a nested-namespace to get the <code class="docutils literal notranslate"><span class="pre">time.ns.time()</span></code> syntax, but it suffers from the same issues.</p> </section> </section> <section id="annex-clocks-resolution-in-python"> <h2><a class="toc-backref" href="#annex-clocks-resolution-in-python" role="doc-backlink">Annex: Clocks Resolution in Python</a></h2> <p>This annex contains the resolution of clocks as measured in Python, and not the resolution announced by the operating system or the resolution of the internal structure used by the operating system.</p> <section id="script"> <h3><a class="toc-backref" href="#script" role="doc-backlink">Script</a></h3> <p>Example of script to measure the smallest difference between two <code class="docutils literal notranslate"><span class="pre">time.time()</span></code> and <code class="docutils literal notranslate"><span class="pre">time.time_ns()</span></code> reads ignoring differences of zero:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">math</span> <span class="kn">import</span> <span class="nn">time</span> <span class="n">LOOPS</span> <span class="o">=</span> <span class="mi">10</span> <span class="o">**</span> <span class="mi">6</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;time.time_ns(): </span><span class="si">%s</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="n">time</span><span class="o">.</span><span class="n">time_ns</span><span class="p">())</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;time.time(): </span><span class="si">%s</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">())</span> <span class="n">min_dt</span> <span class="o">=</span> <span class="p">[</span><span class="nb">abs</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">time_ns</span><span class="p">()</span> <span class="o">-</span> <span class="n">time</span><span class="o">.</span><span class="n">time_ns</span><span class="p">())</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">LOOPS</span><span class="p">)]</span> <span class="n">min_dt</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="nb">filter</span><span class="p">(</span><span class="nb">bool</span><span class="p">,</span> <span class="n">min_dt</span><span class="p">))</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;min time_ns() delta: </span><span class="si">%s</span><span class="s2"> ns&quot;</span> <span class="o">%</span> <span class="n">min_dt</span><span class="p">)</span> <span class="n">min_dt</span> <span class="o">=</span> <span class="p">[</span><span class="nb">abs</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">())</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">LOOPS</span><span class="p">)]</span> <span class="n">min_dt</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="nb">filter</span><span class="p">(</span><span class="nb">bool</span><span class="p">,</span> <span class="n">min_dt</span><span class="p">))</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;min time() delta: </span><span class="si">%s</span><span class="s2"> ns&quot;</span> <span class="o">%</span> <span class="n">math</span><span class="o">.</span><span class="n">ceil</span><span class="p">(</span><span class="n">min_dt</span> <span class="o">*</span> <span class="mf">1e9</span><span class="p">))</span> </pre></div> </div> </section> <section id="linux"> <h3><a class="toc-backref" href="#linux" role="doc-backlink">Linux</a></h3> <p>Clocks resolution measured in Python on Fedora 26 (kernel 4.12):</p> <table class="docutils align-default"> <thead> <tr class="row-odd"><th class="head">Function</th> <th class="head">Resolution</th> </tr> </thead> <tbody> <tr class="row-even"><td>clock()</td> <td>1 us</td> </tr> <tr class="row-odd"><td>monotonic()</td> <td>81 ns</td> </tr> <tr class="row-even"><td>monotonic_ns()</td> <td>84 ns</td> </tr> <tr class="row-odd"><td>perf_counter()</td> <td>82 ns</td> </tr> <tr class="row-even"><td>perf_counter_ns()</td> <td>84 ns</td> </tr> <tr class="row-odd"><td>process_time()</td> <td>2 ns</td> </tr> <tr class="row-even"><td>process_time_ns()</td> <td>1 ns</td> </tr> <tr class="row-odd"><td>resource.getrusage()</td> <td>1 us</td> </tr> <tr class="row-even"><td>time()</td> <td><strong>239 ns</strong></td> </tr> <tr class="row-odd"><td>time_ns()</td> <td><strong>84 ns</strong></td> </tr> <tr class="row-even"><td>times().elapsed</td> <td>10 ms</td> </tr> <tr class="row-odd"><td>times().user</td> <td>10 ms</td> </tr> </tbody> </table> <p>Notes on resolutions:</p> <ul class="simple"> <li><code class="docutils literal notranslate"><span class="pre">clock()</span></code> frequency is <code class="docutils literal notranslate"><span class="pre">CLOCKS_PER_SECOND</span></code> which is 1,000,000 Hz (1 MHz): resolution of 1 us.</li> <li><code class="docutils literal notranslate"><span class="pre">times()</span></code> frequency is <code class="docutils literal notranslate"><span class="pre">os.sysconf(&quot;SC_CLK_TCK&quot;)</span></code> (or the <code class="docutils literal notranslate"><span class="pre">HZ</span></code> constant) which is equal to 100 Hz: resolution of 10 ms.</li> <li><code class="docutils literal notranslate"><span class="pre">resource.getrusage()</span></code>, <code class="docutils literal notranslate"><span class="pre">os.wait3()</span></code> and <code class="docutils literal notranslate"><span class="pre">os.wait4()</span></code> use the <code class="docutils literal notranslate"><span class="pre">ru_usage</span></code> structure. The type of the <code class="docutils literal notranslate"><span class="pre">ru_usage.ru_utime</span></code> and <code class="docutils literal notranslate"><span class="pre">ru_usage.ru_stime</span></code> fields is the <code class="docutils literal notranslate"><span class="pre">timeval</span></code> structure which has a resolution of 1 us.</li> </ul> </section> <section id="windows"> <h3><a class="toc-backref" href="#windows" role="doc-backlink">Windows</a></h3> <p>Clocks resolution measured in Python on Windows 8.1:</p> <table class="docutils align-default"> <thead> <tr class="row-odd"><th class="head">Function</th> <th class="head">Resolution</th> </tr> </thead> <tbody> <tr class="row-even"><td>monotonic()</td> <td>15 ms</td> </tr> <tr class="row-odd"><td>monotonic_ns()</td> <td>15 ms</td> </tr> <tr class="row-even"><td>perf_counter()</td> <td>100 ns</td> </tr> <tr class="row-odd"><td>perf_counter_ns()</td> <td>100 ns</td> </tr> <tr class="row-even"><td>process_time()</td> <td>15.6 ms</td> </tr> <tr class="row-odd"><td>process_time_ns()</td> <td>15.6 ms</td> </tr> <tr class="row-even"><td>time()</td> <td><strong>894.1 us</strong></td> </tr> <tr class="row-odd"><td>time_ns()</td> <td><strong>318 us</strong></td> </tr> </tbody> </table> <p>The frequency of <code class="docutils literal notranslate"><span class="pre">perf_counter()</span></code> and <code class="docutils literal notranslate"><span class="pre">perf_counter_ns()</span></code> comes from <code class="docutils literal notranslate"><span class="pre">QueryPerformanceFrequency()</span></code>. The frequency is usually 10 MHz: resolution of 100 ns. In old Windows versions, the frequency was sometimes 3,579,545 Hz (3.6 MHz): resolution of 279 ns.</p> </section> <section id="analysis"> <h3><a class="toc-backref" href="#analysis" role="doc-backlink">Analysis</a></h3> <p>The resolution of <code class="docutils literal notranslate"><span class="pre">time.time_ns()</span></code> is much better than <code class="docutils literal notranslate"><span class="pre">time.time()</span></code>: <strong>84 ns (2.8x better) vs 239 ns on Linux and 318 us (2.8x better) vs 894 us on Windows</strong>. The <code class="docutils literal notranslate"><span class="pre">time.time()</span></code> resolution will only become larger (worse) as years pass since every day adds 86,400,000,000,000 nanoseconds to the system clock, which increases the precision loss.</p> <p>The difference between <code class="docutils literal notranslate"><span class="pre">time.perf_counter()</span></code>, <code class="docutils literal notranslate"><span class="pre">time.monotonic()</span></code>, <code class="docutils literal notranslate"><span class="pre">time.process_time()</span></code> and their respective nanosecond variants is 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 smaller than 1 week. A significant difference may be seen if uptime reaches 104 days or more.</p> <p><code class="docutils literal notranslate"><span class="pre">resource.getrusage()</span></code> and <code class="docutils literal notranslate"><span class="pre">times()</span></code> have a resolution greater or equal to 1 microsecond, and so don’t need a variant with nanosecond resolution.</p> <div class="admonition note"> <p class="admonition-title">Note</p> <p>Internally, Python starts <code class="docutils literal notranslate"><span class="pre">monotonic()</span></code> and <code class="docutils literal notranslate"><span class="pre">perf_counter()</span></code> clocks at zero on some platforms which indirectly reduce the precision loss.</p> </div> </section> </section> <section id="links"> <h2><a class="toc-backref" href="#links" role="doc-backlink">Links</a></h2> <ul class="simple"> <li><a class="reference external" href="https://bugs.python.org/issue31784">bpo-31784: Implementation of the PEP 564</a></li> </ul> </section> <section id="copyright"> <h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2> <p>This document has been placed in the public domain.</p> </section> </section> <hr class="docutils" /> <p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0564.rst">https://github.com/python/peps/blob/main/peps/pep-0564.rst</a></p> <p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0564.rst">2023-09-09 17:39:29 GMT</a></p> </article> <nav id="pep-sidebar"> <h2>Contents</h2> <ul> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#rationale">Rationale</a><ul> <li><a class="reference internal" href="#float-type-limited-to-104-days">Float type limited to 104 days</a></li> <li><a class="reference internal" href="#previous-rejected-pep">Previous rejected PEP</a></li> <li><a class="reference internal" href="#issues-caused-by-precision-loss">Issues caused by precision loss</a><ul> <li><a class="reference internal" href="#example-1-measure-time-delta-in-long-running-process">Example 1: measure time delta in long-running process</a></li> <li><a class="reference internal" href="#example-2-compare-times-with-different-resolution">Example 2: compare times with different resolution</a></li> </ul> </li> <li><a class="reference internal" href="#cpython-enhancements-of-the-last-5-years">CPython enhancements of the last 5 years</a></li> <li><a class="reference internal" href="#existing-python-apis-using-nanoseconds-as-int">Existing Python APIs using nanoseconds as int</a></li> </ul> </li> <li><a class="reference internal" href="#changes">Changes</a><ul> <li><a class="reference internal" href="#new-functions">New functions</a></li> <li><a class="reference internal" href="#unchanged-functions">Unchanged functions</a></li> </ul> </li> <li><a class="reference internal" href="#alternatives-and-discussion">Alternatives and discussion</a><ul> <li><a class="reference internal" href="#sub-nanosecond-resolution">Sub-nanosecond resolution</a></li> <li><a class="reference internal" href="#modifying-time-time-result-type">Modifying time.time() result type</a></li> <li><a class="reference internal" href="#different-types">Different types</a></li> <li><a class="reference internal" href="#different-api">Different API</a></li> <li><a class="reference internal" href="#a-new-module">A new module</a></li> </ul> </li> <li><a class="reference internal" href="#annex-clocks-resolution-in-python">Annex: Clocks Resolution in Python</a><ul> <li><a class="reference internal" href="#script">Script</a></li> <li><a class="reference internal" href="#linux">Linux</a></li> <li><a class="reference internal" href="#windows">Windows</a></li> <li><a class="reference internal" href="#analysis">Analysis</a></li> </ul> </li> <li><a class="reference internal" href="#links">Links</a></li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> <br> <a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0564.rst">Page Source (GitHub)</a> </nav> </section> <script src="../_static/colour_scheme.js"></script> <script src="../_static/wrap_tables.js"></script> <script src="../_static/sticky_banner.js"></script> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10