From bee09aecc2c317fd3488b819ab88a13e0755189e Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 2 Feb 2017 11:12:47 +0200 Subject: Issue #29368: The extend() method is now called instead of the append() method when unpickle collections.deque and other list-like objects. This can speed up unpickling to 2 times. --- Lib/pickle.py | 17 +++++++++++----- Misc/NEWS | 4 ++++ Modules/_pickle.c | 60 +++++++++++++++++++++++++++++++++++++------------------ 3 files changed, 57 insertions(+), 24 deletions(-) diff --git a/Lib/pickle.py b/Lib/pickle.py index c8370c9..702b0b3 100644 --- a/Lib/pickle.py +++ b/Lib/pickle.py @@ -1464,12 +1464,19 @@ class _Unpickler: def load_appends(self): items = self.pop_mark() list_obj = self.stack[-1] - if isinstance(list_obj, list): - list_obj.extend(items) + try: + extend = list_obj.extend + except AttributeError: + pass else: - append = list_obj.append - for item in items: - append(item) + extend(items) + return + # Even if the PEP 307 requires extend() and append() methods, + # fall back on append() if the object has no extend() method + # for backward compatibility. + append = list_obj.append + for item in items: + append(item) dispatch[APPENDS[0]] = load_appends def load_setitem(self): diff --git a/Misc/NEWS b/Misc/NEWS index 21685d3..a4adbda 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -230,6 +230,10 @@ Library - Issue #29218: Unused install_misc command is now removed. It has been documented as unused since 2000. Patch by Eric N. Vander Weele. +- Issue #29368: The extend() method is now called instead of the append() + method when unpickle collections.deque and other list-like objects. + This can speed up unpickling to 2 times. + - Issue #29338: The help of a builtin or extension class now includes the constructor signature if __text_signature__ is provided for the class. diff --git a/Modules/_pickle.c b/Modules/_pickle.c index 19f94dc..996d16e 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -5807,7 +5807,9 @@ static int do_append(UnpicklerObject *self, Py_ssize_t x) { PyObject *value; + PyObject *slice; PyObject *list; + PyObject *result; Py_ssize_t len, i; len = Py_SIZE(self->stack); @@ -5818,8 +5820,7 @@ do_append(UnpicklerObject *self, Py_ssize_t x) list = self->stack->data[x - 1]; - if (PyList_Check(list)) { - PyObject *slice; + if (PyList_CheckExact(list)) { Py_ssize_t list_len; int ret; @@ -5832,27 +5833,48 @@ do_append(UnpicklerObject *self, Py_ssize_t x) return ret; } else { - PyObject *append_func; - _Py_IDENTIFIER(append); - - append_func = _PyObject_GetAttrId(list, &PyId_append); - if (append_func == NULL) - return -1; - for (i = x; i < len; i++) { - PyObject *result; - - value = self->stack->data[i]; - result = _Pickle_FastCall(append_func, value); - if (result == NULL) { - Pdata_clear(self->stack, i + 1); - Py_SIZE(self->stack) = x; - Py_DECREF(append_func); + PyObject *extend_func; + _Py_IDENTIFIER(extend); + + extend_func = _PyObject_GetAttrId(list, &PyId_extend); + if (extend_func != NULL) { + slice = Pdata_poplist(self->stack, x); + if (!slice) { + Py_DECREF(extend_func); return -1; } + result = _Pickle_FastCall(extend_func, slice); + Py_DECREF(slice); + Py_DECREF(extend_func); + if (result == NULL) + return -1; Py_DECREF(result); } - Py_SIZE(self->stack) = x; - Py_DECREF(append_func); + else { + PyObject *append_func; + _Py_IDENTIFIER(append); + + /* Even if the PEP 307 requires extend() and append() methods, + fall back on append() if the object has no extend() method + for backward compatibility. */ + PyErr_Clear(); + append_func = _PyObject_GetAttrId(list, &PyId_append); + if (append_func == NULL) + return -1; + for (i = x; i < len; i++) { + value = self->stack->data[i]; + result = _Pickle_FastCall(append_func, value); + if (result == NULL) { + Pdata_clear(self->stack, i + 1); + Py_SIZE(self->stack) = x; + Py_DECREF(append_func); + return -1; + } + Py_DECREF(result); + } + Py_SIZE(self->stack) = x; + Py_DECREF(append_func); + } } return 0; -- cgit v0.12