summaryrefslogtreecommitdiffstats
path: root/Modules
diff options
context:
space:
mode:
authorCollin Winter <collinw@gmail.com>2009-05-25 04:34:39 (GMT)
committerCollin Winter <collinw@gmail.com>2009-05-25 04:34:39 (GMT)
commit179bf213ea0432f2219c9b72ff4c4e18062fb588 (patch)
treecb370e4d53246cc57d55d70b787949a8f108e913 /Modules
parent87e5006d8c5974597df9e3074b95227187d32be3 (diff)
downloadcpython-179bf213ea0432f2219c9b72ff4c4e18062fb588.zip
cpython-179bf213ea0432f2219c9b72ff4c4e18062fb588.tar.gz
cpython-179bf213ea0432f2219c9b72ff4c4e18062fb588.tar.bz2
Issue 5670: special-case pickling of dicts. This nearly doubles the performance of dict pickling in cPickle.
Diffstat (limited to 'Modules')
-rw-r--r--Modules/cPickle.c87
1 files changed, 78 insertions, 9 deletions
diff --git a/Modules/cPickle.c b/Modules/cPickle.c
index a0e443e..fbb1888 100644
--- a/Modules/cPickle.c
+++ b/Modules/cPickle.c
@@ -1860,13 +1860,74 @@ BatchFailed:
return -1;
}
+/* This is a variant of batch_dict() above that specializes for dicts, with no
+ * support for dict subclasses. Like batch_dict(), we batch up chunks of
+ * MARK key value ... key value SETITEMS
+ * opcode sequences. Calling code should have arranged to first create an
+ * empty dict, or dict-like object, for the SETITEMS to operate on.
+ * Returns 0 on success, -1 on error.
+ *
+ * Note that this currently doesn't work for protocol 0.
+ */
+static int
+batch_dict_exact(Picklerobject *self, PyObject *obj)
+{
+ PyObject *key = NULL, *value = NULL;
+ int i;
+ Py_ssize_t dict_size, ppos = 0;
+
+ static char setitem = SETITEM;
+ static char setitems = SETITEMS;
+
+ assert(obj != NULL);
+ assert(self->proto > 0);
+
+ dict_size = PyDict_Size(obj);
+
+ /* Special-case len(d) == 1 to save space. */
+ if (dict_size == 1) {
+ PyDict_Next(obj, &ppos, &key, &value);
+ if (save(self, key, 0) < 0)
+ return -1;
+ if (save(self, value, 0) < 0)
+ return -1;
+ if (self->write_func(self, &setitem, 1) < 0)
+ return -1;
+ return 0;
+ }
+
+ /* Write in batches of BATCHSIZE. */
+ do {
+ i = 0;
+ if (self->write_func(self, &MARKv, 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 (self->write_func(self, &setitems, 1) < 0)
+ return -1;
+ if (PyDict_Size(obj) != dict_size) {
+ PyErr_Format(
+ PyExc_RuntimeError,
+ "dictionary changed size during iteration");
+ return -1;
+ }
+
+ } while (i == BATCHSIZE);
+ return 0;
+}
+
static int
save_dict(Picklerobject *self, PyObject *args)
{
int res = -1;
char s[3];
int len;
- PyObject *iter;
if (self->fast && !fast_save_enter(self, args))
goto finally;
@@ -1898,15 +1959,23 @@ save_dict(Picklerobject *self, PyObject *args)
goto finally;
/* Materialize the dict items. */
- iter = PyObject_CallMethod(args, "iteritems", "()");
- if (iter == NULL)
- goto finally;
- if (Py_EnterRecursiveCall(" while pickling an object") == 0)
- {
- res = batch_dict(self, iter);
- Py_LeaveRecursiveCall();
+ if (PyDict_CheckExact(args) && 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) {
+ res = batch_dict_exact(self, args);
+ Py_LeaveRecursiveCall();
+ }
+ } else {
+ PyObject *iter = PyObject_CallMethod(args, "iteritems", "()");
+ if (iter == NULL)
+ goto finally;
+ if (Py_EnterRecursiveCall(" while pickling an object") == 0) {
+ res = batch_dict(self, iter);
+ Py_LeaveRecursiveCall();
+ }
+ Py_DECREF(iter);
}
- Py_DECREF(iter);
finally:
if (self->fast && !fast_save_leave(self, args))