New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The protocol > 0 of cPickle does not given stable dictionary values #57944
Comments
Hello, I am implementing a Python compiler (Nuitka) that is testing if when it compiles itself, it gives the same output. I have been using "protocol = 0" ever since with "pickle" module for historic reasons (gcc bug with raw strings lead me to believe it's better) and lately, I have changed to "protocol = 2" and cPickle. But then I noticed that my compile itself test now fail to give same code from pickling of dictionary constants. Imanaged and isolated the issue, and it's a Python2.7 regression, Python2.6 is fine: Observe this output from "cPickle.dumps" for a constant dictionary with one element: Protocol 0 : It seems that cPickle as of CPython2.7 does give a better stream for dictionaries it itself emitted. With CPython2.6 I observe no difference. My work-around is to "re-stream", "dumps" -> "loads" -> "dumps" with CPython2.7 for the time being. Can you either: Fix cPickle to treat the dictionaries the same, or enhance to core to produce the same dict as cPickle does? It appears at least some kind of efficiency might be missed out for marshall as well. |
marshal doesn't use the pickle protocols, so it's unlikely to be affected. |
It seems that there is an extra "BINPUT 2", whatever it does. I am attaching a variant that does pickletools.dis on the 3 dumps. Protocol 2 : |
Ah, right. BINPUT is used to memoize objects in case a restructive structure happens. You can use pickletools.optimize() to remove the useless BINPUTs out of a pickle stream. Note that 3.x is more consistent and always emits the BINPUTs. It seems 2.x's cPickle (which is really a weird piece of code) may do some premature optimization here. |
Sending my attached file "stream.py" through "2to3.py" it shows that CPython 3.2 doesn't exihibit the issue for either protocol, which may be because it's now "unicode" key, but as it's the only value I tried, I can't tell. Hope this helps. Regarding the "marshal", I presume, that somehow the dictionary when created via "marshal" (or compile, if no ".pyc" is involved?) first time is somehow less efficient to determine/stream that the one "cPickle" created. My assumption is that somehow, when creating the dictionary from cPickle, it is different (has to be), and that's interesting. And of course the easier to stream dictionary may be the nicer one at runtime as well. But that is just my speculation. Yours, |
I think the "premature" optimization comes down to that code: """
static int
put(Picklerobject *self, PyObject *ob)
{
if (Py_REFCNT(ob) < 2 || self->fast)
return 0;
} (put2() being the function which emits BINPUT) When you unpickle a dict, its contents are created anew and therefore the refcount is 1 => next pickling avoids emitting a BINPUT. |
I don't think so. marshal uses a much simpler and less general |
I see, if it's refcount dependent, that explains why it changes from interpreter provided dictionary and self-created one. So, I take, I should always call "pickletools.optimize( cPickle.dumps( value ))" then. Thanks, |
Closing the issue as it's really a quirk rather than a bug. Thanks for reporting, though. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: