Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

gh-139103: Improve namedtuple scaling in free-threaded build#144332

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
colesbury wants to merge4 commits intopython:main
base:main
Choose a base branch
Loading
fromcolesbury:gh-139103-named-tuple
Open
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletionsInclude/internal/pycore_function.h
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,6 +47,12 @@ static inline PyObject* _PyFunction_GET_BUILTINS(PyObject *func) {
#define _PyFunction_GET_BUILTINS(func) _PyFunction_GET_BUILTINS(_PyObject_CAST(func))


/* Get the callable wrapped by a staticmethod.
Returns a borrowed reference, or NULL if uninitialized.
The caller must ensure 'sm' is a staticmethod object. */
extern PyObject *_PyStaticMethod_GetFunc(PyObject *sm);


#ifdef __cplusplus
}
#endif
Expand Down
4 changes: 4 additions & 0 deletionsInclude/internal/pycore_object.h
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -898,6 +898,10 @@ _PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef
PyAPI_FUNC(int) _PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj,
PyObject *name, _PyStackRef *method);

// Like PyObject_GetAttr but returns a _PyStackRef. For types, this can
// return a deferred reference to reduce reference count contention.
PyAPI_FUNC(_PyStackRef) _PyObject_GetAttrStackRef(PyObject *obj, PyObject *name);

// Cache the provided init method in the specialization cache of type if the
// provided type version matches the current version of the type.
//
Expand Down
3 changes: 3 additions & 0 deletionsInclude/internal/pycore_typeobject.h
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,7 @@ extern "C" {

#include "pycore_interp_structs.h" // managed_static_type_state
#include "pycore_moduleobject.h" // PyModuleObject
#include "pycore_structs.h" // _PyStackRef


/* state */
Expand DownExpand Up@@ -112,6 +113,8 @@ _PyType_IsReady(PyTypeObject *type)
extern PyObject* _Py_type_getattro_impl(PyTypeObject *type, PyObject *name,
int *suppress_missing_attribute);
extern PyObject* _Py_type_getattro(PyObject *type, PyObject *name);
extern _PyStackRef _Py_type_getattro_stackref(PyTypeObject *type, PyObject *name,
int *suppress_missing_attribute);

extern PyObject* _Py_BaseObject_RichCompare(PyObject* self, PyObject* other, int op);

Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
Improve scaling of:func:`~collections.namedtuple` instantiation in the
free-threaded build.
10 changes: 5 additions & 5 deletionsModules/_testinternalcapi/test_cases.c.h
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

9 changes: 9 additions & 0 deletionsObjects/funcobject.c
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@
#include "pycore_long.h" // _PyLong_GetOne()
#include "pycore_modsupport.h" // _PyArg_NoKeywords()
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
#include "pycore_object_deferred.h" // _PyObject_SetDeferredRefcount()
#include "pycore_pyerrors.h" // _PyErr_Occurred()
#include "pycore_setobject.h" // _PySet_NextEntry()
#include "pycore_stats.h"
Expand DownExpand Up@@ -1868,7 +1869,15 @@ PyStaticMethod_New(PyObject *callable)
staticmethod *sm = (staticmethod *)
PyType_GenericAlloc(&PyStaticMethod_Type, 0);
if (sm != NULL) {
_PyObject_SetDeferredRefcount((PyObject *)sm);
Copy link
Member

Choose a reason for hiding this comment

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

Should we also call it in sm_init()? If not, should we move this _PyObject_SetDeferredRefcount() call to typeobject.c after the two PyStaticMethod_New() calls?

sm->sm_callable = Py_NewRef(callable);
}
return (PyObject *)sm;
}

PyObject *
_PyStaticMethod_GetFunc(PyObject *self)
{
staticmethod *sm = _PyStaticMethod_CAST(self);
return sm->sm_callable;
}
49 changes: 49 additions & 0 deletionsObjects/object.c
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,6 +31,7 @@
#include "pycore_tuple.h" // _PyTuple_DebugMallocStats()
#include "pycore_typeobject.h" // _PyBufferWrapper_Type
#include "pycore_typevarobject.h" // _PyTypeAlias_Type
#include "pycore_stackref.h" // PyStackRef_FromPyObjectSteal
#include "pycore_unionobject.h" // _PyUnion_Type


Expand DownExpand Up@@ -1334,6 +1335,54 @@ PyObject_GetAttr(PyObject *v, PyObject *name)
return result;
}

/* Like PyObject_GetAttr but returns a _PyStackRef.
For types (tp_getattro == _Py_type_getattro), this can return
a deferred reference to reduce reference count contention. */
_PyStackRef
_PyObject_GetAttrStackRef(PyObject *v, PyObject *name)
{
PyTypeObject *tp = Py_TYPE(v);
if (!PyUnicode_Check(name)) {
PyErr_Format(PyExc_TypeError,
"attribute name must be string, not '%.200s'",
Py_TYPE(name)->tp_name);
return PyStackRef_NULL;
}

/* Fast path for types - can return deferred references */
if (tp->tp_getattro == _Py_type_getattro) {
_PyStackRef result = _Py_type_getattro_stackref((PyTypeObject *)v, name, NULL);
if (PyStackRef_IsNull(result)) {
_PyObject_SetAttributeErrorContext(v, name);
}
return result;
}

/* Fall back to regular PyObject_GetAttr and convert to stackref */
PyObject *result = NULL;
if (tp->tp_getattro != NULL) {
result = (*tp->tp_getattro)(v, name);
}
else if (tp->tp_getattr != NULL) {
const char *name_str = PyUnicode_AsUTF8(name);
if (name_str == NULL) {
return PyStackRef_NULL;
}
result = (*tp->tp_getattr)(v, (char *)name_str);
}
else {
PyErr_Format(PyExc_AttributeError,
"'%.100s' object has no attribute '%U'",
tp->tp_name, name);
}

if (result == NULL) {
_PyObject_SetAttributeErrorContext(v, name);
return PyStackRef_NULL;
}
return PyStackRef_FromPyObjectSteal(result);
}

int
PyObject_GetOptionalAttr(PyObject *v, PyObject *name, PyObject **result)
{
Expand Down
151 changes: 103 additions & 48 deletionsObjects/typeobject.c
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -6375,102 +6375,153 @@ _PyType_SetFlagsRecursive(PyTypeObject *self, unsigned long mask, unsigned long

*/
PyObject *
_Py_type_getattro_impl(PyTypeObject *type, PyObject *name, int * suppress_missing_attribute)
_Py_type_getattro_impl(PyTypeObject *type, PyObject *name, int *suppress_missing_attribute)
{
_PyStackRef ref = _Py_type_getattro_stackref(type, name, suppress_missing_attribute);
if (PyStackRef_IsNull(ref)) {
return NULL;
}
return PyStackRef_AsPyObjectSteal(ref);
}

/* This is similar to PyObject_GenericGetAttr(),
but uses _PyType_LookupRef() instead of just looking in type->tp_dict. */
PyObject *
_Py_type_getattro(PyObject *tp, PyObject *name)
{
PyTypeObject *type = PyTypeObject_CAST(tp);
return _Py_type_getattro_impl(type, name, NULL);
}

/* Like _Py_type_getattro but returns a _PyStackRef.
This can return a deferred reference in the free-threaded build
when the attribute is found without going through a descriptor.

suppress_missing_attribute (optional):
* NULL: do not suppress the exception
* Non-zero pointer: suppress the PyExc_AttributeError and
set *suppress_missing_attribute to 1 to signal we are returning NULL while
having suppressed the exception (other exceptions are not suppressed)
*/
_PyStackRef
_Py_type_getattro_stackref(PyTypeObject *type, PyObject *name,
int *suppress_missing_attribute)
{
PyTypeObject *metatype = Py_TYPE(type);
PyObject *meta_attribute, *attribute;
descrgetfunc meta_get;
PyObject* res;
descrgetfunc meta_get = NULL;

if (!PyUnicode_Check(name)) {
PyErr_Format(PyExc_TypeError,
"attribute name must be string, not '%.200s'",
Py_TYPE(name)->tp_name);
returnNULL;
returnPyStackRef_NULL;
}

/* Initialize this type (we'll assume the metatype is initialized) */
if (!_PyType_IsReady(type)) {
if (PyType_Ready(type) < 0)
returnNULL;
returnPyStackRef_NULL;
}

/* No readable descriptor found yet */
meta_get = NULL;
/* Set up GC-visible stack refs */
_PyCStackRef result_ref, meta_attribute_ref, attribute_ref;
PyThreadState *tstate = _PyThreadState_GET();
_PyThreadState_PushCStackRef(tstate, &result_ref);
_PyThreadState_PushCStackRef(tstate, &meta_attribute_ref);
_PyThreadState_PushCStackRef(tstate, &attribute_ref);

/* Look for the attribute in the metatype */
meta_attribute = _PyType_LookupRef(metatype, name);
_PyType_LookupStackRefAndVersion(metatype, name, &meta_attribute_ref.ref);

if (meta_attribute != NULL) {
meta_get = Py_TYPE(meta_attribute)->tp_descr_get;
if (!PyStackRef_IsNull(meta_attribute_ref.ref)) {
PyObject *meta_attr_obj = PyStackRef_AsPyObjectBorrow(meta_attribute_ref.ref);
meta_get = Py_TYPE(meta_attr_obj)->tp_descr_get;

if (meta_get != NULL && PyDescr_IsData(meta_attribute)) {
if (meta_get != NULL && PyDescr_IsData(meta_attr_obj)) {
/* Data descriptors implement tp_descr_set to intercept
* writes. Assume the attribute is not overridden in
* type's tp_dict (and bases): call the descriptor now.
*/
res = meta_get(meta_attribute, (PyObject *)type,
(PyObject *)metatype);
Py_DECREF(meta_attribute);
return res;
PyObject *res = meta_get(meta_attr_obj, (PyObject *)type,
(PyObject *)metatype);
if (res != NULL) {
result_ref.ref = PyStackRef_FromPyObjectSteal(res);
}
goto done;
}
}

/* No data descriptor found on metatype. Look in tp_dict of this
* type and its bases */
attribute = _PyType_LookupRef(type, name);
if (attribute != NULL) {
_PyType_LookupStackRefAndVersion(type, name, &attribute_ref.ref);
if (!PyStackRef_IsNull(attribute_ref.ref)) {
/* Implement descriptor functionality, if any */
descrgetfunc local_get = Py_TYPE(attribute)->tp_descr_get;
PyObject *attr_obj = PyStackRef_AsPyObjectBorrow(attribute_ref.ref);
descrgetfunc local_get = Py_TYPE(attr_obj)->tp_descr_get;

Py_XDECREF(meta_attribute);
/* Release meta_attribute early since we found in local dict */
PyStackRef_CLEAR(meta_attribute_ref.ref);

if (local_get != NULL) {
/* Special case staticmethod to avoid descriptor call overhead.
* staticmethod.__get__ just returns the wrapped callable. */
if (Py_TYPE(attr_obj) == &PyStaticMethod_Type) {
PyObject *callable = _PyStaticMethod_GetFunc(attr_obj);
if (callable) {
result_ref.ref = PyStackRef_FromPyObjectNew(callable);
goto done;
}
}
/* NULL 2nd argument indicates the descriptor was
* found on the target object itself (or a base) */
res = local_get(attribute, (PyObject *)NULL,
(PyObject *)type);
Py_DECREF(attribute);
return res;
PyObject *res = local_get(attr_obj, (PyObject *)NULL,
(PyObject *)type);
if (res != NULL) {
result_ref.ref = PyStackRef_FromPyObjectSteal(res);
}
goto done;
}

return attribute;
/* No descriptor, return the attribute directly */
result_ref.ref = attribute_ref.ref;
attribute_ref.ref = PyStackRef_NULL;
goto done;
}

/* No attribute found in local __dict__ (or bases): use the
* descriptor from the metatype, if any */
if (meta_get != NULL) {
PyObject *res;
res = meta_get(meta_attribute, (PyObject *)type,
(PyObject *)metatype);
Py_DECREF(meta_attribute);
return res;
PyObject *meta_attr_obj = PyStackRef_AsPyObjectBorrow(meta_attribute_ref.ref);
PyObject *res = meta_get(meta_attr_obj, (PyObject *)type,
(PyObject *)metatype);
if (res != NULL) {
result_ref.ref = PyStackRef_FromPyObjectSteal(res);
}
goto done;
}

/* If an ordinary attribute was found on the metatype, return it now */
if (meta_attribute != NULL) {
return meta_attribute;
if (!PyStackRef_IsNull(meta_attribute_ref.ref)) {
result_ref.ref = meta_attribute_ref.ref;
meta_attribute_ref.ref = PyStackRef_NULL;
goto done;
}

/* Give up */
if (suppress_missing_attribute == NULL) {
PyErr_Format(PyExc_AttributeError,
"type object '%.100s' has no attribute '%U'",
type->tp_name, name);
} else {
"type object '%.100s' has no attribute '%U'",
type->tp_name, name);
}
else {
// signal the caller we have not set an PyExc_AttributeError and gave up
*suppress_missing_attribute = 1;
}
return NULL;
}

/* This is similar to PyObject_GenericGetAttr(),
but uses _PyType_LookupRef() instead of just looking in type->tp_dict. */
PyObject *
_Py_type_getattro(PyObject *tp, PyObject *name)
{
PyTypeObject *type = PyTypeObject_CAST(tp);
return _Py_type_getattro_impl(type, name, NULL);
done:
_PyThreadState_PopCStackRef(tstate, &attribute_ref);
_PyThreadState_PopCStackRef(tstate, &meta_attribute_ref);
return _PyThreadState_PopCStackRefSteal(tstate, &result_ref);
}

// Called by type_setattro(). Updates both the type dict and
Expand DownExpand Up@@ -10937,15 +10988,19 @@ static PyObject *
slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyThreadState *tstate = _PyThreadState_GET();
PyObject *func, *result;
PyObject *result;

func = PyObject_GetAttr((PyObject *)type, &_Py_ID(__new__));
if (func == NULL) {
_PyCStackRef func_ref;
_PyThreadState_PushCStackRef(tstate, &func_ref);
func_ref.ref = _PyObject_GetAttrStackRef((PyObject *)type, &_Py_ID(__new__));
if (PyStackRef_IsNull(func_ref.ref)) {
_PyThreadState_PopCStackRef(tstate, &func_ref);
return NULL;
}

PyObject *func = PyStackRef_AsPyObjectBorrow(func_ref.ref);
result = _PyObject_Call_Prepend(tstate, func, (PyObject *)type, args, kwds);
Py_DECREF(func);
_PyThreadState_PopCStackRef(tstate, &func_ref);
return result;
}

Expand Down
5 changes: 2 additions & 3 deletionsPython/bytecodes.c
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -2399,10 +2399,9 @@ dummy_func(
}
else {
/* Classic, pushes one value. */
PyObject*attr_o=PyObject_GetAttr(PyStackRef_AsPyObjectBorrow(owner),name);
attr=_PyObject_GetAttrStackRef(PyStackRef_AsPyObjectBorrow(owner),name);
PyStackRef_CLOSE(owner);
ERROR_IF(attr_o==NULL);
attr=PyStackRef_FromPyObjectSteal(attr_o);
ERROR_IF(PyStackRef_IsNull(attr));
}
}

Expand Down
Loading
Loading

[8]ページ先頭

©2009-2026 Movatter.jp