summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcolorfulappl <colorfulappl@qq.com>2023-12-11 19:27:06 (GMT)
committerGitHub <noreply@github.com>2023-12-11 19:27:06 (GMT)
commit0066ab5bc58a036b3f448cd6f9bbdd92120e39ba (patch)
treefb1f0c8d2e291198885dd6c3c3f693cc2d753d1f
parentd70e27f25886e3ac1aa9fcc2d44dd38b4001d8bb (diff)
downloadcpython-0066ab5bc58a036b3f448cd6f9bbdd92120e39ba.zip
cpython-0066ab5bc58a036b3f448cd6f9bbdd92120e39ba.tar.gz
cpython-0066ab5bc58a036b3f448cd6f9bbdd92120e39ba.tar.bz2
gh-90350: Optimize builtin functions min() and max() (GH-30286)
Builtin functions min() and max() now use METH_FASTCALL
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2023-12-11-19-53-32.gh-issue-90350.-FQy3E.rst1
-rw-r--r--Python/bltinmodule.c81
2 files changed, 46 insertions, 36 deletions
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-12-11-19-53-32.gh-issue-90350.-FQy3E.rst b/Misc/NEWS.d/next/Core and Builtins/2023-12-11-19-53-32.gh-issue-90350.-FQy3E.rst
new file mode 100644
index 0000000..6b7881b
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-12-11-19-53-32.gh-issue-90350.-FQy3E.rst
@@ -0,0 +1 @@
+Optimize builtin functions :func:`min` and :func:`max`.
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index 960bca0..e54d5cb 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -1766,35 +1766,27 @@ builtin_locals_impl(PyObject *module)
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 *emptytuple, *defaultval = NULL;
- static char *kwlist[] = {"key", "default", NULL};
- const char *name = op == Py_LT ? "min" : "max";
- const int positional = PyTuple_Size(args) > 1;
- int ret;
+ PyObject *it = NULL, *item, *val, *maxitem, *maxval, *keyfunc=NULL;
+ PyObject *defaultval = NULL;
+ static const char * const keywords[] = {"key", "default", NULL};
+ static _PyArg_Parser _parser_min = {"|$OO:min", keywords, 0};
+ static _PyArg_Parser _parser_max = {"|$OO:max", keywords, 0};
+ const char *name = (op == Py_LT) ? "min" : "max";
+ _PyArg_Parser *_parser = (op == Py_LT) ? &_parser_min : &_parser_max;
- if (positional) {
- v = args;
- }
- 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);
- }
+ if (nargs == 0) {
+ PyErr_Format(PyExc_TypeError, "%s expected at least 1 argument, got 0", name);
return NULL;
}
- emptytuple = PyTuple_New(0);
- if (emptytuple == NULL)
- return NULL;
- ret = PyArg_ParseTupleAndKeywords(emptytuple, kwds,
- (op == Py_LT) ? "|$OO:min" : "|$OO:max",
- kwlist, &keyfunc, &defaultval);
- Py_DECREF(emptytuple);
- if (!ret)
+ if (kwnames != NULL && !_PyArg_ParseStackAndKeywords(args + nargs, 0, kwnames, _parser,
+ &keyfunc, &defaultval)) {
return NULL;
+ }
+ const int positional = nargs > 1; // False iff nargs == 1
if (positional && defaultval != NULL) {
PyErr_Format(PyExc_TypeError,
"Cannot specify a default for %s() with multiple "
@@ -1802,9 +1794,11 @@ min_max(PyObject *args, PyObject *kwds, int op)
return NULL;
}
- it = PyObject_GetIter(v);
- if (it == NULL) {
- return NULL;
+ if (!positional) {
+ it = PyObject_GetIter(args[0]);
+ if (it == NULL) {
+ return NULL;
+ }
}
if (keyfunc == Py_None) {
@@ -1813,7 +1807,24 @@ min_max(PyObject *args, PyObject *kwds, int op)
maxitem = NULL; /* 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 */
if (keyfunc != NULL) {
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) {
assert(maxitem == NULL);
if (defaultval != NULL) {
@@ -1860,7 +1869,7 @@ min_max(PyObject *args, PyObject *kwds, int op)
}
else
Py_DECREF(maxval);
- Py_DECREF(it);
+ Py_XDECREF(it);
return maxitem;
Fail_it_item_and_val:
@@ -1870,15 +1879,15 @@ Fail_it_item:
Fail_it:
Py_XDECREF(maxval);
Py_XDECREF(maxitem);
- Py_DECREF(it);
+ Py_XDECREF(it);
return NULL;
}
/* AC: cannot convert yet, waiting for *args support */
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,
@@ -1893,9 +1902,9 @@ With two or more positional arguments, return the smallest argument.");
/* AC: cannot convert yet, waiting for *args support */
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,
@@ -3054,8 +3063,8 @@ static PyMethodDef builtin_methods[] = {
BUILTIN_AITER_METHODDEF
BUILTIN_LEN_METHODDEF
BUILTIN_LOCALS_METHODDEF
- {"max", _PyCFunction_CAST(builtin_max), METH_VARARGS | METH_KEYWORDS, max_doc},
- {"min", _PyCFunction_CAST(builtin_min), METH_VARARGS | METH_KEYWORDS, min_doc},
+ {"max", _PyCFunction_CAST(builtin_max), METH_FASTCALL | METH_KEYWORDS, max_doc},
+ {"min", _PyCFunction_CAST(builtin_min), METH_FASTCALL | METH_KEYWORDS, min_doc},
{"next", _PyCFunction_CAST(builtin_next), METH_FASTCALL, next_doc},
BUILTIN_ANEXT_METHODDEF
BUILTIN_OCT_METHODDEF