Skip to content

use stackrefs in _PyObject_GetMethod and calling APIs #134043

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
kumaraditya303 opened this issue May 15, 2025 · 0 comments
Open

use stackrefs in _PyObject_GetMethod and calling APIs #134043

kumaraditya303 opened this issue May 15, 2025 · 0 comments
Assignees
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs) performance Performance or resource usage topic-free-threading type-feature A feature request or enhancement

Comments

@kumaraditya303
Copy link
Contributor

kumaraditya303 commented May 15, 2025

Feature or enhancement

Currently the calling APIs such as PyObject_VectorcallMethod use _PyObject_GetMethod to avoid creating a bound method object, however _PyObject_GetMethod increfs and decrefs the object even when the underlying object supports deferred reference counting. This leads to reference counting contention on the object and it doesn't scale well in free threading. This API is heavily used by modules such as asyncio so it is important that _PyObject_GetMethod should scale well with threads.

cpython/Objects/call.c

Lines 829 to 859 in 54a6875

PyObject_VectorcallMethod(PyObject *name, PyObject *const *args,
size_t nargsf, PyObject *kwnames)
{
assert(name != NULL);
assert(args != NULL);
assert(PyVectorcall_NARGS(nargsf) >= 1);
PyThreadState *tstate = _PyThreadState_GET();
PyObject *callable = NULL;
/* Use args[0] as "self" argument */
int unbound = _PyObject_GetMethod(args[0], name, &callable);
if (callable == NULL) {
return NULL;
}
if (unbound) {
/* We must remove PY_VECTORCALL_ARGUMENTS_OFFSET since
* that would be interpreted as allowing to change args[-1] */
nargsf &= ~PY_VECTORCALL_ARGUMENTS_OFFSET;
}
else {
/* Skip "self". We can keep PY_VECTORCALL_ARGUMENTS_OFFSET since
* args[-1] in the onward call is args[0] here. */
args++;
nargsf--;
}
EVAL_CALL_STAT_INC_IF_FUNCTION(EVAL_CALL_METHOD, callable);
PyObject *result = _PyObject_VectorcallTstate(tstate, callable,
args, nargsf, kwnames);
Py_DECREF(callable);
return result;

Proposal:

To take advantage of deferred reference counting, we should add stack ref variant of _PyObject_GetMethod and use it in all calling APIs to avoid reference counting contention on the function object.

Implementation:

  • The new stack ref variant _PyObject_GetMethodStackRef will use _PyType_LookupStackRefAndVersion when looking up method from type cache.
  • A new stack ref variant of _PyObject_TryGetInstanceAttributeStackRef will be added to to be used with _PyObject_GetMethodStackRef.
  • Possibly we can avoid incref and decref when looking up the attribute on instance dict by delaying freeing of object's dictionary and using _Py_dict_lookup_threadsafe_stackref.

    cpython/Objects/object.c

    Lines 1632 to 1640 in 54a6875

    Py_INCREF(dict);
    if (PyDict_GetItemRef(dict, name, method) != 0) {
    // found or error
    Py_DECREF(dict);
    Py_XDECREF(descr);
    return 0;
    }
    // not found
    Py_DECREF(dict);

Linked PRs

@kumaraditya303 kumaraditya303 self-assigned this May 15, 2025
@kumaraditya303 kumaraditya303 added type-feature A feature request or enhancement interpreter-core (Objects, Python, Grammar, and Parser dirs) topic-free-threading 3.15 new features, bugs and security fixes labels May 15, 2025
@picnixz picnixz removed the 3.15 new features, bugs and security fixes label May 15, 2025
@kumaraditya303 kumaraditya303 added the performance Performance or resource usage label May 16, 2025
kumaraditya303 added a commit that referenced this issue May 27, 2025
Adds `_PyObject_GetMethodStackRef` which uses stackrefs and takes advantage of deferred reference counting in free-threading while calling method objects in vectorcall.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs) performance Performance or resource usage topic-free-threading type-feature A feature request or enhancement
Projects
None yet
Development

No branches or pull requests

2 participants