8000 PERF cache unicode version of __array_ufunc__ · numpy/numpy@ee8c683 · GitHub
[go: up one dir, main page]

Skip to content
10000

Commit ee8c683

Browse files
committed
PERF cache unicode version of __array_ufunc__
Cache PyUniCode object with value __array_ufunc__ for attribute lookup
1 parent fd646bd commit ee8c683

File tree

2 files changed

+63
-1
lines changed

2 files changed

+63
-1
lines changed

numpy/core/src/common/get_attr_string.h

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,68 @@ PyArray_LookupSpecial(PyObject *obj, char const *name)
9292
return maybe_get_attr((PyObject *)tp, name);
9393
}
9494

95+
/*
96+
* Stripped down version of PyObject_GetAttrString(obj, name) that does not
97+
* raise PyExc_AttributeError.
98+
*
99+
* This allows it to avoid creating then discarding exception objects when
100+
* performing lookups on objects without any attributes.
101+
*
102+
* Returns attribute value on success, NULL without an exception set if
103+
* there is no such attribute, and NULL with an exception on failure.
104+
*/
105+
static NPY_INLINE PyObject *
106+
_maybe_get_attr_unicode(PyObject *obj, char const *name, PyObject *name_unicode)
107+
{
108+
PyTypeObject *tp = Py_TYPE(obj);
109+
PyObject *res = (PyObject *)NULL;
110+
111+
/* Attribute referenced by (char *)name */
112+
if (tp->tp_getattr != NULL) {
113+
res = (*tp->tp_getattr)(obj, (char *)name);
114+
if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
115+
PyErr_Clear();
116+
}
117+
}
118+
/* Attribute referenced by (PyObject *)name */
119+
else if (tp->tp_getattro != NULL) {
120+
res = (*tp->tp_getattro)(obj, name_unicode);
121+
if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
122+
PyErr_Clear();
123+
}
124+
}
125+
return res;
126+
}
127+
128+
/*
129+
* Lookup a special method "__array_ufunc__", following the python approach of looking up
130+
* on the type object, rather than on the instance itself.
131+
*
132+
* Assumes that the special method is a numpy-specific one, so does not look
133+
* at builtin types, nor does it look at a base ndarray.
134+
*
135+
* In future, could be made more like _Py_LookupSpecial
136+
*/
137+
static NPY_INLINE PyObject *
138+
PyArray_Lookup_Array_UFunc(PyObject *obj)
139+
{
140+
static const char *name = "__array_ufunc__";
141+
static PyObject *name_unicode = NULL;
142+
143+
/* On first entry, cache unicode version of __array_ufunc__ */
144+
if (name_unicode == NULL) {
145+
name_unicode = PyUnicode_InternFromString(name);
146+
}
147+
148+
PyTypeObject *tp = Py_TYPE(obj);
149+
150+
/* We do not need to check for special attributes on trivial types */
151+
if (_is_basic_python_type(tp)) {
152+
return NULL;
153+
}
154+
return _maybe_get_attr_unicode((PyObject *)tp, name, name_unicode);
155+
}
156+
95157
/*
96158
* PyArray_LookupSpecial_OnInstance:
97159
*

numpy/core/src/common/ufunc_override.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ PyUFuncOverride_GetNonDefaultArrayUfunc(PyObject *obj)
3434
* Does the class define __array_ufunc__? (Note that LookupSpecial has fast
3535
* return for basic python types, so no need to worry about those here)
3636
*/
37-
cls_array_ufunc = PyArray_LookupSpecial(obj, "__array_ufunc__");
37+
cls_array_ufunc = PyArray_Lookup_Array_UFunc(obj);
3838
if (cls_array_ufunc == NULL) {
3939
if (PyErr_Occurred()) {
4040
PyErr_Clear(); /* TODO[gh-14801]: propagate crashes during attribute access? */

0 commit comments

Comments
 (0)
0