[Python-3000] Dict literal bytecode

Adam Olsen rhamph at gmail.com
Tue Mar 25 20:32:43 CET 2008


On Tue, Mar 25, 2008 at 12:35 PM, Alexander Belopolsky
<alexander.belopolsky at gmail.com> wrote:
> Python 3.0 set and dict literals produce very different bytecode:
>
>  >>> dis(lambda:{1,2,3})
>   1           0 LOAD_CONST               0 (1)
>               3 LOAD_CONST               1 (2)
>               6 LOAD_CONST               2 (3)
>               9 BUILD_SET                3
>              12 RETURN_VALUE
>
>  >>> dis(lambda:{1:1,2:2,3:3})
>   1           0 BUILD_MAP                3
>               3 LOAD_CONST               0 (1)
>               6 LOAD_CONST               0 (1)
>               9 STORE_MAP
>              10 LOAD_CONST               1 (2)
>              13 LOAD_CONST               1 (2)
>              16 STORE_MAP
>              17 LOAD_CONST               2 (3)
>              20 LOAD_CONST               2 (3)
>              23 STORE_MAP
>              24 RETURN_VALUE
>
>  I have recently proposed a patch that would make dict bytecode similar
>  to set.  (See http://bugs.python.org/issue2197).  I have later
>  realized that a similar idea has been proposed before
>  (http://bugs.python.org/issue402227) and rejected because it slowed
>  down pybench dict creation benchmark.  However, in my tests (on Linux
>  and Mac OS X) my patch gives a slight improvement 1-2% on the same
>  benchmark.
>
>  Two other issues have been raised with respect to my proposal:
>
>  1. The patch "changes semantics by executing all of the stores after the inputs
>  are generated."
>
>  2. All of the pending item pairs get built-up on an ever-growing stack
>  instead of
>  being consumed pairwise.
>
>  I have promised to think some more about these issues and here are my thoughts:
>
>  1. The only case I can think of when the change of semantics would be
>  noticeable is when keys have custom comparison/hash methods with
>  side-effects.  I doubt this can be an issue in real life and I don't
>  think python makes any guarantees in this respect.  If, however, it is
>  desirable to have this order specified (note that we are not talking
>  about the order of insertions, which is preserved, but whether keys
>  are created between insertions or before the first insertion) than it
>  is hard to justify the difference between set and dict literals
>  semantics.
>
>  2.  If I understand correctly how the code works, frame stack does not
>  grow during code execution.  The required stack size is precomputed
>  during calculation and the necessary array is allocated during
>  execution.  So the growing stack should not slow things down.  With
>  respect to memory usage, note that the frame stack allocation is
>  transient  while bytecode is loaded permanently.  In some applications
>  bytecode size reduction may be more important (in terms of peak memory
>  usage) than stack size increase for some frames.

I worry that there might be generated code using disgustingly large
literals.  Does batch creation still work for that?


>  There are further optimizations that I would like to try once this
>  issue is resolved.  (These optimizations are not strictly speaking
>  dependent on the acceptance of my patch, but a unification of dict and
>  set literals would make the coding easier.)   I understand that the
>  idea of making set literal produce a frozenset was accepted
>  <http://mail.python.org/pipermail/python-3000/2008-January/011814.html>.
>   Once that is implemented, it will be natural to fold constant set
>  literals in the peephole optimizer.   While a similar optimization
>  will not be immediately available for (mutable) dictionaries, the
>  following approach is conceivable: for the dicts with constant keys,
>  push a frozen set of keys constant on the stack preceded by the values
>  in the order that they will be stored in the hash table.  Indicate
>  this situation with a -1 argument to BUILD_MAP (the size of the
>  dictionary will be inferred from the size of the frozenset at the top
>  of the stack).  Now as long as the hash table implementation is
>  compatible between dicts and sets, dictionary creation will be a
>  simple copy operation with all the hard work done during compilation.

Guido's support for frozenset was later withdrawn:
http://mail.python.org/pipermail/python-3000/2008-January/011868.html

In certain contexts, such as "if i in {1, 2, 3}:", you can reuse the
literal regardless of whether it's a set or a frozenset.  I don't know
if anybody has begun implementing that though.

Playing around a bit, I've found that "_x = {1, 2, 3}; x = set(_x)"
(assuming _x were precomputed) is faster for larger literals, with the
break even point at about 10.  Perhaps you could investigate that as
well?

-- 
Adam Olsen, aka Rhamphoryncus


More information about the Python-3000 mailing list