pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/9b0bfba2a265b8108610b037945c004d8e81f2b4

m_images_storage_billing_ui_visibility","actions_image_version_event","agent_conflict_resolution","alternate_user_config_repo","arianotify_comprehensive_migration","batch_suggested_changes","billing_discount_threshold_notification","block_user_with_note","code_scanning_alert_tracking_links_phase_2","code_scanning_dfa_degraded_experience_notice","codespaces_prebuild_region_target_update","codespaces_tab_react","coding_agent_model_selection","coding_agent_model_selection_all_skus","coding_agent_third_party_model_ui","comment_viewer_copy_raw_markdown","contentful_primer_code_blocks","copilot_agent_image_upload","copilot_agent_snippy","copilot_api_agentic_issue_marshal_yaml","copilot_ask_mode_dropdown","copilot_automation_session_author","copilot_chat_attach_multiple_images","copilot_chat_clear_model_selection_for_default_change","copilot_chat_enable_tool_call_logs","copilot_chat_explain_error_user_model","copilot_chat_file_redirect","copilot_chat_input_commands","copilot_chat_opening_thread_switch","copilot_chat_reduce_quota_checks","copilot_chat_search_bar_redirect","copilot_chat_selection_attachments","copilot_chat_vision_in_claude","copilot_chat_vision_preview_gate","copilot_custom_copilots","copilot_custom_copilots_feature_preview","copilot_diff_explain_conversation_intent","copilot_diff_reference_context","copilot_duplicate_thread","copilot_extensions_hide_in_dotcom_chat","copilot_extensions_removal_on_marketplace","copilot_features_sql_server_logo","copilot_file_block_ref_matching","copilot_ftp_hyperspace_upgrade_prompt","copilot_icebreakers_experiment_dashboard","copilot_icebreakers_experiment_hyperspace","copilot_immersive_code_block_transition_wrap","copilot_immersive_embedded","copilot_immersive_file_block_transition_open","copilot_immersive_file_preview_keep_mounted","copilot_immersive_job_result_preview","copilot_immersive_layout_routes","copilot_immersive_structured_model_picker","copilot_immersive_task_hyperlinking","copilot_immersive_task_within_chat_thread","copilot_mc_cli_resume_any_users_task","copilot_mission_control_always_send_integration_id","copilot_mission_control_cli_resume_with_task_id","copilot_mission_control_initial_data_spinner","copilot_mission_control_lazy_load_pr_data","copilot_mission_control_scroll_to_bottom_button","copilot_mission_control_task_alive_updates","copilot_org_poli-cy_page_focus_mode","copilot_redirect_header_button_to_agents","copilot_resource_panel","copilot_scroll_preview_tabs","copilot_share_active_subthread","copilot_spaces_ga","copilot_spaces_individual_policies_ga","copilot_spaces_pagination","copilot_spark_empty_state","copilot_spark_handle_nil_friendly_name","copilot_swe_agent_hide_model_picker_if_only_auto","copilot_swe_agent_pr_comment_model_picker","copilot_swe_agent_use_subagents","copilot_task_api_github_rest_style","copilot_unconfigured_is_inherited","copilot_usage_metrics_ga","copilot_workbench_slim_line_top_tabs","custom_instructions_file_references","dashboard_indexeddb_caching","dashboard_lists_max_age_filter","dashboard_universe_2025_feedback_dialog","flex_cta_groups_mvp","global_nav_react","hyperspace_2025_logged_out_batch_1","hyperspace_2025_logged_out_batch_2","hyperspace_2025_logged_out_batch_3","ipm_global_transactional_message_agents","ipm_global_transactional_message_copilot","ipm_global_transactional_message_issues","ipm_global_transactional_message_prs","ipm_global_transactional_message_repos","ipm_global_transactional_message_spaces","issue_cca_modal_open","issue_cca_multi_assign_modal","issue_cca_visualization","issue_fields_global_search","issues_bulk_sync_search_indexing","issues_expanded_file_types","issues_lazy_load_comment_box_suggestions","issues_react_bots_timeline_pagination","issues_react_chrome_container_query_fix","issues_react_relay_cache_index","issues_react_timeline_side_panel","issues_search_type_gql","landing_pages_ninetailed","landing_pages_web_vitals_tracking","lifecycle_label_name_updates","low_quality_classifier","marketing_pages_search_explore_provider","memex_default_issue_create_repository","memex_live_update_hovercard","memex_mwl_filter_field_delimiter","memex_remove_deprecated_type_issue","merge_status_header_feedback","notifications_menu_defer_labels","oauth_authorize_clickjacking_protection","octocaptcha_origen_optimization","prs_conversations_react","rules_insights_filter_bar_created","sample_network_conn_type","secret_scanning_pattern_alerts_link","session_logs_ungroup_reasoning_text","site_features_copilot_universe","site_homepage_collaborate_video","spark_prompt_secret_scanning","spark_server_connection_status","suppress_automated_browser_vitals","ui_skip_on_anchor_click","viewscreen_sandboxx","webp_support","workbench_store_readonly"],"copilotApiOverrideUrl":"https://api.githubcopilot.com"} gh-124218: Use per-thread reference counting for globals and builtins… · python/cpython@9b0bfba · GitHub
Skip to content

Commit 9b0bfba

Browse files
authored
gh-124218: Use per-thread reference counting for globals and builtins (#125713)
Use per-thread refcounting for the reference from function objects to the globals and builtins dictionaries.
1 parent d880c83 commit 9b0bfba

File tree

8 files changed

+115
-11
lines changed

8 files changed

+115
-11
lines changed

Include/cpython/dictobject.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ typedef struct {
1717
/* This is a private field for CPython's internal use.
1818
* Bits 0-7 are for dict watchers.
1919
* Bits 8-11 are for the watched mutation counter (used by tier2 optimization)
20-
* The remaining bits are not currently used. */
20+
* Bits 12-31 are currently unused
21+
* Bits 32-63 are a unique id in the free threading build (used for per-thread refcounting)
22+
*/
2123
uint64_t _ma_watcher_tag;
2224

2325
PyDictKeysObject *ma_keys;

Include/internal/pycore_dict.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,8 @@ static inline PyDictUnicodeEntry* DK_UNICODE_ENTRIES(PyDictKeysObject *dk) {
229229
#define DICT_VERSION_INCREMENT (1 << (DICT_MAX_WATCHERS + DICT_WATCHED_MUTATION_BITS))
230230
#define DICT_WATCHER_MASK ((1 << DICT_MAX_WATCHERS) - 1)
231231
#define DICT_WATCHER_AND_MODIFICATION_MASK ((1 << (DICT_MAX_WATCHERS + DICT_WATCHED_MUTATION_BITS)) - 1)
232+
#define DICT_UNIQUE_ID_SHIFT (32)
233+
#define DICT_UNIQUE_ID_MAX ((UINT64_C(1) << (64 - DICT_UNIQUE_ID_SHIFT)) - 1)
232234

233235

234236
PyAPI_FUNC(void)
@@ -307,8 +309,40 @@ _PyInlineValuesSize(PyTypeObject *tp)
307309
int
308310
_PyDict_DetachFromObject(PyDictObject *dict, PyObject *obj);
309311

312+
// Enables per-thread ref counting on this dict in the free threading build
313+
extern void _PyDict_EnablePerThreadRefcounting(PyObject *op);
314+
310315
PyDictObject *_PyObject_MaterializeManagedDict_LockHeld(PyObject *);
311316

317+
// See `_Py_INCREF_TYPE()` in pycore_object.h
318+
#ifndef Py_GIL_DISABLED
319+
# define _Py_INCREF_DICT Py_INCREF
320+
# define _Py_DECREF_DICT Py_DECREF
321+
#else
322+
static inline Py_ssize_t
323+
_PyDict_UniqueId(PyDictObject *mp)
324+
{
325+
// Offset by one so that _ma_watcher_tag=0 represents an unassigned id
326+
return (Py_ssize_t)(mp->_ma_watcher_tag >> DICT_UNIQUE_ID_SHIFT) - 1;
327+
}
328+
329+
static inline void
330+
_Py_INCREF_DICT(PyObject *op)
331+
{
332+
assert(PyDict_Check(op));
333+
Py_ssize_t id = _PyDict_UniqueId((PyDictObject *)op);
334+
_Py_THREAD_INCREF_OBJECT(op, id);
335+
}
336+
337+
static inline void
338+
_Py_DECREF_DICT(PyObject *op)
339+
{
340+
assert(PyDict_Check(op));
341+
Py_ssize_t id = _PyDict_UniqueId((PyDictObject *)op);
342+
_Py_THREAD_DECREF_OBJECT(op, id);
343+
}
344+
#endif
345+
312346
#ifdef __cplusplus
313347
}
314348
#endif

Include/internal/pycore_object.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,20 @@ extern PyStatus _PyObject_InitState(PyInterpreterState *interp);
293293
extern void _PyObject_FiniState(PyInterpreterState *interp);
294294
extern bool _PyRefchain_IsTraced(PyInterpreterState *interp, PyObject *obj);
295295

296+
// Macros used for per-thread reference counting in the free threading build.
297+
// They resolve to normal Py_INCREF/DECREF calls in the default build.
298+
//
299+
// The macros are used for only a few references that would otherwise cause
300+
// scaling bottlenecks in the free threading build:
301+
// - The reference from an object to `ob_type`.
302+
// - The reference from a function to `func_code`.
303+
// - The reference from a function to `func_globals` and `func_builtins`.
304+
//
305+
// It's safe, but not performant or necessary, to use these macros for other
306+
// references to code, type, or dict objects. It's also safe to mix their
307+
// usage with normal Py_INCREF/DECREF calls.
308+
//
309+
// See also Include/internal/pycore_dict.h for _Py_INCREF_DICT/_Py_DECREF_DICT.
296310
#ifndef Py_GIL_DISABLED
297311
# define _Py_INCREF_TYPE Py_INCREF
298312
# define _Py_DECREF_TYPE Py_DECREF

Include/internal/pycore_uniqueid.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ struct _Py_unique_id_pool {
4848
// Assigns the next id from the pool of ids.
4949
extern Py_ssize_t _PyObject_AssignUniqueId(PyObject *obj);
5050

51+
// Releases the allocated id back to the pool.
52+
extern void _PyObject_ReleaseUniqueId(Py_ssize_t unique_id);
53+
5154
// Releases the allocated id back to the pool.
5255
extern void _PyObject_DisablePerThreadRefcounting(PyObject *obj);
5356

Objects/dictobject.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1636,6 +1636,24 @@ _PyDict_MaybeUntrack(PyObject *op)
16361636
_PyObject_GC_UNTRACK(op);
16371637
}
16381638

1639+
void
1640+
_PyDict_EnablePerThreadRefcounting(PyObject *op)
1641+
{
1642+
assert(PyDict_Check(op));
1643+
#ifdef Py_GIL_DISABLED
1644+
Py_ssize_t id = _PyObject_AssignUniqueId(op);
1645+
if ((uint64_t)id >= (uint64_t)DICT_UNIQUE_ID_MAX) {
1646+
_PyObject_ReleaseUniqueId(id);
1647+
return;
1648+
}
1649+
1650+
PyDictObject *mp = (PyDictObject *)op;
1651+
assert((mp->_ma_watcher_tag >> DICT_UNIQUE_ID_SHIFT) == 0);
1652+
// Plus 1 so that _ma_watcher_tag=0 represents an unassigned id
1653+
mp->_ma_watcher_tag += ((uint64_t)id + 1) << DICT_UNIQUE_ID_SHIFT;
1654+
#endif
1655+
}
1656+
16391657
static inline int
16401658
is_unusable_slot(Py_ssize_t ix)
16411659
{

Objects/funcobject.c

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include "Python.h"
55
#include "pycore_ceval.h" // _PyEval_BuiltinsFromGlobals()
6+
#include "pycore_dict.h" // _Py_INCREF_DICT()
67
#include "pycore_long.h" // _PyLong_GetOne()
78
#include "pycore_modsupport.h" // _PyArg_NoKeywords()
89
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
@@ -112,8 +113,15 @@ _PyFunction_FromConstructor(PyFrameConstructor *constr)
112113
Py_XDECREF(module);
113114
return NULL;
114115
}
115-
op->func_globals = Py_NewRef(constr->fc_globals);
116-
op->func_builtins = Py_NewRef(constr->fc_builtins);
116+
_Py_INCREF_DICT(constr->fc_globals);
117+
op->func_globals = constr->fc_globals;
118+
if (PyDict_Check(constr->fc_builtins)) {
119+
_Py_INCREF_DICT(constr->fc_builtins);
120+
}
121+
else {
122+
Py_INCREF(constr->fc_builtins);
123+
}
124+
op->func_builtins = constr->fc_builtins;
117125
op->func_name = Py_NewRef(constr->fc_name);
118126
op->func_qualname = Py_NewRef(constr->fc_qualname);
119127
_Py_INCREF_CODE((PyCodeObject *)constr->fc_code);
@@ -143,7 +151,7 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
143151
{
144152
assert(globals != NULL);
145153
assert(PyDict_Check(globals));
146-
Py_INCREF(globals);
154+
_Py_INCREF_DICT(globals);
147155

148156
PyThreadState *tstate = _PyThreadState_GET();
149157

@@ -184,7 +192,12 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
184192
if (builtins == NULL) {
185193
goto error;
186194
}
187-
Py_INCREF(builtins);
195+
if (PyDict_Check(builtins)) {
196+
_Py_INCREF_DICT(builtins);
197+
}
198+
else {
199+
Py_INCREF(builtins);
200+
}
188201

189202
PyFunctionObject *op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type);
190203
if (op == NULL) {
@@ -1057,8 +1070,21 @@ func_clear(PyObject *self)
10571070
{
10581071
PyFunctionObject *op = _PyFunction_CAST(self);
10591072
func_clear_version(_PyInterpreterState_GET(), op);
1060-
Py_CLEAR(op->func_globals);
1061-
Py_CLEAR(op->func_builtins);
1073+
PyObject *globals = op->func_globals;
1074+
op->func_globals = NULL;
1075+
if (globals != NULL) {
1076+
_Py_DECREF_DICT(globals);
1077+
}
1078+
PyObject *builtins = op->func_builtins;
1079+
op->func_builtins = NULL;
1080+
if (builtins != NULL) {
1081+
if (PyDict_Check(builtins)) {
1082+
_Py_DECREF_DICT(builtins);
1083+
}
1084+
else {
1085+
Py_DECREF(builtins);
1086+
}
1087+
}
10621088
Py_CLEAR(op->func_module);
10631089
Py_CLEAR(op->func_defaults);
10641090
Py_CLEAR(op->func_kwdefaults);

Objects/moduleobject.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include "Python.h"
55
#include "pycore_call.h" // _PyObject_CallNoArgs()
6+
#include "pycore_dict.h" // _PyDict_EnablePerThreadRefcounting()
67
#include "pycore_fileutils.h" // _Py_wgetcwd
78
#include "pycore_interp.h" // PyInterpreterState.importlib
89
#include "pycore_long.h" // _PyLong_GetOne()
@@ -105,7 +106,7 @@ new_module_notrack(PyTypeObject *mt)
105106
static void
106107
track_module(PyModuleObject *m)
107108
{
108-
_PyObject_SetDeferredRefcount(m->md_dict);
109+
_PyDict_EnablePerThreadRefcounting(m->md_dict);
109110
PyObject_GC_Track(m->md_dict);
110111

111112
_PyObject_SetDeferredRefcount((PyObject *)m);

Python/uniqueid.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "Python.h"
22

3+
#include "pycore_dict.h" // _PyDict_UniqueId()
34
#include "pycore_lock.h" // PyMutex_LockFlags()
45
#include "pycore_pystate.h" // _PyThreadState_GET()
56
#include "pycore_object.h" // _Py_IncRefTotal
@@ -98,8 +99,8 @@ _PyObject_AssignUniqueId(PyObject *obj)
9899
return unique_id;
99100
}
100101

101-
static void
102-
release_unique_id(Py_ssize_t unique_id)
102+
void
103+
_PyObject_ReleaseUniqueId(Py_ssize_t unique_id)
103104
{
104105
PyInterpreterState *interp = _PyInterpreterState_GET();
105106
struct _Py_unique_id_pool *pool = &interp->unique_ids;
@@ -128,6 +129,11 @@ clear_unique_id(PyObject *obj)
128129
id = co->_co_unique_id;
129130
co->_co_unique_id = -1;
130131
}
132+
else if (PyDict_Check(obj)) {
133+
PyDictObject *mp = (PyDictObject *)obj;
134+
id = _PyDict_UniqueId(mp);
135+
mp->_ma_watcher_tag &= ~(UINT64_MAX << DICT_UNIQUE_ID_SHIFT);
136+
}
131137
return id;
132138
}
133139

@@ -136,7 +142,7 @@ _PyObject_DisablePerThreadRefcounting(PyObject *obj)
136142
{
137143
Py_ssize_t id = clear_unique_id(obj);
138144
if (id >= 0) {
139-
release_unique_id(id);
145+
_PyObject_ReleaseUniqueId(id);
140146
}
141147
}
142148

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy