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
This commit is contained in:
Sam Gross 2023-01-23 11:06:13 -06:00 committed by GitHub
parent b4de31296d
commit d9503c0be9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 79 additions and 17 deletions

View File

@ -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 <https://discuss.python.org/t/22606>`__
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.