gh-90350: Optimize builtin functions min() and max() (GH-30286)

Builtin functions min() and max() now use METH_FASTCALL
This commit is contained in:
colorfulappl 2023-12-12 03:27:06 +08:00 committed by GitHub
parent d70e27f258
commit 0066ab5bc5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 46 additions and 36 deletions

View file

@ -0,0 +1 @@
Optimize builtin functions :func:`min` and :func:`max`.

View file

@ -1766,35 +1766,27 @@ builtin_locals_impl(PyObject *module)
static PyObject * static PyObject *
min_max(PyObject *args, PyObject *kwds, int op) min_max(PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames, int op)
{ {
PyObject *v, *it, *item, *val, *maxitem, *maxval, *keyfunc=NULL; PyObject *it = NULL, *item, *val, *maxitem, *maxval, *keyfunc=NULL;
PyObject *emptytuple, *defaultval = NULL; PyObject *defaultval = NULL;
static char *kwlist[] = {"key", "default", NULL}; static const char * const keywords[] = {"key", "default", NULL};
const char *name = op == Py_LT ? "min" : "max"; static _PyArg_Parser _parser_min = {"|$OO:min", keywords, 0};
const int positional = PyTuple_Size(args) > 1; static _PyArg_Parser _parser_max = {"|$OO:max", keywords, 0};
int ret; const char *name = (op == Py_LT) ? "min" : "max";
_PyArg_Parser *_parser = (op == Py_LT) ? &_parser_min : &_parser_max;
if (positional) { if (nargs == 0) {
v = args; PyErr_Format(PyExc_TypeError, "%s expected at least 1 argument, got 0", name);
}
else if (!PyArg_UnpackTuple(args, name, 1, 1, &v)) {
if (PyExceptionClass_Check(PyExc_TypeError)) {
PyErr_Format(PyExc_TypeError, "%s expected at least 1 argument, got 0", name);
}
return NULL; return NULL;
} }
emptytuple = PyTuple_New(0); if (kwnames != NULL && !_PyArg_ParseStackAndKeywords(args + nargs, 0, kwnames, _parser,
if (emptytuple == NULL) &keyfunc, &defaultval)) {
return NULL;
ret = PyArg_ParseTupleAndKeywords(emptytuple, kwds,
(op == Py_LT) ? "|$OO:min" : "|$OO:max",
kwlist, &keyfunc, &defaultval);
Py_DECREF(emptytuple);
if (!ret)
return NULL; return NULL;
}
const int positional = nargs > 1; // False iff nargs == 1
if (positional && defaultval != NULL) { if (positional && defaultval != NULL) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"Cannot specify a default for %s() with multiple " "Cannot specify a default for %s() with multiple "
@ -1802,9 +1794,11 @@ min_max(PyObject *args, PyObject *kwds, int op)
return NULL; return NULL;
} }
it = PyObject_GetIter(v); if (!positional) {
if (it == NULL) { it = PyObject_GetIter(args[0]);
return NULL; if (it == NULL) {
return NULL;
}
} }
if (keyfunc == Py_None) { if (keyfunc == Py_None) {
@ -1813,7 +1807,24 @@ min_max(PyObject *args, PyObject *kwds, int op)
maxitem = NULL; /* the result */ maxitem = NULL; /* the result */
maxval = NULL; /* the value associated with the result */ maxval = NULL; /* the value associated with the result */
while (( item = PyIter_Next(it) )) { while (1) {
if (it == NULL) {
if (nargs-- <= 0) {
break;
}
item = *args++;
Py_INCREF(item);
}
else {
item = PyIter_Next(it);
if (item == NULL) {
if (PyErr_Occurred()) {
goto Fail_it;
}
break;
}
}
/* get the value from the key function */ /* get the value from the key function */
if (keyfunc != NULL) { if (keyfunc != NULL) {
val = PyObject_CallOneArg(keyfunc, item); val = PyObject_CallOneArg(keyfunc, item);
@ -1847,8 +1858,6 @@ min_max(PyObject *args, PyObject *kwds, int op)
} }
} }
} }
if (PyErr_Occurred())
goto Fail_it;
if (maxval == NULL) { if (maxval == NULL) {
assert(maxitem == NULL); assert(maxitem == NULL);
if (defaultval != NULL) { if (defaultval != NULL) {
@ -1860,7 +1869,7 @@ min_max(PyObject *args, PyObject *kwds, int op)
} }
else else
Py_DECREF(maxval); Py_DECREF(maxval);
Py_DECREF(it); Py_XDECREF(it);
return maxitem; return maxitem;
Fail_it_item_and_val: Fail_it_item_and_val:
@ -1870,15 +1879,15 @@ min_max(PyObject *args, PyObject *kwds, int op)
Fail_it: Fail_it:
Py_XDECREF(maxval); Py_XDECREF(maxval);
Py_XDECREF(maxitem); Py_XDECREF(maxitem);
Py_DECREF(it); Py_XDECREF(it);
return NULL; return NULL;
} }
/* AC: cannot convert yet, waiting for *args support */ /* AC: cannot convert yet, waiting for *args support */
static PyObject * static PyObject *
builtin_min(PyObject *self, PyObject *args, PyObject *kwds) builtin_min(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{ {
return min_max(args, kwds, Py_LT); return min_max(args, nargs, kwnames, Py_LT);
} }
PyDoc_STRVAR(min_doc, PyDoc_STRVAR(min_doc,
@ -1893,9 +1902,9 @@ With two or more positional arguments, return the smallest argument.");
/* AC: cannot convert yet, waiting for *args support */ /* AC: cannot convert yet, waiting for *args support */
static PyObject * static PyObject *
builtin_max(PyObject *self, PyObject *args, PyObject *kwds) builtin_max(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{ {
return min_max(args, kwds, Py_GT); return min_max(args, nargs, kwnames, Py_GT);
} }
PyDoc_STRVAR(max_doc, PyDoc_STRVAR(max_doc,
@ -3054,8 +3063,8 @@ static PyMethodDef builtin_methods[] = {
BUILTIN_AITER_METHODDEF BUILTIN_AITER_METHODDEF
BUILTIN_LEN_METHODDEF BUILTIN_LEN_METHODDEF
BUILTIN_LOCALS_METHODDEF BUILTIN_LOCALS_METHODDEF
{"max", _PyCFunction_CAST(builtin_max), METH_VARARGS | METH_KEYWORDS, max_doc}, {"max", _PyCFunction_CAST(builtin_max), METH_FASTCALL | METH_KEYWORDS, max_doc},
{"min", _PyCFunction_CAST(builtin_min), METH_VARARGS | METH_KEYWORDS, min_doc}, {"min", _PyCFunction_CAST(builtin_min), METH_FASTCALL | METH_KEYWORDS, min_doc},
{"next", _PyCFunction_CAST(builtin_next), METH_FASTCALL, next_doc}, {"next", _PyCFunction_CAST(builtin_next), METH_FASTCALL, next_doc},
BUILTIN_ANEXT_METHODDEF BUILTIN_ANEXT_METHODDEF
BUILTIN_OCT_METHODDEF BUILTIN_OCT_METHODDEF