GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Performance regression from PyMutex spin-loop reloading in free-threaded builds · Issue #157914 · python/cpython

Performance regression from PyMutex spin-loop reloading in free-threaded builds #157914

New issue
Copy link
New issue
Copy link
Closed
Closed
Performance regression from PyMutex spin-loop reloading in free-threaded builds#157914
Copy link
Labels
3.15bugs and security fixesbugs and security fixesinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)topic-free-threadingtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@dpdani
dpdani
opened on Sep 21, 2026
Issue body actions

Bug report

Bug description:

While testing out the new releases of 3.15, I have noticed that this PR, which I authored, introduced a significant performance regression and I would advise to revert it.

I had found this issue while testing a program like this one:
import time
from threading import Thread

THREADS = 4
KEYS = tuple(f"key-{i}" for i in range(16))
UPDATES_PER_THREAD = 1_000_000


def update_dict():
    d = dict.fromkeys(KEYS, 0)

    def worker():
        keys = KEYS
        h = d
        for update in range(UPDATES_PER_THREAD):
            key = keys[update % len(keys)]
            h[key] = update

    workers = [Thread(target=worker) for _ in range(THREADS)]
    for w in workers: w.start()
    for w in workers: w.join()


started = time.perf_counter()
update_dict()
took = time.perf_counter() - started
print(f"{took:.3f}s")

Which outputs 0.507s on main and 0.260s with the revert, on an M4 Max. Note that essentially this is the case where several threads try to write into a shared dictionary, so I wouldn't necessarily call this artificial.

The patch changes the acquisition path of PyMutex to reload the lock state while in the spinning phase, before parking the thread after a number of failed attempts. Prior to my change, the lock state was never reloaded during the spinning phase, which is arguably a bad thing for a spinlock to do. Regardless, reloading is a tradeoff between increasing the contention on the lock state due to the read and promptness of acquisition. Under certain workloads, the tradeoff works in favor of reloading and in others it doesn't, hindering the acquisition rate.

This was always known, but I didn't realize the extent with which this impacts certain scenarios. From what I could observe by running the lockbench.py script under a wide variety of parameter values, the greatest improvement is around 3x more throughput, while the greatest slowdown is around 6x less throughput. I ran this comparison sweep with Codex between main and main with commit
daa159f98b3689ae6a587bfb978b931762f9dbc9 reverted. (See attached pdf for the results.)

I have tried for the past week to work on a patch that would keep the speedups and cope with the slowdowns, but I couldn't find a way to do it.

I believe the mistake made with this patch was looking for a mutex performance improvement without a good enough set of benchmarks, though that would be a big problem to tackle in and of itself. It was thus difficult to see how this would have impacted related code like the one shown. One way to deal with this in the future would be to bruteforce it and try many possible parameter sets for lockbench, as I did in writing this. (Which is easier than coming up with a good set of benchmarks for PyMutex.)

My gut feeling is to follow the principle of least surprise and revert the patch, at least for now. We may evaluate alternative implementations later, possibly once we have broader benchmark coverage.

Reverting would arguably re-open the associated issue, as it pertains this patch, though I'm not sure if it can be fixed given how much reloading hurts performance in some cases. Note that the non-blocking call problem mentioned in the issue was fixed separately and reverting the PR above would not impact it.

CPython versions tested on:

CPython main branch

Operating systems tested on:

macOS

Linked PRs

  • gh-157914: Revert "gh-135871: Reload lock internal state while spinning in PyMutex_LockTimed (gh-146064)" #158082
  • [3.15] gh-157914: Revert "gh-135871: Reload lock internal state while spinning in PyMutex_LockTimed (gh-146064)" (gh-158082) #158290

Activity

  1. dpdani
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    on Sep 21, 2026
  2. ngoldbaum commented on Sep 21, 2026

    @ngoldbaum
    ngoldbaum
    on Sep 21, 2026
    Contributor
    More actions

    ping @colesbury @mpage

  3. StanFromIreland
    added
    interpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)
    topic-free-threading
    on Sep 21, 2026
  4. corona10 commented on Sep 22, 2026

    @corona10
    corona10
    on Sep 22, 2026
    Member
    More actions

    I don't see any harm in reverting the original PR.
    Could you open the revert PR?
    cc @hugovk

    Reacted by Daniele Parmeggiani
  5. corona10 commented on Sep 22, 2026

    @corona10
    corona10
    on Sep 22, 2026
    Member
    More actions

    IIUC, the related issue only occurs during an unnecessary operation, so reverting the original PR should not reintroduce any significant issue.

  6. corona10
    added
    3.15bugs and security fixesbugs and security fixes
    on Sep 22, 2026
  7. dpdani commented on Sep 22, 2026

    @dpdani
    dpdani
    on Sep 22, 2026
    ContributorAuthor
    More actions

    Going to open it tomorrow. Quick question in the meantime, since the news entry got merged in here, my guess is I should delete it from there, correct?

    Reacted by Donghee Na
  8. corona10 commented on Sep 22, 2026

    @corona10
    corona10
    on Sep 22, 2026
    Member
    More actions

    Going to open it tomorrow. Quick question in the meantime, since the news entry got merged in here, my guess is I should delete it from there, correct?

    I think yes, but @hugovk can reply more correctly than me!

    Reacted by Daniele Parmeggiani
  9. hugovk commented on Sep 24, 2026

    @hugovk
    hugovk
    on Sep 24, 2026
    Member
    More actions

    The devguide says:

    Most changes made to the codebase deserve an entry in Misc/NEWS.d, except for the following:

    • ...
    • changes that already have a NEWS entry
    • reverts that have not yet been included in any formal release (including alpha and beta releases)

    For the last two, note the following:

    1. If a change is reverted prior to release, then the corresponding entry is simply removed. Otherwise, a new entry must be added noting that the change has been reverted (for example, when a feature is released in an alpha and then cut prior to the first beta).

    2. If a change is a fix (or other adjustment) to an earlier unreleased change and the original NEWS entry remains valid, then no additional entry is needed.

    #146064 was released in v3.15.0a8, so leave the existing NEWS entry, and add a new one for this revert.

    Reacted by Daniele Parmeggiani and Donghee Na
  10. corona10
    added a commit that references this issue on Sep 27, 2026

    gh-157914: Revert "gh-135871: Reload lock internal state while spinni…

    baec764
  11. hugovk
    added a commit that references this issue on Sep 30, 2026

    [3.15] gh-157914: Revert "gh-135871: Reload lock internal state while…

    55ab336
  12. hugovk
    closed this as completedon Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.15bugs and security fixesbugs and security fixesinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)topic-free-threadingtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.