Index: Modules/_pickle.c =================================================================== --- Modules/_pickle.c (revision 71084) +++ Modules/_pickle.c (working copy) @@ -1551,6 +1551,62 @@ return status; } +static int +batch_dict_exact(PicklerObject *self, PyObject *obj) +{ + PyObject *key = NULL; + PyObject *value = NULL; + int i; + Py_ssize_t dict_size, ppos = 0; + + const char mark_op = MARK; + const char setitem_op = SETITEM; + const char setitems_op = SETITEMS; + + assert(obj != NULL); + assert(self->proto > 0); + + dict_size = PyDict_Size(obj); + if (dict_size == 1) { + /* Only one item to save */ + if (!PyDict_Next(obj, &ppos, &key, &value)) + return -1; /* This should never happen. */ + + if (save(self, key, 0) < 0) + return -1; + if (save(self, value, 0) < 0) + return -1; + if (pickler_write(self, &setitem_op, 1) < 0) + return -1; + + return 0; /* and we're done! */ + } + + /* More than one item to save; write them in batches of BATCHSIZE. */ + do { + i = 0; + if (pickler_write(self, &mark_op, 1) < 0) + return -1; + while (PyDict_Next(obj, &ppos, &key, &value)) { + if (save(self, key, 0) < 0) + return -1; + if (save(self, value, 0) < 0) + return -1; + if (++i == BATCHSIZE) + break; + } + if (pickler_write(self, &setitems_op, 1) < 0) + return -1; + if (Py_SIZE(obj) != dict_size) { + PyErr_Format(PyExc_RuntimeError, + "dictionary changed size during iteration"); + return -1; + } + } while (i == BATCHSIZE); + + return 0; +} + /* iter is an iterator giving (key, value) pairs, and we batch up chunks of * MARK key value ... key value SETITEMS * opcode sequences. Calling code should have arranged to first create an @@ -1687,7 +1743,6 @@ static int save_dict(PicklerObject *self, PyObject *obj) { - PyObject *items, *iter; char header[3]; int len; int status = 0; @@ -1717,16 +1772,31 @@ goto error; if (len != 0) { - /* Save the dict items. */ - items = PyObject_CallMethod(obj, "items", "()"); - if (items == NULL) - goto error; - iter = PyObject_GetIter(items); - Py_DECREF(items); - if (iter == NULL) - goto error; - status = batch_dict(self, iter); - Py_DECREF(iter); + if (PyDict_CheckExact(obj) && self->proto > 0) { + /* We can take certain shortcuts if we know this is a dict and + not a dict subclass. */ + if (Py_EnterRecursiveCall(" while pickling an object") == 0) { + status = batch_dict_exact(self, obj); + Py_LeaveRecursiveCall(); + } + } + else { + PyObject *items, *iter; + + /* Save the dict items. */ + items = PyObject_CallMethod(obj, "items", "()"); + if (items == NULL) + goto error; + iter = PyObject_GetIter(items); + Py_DECREF(items); + if (iter == NULL) + goto error; + if (Py_EnterRecursiveCall(" while pickling an object") == 0) { + status = batch_dict(self, iter); + Py_LeaveRecursiveCall(); + } + Py_DECREF(iter); + } } if (0) { @@ -2194,6 +2264,8 @@ PyObject *memo_key = NULL; int status = 0; + assert(obj != NULL); + if (Py_EnterRecursiveCall(" while pickling an object") < 0) return -1;