Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 35.2k
gh-138912: Improve MATCH_CLASS opcode performance#138915
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
7c81286597adb0a2fa0a8f5852535281c71b468647ef84a1030ac89fe0c2c1e133f94e640b2059375e2bf5eeaccfd7baee8d6e099File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Improve :opcode:`MATCH_CLASS` performance by up to 52% in certain cases. Patch by Marc Mueller. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -509,15 +509,18 @@ match_class_attr(PyThreadState *tstate, PyObject *subject, PyObject *type, | ||
| PyObject *name, PyObject *seen) | ||
| { | ||
| assert(PyUnicode_CheckExact(name)); | ||
| assert(PySet_CheckExact(seen)); | ||
| if (PySet_Contains(seen, name) || PySet_Add(seen, name)) { | ||
| if (!_PyErr_Occurred(tstate)) { | ||
| // Seen it before! | ||
| _PyErr_Format(tstate, PyExc_TypeError, | ||
| "%s() got multiple sub-patterns for attribute %R", | ||
| ((PyTypeObject*)type)->tp_name, name); | ||
| // Only check for duplicates if seen is not NULL. | ||
| if (seen != NULL) { | ||
| assert(PySet_CheckExact(seen)); | ||
| if (PySet_Contains(seen, name) || PySet_Add(seen, name)) { | ||
| if (!_PyErr_Occurred(tstate)) { | ||
| // Seen it before! | ||
| _PyErr_Format(tstate, PyExc_TypeError, | ||
| "%s() got multiple sub-patterns for attribute %R", | ||
| ((PyTypeObject*)type)->tp_name, name); | ||
| } | ||
| return NULL; | ||
| } | ||
| return NULL; | ||
| } | ||
| PyObject *attr; | ||
| (void)PyObject_GetOptionalAttr(subject, name, &attr); | ||
| @@ -540,14 +543,26 @@ _PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type, | ||
| if (PyObject_IsInstance(subject, type) <= 0) { | ||
| return NULL; | ||
| } | ||
| // Short circuit if there aren't any arguments: | ||
| Py_ssize_t nkwargs = PyTuple_GET_SIZE(kwargs); | ||
| Py_ssize_t nattrs = nargs + nkwargs; | ||
| if (!nattrs) { | ||
| return PyTuple_New(0); | ||
| } | ||
| // So far so good: | ||
| PyObject *seen = PySet_New(NULL); | ||
| if (seen == NULL) { | ||
| return NULL; | ||
| PyObject *seen = NULL; | ||
| // Only check for duplicates if there is at least one positional attribute | ||
| // and two or more attributes in total. Duplicate keyword attributes are | ||
| // detected during the compile stage and raise a SyntaxError. | ||
| if (nargs > 0 && nattrs > 1) { | ||
| seen = PySet_New(NULL); | ||
| if (seen == NULL) { | ||
| return NULL; | ||
| } | ||
| } | ||
| PyObject *attrs = PyList_New(0); | ||
| PyObject *attrs = PyTuple_New(nattrs); | ||
| if (attrs == NULL) { | ||
| Py_DECREF(seen); | ||
| Py_XDECREF(seen); | ||
| return NULL; | ||
| } | ||
| // NOTE: From this point on, goto fail on failure: | ||
| @@ -588,9 +603,8 @@ _PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type, | ||
| } | ||
| if (match_self) { | ||
| // Easy. Copy the subject itself, and move on to kwargs. | ||
| if (PyList_Append(attrs, subject) < 0) { | ||
| goto fail; | ||
| } | ||
| assert(PyTuple_GET_ITEM(attrs, 0) == NULL); | ||
| PyTuple_SET_ITEM(attrs, 0, Py_NewRef(subject)); | ||
| } | ||
| else { | ||
| for (Py_ssize_t i = 0; i < nargs; i++) { | ||
| @@ -606,36 +620,29 @@ _PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type, | ||
| if (attr == NULL) { | ||
| goto fail; | ||
| } | ||
| if (PyList_Append(attrs, attr) < 0) { | ||
| Py_DECREF(attr); | ||
| goto fail; | ||
| } | ||
| Py_DECREF(attr); | ||
| assert(PyTuple_GET_ITEM(attrs, i) == NULL); | ||
| PyTuple_SET_ITEM(attrs, i, attr); | ||
cdce8p marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| } | ||
| Py_CLEAR(match_args); | ||
| } | ||
| // Finally, the keyword subpatterns: | ||
| for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(kwargs); i++) { | ||
| for (Py_ssize_t i = 0; i < nkwargs; i++) { | ||
| PyObject *name = PyTuple_GET_ITEM(kwargs, i); | ||
| PyObject *attr = match_class_attr(tstate, subject, type, name, seen); | ||
| if (attr == NULL) { | ||
| goto fail; | ||
| } | ||
| if (PyList_Append(attrs, attr) < 0) { | ||
| Py_DECREF(attr); | ||
| goto fail; | ||
| } | ||
| Py_DECREF(attr); | ||
| assert(PyTuple_GET_ITEM(attrs, nargs + i) == NULL); | ||
| PyTuple_SET_ITEM(attrs, nargs + i, attr); | ||
cdce8p marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| Py_SETREF(attrs, PyList_AsTuple(attrs)); | ||
| Py_DECREF(seen); | ||
| Py_XDECREF(seen); | ||
| return attrs; | ||
| fail: | ||
| // We really don't care whether an error was raised or not... that's our | ||
| // caller's problem. All we know is that the match failed. | ||
| Py_XDECREF(match_args); | ||
| Py_DECREF(seen); | ||
| Py_XDECREF(seen); | ||
| Py_DECREF(attrs); | ||
| return NULL; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.