Skip to content

Commit bee09ae

Browse files
Issue python#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.
1 parent bb19bf2 commit bee09ae

3 files changed

Lines changed: 57 additions & 24 deletions

File tree

‎Lib/pickle.py‎

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1464,12 +1464,19 @@ def load_append(self):
14641464
def load_appends(self):
14651465
items = self.pop_mark()
14661466
list_obj = self.stack[-1]
1467-
if isinstance(list_obj, list):
1468-
list_obj.extend(items)
1467+
try:
1468+
extend = list_obj.extend
1469+
except AttributeError:
1470+
pass
14691471
else:
1470-
append = list_obj.append
1471-
for item in items:
1472-
append(item)
1472+
extend(items)
1473+
return
1474+
# Even if the PEP 307 requires extend() and append() methods,
1475+
# fall back on append() if the object has no extend() method
1476+
# for backward compatibility.
1477+
append = list_obj.append
1478+
for item in items:
1479+
append(item)
14731480
dispatch[APPENDS[0]] = load_appends
14741481

14751482
def load_setitem(self):

‎Misc/NEWS‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ Library
230230
- Issue #29218: Unused install_misc command is now removed. It has been
231231
documented as unused since 2000. Patch by Eric N. Vander Weele.
232232

233+
- Issue #29368: The extend() method is now called instead of the append()
234+
method when unpickle collections.deque and other list-like objects.
235+
This can speed up unpickling to 2 times.
236+
233237
- Issue #29338: The help of a builtin or extension class now includes the
234238
constructor signature if __text_signature__ is provided for the class.
235239

‎Modules/_pickle.c‎

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5807,7 +5807,9 @@ static int
58075807
do_append(UnpicklerObject *self, Py_ssize_t x)
58085808
{
58095809
PyObject *value;
5810+
PyObject *slice;
58105811
PyObject *list;
5812+
PyObject *result;
58115813
Py_ssize_t len, i;
58125814

58135815
len = Py_SIZE(self->stack);
@@ -5818,8 +5820,7 @@ do_append(UnpicklerObject *self, Py_ssize_t x)
58185820

58195821
list = self->stack->data[x - 1];
58205822

5821-
if (PyList_Check(list)) {
5822-
PyObject *slice;
5823+
if (PyList_CheckExact(list)) {
58235824
Py_ssize_t list_len;
58245825
int ret;
58255826

@@ -5832,27 +5833,48 @@ do_append(UnpicklerObject *self, Py_ssize_t x)
58325833
return ret;
58335834
}
58345835
else {
5835-
PyObject *append_func;
5836-
_Py_IDENTIFIER(append);
5837-
5838-
append_func = _PyObject_GetAttrId(list, &PyId_append);
5839-
if (append_func == NULL)
5840-
return -1;
5841-
for (i = x; i < len; i++) {
5842-
PyObject *result;
5843-
5844-
value = self->stack->data[i];
5845-
result = _Pickle_FastCall(append_func, value);
5846-
if (result == NULL) {
5847-
Pdata_clear(self->stack, i + 1);
5848-
Py_SIZE(self->stack) = x;
5849-
Py_DECREF(append_func);
5836+
PyObject *extend_func;
5837+
_Py_IDENTIFIER(extend);
5838+
5839+
extend_func = _PyObject_GetAttrId(list, &PyId_extend);
5840+
if (extend_func != NULL) {
5841+
slice = Pdata_poplist(self->stack, x);
5842+
if (!slice) {
5843+
Py_DECREF(extend_func);
58505844
return -1;
58515845
}
5846+
result = _Pickle_FastCall(extend_func, slice);
5847+
Py_DECREF(slice);
5848+
Py_DECREF(extend_func);
5849+
if (result == NULL)
5850+
return -1;
58525851
Py_DECREF(result);
58535852
}
5854-
Py_SIZE(self->stack) = x;
5855-
Py_DECREF(append_func);
5853+
else {
5854+
PyObject *append_func;
5855+
_Py_IDENTIFIER(append);
5856+
5857+
/* Even if the PEP 307 requires extend() and append() methods,
5858+
fall back on append() if the object has no extend() method
5859+
for backward compatibility. */
5860+
PyErr_Clear();
5861+
append_func = _PyObject_GetAttrId(list, &PyId_append);
5862+
if (append_func == NULL)
5863+
return -1;
5864+
for (i = x; i < len; i++) {
5865+
value = self->stack->data[i];
5866+
result = _Pickle_FastCall(append_func, value);
5867+
if (result == NULL) {
5868+
Pdata_clear(self->stack, i + 1);
5869+
Py_SIZE(self->stack) = x;
5870+
Py_DECREF(append_func);
5871+
return -1;
5872+
}
5873+
Py_DECREF(result);
5874+
}
5875+
Py_SIZE(self->stack) = x;
5876+
Py_DECREF(append_func);
5877+
}
58565878
}
58575879

58585880
return 0;

0 commit comments

Comments
 (0)