From d9503c0be9c2a2c8f7d61ca265f84a1a894f0ebe Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Mon, 23 Jan 2023 11:06:13 -0600 Subject: [PATCH] PEP 703: --disable-gil and other small changes (#2979) * Rename `--without-gil` to `--disable-gil`. Suggested by Inada Naoki. * Add two sections to "Rejected Ideas": - Why Not Deprecate ``PyDict_GetItem`` in Favor of ``PyDict_FetchItem``? - Why Not Use PEP 683 Immortalization? * Expand on Python build modes --- pep-0703.rst | 96 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 79 insertions(+), 17 deletions(-) diff --git a/pep-0703.rst b/pep-0703.rst index 0ef4f63ad..c8cdada7a 100644 --- a/pep-0703.rst +++ b/pep-0703.rst @@ -8,7 +8,7 @@ Type: Standards Track Content-Type: text/x-rst Created: 09-Jan-2023 Python-Version: 3.12 -Post-History: 09-Jan-2023 +Post-History: `09-Jan-2023 `__ Resolution: @@ -18,7 +18,7 @@ Abstract CPython's global interpreter lock ("GIL") prevents multiple threads from executing Python code at the same time. The GIL is an obstacle to using multi-core CPUs from Python efficiently. This PEP proposes -adding a build configuration (``--without-gil``) to CPython to let it +adding a build configuration (``--disable-gil``) to CPython to let it run Python code without the global interpreter lock and with the necessary changes needed to make the interpreter thread-safe. @@ -326,10 +326,10 @@ Build Configuration Changes The global interpreter lock will remain the default for CPython builds and python.org downloads. A new build configuration flag, -``--without-gil`` will be added to the configure script that will +``--disable-gil`` will be added to the configure script that will build CPython without the global interpreter lock. -When built with ``--without-gil``, CPython will define the +When built with ``--disable-gil``, CPython will define the ``Py_NOGIL`` macro in Python/patchlevel.h. The ABI tag will include the letter "n" (for "nogil"). @@ -647,7 +647,7 @@ CPython Free Lists CPython makes use of free lists to speed up the allocation of small, frequently allocated objects like tuples and numbers. These free lists are not thread-safe and will need to be disabled when building -Python in the ``--without-gil`` mode. +Python in the ``--disable-gil`` mode. @@ -1280,7 +1280,7 @@ Backwards Compatibility ======================= This PEP poses a number of backwards compatibility issues when -building CPython with the ``--without-gil`` flag, but those issues do +building CPython with the ``--disable-gil`` flag, but those issues do not occur when using the default build configuration. Nearly all the backwards compatibility concerns involve the C-API: @@ -1332,14 +1332,14 @@ Distribution This PEP poses new challenges for distributing Python. At least for some time, there will be two versions of Python requiring separately compiled C-API extensions. It may take some time for C-API extension -authors to build ``--without-gil`` compatible packages and upload +authors to build ``--disable-gil`` compatible packages and upload them to PyPI. Additionally, some authors may be hesitant to support -the ``--without-gil`` mode until it has wide adoption, but adoption +the ``--disable-gil`` mode until it has wide adoption, but adoption will likely depend on the availability of Python's rich set of extensions. To mitigate this, the author will work with Anaconda to distribute -a ``--without-gil`` version of Python together with compatible +a ``--disable-gil`` version of Python together with compatible packages from conda channels. This centralizes the challenges of building extensions, and the author believes this will enable more people to use Python without the GIL sooner than they would otherwise @@ -1370,7 +1370,7 @@ The other changes with significant performance impact are: How to Teach This ================= -As part of implementing the ``--without-gil`` mode, the author will +As part of implementing the ``--disable-gil`` mode, the author will write a "HOWTO" guide [#howto]_ for making packages compatible when running Python without the GIL. @@ -1531,6 +1531,52 @@ author is not aware of a way to add write barriers to CPython without substantially breaking the C-API. +Why Not Deprecate ``PyDict_GetItem`` in Favor of ``PyDict_FetchItem``? +---------------------------------------------------------------------- + +This PEP proposes a new API ``PyDict_FetchItem`` which behaves like +``PyDict_GetItem``, but returns a new reference instead of a borrowed +reference. As described in `Borrowed References`_, some uses of +borrowed references that were safe when running with the GIL are +unsafe when running without the GIL and need to be replaced by +functions like ``PyDict_FetchItem`` that return new references. + +This PEP does *not* propose deprecating ``PyDict_GetItem`` and similar +functions that return borrowed references for a few reasons: + +* Many of the uses of borrowed references are safe, even when running + without the GIL. For example, C API functions often use + ``PyDict_GetItem`` to retrieve items from the keyword + argument dictionary. These calls are safe because the keyword + argument dictionary is only visible to a single thread. +* I tried this approach early on and found that wholesale replacing of + ``PyDict_GetItem`` with ``PyDict_FetchItem`` frequently introduced + new reference counting bugs. In my opinion, the risk of + introducing new reference counting bugs generally outweighs the + risks of missing a ``PyDict_GetItem`` call that is unsafe without + the GIL. + + +Why Not Use PEP 683 Immortalization? +------------------------------------ + +Like :pep:`683`, this PEP proposes an immortalization scheme for +Python objects, but the PEPs use different bit representations to +mark immortal objects. The schemes cannot be identical because this +PEP depends on biased reference counting, which has two reference +count fields instead of one. The schemes could be made more +superficially similar, but it is not clear that would be worthwhile. +PEP 683 maintains compatibility with extensions compiled to the +stable ABI, and therefore uses the second most significant bit +(i.e., 2^62 on 64-bit platforms) to mark immortal objects. Checking +that bit typically requires an extra instruction on x86-64 compared +with checking the sign bit or one of the low 32 bits. This PEP +cannot maintain compatibility with extensions compiled to the stable +ABI because of the use of two reference count fields, and so this PEP +is free to propose a representation that allows slightly more +efficient checks for immortality on x86-64. + + Open Issues =========== @@ -1548,23 +1594,39 @@ manner. Python Build Modes ------------------ -This PEP introduces a new build mode (``--without-gil``) that is not +This PEP introduces a new build mode (``--disable-gil``) that is not ABI compatible with the standard build mode. The additional build mode adds complexity for both Python core developers and extension -developers. The author believes a worthwhile long-term goal is to -combine these build modes and have the global interpreter lock -controlled at runtime, possibly disabled by default. The path to -this goal remains an open issue. +developers. The author believes a worthwhile goal is to combine +these build modes and have the global interpreter lock controlled at +runtime, possibly disabled by default. The path to this goal remains +an open issue, but a possible path might look like the following: + +#. In 2024, CPython 3.13 is released with support for a + ``--disable-gil`` build time flag. There are two ABIs for + CPython, one with the GIL and one without. Extension authors + target both ABIs. +#. After 2--3 releases, (i.e., in 2026--2027), CPython is released + with with the GIL controlled by a runtime environment variable or + flag. The GIL is enabled by default. There is only a single ABI. +#. After another 2--3 release (i.e., 2028--2030), CPython switches to + the GIL being disabled by default. The GIL can still be enabled + at runtime via an environment variable or command line flag. + +This PEP covers the first step, with the remaining steps left as open +issues. In this scenario, there would be a two to three year period +where extension authors would target an extra CPython build per +supported CPU architecture and OS. Mitigations for Single-Threaded Performance ------------------------------------------- The changes proposed in the PEP will increase execution overhead for -``--without-gil`` builds compared to Python builds with the GIL. In +``--disable-gil`` builds compared to Python builds with the GIL. In other words, it will have slower single-threaded performance. There are some possible optimizations to reduce execution overhead, -especially for ``--without-gil`` builds that only use a single +especially for ``--disable-gil`` builds that only use a single thread. These may be worthwhile if a longer term goal is to have a single build mode, but the choice of optimizations and their trade-offs remain an open issue.