Skip to content

Commit 1c60567

Browse files
authored
bpo-37207: Use PEP 590 vectorcall to speed up frozenset() (GH-19053)
1 parent d18de46 commit 1c60567

2 files changed

Lines changed: 52 additions & 18 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Speed up calls to ``frozenset()`` by using the :pep:`590` ``vectorcall``
2+
calling convention. Patch by Dong-hee Na.

‎Objects/setobject.c‎

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,6 +1024,7 @@ PyDoc_STRVAR(update_doc,
10241024
static PyObject *
10251025
make_new_set(PyTypeObject *type, PyObject *iterable)
10261026
{
1027+
assert(PyType_Check(type));
10271028
PySetObject *so;
10281029

10291030
so = (PySetObject *)type->tp_alloc(type, 0);
@@ -1064,37 +1065,67 @@ make_new_set_basetype(PyTypeObject *type, PyObject *iterable)
10641065
static PyObject *emptyfrozenset = NULL;
10651066

10661067
static PyObject *
1067-
frozenset_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
1068+
make_new_frozenset(PyTypeObject *type, PyObject *iterable)
10681069
{
1069-
PyObject *iterable = NULL, *result;
1070-
1071-
if (type == &PyFrozenSet_Type && !_PyArg_NoKeywords("frozenset", kwds))
1072-
return NULL;
1073-
1074-
if (!PyArg_UnpackTuple(args, type->tp_name, 0, 1, &iterable))
1075-
return NULL;
1076-
1077-
if (type != &PyFrozenSet_Type)
1070+
if (type != &PyFrozenSet_Type) {
10781071
return make_new_set(type, iterable);
1072+
}
10791073

10801074
if (iterable != NULL) {
1081-
/* frozenset(f) is idempotent */
10821075
if (PyFrozenSet_CheckExact(iterable)) {
1076+
/* frozenset(f) is idempotent */
10831077
Py_INCREF(iterable);
10841078
return iterable;
10851079
}
1086-
result = make_new_set(type, iterable);
1087-
if (result == NULL || PySet_GET_SIZE(result))
1088-
return result;
1089-
Py_DECREF(result);
1080+
PyObject *res = make_new_set((PyTypeObject *)type, iterable);
1081+
if (res == NULL || PySet_GET_SIZE(res) != 0) {
1082+
return res;
1083+
}
1084+
/* If the created frozenset is empty, return the empty frozenset singleton instead */
1085+
Py_DECREF(res);
1086+
}
1087+
1088+
// The empty frozenset is a singleton
1089+
if (emptyfrozenset == NULL) {
1090+
emptyfrozenset = make_new_set((PyTypeObject *)type, NULL);
10901091
}
1091-
/* The empty frozenset is a singleton */
1092-
if (emptyfrozenset == NULL)
1093-
emptyfrozenset = make_new_set(type, NULL);
10941092
Py_XINCREF(emptyfrozenset);
10951093
return emptyfrozenset;
10961094
}
10971095

1096+
static PyObject *
1097+
frozenset_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
1098+
{
1099+
PyObject *iterable = NULL;
1100+
1101+
if (type == &PyFrozenSet_Type && !_PyArg_NoKeywords("frozenset", kwds)) {
1102+
return NULL;
1103+
}
1104+
1105+
if (!PyArg_UnpackTuple(args, type->tp_name, 0, 1, &iterable)) {
1106+
return NULL;
1107+
}
1108+
1109+
return make_new_frozenset(type, iterable);
1110+
}
1111+
1112+
static PyObject *
1113+
frozenset_vectorcall(PyObject *type, PyObject * const*args,
1114+
size_t nargsf, PyObject *kwnames)
1115+
{
1116+
if (!_PyArg_NoKwnames("frozenset", kwnames)) {
1117+
return NULL;
1118+
}
1119+
1120+
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
1121+
if (!_PyArg_CheckPositional("frozenset", nargs, 0, 1)) {
1122+
return NULL;
1123+
}
1124+
1125+
PyObject *iterable = (nargs ? args[0] : NULL);
1126+
return make_new_frozenset((PyTypeObject *)type, iterable);
1127+
}
1128+
10981129
static PyObject *
10991130
set_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
11001131
{
@@ -2283,6 +2314,7 @@ PyTypeObject PyFrozenSet_Type = {
22832314
PyType_GenericAlloc, /* tp_alloc */
22842315
frozenset_new, /* tp_new */
22852316
PyObject_GC_Del, /* tp_free */
2317+
.tp_vectorcall = frozenset_vectorcall,
22862318
};
22872319

22882320

0 commit comments

Comments
 (0)