Skip to content

Commit c292b70

Browse files
colorfulapplGlyphack
authored andcommitted
pythongh-90350: Optimize builtin functions min() and max() (pythonGH-30286)
Builtin functions min() and max() now use METH_FASTCALL
1 parent 8ae3ca8 commit c292b70

File tree

2 files changed

+46
-36
lines changed

2 files changed

+46
-36
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Optimize builtin functions :func:`min` and :func:`max`.

Python/bltinmodule.c

+45-36
Original file line numberDiff line numberDiff line change
@@ -1766,45 +1766,39 @@ builtin_locals_impl(PyObject *module)
17661766

17671767

17681768
static PyObject *
1769-
min_max(PyObject *args, PyObject *kwds, int op)
1769+
min_max(PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames, int op)
17701770
{
1771-
PyObject *v, *it, *item, *val, *maxitem, *maxval, *keyfunc=NULL;
1772-
PyObject *emptytuple, *defaultval = NULL;
1773-
static char *kwlist[] = {"key", "default", NULL};
1774-
const char *name = op == Py_LT ? "min" : "max";
1775-
const int positional = PyTuple_Size(args) > 1;
1776-
int ret;
1771+
PyObject *it = NULL, *item, *val, *maxitem, *maxval, *keyfunc=NULL;
1772+
PyObject *defaultval = NULL;
1773+
static const char * const keywords[] = {"key", "default", NULL};
1774+
static _PyArg_Parser _parser_min = {"|$OO:min", keywords, 0};
1775+
static _PyArg_Parser _parser_max = {"|$OO:max", keywords, 0};
1776+
const char *name = (op == Py_LT) ? "min" : "max";
1777+
_PyArg_Parser *_parser = (op == Py_LT) ? &_parser_min : &_parser_max;
17771778

1778-
if (positional) {
1779-
v = args;
1780-
}
1781-
else if (!PyArg_UnpackTuple(args, name, 1, 1, &v)) {
1782-
if (PyExceptionClass_Check(PyExc_TypeError)) {
1783-
PyErr_Format(PyExc_TypeError, "%s expected at least 1 argument, got 0", name);
1784-
}
1779+
if (nargs == 0) {
1780+
PyErr_Format(PyExc_TypeError, "%s expected at least 1 argument, got 0", name);
17851781
return NULL;
17861782
}
17871783

1788-
emptytuple = PyTuple_New(0);
1789-
if (emptytuple == NULL)
1790-
return NULL;
1791-
ret = PyArg_ParseTupleAndKeywords(emptytuple, kwds,
1792-
(op == Py_LT) ? "|$OO:min" : "|$OO:max",
1793-
kwlist, &keyfunc, &defaultval);
1794-
Py_DECREF(emptytuple);
1795-
if (!ret)
1784+
if (kwnames != NULL && !_PyArg_ParseStackAndKeywords(args + nargs, 0, kwnames, _parser,
1785+
&keyfunc, &defaultval)) {
17961786
return NULL;
1787+
}
17971788

1789+
const int positional = nargs > 1; // False iff nargs == 1
17981790
if (positional && defaultval != NULL) {
17991791
PyErr_Format(PyExc_TypeError,
18001792
"Cannot specify a default for %s() with multiple "
18011793
"positional arguments", name);
18021794
return NULL;
18031795
}
18041796

1805-
it = PyObject_GetIter(v);
1806-
if (it == NULL) {
1807-
return NULL;
1797+
if (!positional) {
1798+
it = PyObject_GetIter(args[0]);
1799+
if (it == NULL) {
1800+
return NULL;
1801+
}
18081802
}
18091803

18101804
if (keyfunc == Py_None) {
@@ -1813,7 +1807,24 @@ min_max(PyObject *args, PyObject *kwds, int op)
18131807

18141808
maxitem = NULL; /* the result */
18151809
maxval = NULL; /* the value associated with the result */
1816-
while (( item = PyIter_Next(it) )) {
1810+
while (1) {
1811+
if (it == NULL) {
1812+
if (nargs-- <= 0) {
1813+
break;
1814+
}
1815+
item = *args++;
1816+
Py_INCREF(item);
1817+
}
1818+
else {
1819+
item = PyIter_Next(it);
1820+
if (item == NULL) {
1821+
if (PyErr_Occurred()) {
1822+
goto Fail_it;
1823+
}
1824+
break;
1825+
}
1826+
}
1827+
18171828
/* get the value from the key function */
18181829
if (keyfunc != NULL) {
18191830
val = PyObject_CallOneArg(keyfunc, item);
@@ -1847,8 +1858,6 @@ min_max(PyObject *args, PyObject *kwds, int op)
18471858
}
18481859
}
18491860
}
1850-
if (PyErr_Occurred())
1851-
goto Fail_it;
18521861
if (maxval == NULL) {
18531862
assert(maxitem == NULL);
18541863
if (defaultval != NULL) {
@@ -1860,7 +1869,7 @@ min_max(PyObject *args, PyObject *kwds, int op)
18601869
}
18611870
else
18621871
Py_DECREF(maxval);
1863-
Py_DECREF(it);
1872+
Py_XDECREF(it);
18641873
return maxitem;
18651874

18661875
Fail_it_item_and_val:
@@ -1870,15 +1879,15 @@ min_max(PyObject *args, PyObject *kwds, int op)
18701879
Fail_it:
18711880
Py_XDECREF(maxval);
18721881
Py_XDECREF(maxitem);
1873-
Py_DECREF(it);
1882+
Py_XDECREF(it);
18741883
return NULL;
18751884
}
18761885

18771886
/* AC: cannot convert yet, waiting for *args support */
18781887
static PyObject *
1879-
builtin_min(PyObject *self, PyObject *args, PyObject *kwds)
1888+
builtin_min(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
18801889
{
1881-
return min_max(args, kwds, Py_LT);
1890+
return min_max(args, nargs, kwnames, Py_LT);
18821891
}
18831892

18841893
PyDoc_STRVAR(min_doc,
@@ -1893,9 +1902,9 @@ With two or more positional arguments, return the smallest argument.");
18931902

18941903
/* AC: cannot convert yet, waiting for *args support */
18951904
static PyObject *
1896-
builtin_max(PyObject *self, PyObject *args, PyObject *kwds)
1905+
builtin_max(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
18971906
{
1898-
return min_max(args, kwds, Py_GT);
1907+
return min_max(args, nargs, kwnames, Py_GT);
18991908
}
19001909

19011910
PyDoc_STRVAR(max_doc,
@@ -3054,8 +3063,8 @@ static PyMethodDef builtin_methods[] = {
30543063
BUILTIN_AITER_METHODDEF
30553064
BUILTIN_LEN_METHODDEF
30563065
BUILTIN_LOCALS_METHODDEF
3057-
{"max", _PyCFunction_CAST(builtin_max), METH_VARARGS | METH_KEYWORDS, max_doc},
3058-
{"min", _PyCFunction_CAST(builtin_min), METH_VARARGS | METH_KEYWORDS, min_doc},
3066+
{"max", _PyCFunction_CAST(builtin_max), METH_FASTCALL | METH_KEYWORDS, max_doc},
3067+
{"min", _PyCFunction_CAST(builtin_min), METH_FASTCALL | METH_KEYWORDS, min_doc},
30593068
{"next", _PyCFunction_CAST(builtin_next), METH_FASTCALL, next_doc},
30603069
BUILTIN_ANEXT_METHODDEF
30613070
BUILTIN_OCT_METHODDEF

0 commit comments

Comments
 (0)