CINXE.COM
PEP 523 – Adding a frame evaluation API to CPython | 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 523 – Adding a frame evaluation API to CPython | peps.python.org</title> <link rel="shortcut icon" href="../_static/py.png"> <link rel="canonical" href="https://peps.python.org/pep-0523/"> <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 523 – Adding a frame evaluation API to CPython | peps.python.org'> <meta property="og:description" content="This PEP proposes to expand CPython’s C API 2 to allow for the specification of a per-interpreter function pointer to handle the evaluation of frames 5. This proposal also suggests adding a new field to code objects 3 to store arbitrary data for use by ..."> <meta property="og:type" content="website"> <meta property="og:url" content="https://peps.python.org/pep-0523/"> <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="This PEP proposes to expand CPython’s C API 2 to allow for the specification of a per-interpreter function pointer to handle the evaluation of frames 5. This proposal also suggests adding a new field to code objects 3 to store arbitrary data for use by ..."> <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> » </li> <li><a href="../pep-0000/">PEP Index</a> » </li> <li>PEP 523</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 523 – Adding a frame evaluation API to CPython</h1> <dl class="rfc2822 field-list simple"> <dt class="field-odd">Author<span class="colon">:</span></dt> <dd class="field-odd">Brett Cannon <brett at python.org>, Dino Viehland <dinov at microsoft.com></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-May-2016</dd> <dt class="field-odd">Python-Version<span class="colon">:</span></dt> <dd class="field-odd">3.6</dd> <dt class="field-even">Post-History<span class="colon">:</span></dt> <dd class="field-even">16-May-2016</dd> <dt class="field-odd">Resolution<span class="colon">:</span></dt> <dd class="field-odd"><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2016-August/145937.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></li> <li><a class="reference internal" href="#proposal">Proposal</a><ul> <li><a class="reference internal" href="#expanding-pycodeobject">Expanding <code class="docutils literal notranslate"><span class="pre">PyCodeObject</span></code></a></li> <li><a class="reference internal" href="#expanding-pyinterpreterstate">Expanding <code class="docutils literal notranslate"><span class="pre">PyInterpreterState</span></code></a></li> <li><a class="reference internal" href="#changes-to-python-ceval-c">Changes to <code class="docutils literal notranslate"><span class="pre">Python/ceval.c</span></code></a></li> <li><a class="reference internal" href="#updating-python-gdb-py">Updating <code class="docutils literal notranslate"><span class="pre">python-gdb.py</span></code></a></li> </ul> </li> <li><a class="reference internal" href="#performance-impact">Performance impact</a></li> <li><a class="reference internal" href="#example-usage">Example Usage</a><ul> <li><a class="reference internal" href="#a-jit-for-cpython">A JIT for CPython</a><ul> <li><a class="reference internal" href="#pyjion">Pyjion</a></li> <li><a class="reference internal" href="#other-jits">Other JITs</a></li> </ul> </li> <li><a class="reference internal" href="#debugging">Debugging</a></li> </ul> </li> <li><a class="reference internal" href="#implementation">Implementation</a></li> <li><a class="reference internal" href="#open-issues">Open Issues</a><ul> <li><a class="reference internal" href="#allow-eval-frame-to-be-null">Allow <code class="docutils literal notranslate"><span class="pre">eval_frame</span></code> to be <code class="docutils literal notranslate"><span class="pre">NULL</span></code></a></li> </ul> </li> <li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul> <li><a class="reference internal" href="#a-jit-specific-c-api">A JIT-specific C API</a></li> <li><a class="reference internal" href="#is-co-extra-needed">Is co_extra needed?</a></li> </ul> </li> <li><a class="reference internal" href="#references">References</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>This PEP proposes to expand CPython’s C API <a class="footnote-reference brackets" href="#c-api" id="id1">[2]</a> to allow for the specification of a per-interpreter function pointer to handle the evaluation of frames <a class="footnote-reference brackets" href="#pyeval-evalframeex" id="id2">[5]</a>. This proposal also suggests adding a new field to code objects <a class="footnote-reference brackets" href="#pycodeobject" id="id3">[3]</a> to store arbitrary data for use by the frame evaluation function.</p> </section> <section id="rationale"> <h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2> <p>One place where flexibility has been lacking in Python is in the direct execution of Python code. While CPython’s C API <a class="footnote-reference brackets" href="#c-api" id="id4">[2]</a> allows for constructing the data going into a frame object and then evaluating it via <code class="docutils literal notranslate"><span class="pre">PyEval_EvalFrameEx()</span></code> <a class="footnote-reference brackets" href="#pyeval-evalframeex" id="id5">[5]</a>, control over the execution of Python code comes down to individual objects instead of a holistic control of execution at the frame level.</p> <p>While wanting to have influence over frame evaluation may seem a bit too low-level, it does open the possibility for things such as a method-level JIT to be introduced into CPython without CPython itself having to provide one. By allowing external C code to control frame evaluation, a JIT can participate in the execution of Python code at the key point where evaluation occurs. This then allows for a JIT to conditionally recompile Python bytecode to machine code as desired while still allowing for executing regular CPython bytecode when running the JIT is not desired. This can be accomplished by allowing interpreters to specify what function to call to evaluate a frame. And by placing the API at the frame evaluation level it allows for a complete view of the execution environment of the code for the JIT.</p> <p>This ability to specify a frame evaluation function also allows for other use-cases beyond just opening CPython up to a JIT. For instance, it would not be difficult to implement a tracing or profiling function at the call level with this API. While CPython does provide the ability to set a tracing or profiling function at the Python level, this would be able to match the data collection of the profiler and quite possibly be faster for tracing by simply skipping per-line tracing support.</p> <p>It also opens up the possibility of debugging where the frame evaluation function only performs special debugging work when it detects it is about to execute a specific code object. In that instance the bytecode could be theoretically rewritten in-place to inject a breakpoint function call at the proper point for help in debugging while not having to do a heavy-handed approach as required by <code class="docutils literal notranslate"><span class="pre">sys.settrace()</span></code>.</p> <p>To help facilitate these use-cases, we are also proposing the adding of a “scratch space” on code objects via a new field. This will allow per-code object data to be stored with the code object itself for easy retrieval by the frame evaluation function as necessary. The field itself will simply be a <code class="docutils literal notranslate"><span class="pre">PyObject</span> <span class="pre">*</span></code> type so that any data stored in the field will participate in normal object memory management.</p> </section> <section id="proposal"> <h2><a class="toc-backref" href="#proposal" role="doc-backlink">Proposal</a></h2> <p>All proposed C API changes below will not be part of the stable ABI.</p> <section id="expanding-pycodeobject"> <h3><a class="toc-backref" href="#expanding-pycodeobject" role="doc-backlink">Expanding <code class="docutils literal notranslate"><span class="pre">PyCodeObject</span></code></a></h3> <p>One field is to be added to the <code class="docutils literal notranslate"><span class="pre">PyCodeObject</span></code> struct <a class="footnote-reference brackets" href="#pycodeobject" id="id6">[3]</a>:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">typedef</span> <span class="n">struct</span> <span class="p">{</span> <span class="o">...</span> <span class="n">void</span> <span class="o">*</span><span class="n">co_extra</span><span class="p">;</span> <span class="o">/*</span> <span class="s2">"Scratch space"</span> <span class="k">for</span> <span class="n">the</span> <span class="n">code</span> <span class="nb">object</span><span class="o">.</span> <span class="o">*/</span> <span class="p">}</span> <span class="n">PyCodeObject</span><span class="p">;</span> </pre></div> </div> <p>The <code class="docutils literal notranslate"><span class="pre">co_extra</span></code> will be <code class="docutils literal notranslate"><span class="pre">NULL</span></code> by default and only filled in as needed. Values stored in the field are expected to not be required in order for the code object to function, allowing the loss of the data of the field to be acceptable.</p> <p>A private API has been introduced to work with the field:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">PyAPI_FUNC</span><span class="p">(</span><span class="n">Py_ssize_t</span><span class="p">)</span> <span class="n">_PyEval_RequestCodeExtraIndex</span><span class="p">(</span><span class="n">freefunc</span><span class="p">);</span> <span class="n">PyAPI_FUNC</span><span class="p">(</span><span class="nb">int</span><span class="p">)</span> <span class="n">_PyCode_GetExtra</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">code</span><span class="p">,</span> <span class="n">Py_ssize_t</span> <span class="n">index</span><span class="p">,</span> <span class="n">void</span> <span class="o">**</span><span class="n">extra</span><span class="p">);</span> <span class="n">PyAPI_FUNC</span><span class="p">(</span><span class="nb">int</span><span class="p">)</span> <span class="n">_PyCode_SetExtra</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">code</span><span class="p">,</span> <span class="n">Py_ssize_t</span> <span class="n">index</span><span class="p">,</span> <span class="n">void</span> <span class="o">*</span><span class="n">extra</span><span class="p">);</span> </pre></div> </div> <p>Users of the field are expected to call <code class="docutils literal notranslate"><span class="pre">_PyEval_RequestCodeExtraIndex()</span></code> to receive (what should be considered) an opaque index value to adding data into <code class="docutils literal notranslate"><span class="pre">co-extra</span></code>. With that index, users can set data using <code class="docutils literal notranslate"><span class="pre">_PyCode_SetExtra()</span></code> and later retrieve the data with <code class="docutils literal notranslate"><span class="pre">_PyCode_GetExtra()</span></code>. The API is purposefully listed as private to communicate the fact that there are no semantic guarantees of the API between Python releases.</p> <p>Using a list and tuple were considered but was found to be less performant, and with a key use-case being JIT usage the performance consideration won out for using a custom struct instead of a Python object.</p> <p>A dict was also considered, but once again performance was more important. While a dict will have constant overhead in looking up data, the overhead for the common case of a single object being stored in the data structure leads to a tuple having better performance characteristics (i.e. iterating a tuple of length 1 is faster than the overhead of hashing and looking up an object in a dict).</p> </section> <section id="expanding-pyinterpreterstate"> <h3><a class="toc-backref" href="#expanding-pyinterpreterstate" role="doc-backlink">Expanding <code class="docutils literal notranslate"><span class="pre">PyInterpreterState</span></code></a></h3> <p>The entrypoint for the frame evaluation function is per-interpreter:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">//</span> <span class="n">Same</span> <span class="nb">type</span> <span class="n">signature</span> <span class="k">as</span> <span class="n">PyEval_EvalFrameEx</span><span class="p">()</span><span class="o">.</span> <span class="n">typedef</span> <span class="n">PyObject</span><span class="o">*</span> <span class="p">(</span><span class="o">*</span><span class="n">_PyFrameEvalFunction</span><span class="p">)(</span><span class="n">PyFrameObject</span><span class="o">*</span><span class="p">,</span> <span class="nb">int</span><span class="p">);</span> <span class="n">typedef</span> <span class="n">struct</span> <span class="p">{</span> <span class="o">...</span> <span class="n">_PyFrameEvalFunction</span> <span class="n">eval_frame</span><span class="p">;</span> <span class="p">}</span> <span class="n">PyInterpreterState</span><span class="p">;</span> </pre></div> </div> <p>By default, the <code class="docutils literal notranslate"><span class="pre">eval_frame</span></code> field will be initialized to a function pointer that represents what <code class="docutils literal notranslate"><span class="pre">PyEval_EvalFrameEx()</span></code> currently is (called <code class="docutils literal notranslate"><span class="pre">_PyEval_EvalFrameDefault()</span></code>, discussed later in this PEP). Third-party code may then set their own frame evaluation function instead to control the execution of Python code. A pointer comparison can be used to detect if the field is set to <code class="docutils literal notranslate"><span class="pre">_PyEval_EvalFrameDefault()</span></code> and thus has not been mutated yet.</p> </section> <section id="changes-to-python-ceval-c"> <h3><a class="toc-backref" href="#changes-to-python-ceval-c" role="doc-backlink">Changes to <code class="docutils literal notranslate"><span class="pre">Python/ceval.c</span></code></a></h3> <p><code class="docutils literal notranslate"><span class="pre">PyEval_EvalFrameEx()</span></code> <a class="footnote-reference brackets" href="#pyeval-evalframeex" id="id7">[5]</a> as it currently stands will be renamed to <code class="docutils literal notranslate"><span class="pre">_PyEval_EvalFrameDefault()</span></code>. The new <code class="docutils literal notranslate"><span class="pre">PyEval_EvalFrameEx()</span></code> will then become:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">PyObject</span> <span class="o">*</span> <span class="n">PyEval_EvalFrameEx</span><span class="p">(</span><span class="n">PyFrameObject</span> <span class="o">*</span><span class="n">frame</span><span class="p">,</span> <span class="nb">int</span> <span class="n">throwflag</span><span class="p">)</span> <span class="p">{</span> <span class="n">PyThreadState</span> <span class="o">*</span><span class="n">tstate</span> <span class="o">=</span> <span class="n">PyThreadState_GET</span><span class="p">();</span> <span class="k">return</span> <span class="n">tstate</span><span class="o">-></span><span class="n">interp</span><span class="o">-></span><span class="n">eval_frame</span><span class="p">(</span><span class="n">frame</span><span class="p">,</span> <span class="n">throwflag</span><span class="p">);</span> <span class="p">}</span> </pre></div> </div> <p>This allows third-party code to place themselves directly in the path of Python code execution while being backwards-compatible with code already using the pre-existing C API.</p> </section> <section id="updating-python-gdb-py"> <h3><a class="toc-backref" href="#updating-python-gdb-py" role="doc-backlink">Updating <code class="docutils literal notranslate"><span class="pre">python-gdb.py</span></code></a></h3> <p>The generated <code class="docutils literal notranslate"><span class="pre">python-gdb.py</span></code> file used for Python support in GDB makes some hard-coded assumptions about <code class="docutils literal notranslate"><span class="pre">PyEval_EvalFrameEx()</span></code>, e.g. the names of local variables. It will need to be updated to work with the proposed changes.</p> </section> </section> <section id="performance-impact"> <h2><a class="toc-backref" href="#performance-impact" role="doc-backlink">Performance impact</a></h2> <p>As this PEP is proposing an API to add pluggability, performance impact is considered only in the case where no third-party code has made any changes.</p> <p>Several runs of pybench <a class="footnote-reference brackets" href="#pybench" id="id8">[14]</a> consistently showed no performance cost from the API change alone.</p> <p>A run of the Python benchmark suite <a class="footnote-reference brackets" href="#py-benchmarks" id="id9">[9]</a> showed no measurable cost in performance.</p> <p>In terms of memory impact, since there are typically not many CPython interpreters executing in a single process that means the impact of <code class="docutils literal notranslate"><span class="pre">co_extra</span></code> being added to <code class="docutils literal notranslate"><span class="pre">PyCodeObject</span></code> is the only worry. According to <a class="footnote-reference brackets" href="#code-object-count" id="id10">[8]</a>, a run of the Python test suite results in about 72,395 code objects being created. On a 64-bit CPU that would result in 579,160 bytes of extra memory being used if all code objects were alive at once and had nothing set in their <code class="docutils literal notranslate"><span class="pre">co_extra</span></code> fields.</p> </section> <section id="example-usage"> <h2><a class="toc-backref" href="#example-usage" role="doc-backlink">Example Usage</a></h2> <section id="a-jit-for-cpython"> <h3><a class="toc-backref" href="#a-jit-for-cpython" role="doc-backlink">A JIT for CPython</a></h3> <section id="pyjion"> <h4><a class="toc-backref" href="#pyjion" role="doc-backlink">Pyjion</a></h4> <p>The Pyjion project <a class="footnote-reference brackets" href="#id19" id="id11">[1]</a> has used this proposed API to implement a JIT for CPython using the CoreCLR’s JIT <a class="footnote-reference brackets" href="#coreclr" id="id12">[4]</a>. Each code object has its <code class="docutils literal notranslate"><span class="pre">co_extra</span></code> field set to a <code class="docutils literal notranslate"><span class="pre">PyjionJittedCode</span></code> object which stores four pieces of information:</p> <ol class="arabic simple"> <li>Execution count</li> <li>A boolean representing whether a previous attempt to JIT failed</li> <li>A function pointer to a trampoline (which can be type tracing or not)</li> <li>A void pointer to any JIT-compiled machine code</li> </ol> <p>The frame evaluation function has (roughly) the following algorithm:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">eval_frame</span><span class="p">(</span><span class="n">frame</span><span class="p">,</span> <span class="n">throw_flag</span><span class="p">):</span> <span class="n">pyjion_code</span> <span class="o">=</span> <span class="n">frame</span><span class="o">.</span><span class="n">code</span><span class="o">.</span><span class="n">co_extra</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">pyjion_code</span><span class="p">:</span> <span class="n">frame</span><span class="o">.</span><span class="n">code</span><span class="o">.</span><span class="n">co_extra</span> <span class="o">=</span> <span class="n">PyjionJittedCode</span><span class="p">()</span> <span class="k">elif</span> <span class="ow">not</span> <span class="n">pyjion_code</span><span class="o">.</span><span class="n">jit_failed</span><span class="p">:</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">pyjion_code</span><span class="o">.</span><span class="n">jit_code</span><span class="p">:</span> <span class="k">return</span> <span class="n">pyjion_code</span><span class="o">.</span><span class="n">eval</span><span class="p">(</span><span class="n">pyjion_code</span><span class="o">.</span><span class="n">jit_code</span><span class="p">,</span> <span class="n">frame</span><span class="p">)</span> <span class="k">elif</span> <span class="n">pyjion_code</span><span class="o">.</span><span class="n">exec_count</span> <span class="o">></span> <span class="mi">20_000</span><span class="p">:</span> <span class="k">if</span> <span class="n">jit_compile</span><span class="p">(</span><span class="n">frame</span><span class="p">):</span> <span class="k">return</span> <span class="n">pyjion_code</span><span class="o">.</span><span class="n">eval</span><span class="p">(</span><span class="n">pyjion_code</span><span class="o">.</span><span class="n">jit_code</span><span class="p">,</span> <span class="n">frame</span><span class="p">)</span> <span class="k">else</span><span class="p">:</span> <span class="n">pyjion_code</span><span class="o">.</span><span class="n">jit_failed</span> <span class="o">=</span> <span class="kc">True</span> <span class="n">pyjion_code</span><span class="o">.</span><span class="n">exec_count</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">return</span> <span class="n">_PyEval_EvalFrameDefault</span><span class="p">(</span><span class="n">frame</span><span class="p">,</span> <span class="n">throw_flag</span><span class="p">)</span> </pre></div> </div> <p>The key point, though, is that all of this work and logic is separate from CPython and yet with the proposed API changes it is able to provide a JIT that is compliant with Python semantics (as of this writing, performance is almost equivalent to CPython without the new API). This means there’s nothing technically preventing others from implementing their own JITs for CPython by utilizing the proposed API.</p> </section> <section id="other-jits"> <h4><a class="toc-backref" href="#other-jits" role="doc-backlink">Other JITs</a></h4> <p>It should be mentioned that the Pyston team was consulted on an earlier version of this PEP that was more JIT-specific and they were not interested in utilizing the changes proposed because they want control over memory layout they had no interest in directly supporting CPython itself. An informal discussion with a developer on the PyPy team led to a similar comment.</p> <p>Numba <a class="footnote-reference brackets" href="#numba" id="id13">[6]</a>, on the other hand, suggested that they would be interested in the proposed change in a post-1.0 future for themselves <a class="footnote-reference brackets" href="#numba-interest" id="id14">[7]</a>.</p> <p>The experimental Coconut JIT <a class="footnote-reference brackets" href="#coconut" id="id15">[13]</a> could have benefitted from this PEP. In private conversations with Coconut’s creator we were told that our API was probably superior to the one they developed for Coconut to add JIT support to CPython.</p> </section> </section> <section id="debugging"> <h3><a class="toc-backref" href="#debugging" role="doc-backlink">Debugging</a></h3> <p>In conversations with the Python Tools for Visual Studio team (PTVS) <a class="footnote-reference brackets" href="#ptvs" id="id16">[12]</a>, they thought they would find these API changes useful for implementing more performant debugging. As mentioned in the <a class="reference internal" href="#rationale">Rationale</a> section, this API would allow for switching on debugging functionality only in frames where it is needed. This could allow for either skipping information that <code class="docutils literal notranslate"><span class="pre">sys.settrace()</span></code> normally provides and even go as far as to dynamically rewrite bytecode prior to execution to inject e.g. breakpoints in the bytecode.</p> <p>It also turns out that Google provides a very similar API internally. It has been used for performant debugging purposes.</p> </section> </section> <section id="implementation"> <h2><a class="toc-backref" href="#implementation" role="doc-backlink">Implementation</a></h2> <p>A set of patches implementing the proposed API is available through the Pyjion project <a class="footnote-reference brackets" href="#id19" id="id17">[1]</a>. In its current form it has more changes to CPython than just this proposed API, but that is for ease of development instead of strict requirements to accomplish its goals.</p> </section> <section id="open-issues"> <h2><a class="toc-backref" href="#open-issues" role="doc-backlink">Open Issues</a></h2> <section id="allow-eval-frame-to-be-null"> <h3><a class="toc-backref" href="#allow-eval-frame-to-be-null" role="doc-backlink">Allow <code class="docutils literal notranslate"><span class="pre">eval_frame</span></code> to be <code class="docutils literal notranslate"><span class="pre">NULL</span></code></a></h3> <p>Currently the frame evaluation function is expected to always be set. It could very easily simply default to <code class="docutils literal notranslate"><span class="pre">NULL</span></code> instead which would signal to use <code class="docutils literal notranslate"><span class="pre">_PyEval_EvalFrameDefault()</span></code>. The current proposal of not special-casing the field seemed the most straightforward, but it does require that the field not accidentally be cleared, else a crash may occur.</p> </section> </section> <section id="rejected-ideas"> <h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2> <section id="a-jit-specific-c-api"> <h3><a class="toc-backref" href="#a-jit-specific-c-api" role="doc-backlink">A JIT-specific C API</a></h3> <p>Originally this PEP was going to propose a much larger API change which was more JIT-specific. After soliciting feedback from the Numba team <a class="footnote-reference brackets" href="#numba" id="id18">[6]</a>, though, it became clear that the API was unnecessarily large. The realization was made that all that was truly needed was the opportunity to provide a trampoline function to handle execution of Python code that had been JIT-compiled and a way to attach that compiled machine code along with other critical data to the corresponding Python code object. Once it was shown that there was no loss in functionality or in performance while minimizing the API changes required, the proposal was changed to its current form.</p> </section> <section id="is-co-extra-needed"> <h3><a class="toc-backref" href="#is-co-extra-needed" role="doc-backlink">Is co_extra needed?</a></h3> <p>While discussing this PEP at PyCon US 2016, some core developers expressed their worry of the <code class="docutils literal notranslate"><span class="pre">co_extra</span></code> field making code objects mutable. The thinking seemed to be that having a field that was mutated after the creation of the code object made the object seem mutable, even though no other aspect of code objects changed.</p> <p>The view of this PEP is that the <code class="docutils literal notranslate"><span class="pre">co_extra</span></code> field doesn’t change the fact that code objects are immutable. The field is specified in this PEP to not contain information required to make the code object usable, making it more of a caching field. It could be viewed as similar to the UTF-8 cache that string objects have internally; strings are still considered immutable even though they have a field that is conditionally set.</p> <p>Performance measurements were also made where the field was not available for JIT workloads. The loss of the field was deemed too costly to performance when using an unordered map from C++ or Python’s dict to associated a code object with JIT-specific data objects.</p> </section> </section> <section id="references"> <h2><a class="toc-backref" href="#references" role="doc-backlink">References</a></h2> <aside class="footnote-list brackets"> <aside class="footnote brackets" id="id19" role="doc-footnote"> <dt class="label" id="id19">[1]<em> (<a href='#id11'>1</a>, <a href='#id17'>2</a>) </em></dt> <dd>Pyjion project (<a class="reference external" href="https://github.com/microsoft/pyjion">https://github.com/microsoft/pyjion</a>)</aside> <aside class="footnote brackets" id="c-api" role="doc-footnote"> <dt class="label" id="c-api">[2]<em> (<a href='#id1'>1</a>, <a href='#id4'>2</a>) </em></dt> <dd>CPython’s C API (<a class="reference external" href="https://docs.python.org/3/c-api/index.html">https://docs.python.org/3/c-api/index.html</a>)</aside> <aside class="footnote brackets" id="pycodeobject" role="doc-footnote"> <dt class="label" id="pycodeobject">[3]<em> (<a href='#id3'>1</a>, <a href='#id6'>2</a>) </em></dt> <dd><code class="docutils literal notranslate"><span class="pre">PyCodeObject</span></code> (<a class="reference external" href="https://docs.python.org/3/c-api/code.html#c.PyCodeObject">https://docs.python.org/3/c-api/code.html#c.PyCodeObject</a>)</aside> <aside class="footnote brackets" id="coreclr" role="doc-footnote"> <dt class="label" id="coreclr">[<a href="#id12">4</a>]</dt> <dd>.NET Core Runtime (CoreCLR) (<a class="reference external" href="https://github.com/dotnet/coreclr">https://github.com/dotnet/coreclr</a>)</aside> <aside class="footnote brackets" id="pyeval-evalframeex" role="doc-footnote"> <dt class="label" id="pyeval-evalframeex">[5]<em> (<a href='#id2'>1</a>, <a href='#id5'>2</a>, <a href='#id7'>3</a>) </em></dt> <dd><code class="docutils literal notranslate"><span class="pre">PyEval_EvalFrameEx()</span></code> (<a class="reference external" href="https://docs.python.org/3/c-api/veryhigh.html?highlight=pyframeobject#c.PyEval_EvalFrameEx">https://docs.python.org/3/c-api/veryhigh.html?highlight=pyframeobject#c.PyEval_EvalFrameEx</a>)</aside> <aside class="footnote brackets" id="numba" role="doc-footnote"> <dt class="label" id="numba">[6]<em> (<a href='#id13'>1</a>, <a href='#id18'>2</a>) </em></dt> <dd>Numba (<a class="reference external" href="http://numba.pydata.org/">http://numba.pydata.org/</a>)</aside> <aside class="footnote brackets" id="numba-interest" role="doc-footnote"> <dt class="label" id="numba-interest">[<a href="#id14">7</a>]</dt> <dd>numba-users mailing list: “Would the C API for a JIT entrypoint being proposed by Pyjion help out Numba?” (<a class="reference external" href="https://groups.google.com/a/continuum.io/forum/#!topic/numba-users/yRl_0t8-m1g">https://groups.google.com/a/continuum.io/forum/#!topic/numba-users/yRl_0t8-m1g</a>)</aside> <aside class="footnote brackets" id="code-object-count" role="doc-footnote"> <dt class="label" id="code-object-count">[<a href="#id10">8</a>]</dt> <dd>[Python-Dev] Opcode cache in ceval loop (<a class="reference external" href="https://mail.python.org/pipermail/python-dev/2016-February/143025.html">https://mail.python.org/pipermail/python-dev/2016-February/143025.html</a>)</aside> <aside class="footnote brackets" id="py-benchmarks" role="doc-footnote"> <dt class="label" id="py-benchmarks">[<a href="#id9">9</a>]</dt> <dd>Python benchmark suite (<a class="reference external" href="https://hg.python.org/benchmarks">https://hg.python.org/benchmarks</a>)</aside> <aside class="footnote brackets" id="pyston" role="doc-footnote"> <dt class="label" id="pyston">[10]</dt> <dd>Pyston (<a class="reference external" href="http://pyston.org">http://pyston.org</a>)</aside> <aside class="footnote brackets" id="pypy" role="doc-footnote"> <dt class="label" id="pypy">[11]</dt> <dd>PyPy (<a class="reference external" href="http://pypy.org/">http://pypy.org/</a>)</aside> <aside class="footnote brackets" id="ptvs" role="doc-footnote"> <dt class="label" id="ptvs">[<a href="#id16">12</a>]</dt> <dd>Python Tools for Visual Studio (<a class="reference external" href="http://microsoft.github.io/PTVS/">http://microsoft.github.io/PTVS/</a>)</aside> <aside class="footnote brackets" id="coconut" role="doc-footnote"> <dt class="label" id="coconut">[<a href="#id15">13</a>]</dt> <dd>Coconut (<a class="reference external" href="https://github.com/davidmalcolm/coconut">https://github.com/davidmalcolm/coconut</a>)</aside> <aside class="footnote brackets" id="pybench" role="doc-footnote"> <dt class="label" id="pybench">[<a href="#id8">14</a>]</dt> <dd>pybench (<a class="reference external" href="https://hg.python.org/cpython/file/default/Tools/pybench">https://hg.python.org/cpython/file/default/Tools/pybench</a>)</aside> </aside> </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-0523.rst">https://github.com/python/peps/blob/main/peps/pep-0523.rst</a></p> <p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0523.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></li> <li><a class="reference internal" href="#proposal">Proposal</a><ul> <li><a class="reference internal" href="#expanding-pycodeobject">Expanding <code class="docutils literal notranslate"><span class="pre">PyCodeObject</span></code></a></li> <li><a class="reference internal" href="#expanding-pyinterpreterstate">Expanding <code class="docutils literal notranslate"><span class="pre">PyInterpreterState</span></code></a></li> <li><a class="reference internal" href="#changes-to-python-ceval-c">Changes to <code class="docutils literal notranslate"><span class="pre">Python/ceval.c</span></code></a></li> <li><a class="reference internal" href="#updating-python-gdb-py">Updating <code class="docutils literal notranslate"><span class="pre">python-gdb.py</span></code></a></li> </ul> </li> <li><a class="reference internal" href="#performance-impact">Performance impact</a></li> <li><a class="reference internal" href="#example-usage">Example Usage</a><ul> <li><a class="reference internal" href="#a-jit-for-cpython">A JIT for CPython</a><ul> <li><a class="reference internal" href="#pyjion">Pyjion</a></li> <li><a class="reference internal" href="#other-jits">Other JITs</a></li> </ul> </li> <li><a class="reference internal" href="#debugging">Debugging</a></li> </ul> </li> <li><a class="reference internal" href="#implementation">Implementation</a></li> <li><a class="reference internal" href="#open-issues">Open Issues</a><ul> <li><a class="reference internal" href="#allow-eval-frame-to-be-null">Allow <code class="docutils literal notranslate"><span class="pre">eval_frame</span></code> to be <code class="docutils literal notranslate"><span class="pre">NULL</span></code></a></li> </ul> </li> <li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul> <li><a class="reference internal" href="#a-jit-specific-c-api">A JIT-specific C API</a></li> <li><a class="reference internal" href="#is-co-extra-needed">Is co_extra needed?</a></li> </ul> </li> <li><a class="reference internal" href="#references">References</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-0523.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>