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 — gh-132657: Add maybe_enable_deferred_ref_count(). by nascheme · Pull Request #142843 · python/cpython

Conversation

@nascheme

@nascheme nascheme commented Dec 16, 2025 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

If we are specializing to LOAD_GLOBAL_MODULE or LOAD_ATTR_MODULE, try to enable deferred reference counting for the value, if the object is owned by a different thread.

Add a benchmark to ftscalingbench.py that shows the benefit of this change. The deepcopy benchmark uses globals like _local_atomic_types.

  • Issue: copy.copy and copy.deepcopy scale poorly with free-threading #132657

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

eendebakpt reacted with eyes emoji
All reactions
  • 👀 1 reaction
@nascheme
Add maybe_enable_deferred_ref_count().
8fae6ec
If we are specializing to LOAD_GLOBAL_MODULE, set deferred reference
counting for the value, if it meets criteria. For now, it's only done
for frozenset objects.
@bedevere-app bedevere-app Bot mentioned this pull request Dec 16, 2025
copy.copy and copy.deepcopy scale poorly with free-threading #132657
Closed
@nascheme
Improve maybe_enable_deferred_ref_count() function.
2372f96
Use it for LOAD_ATTR_MODULE in addition to LOAD_GLOBAL_MODULE.  Don't
enable deferred ref counts if the object is owned by the current thread.
Specialized bytecode is per-thread so this works.  Enable for
frozensets, tuples and type objects.
@colesbury
colesbury requested review from Yhg1s, colesbury and mpage December 17, 2025 21:23
colesbury
colesbury reviewed Dec 17, 2025
View reviewed changes

@colesbury colesbury left a comment

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this makes sense and is worth doing. Let's also add a scaling test to ftscalingbench.py

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Python/specialize.c Outdated
Comment on lines +362 to +365
PyObject *op;
if (PyDict_GetItemRef(dict, name, &op) != 1) {
return;
}

@colesbury colesbury Dec 17, 2025

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's combine this lookup with the earlier _PyDict_LookupIndex. _PyDict_LookupIndex already gets the value internally and throws it away.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Python/specialize.c Outdated
Comment on lines +366 to +371
if (_Py_IsOwnedByCurrentThread(op) ||
!PyType_IS_GC(Py_TYPE(op)) ||
_PyObject_HasDeferredRefcount(op)) {
Py_DECREF(op);
return;
}

@colesbury colesbury Dec 17, 2025

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The !PyType_IS_GC and _PyObject_HasDeferredRefcount checks aren't really necessary because PyUnstable_Object_EnableDeferredRefcount handles those cases internally.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Python/specialize.c Outdated
Comment on lines +372 to +374
if (PyFrozenSet_Check(op) || PyTuple_Check(op) || PyType_Check(op)) {
PyUnstable_Object_EnableDeferredRefcount(op);
}

@colesbury colesbury Dec 17, 2025

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should do this for all objects, not just a few types. Otherwise, I think we will keep run into scaling bottlenecks with global variables.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

eendebakpt reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

@Yhg1s Yhg1s Dec 17, 2025

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm... That's going to push a lot of objects to the GC for deallocation in programs that do a lot of work at the global level (but do have a few functions that are called enough that use those globals.) I'm thinking of naively written "scripts". Then again, it would only do that for objects that participate in GC (i.e. not strings or integers), so it's probably fine. I mean, nobody's too surprised when things aren't deallocated quite as quickly as they expect... right?

Yeah, I think this should be fine for main. I definitely wouldn't backport it to 3.14.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@colesbury colesbury Dec 17, 2025 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely agree regarding not backporting to 3.14.

I think the above check that excludes owned by the current thread should help with most scripts -- it'll only affect global variables accessed by multiple threads.

Another thing that may help is that I think the PyUnstable_Object_EnableDeferredRefcount call only happens at specialization time, not every time a variable is updated or accessed.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@nascheme nascheme Dec 18, 2025

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm a bit worried about increased memory usage. Since it's only for objects shared between threads and only for hot LOAD_GLOBAL/LOAD_ATTR instructions, maybe it's okay. Perhaps we should consider changing gc_should_collect() to check not only the young object count but also the process memory size increase as well. The condition with count < gcstate->long_lived_total / 4 could be done with an "or" condition with the process size check.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
nascheme added 7 commits December 17, 2025 16:45
@nascheme
Enable deferred counting for more types.
03977d9
Also, avoid PyDict_GetItemRef() call by returning the value when the
index is looked up (as previously discarded).
@nascheme
Add deepcopy benchmark to ftscalingbench.
5ef77af
This is taken from the PR pythonGH-132658.
@nascheme
Add comment for maybe_enable_deferred_ref_count().
04826f7
@nascheme
Merge remote-tracking branch 'origin/main' into specialize-enable-def…
1c59358
…erred
@nascheme
Add NEWS.
25192bd
@nascheme
Merge remote-tracking branch 'origin/main' into specialize-enable-def…
7888d1d
…erred
@nascheme
Don't use :opcode: markup.
9b9d01a
@nascheme
nascheme marked this pull request as ready for review December 29, 2025 16:58
@nascheme
nascheme requested review from markshannon and methane as code owners December 29, 2025 16:58
@bedevere-app bedevere-app Bot added the awaiting core review label Dec 29, 2025
@kumaraditya303 kumaraditya303 added performance Performance or resource usage topic-free-threading labels Jan 12, 2026
@kumaraditya303

kumaraditya303 commented Jan 12, 2026

Copy link
Copy Markdown
Contributor

@nascheme There are merge conflicts.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@nascheme
Merge 'origin/main' into specialize-enable-deferred
b0abdbd
kumaraditya303
kumaraditya303 approved these changes Jan 13, 2026
View reviewed changes
@bedevere-app bedevere-app Bot added awaiting merge and removed awaiting core review labels Jan 13, 2026
@nascheme
Merge branch 'main' into specialize-enable-deferred
36d0710
@nascheme
nascheme enabled auto-merge (squash) January 17, 2026 01:40
@nascheme
nascheme merged commit bb25f72 into python:main Jan 17, 2026
47 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-app bedevere-app Bot removed the awaiting merge label Jan 17, 2026
@bedevere-bot

bedevere-bot commented Jan 17, 2026

Copy link
Copy Markdown

⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️

Hi! The buildbot ARM64 macOS 3.x (tier-2) has failed when building commit bb25f72.

What do you need to do:

  1. Don't panic.
  2. Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/#/builders/725/builds/12575) and take a look at the build logs.
  4. Check if the failure is related to this commit (bb25f72) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

You can take a look at the buildbot page here:

https://buildbot.python.org/#/builders/725/builds/12575

Failed tests:

  • test_urllib2net

Summary of the results of the build (if available):

==

Click to see traceback logs
remote: Enumerating objects: 17, done.        
remote: Counting objects:   6% (1/16)        
remote: Counting objects:  12% (2/16)        
remote: Counting objects:  18% (3/16)        
remote: Counting objects:  25% (4/16)        
remote: Counting objects:  31% (5/16)        
remote: Counting objects:  37% (6/16)        
remote: Counting objects:  43% (7/16)        
remote: Counting objects:  50% (8/16)        
remote: Counting objects:  56% (9/16)        
remote: Counting objects:  62% (10/16)        
remote: Counting objects:  68% (11/16)        
remote: Counting objects:  75% (12/16)        
remote: Counting objects:  81% (13/16)        
remote: Counting objects:  87% (14/16)        
remote: Counting objects:  93% (15/16)        
remote: Counting objects: 100% (16/16)        
remote: Counting objects: 100% (16/16), done.        
remote: Compressing objects:  33% (1/3)        
remote: Compressing objects:  66% (2/3)        
remote: Compressing objects: 100% (3/3)        
remote: Compressing objects: 100% (3/3), done.        
remote: Total 17 (delta 13), reused 13 (delta 13), pack-reused 1 (from 1)        
From https://github.com/python/cpython
 * branch                    main       -> FETCH_HEAD
Note: switching to 'bb25f7280af30831fffa3345b4fc93798949c6c6'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at bb25f7280af gh-132657: Add maybe_enable_deferred_ref_count() (gh-142843)
Switched to and reset branch 'main'

make: *** [buildbottest] Error 2
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@kumaraditya303 kumaraditya303 mentioned this pull request Jan 19, 2026
threading primitives are subject to reference count contention #134761
Closed
@nascheme nascheme mentioned this pull request Jan 19, 2026
Leak with untracked tuples and deferred ref counts #144054
Closed
@eendebakpt eendebakpt mentioned this pull request Jan 20, 2026
gh-132657: Add free-threading scaling benchmark for copy.copy and copy.deepcopy #132658
Closed
@ZeroIntensity ZeroIntensity mentioned this pull request Jan 21, 2026
gh-134761: Use deferred reference counting for threading concurrency primitives #134762
Closed
thunder-coding pushed a commit to thunder-coding/cpython that referenced this pull request Feb 15, 2026
@nascheme @thunder-coding
pythongh-132657: Add maybe_enable_deferred_ref_count() (pythongh-142843)
5a55f82
If we are specializing to `LOAD_GLOBAL_MODULE` or `LOAD_ATTR_MODULE`, try
to enable deferred reference counting for the value, if the object is owned by
a different thread.  This applies to the free-threaded build only and should
improve scaling of multi-threaded programs.
@benediktjohannes benediktjohannes mentioned this pull request Feb 16, 2026
gh-132657: Improve performance in specialize_dict_access_hint() via adding specialization for PY_GIL_DISABLED #144886
Open
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@colesbury colesbury colesbury left review comments
@kumaraditya303 kumaraditya303 kumaraditya303 approved these changes
@mpage mpage Awaiting requested review from mpage
@Yhg1s Yhg1s Awaiting requested review from Yhg1s
@methane methane Awaiting requested review from methane methane is a code owner
@markshannon markshannon Awaiting requested review from markshannon markshannon is a code owner

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

performance Performance or resource usage topic-free-threading

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

5 participants

@nascheme @kumaraditya303 @bedevere-bot @colesbury @Yhg1s
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.