mirror of https://github.com/python/cpython.git
bpo-44953: Add vectorcall for itemgetter and attrgetter instances (GH-27828)
This commit is contained in:
parent
d7a5aca982
commit
0a145069e8
|
@ -0,0 +1 @@
|
|||
Calling ``operator.itemgetter`` objects and ``operator.attrgetter`` objects is now faster due to use of the vectorcall calling convention.
|
|
@ -1,5 +1,6 @@
|
|||
#include "Python.h"
|
||||
#include "pycore_moduleobject.h" // _PyModule_GetState()
|
||||
#include "structmember.h" // PyMemberDef
|
||||
#include "pycore_runtime.h" // _Py_ID()
|
||||
#include "clinic/_operator.c.h"
|
||||
|
||||
|
@ -974,8 +975,15 @@ typedef struct {
|
|||
Py_ssize_t nitems;
|
||||
PyObject *item;
|
||||
Py_ssize_t index; // -1 unless *item* is a single non-negative integer index
|
||||
vectorcallfunc vectorcall;
|
||||
} itemgetterobject;
|
||||
|
||||
// Forward declarations
|
||||
static PyObject *
|
||||
itemgetter_vectorcall(PyObject *, PyObject *const *, size_t, PyObject *);
|
||||
static PyObject *
|
||||
itemgetter_call_impl(itemgetterobject *, PyObject *);
|
||||
|
||||
/* AC 3.5: treats first argument as an iterable, otherwise uses *args */
|
||||
static PyObject *
|
||||
itemgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
|
@ -1021,6 +1029,7 @@ itemgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|||
}
|
||||
}
|
||||
|
||||
ig->vectorcall = (vectorcallfunc)itemgetter_vectorcall;
|
||||
PyObject_GC_Track(ig);
|
||||
return (PyObject *)ig;
|
||||
}
|
||||
|
@ -1053,16 +1062,33 @@ itemgetter_traverse(itemgetterobject *ig, visitproc visit, void *arg)
|
|||
static PyObject *
|
||||
itemgetter_call(itemgetterobject *ig, PyObject *args, PyObject *kw)
|
||||
{
|
||||
PyObject *obj, *result;
|
||||
Py_ssize_t i, nitems=ig->nitems;
|
||||
|
||||
assert(PyTuple_CheckExact(args));
|
||||
if (!_PyArg_NoKeywords("itemgetter", kw))
|
||||
return NULL;
|
||||
if (!_PyArg_CheckPositional("itemgetter", PyTuple_GET_SIZE(args), 1, 1))
|
||||
return NULL;
|
||||
return itemgetter_call_impl(ig, PyTuple_GET_ITEM(args, 0));
|
||||
}
|
||||
|
||||
obj = PyTuple_GET_ITEM(args, 0);
|
||||
static PyObject *
|
||||
itemgetter_vectorcall(PyObject *ig, PyObject *const *args,
|
||||
size_t nargsf, PyObject *kwnames)
|
||||
{
|
||||
if (!_PyArg_NoKwnames("itemgetter", kwnames)) {
|
||||
return NULL;
|
||||
}
|
||||
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
|
||||
if (!_PyArg_CheckPositional("itemgetter", nargs, 1, 1)) {
|
||||
return NULL;
|
||||
}
|
||||
return itemgetter_call_impl((itemgetterobject *)ig, args[0]);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
itemgetter_call_impl(itemgetterobject *ig, PyObject *obj)
|
||||
{
|
||||
PyObject *result;
|
||||
Py_ssize_t i, nitems=ig->nitems;
|
||||
if (nitems == 1) {
|
||||
if (ig->index >= 0
|
||||
&& PyTuple_CheckExact(obj)
|
||||
|
@ -1130,6 +1156,11 @@ static PyMethodDef itemgetter_methods[] = {
|
|||
{NULL}
|
||||
};
|
||||
|
||||
static PyMemberDef itemgetter_members[] = {
|
||||
{"__vectorcalloffset__", T_PYSSIZET, offsetof(itemgetterobject, vectorcall), READONLY},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(itemgetter_doc,
|
||||
"itemgetter(item, ...) --> itemgetter object\n\
|
||||
\n\
|
||||
|
@ -1144,6 +1175,7 @@ static PyType_Slot itemgetter_type_slots[] = {
|
|||
{Py_tp_traverse, itemgetter_traverse},
|
||||
{Py_tp_clear, itemgetter_clear},
|
||||
{Py_tp_methods, itemgetter_methods},
|
||||
{Py_tp_members, itemgetter_members},
|
||||
{Py_tp_new, itemgetter_new},
|
||||
{Py_tp_getattro, PyObject_GenericGetAttr},
|
||||
{Py_tp_repr, itemgetter_repr},
|
||||
|
@ -1155,7 +1187,7 @@ static PyType_Spec itemgetter_type_spec = {
|
|||
.basicsize = sizeof(itemgetterobject),
|
||||
.itemsize = 0,
|
||||
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
|
||||
Py_TPFLAGS_IMMUTABLETYPE),
|
||||
Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_VECTORCALL),
|
||||
.slots = itemgetter_type_slots,
|
||||
};
|
||||
|
||||
|
@ -1165,8 +1197,15 @@ typedef struct {
|
|||
PyObject_HEAD
|
||||
Py_ssize_t nattrs;
|
||||
PyObject *attr;
|
||||
vectorcallfunc vectorcall;
|
||||
} attrgetterobject;
|
||||
|
||||
// Forward declarations
|
||||
static PyObject *
|
||||
attrgetter_vectorcall(PyObject *, PyObject *const *, size_t, PyObject *);
|
||||
static PyObject *
|
||||
attrgetter_call_impl(attrgetterobject *, PyObject *);
|
||||
|
||||
/* AC 3.5: treats first argument as an iterable, otherwise uses *args */
|
||||
static PyObject *
|
||||
attrgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
|
@ -1210,7 +1249,7 @@ attrgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|||
kind = PyUnicode_KIND(item);
|
||||
data = PyUnicode_DATA(item);
|
||||
|
||||
/* check whethere the string is dotted */
|
||||
/* check whether the string is dotted */
|
||||
dot_count = 0;
|
||||
for (char_idx = 0; char_idx < item_len; ++char_idx) {
|
||||
if (PyUnicode_READ(kind, data, char_idx) == '.')
|
||||
|
@ -1276,6 +1315,7 @@ attrgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|||
|
||||
ag->attr = attr;
|
||||
ag->nattrs = nattrs;
|
||||
ag->vectorcall = (vectorcallfunc)attrgetter_vectorcall;
|
||||
|
||||
PyObject_GC_Track(ag);
|
||||
return (PyObject *)ag;
|
||||
|
@ -1342,16 +1382,36 @@ dotted_getattr(PyObject *obj, PyObject *attr)
|
|||
static PyObject *
|
||||
attrgetter_call(attrgetterobject *ag, PyObject *args, PyObject *kw)
|
||||
{
|
||||
PyObject *obj, *result;
|
||||
Py_ssize_t i, nattrs=ag->nattrs;
|
||||
|
||||
if (!_PyArg_NoKeywords("attrgetter", kw))
|
||||
return NULL;
|
||||
if (!_PyArg_CheckPositional("attrgetter", PyTuple_GET_SIZE(args), 1, 1))
|
||||
return NULL;
|
||||
obj = PyTuple_GET_ITEM(args, 0);
|
||||
if (ag->nattrs == 1) /* ag->attr is always a tuple */
|
||||
return attrgetter_call_impl(ag, PyTuple_GET_ITEM(args, 0));
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
attrgetter_vectorcall(PyObject *ag, PyObject *const *args, size_t nargsf, PyObject *kwnames)
|
||||
{
|
||||
if (!_PyArg_NoKwnames("attrgetter", kwnames)) {
|
||||
return NULL;
|
||||
}
|
||||
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
|
||||
if (!_PyArg_CheckPositional("attrgetter", nargs, 1, 1)) {
|
||||
return NULL;
|
||||
}
|
||||
return attrgetter_call_impl((attrgetterobject *)ag, args[0]);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
attrgetter_call_impl(attrgetterobject *ag, PyObject *obj)
|
||||
{
|
||||
PyObject *result;
|
||||
Py_ssize_t i, nattrs=ag->nattrs;
|
||||
|
||||
if (ag->nattrs == 1) {
|
||||
/* ag->attr is always a tuple */
|
||||
return dotted_getattr(obj, PyTuple_GET_ITEM(ag->attr, 0));
|
||||
}
|
||||
|
||||
assert(PyTuple_Check(ag->attr));
|
||||
assert(PyTuple_GET_SIZE(ag->attr) == nattrs);
|
||||
|
@ -1460,6 +1520,11 @@ static PyMethodDef attrgetter_methods[] = {
|
|||
{NULL}
|
||||
};
|
||||
|
||||
static PyMemberDef attrgetter_members[] = {
|
||||
{"__vectorcalloffset__", T_PYSSIZET, offsetof(attrgetterobject, vectorcall), READONLY},
|
||||
{NULL} /* Sentinel*/
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(attrgetter_doc,
|
||||
"attrgetter(attr, ...) --> attrgetter object\n\
|
||||
\n\
|
||||
|
@ -1476,6 +1541,7 @@ static PyType_Slot attrgetter_type_slots[] = {
|
|||
{Py_tp_traverse, attrgetter_traverse},
|
||||
{Py_tp_clear, attrgetter_clear},
|
||||
{Py_tp_methods, attrgetter_methods},
|
||||
{Py_tp_members, attrgetter_members},
|
||||
{Py_tp_new, attrgetter_new},
|
||||
{Py_tp_getattro, PyObject_GenericGetAttr},
|
||||
{Py_tp_repr, attrgetter_repr},
|
||||
|
@ -1487,7 +1553,7 @@ static PyType_Spec attrgetter_type_spec = {
|
|||
.basicsize = sizeof(attrgetterobject),
|
||||
.itemsize = 0,
|
||||
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
|
||||
Py_TPFLAGS_IMMUTABLETYPE),
|
||||
Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_VECTORCALL),
|
||||
.slots = attrgetter_type_slots,
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue