Skip to content

Commit

Permalink
Issue python#29368: The extend() method is now called instead of the …
Browse files Browse the repository at this point in the history
…append()

method when unpickle collections.deque and other list-like objects.
This can speed up unpickling to 2 times.
  • Loading branch information
serhiy-storchaka committed Feb 2, 2017
1 parent bb19bf2 commit bee09ae
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 24 deletions.
17 changes: 12 additions & 5 deletions Lib/pickle.py
Original file line number Diff line number Diff line change
Expand Up @@ -1464,12 +1464,19 @@ def load_append(self):
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):
Expand Down
4 changes: 4 additions & 0 deletions Misc/NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
60 changes: 41 additions & 19 deletions Modules/_pickle.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;

Expand All @@ -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;
Expand Down

0 comments on commit bee09ae

Please sign in to comment.