[pypy-svn] r41213 - pypy/dist/pypy/doc

mwh at codespeak.net mwh at codespeak.net
Fri Mar 23 20:42:37 CET 2007


Author: mwh
Date: Fri Mar 23 20:42:34 2007
New Revision: 41213

Modified:
   pypy/dist/pypy/doc/interpreter-optimizations.txt
Log:
some stuff on the special bytecodes, adapted from the wp06 report, also a
conclusion type thing which could be more bombastic, i guess.


Modified: pypy/dist/pypy/doc/interpreter-optimizations.txt
==============================================================================
--- pypy/dist/pypy/doc/interpreter-optimizations.txt	(original)
+++ pypy/dist/pypy/doc/interpreter-optimizations.txt	Fri Mar 23 20:42:34 2007
@@ -279,9 +279,100 @@
 LOOKUP_METHOD & CALL_METHOD
 +++++++++++++++++++++++++++
 
-Write this!
+An unusual feature of Python's version of object oriented programming is the
+concept of a "bound method".  While the concept is clean and powerful, the
+allocation and initialization of the object is not without its performance cost.
+We have implemented a pair of bytecodes that alleviate this cost.
+
+For a given method call ``obj.meth(x, y)``, the standard bytecode looks like
+this::
+
+    LOAD_GLOBAL     obj      # push 'obj' on the stack
+    LOAD_ATTR       meth     # read the 'meth' attribute out of 'obj'
+    LOAD_GLOBAL     x        # push 'x' on the stack
+    LOAD_GLOBAL     y        # push 'y' on the stack
+    CALL_FUNCTION   2        # call the 'obj.meth' object with arguments x, y
+
+We improved this by keeping method lookup separated from method call, unlike
+some other approaches, but using the value stack as a cache instead of building
+a temporary object.  We extended the bytecode compiler to (optionally) generate
+the following code for ``obj.meth(x)``::
+
+    LOAD_GLOBAL     obj
+    LOOKUP_METHOD   meth
+    LOAD_GLOBAL     x
+    LOAD_GLOBAL     y
+    CALL_METHOD     2
+
+``LOOKUP_METHOD`` contains exactly the same attribute lookup logic as
+``LOAD_ATTR`` - thus fully preserving semantics - but pushes two values onto the
+stack instead of one.  These two values are an "inlined" version of the bound
+method object: the *im_func* and *im_self*, i.e.  respectively the underlying
+Python function object and a reference to ``obj``.  This is only possible when
+the attribute actually refers to a function object from the class; when this is
+not the case, ``LOOKUP_METHOD`` still pushes two values, but one *(im_func)* is
+simply the regular result that ``LOAD_ATTR`` would have returned, and the other
+*(im_self)* is a None placeholder.
+
+After pushing the arguments, the layout of the stack in the above
+example is as follows (the stack grows upwards):
+
++---------------------------------+
+| ``y`` *(2nd arg)*               |
++---------------------------------+
+| ``x`` *(1st arg)*               |
++---------------------------------+
+| ``obj`` *(im_self)*             |
++---------------------------------+
+| ``function object`` *(im_func)* |
++---------------------------------+
+
+The ``CALL_METHOD N`` bytecode emulates a bound method call by
+inspecting the *im_self* entry in the stack below the ``N`` arguments:
+if it is not None, then it is considered to be an additional first
+argument in the call to the *im_func* object from the stack.
 
 CALL_LIKELY_BUILTIN
 +++++++++++++++++++
 
-And this!
+A often heard "tip" for speeding up Python programs is to give an often used
+builtin a local name, since local lookups are faster than lookups of builtins,
+which involve doing two dictionary lookups: one in the globals dictionary and
+one in the the builtins dictionary. PyPy approaches this problem at the
+implementation level, with the introduction of the new ``CALL_LIKELY_BUILTIN``
+bytecode. This bytecode is produced by the compiler for a call whose target is
+the name of a builtin.  Since such a syntactic construct is very often actually
+invoking the expected builtin at run-time, this information can be used to make
+the call to the builtin directly, without going through any dictionary lookup.
+
+However, it can occur that the name is shadowed by a global name from the
+current module.  To catch this case, a special dictionary implementation for
+multidicts is introduced, which is used for the dictionaries of modules. This
+implementation keeps track which builtin name is shadowed by it.  The
+``CALL_LIKELY_BUILTIN`` bytecode asks the dictionary whether it is shadowing the
+builtin that is about to be called and asks the dictionary of ``__builtin__``
+whether the original builtin was changed.  These two checks are cheaper than
+full lookups.  In the common case, neither of these cases is true, so the
+builtin can be directly invoked.
+
+.. more here?
+
+Overall Effects
+===============
+
+The impact these various optimizations have on performance unsurprisingly
+depends on the program being.  Using the default multi-dict implementation that
+simply special cases string-keyed dictionaries is a clear win on all benchmarks,
+improving results by anything from 15-40 per cent.
+
+Another optimization, or rather set of optimizations, that has a uniformly good
+effect is the set of three 'method optimizations', i.e. shadow tracking, the
+method cache and the LOOKUP_METHOD and CALL_METHOD opcodes.  On a heavily
+object-oriented benchmark (richards) they combine to give a speed-up of nearly
+50%, and even on the extremely un-object-oriented pystone benchmark, the
+improvement is over 20%.
+
+.. waffles about ropes
+
+A build with all the generally useful optimizations turned on is between 1.5 and
+2.5 times faster than one without, depending on the benchmark.



More information about the Pypy-commit mailing list