[pypy-commit] pypy default: moved optimizer/overview.rst -> jit/optimizer.rst

plan_rich noreply at buildbot.pypy.org
Mon Mar 16 12:24:55 CET 2015


Author: Richard Plangger <rich at pasra.at>
Branch: 
Changeset: r76399:cea0639cfe6c
Date: 2015-03-16 11:48 +0100
http://bitbucket.org/pypy/pypy/changeset/cea0639cfe6c/

Log:	moved optimizer/overview.rst -> jit/optimizer.rst updated index.rst
	to link to it

diff --git a/rpython/doc/index.rst b/rpython/doc/index.rst
--- a/rpython/doc/index.rst
+++ b/rpython/doc/index.rst
@@ -63,7 +63,7 @@
    translation
    rtyper
    garbage_collection
-   optimizer/overview
+   jit/optimizer
 
 
 Indices and tables
diff --git a/rpython/doc/jit/optimizer.rst b/rpython/doc/jit/optimizer.rst
new file mode 100644
--- /dev/null
+++ b/rpython/doc/jit/optimizer.rst
@@ -0,0 +1,193 @@
+.. _trace_optimizer:
+
+Trace Optimzier
+===============
+
+Traces of user programs are not directly translated into machine code.
+The optimizer module implements several different semantic preserving
+transformations that either allow operations to be swept from the trace
+or convert them to operations that need less time or space.
+
+When you try to make sense of this module, this page might get you started.
+
+Before some optimizations are explained in more detail, it is essential to
+understand how traces look like.
+The optimizer comes with a test suit. It contains many trace
+examples and you might want to take a look at it 
+(in `rpython/jit/metainterp/optimizeopt/test/*.py`).
+The allowed operations can be found in `rpython/jit/metainterp/resoperation.py`.
+Here is an example of a trace:: 
+
+    [p0,i0,i1]
+    label(p0, i0, i1)
+    i2 = getarray_item_raw(p0, i0, descr=<Array Signed>)
+    i3 = int_add(i1,i2)
+    i4 = int_add(i0,1)
+    i5 = int_le(i4, 100) # less equal
+    guard_true(i5)
+    jump(p0, i4, i3)
+
+At the beginning it might be clumsy to read but it makes sense when you start
+to compare the Python code that constructed the trace::
+
+    from array import array
+    a = array('i',range(101))
+    sum = 0; i = 0
+    while i <= 100: # can be seen as label
+        sum += a[i]
+        i += 1
+        # jumps back to the while header
+
+There are better ways to the sum from ``[0..100]``, but it gives a better intuition on how
+traces are constructed than ``sum(range(101))``.
+Note that the trace syntax is the one used in the test suit. It is also very
+similar a printed trace at runtime. The first operation is called ``input``, the
+second a ``label``, the last is the backwards ``jump``.
+
+These instruction mention earlier are special:
+
+* ``input`` defines the input parameter type and name to enter the trace.
+* ``label`` is the instruction a ``jump`` can target. Label instructions have
+  a ``JitCellToken`` associated that uniquely identifies the label. Any jump
+  has a target token of a label.
+
+The token is saved in a so called `descriptor` of the instruction. It is
+not written explicitly because it is not done in the tests either. But
+the test suit creates a dummy token for each trace and adds it as descriptor
+to ``label`` and ``jump``. Of course the optimizer does the same at runtime,
+but using real values.
+The sample trace includes a descriptor in ``getarrayitem_raw``. Here it
+annotates the type of the array. It is a signed integer array.
+
+High level overview
+-------------------
+
+Before the JIT backend transforms any trace into a machine code, it tries to
+transform the trace into an equivalent trace that executes faster. The method
+`optimize_trace` in `rpython/jit/metainterp/optimizeopt/__init__.py` is the
+main entry point.
+
+Optimizations are applied in a sequence one after another and the base
+sequence is as follows::
+
+    intbounds:rewrite:virtualize:string:earlyforce:pure:heap:unroll
+
+Each of the colon separated name has a class attached. It is later
+instantiated as a subclass of `Optimization`. The `Optimizer` class 
+derives from the `Optimization` class and implements the control logic for
+the optimization. Most of the optimizations only require a single forward pass.
+The trace is 'propagated' in to each optimization using the method
+`propagate_forward`. Instruction by instruction then flows from the
+first optimization to the last optimization. The method `emit_operation`
+is called for every operation that is passed to the next optimizer.
+
+A frequently encountered pattern
+--------------------------------
+
+To find potential optimization targets it is necessary to know the instruction
+type. Simple solution is to switch using the operation number (= type)::
+
+    for op in operations:
+        if op.getopnum() == rop.INT_ADD:
+            # handle this instruction
+            pass
+        elif op.getopnum() == rop.INT_FLOOR_DIV:
+            pass
+        # and many more
+
+Things get worse if you start to match the arguments
+(is argument one constant and two variable or vice versa?). The pattern to tackle
+this code bloat is to move it to a separate method using
+`make_dispatcher_method`. It associates methods with instruction types::
+
+    class OptX(Optimization):
+        def prefix_INT_ADD(self, op):
+            pass # emit, transform, ...
+
+    dispatch_opt = make_dispatcher_method(OptX, 'prefix_',
+                                          default=OptX.emit_operation)
+    OptX.propagate_forward = dispatch_opt
+
+    optX = OptX()
+    for op in operations:
+        optX.propagate_forward(op)
+
+``propagate_forward`` searches for the method that is able to handle the instruction
+type. As an example `INT_ADD` will invoke `prefix_INT_ADD`. If there is no function
+for the instruction, it is routed to the default implementation (``emit_operation``
+in this example).
+
+Rewrite optimization
+--------------------
+
+The second optimization is called 'rewrite' an is commonly also known as
+strength reduction. A simple example would be that an integer multiplied
+by 2 is equivalent to the bits shifted to the left once
+(e.g. ``x * 2 == x << 1``). Not only strength reduction is done in this
+optimization but also boolean or arithmetic simplifications. Other examples
+would be: ``x & 0 == 0``, ``x - 0 == x``
+
+Whenever such an operation is encountered (e.g. ``x & 0``), no operation is
+emitted. Instead the variable of x is made equal to 0
+(= ``make_equal_to(op.result, 0)``). The variables found in a trace are
+instances of Box classes that can be found in
+`rpython/jit/metainterp/history.py`. `OptValue` wraps those variables again
+and maps the boxes to the optimization values in the optimizer. When a
+value is made equal, the box in the opt. value. This renders a new value
+to any further access.
+As a result the optimizer must provide the means to access the ``Box``
+instances. The instance method `make_args_key` returns the boxed value.
+
+**NOTE: that OptValue is likely to to be replaced in near future.**
+
+Pure optimization
+-----------------
+
+Is interwoven into the basic optimizer. It saves operations, results,
+arguments to be known to have pure semantics.
+
+Pure is free of side effects and it is referentially transparent
+(the operation can be replaced with its value without changing the program
+semantics). The operations marked as ALWAYS_PURE in `resoperation.py` is a
+subset of the SIDEEFFECT free operations. Operations such as new, new array,
+getfield_(raw/gc) are marked SIDEEFFECT free but not as ALWAYS_PURE.
+
+This can be seen as memoization technique. Once an operation proved to
+be 'pure' it is saved and should not be recomputed later.
+
+Unroll optimization
+-------------------
+
+A detailed description can be found the document
+`Loop-Aware Optimizations in PyPy's Tracing JIT`__
+
+.. __: http://www2.maths.lth.se/matematiklth/vision/publdb/reports/pdf/ardo-bolz-etal-dls-12.pdf
+
+This optimization does not fall into the traditional scheme of one forward
+pass only. In a nutshell it unrolls the trace _once_, connects the two
+traces (by inserting parameters into the jump and label of the peeled trace)
+and uses information to iron out allocations, propagate constants and
+do any other optimization currently present in the 'optimizeopt' module.
+
+It is prepended all optimizations and thus extends the Optimizer class
+and unrolls the loop once before it proceeds.
+
+
+What is missing?
+----------------
+
+* Guards are not explained
+* Several optimizations
+
+
+Further references
+------------------
+
+* `Allocation Removal by Partial Evaluation in a Tracing JIT`__
+* `Loop-Aware Optimizations in PyPy's Tracing JIT`__
+
+.. __: http://www.stups.uni-duesseldorf.de/mediawiki/images/b/b0/Pub-BoCuFiLePeRi2011.pdf
+.. __: http://www2.maths.lth.se/matematiklth/vision/publdb/reports/pdf/ardo-bolz-etal-dls-12.pdf
+
+
+


More information about the pypy-commit mailing list