bpo-37207: Use vectorcall for range() (GH-18464)
This continues the `range()` part of #13930. The complete pull request is stalled on discussions around dicts, but `range()` should not be controversial. (And I plan to open PRs for other parts if this is merged.) On top of Mark's change, I unified `range_new` and `range_vectorcall`, which had a lot of duplicate code. https://bugs.python.org/issue37207
This commit is contained in:
parent
24bba8cf5b
commit
6e35da9763
|
@ -0,0 +1,2 @@
|
|||
Speed up calls to ``range()`` by about 30%, by using the
|
||||
PEP 590 ``vectorcall`` calling convention. Patch by Mark Shannon.
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "Python.h"
|
||||
#include "structmember.h"
|
||||
#include "pycore_tupleobject.h"
|
||||
|
||||
/* Support objects whose length is > PY_SSIZE_T_MAX.
|
||||
|
||||
|
@ -71,34 +72,27 @@ make_range_object(PyTypeObject *type, PyObject *start,
|
|||
range(0, 5, -1)
|
||||
*/
|
||||
static PyObject *
|
||||
range_new(PyTypeObject *type, PyObject *args, PyObject *kw)
|
||||
range_from_array(PyTypeObject *type, PyObject *const *args, Py_ssize_t num_args)
|
||||
{
|
||||
rangeobject *obj;
|
||||
PyObject *start = NULL, *stop = NULL, *step = NULL;
|
||||
|
||||
if (!_PyArg_NoKeywords("range", kw))
|
||||
return NULL;
|
||||
|
||||
Py_ssize_t num_args = PyTuple_GET_SIZE(args);
|
||||
switch (num_args) {
|
||||
case 3:
|
||||
step = PyTuple_GET_ITEM(args, 2);
|
||||
step = args[2];
|
||||
/* fallthrough */
|
||||
case 2:
|
||||
start = PyTuple_GET_ITEM(args, 0);
|
||||
start = PyNumber_Index(start);
|
||||
/* Convert borrowed refs to owned refs */
|
||||
start = PyNumber_Index(args[0]);
|
||||
if (!start) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stop = PyTuple_GET_ITEM(args, 1);
|
||||
stop = PyNumber_Index(stop);
|
||||
stop = PyNumber_Index(args[1]);
|
||||
if (!stop) {
|
||||
Py_DECREF(start);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
step = validate_step(step);
|
||||
step = validate_step(step); /* Caution, this can clear exceptions */
|
||||
if (!step) {
|
||||
Py_DECREF(start);
|
||||
Py_DECREF(stop);
|
||||
|
@ -106,8 +100,7 @@ range_new(PyTypeObject *type, PyObject *args, PyObject *kw)
|
|||
}
|
||||
break;
|
||||
case 1:
|
||||
stop = PyTuple_GET_ITEM(args, 0);
|
||||
stop = PyNumber_Index(stop);
|
||||
stop = PyNumber_Index(args[0]);
|
||||
if (!stop) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -126,10 +119,10 @@ range_new(PyTypeObject *type, PyObject *args, PyObject *kw)
|
|||
num_args);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
obj = make_range_object(type, start, stop, step);
|
||||
if (obj != NULL)
|
||||
if (obj != NULL) {
|
||||
return (PyObject *) obj;
|
||||
}
|
||||
|
||||
/* Failed to create object, release attributes */
|
||||
Py_DECREF(start);
|
||||
|
@ -138,6 +131,28 @@ range_new(PyTypeObject *type, PyObject *args, PyObject *kw)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
range_new(PyTypeObject *type, PyObject *args, PyObject *kw)
|
||||
{
|
||||
if (!_PyArg_NoKeywords("range", kw))
|
||||
return NULL;
|
||||
|
||||
return range_from_array(type, _PyTuple_ITEMS(args), PyTuple_GET_SIZE(args));
|
||||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
range_vectorcall(PyTypeObject *type, PyObject *const *args,
|
||||
size_t nargsf, PyObject *kwnames)
|
||||
{
|
||||
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
|
||||
if (kwnames && PyTuple_GET_SIZE(kwnames) != 0) {
|
||||
PyErr_Format(PyExc_TypeError, "range() takes no keyword arguments");
|
||||
return NULL;
|
||||
}
|
||||
return range_from_array(type, args, nargs);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(range_doc,
|
||||
"range(stop) -> range object\n\
|
||||
range(start, stop[, step]) -> range object\n\
|
||||
|
@ -719,6 +734,7 @@ PyTypeObject PyRange_Type = {
|
|||
0, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
range_new, /* tp_new */
|
||||
.tp_vectorcall = (vectorcallfunc)range_vectorcall
|
||||
};
|
||||
|
||||
/*********************** range Iterator **************************/
|
||||
|
|
Loading…
Reference in New Issue