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:
Petr Viktorin 2020-02-18 16:13:17 +01:00 committed by GitHub
parent 24bba8cf5b
commit 6e35da9763
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 35 additions and 17 deletions

View File

@ -0,0 +1,2 @@
Speed up calls to ``range()`` by about 30%, by using the
PEP 590 ``vectorcall`` calling convention. Patch by Mark Shannon.

View File

@ -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 **************************/