[Python-ideas] Disable all peephole optimizations

Ned Batchelder ned at nedbatchelder.com
Wed May 21 16:23:41 CEST 2014


On 5/21/14 8:21 AM, Jonas Wielicki wrote:
> On 21.05.2014 14:13, Steven D'Aprano wrote:
>> >On Wed, May 21, 2014 at 07:05:49AM -0400, Ned Batchelder wrote:
>>> >>** The problem
>>> >>
>>> >>A long-standing problem with CPython is that the peephole optimizer
>>> >>cannot be completely disabled.  Normally, peephole optimization is a
>>> >>good thing, it improves execution speed.  But in some situations, like
>>> >>coverage testing, it's more important to be able to reason about the
>>> >>code's execution.  I propose that we add a way to completely disable the
>>> >>optimizer.
>> >
>> >I'm not sure whether this is an argument for or against your proposal,
>> >but the continue statement shown below is*not*  dead code and should not
>> >be optimized out. The assert fails if you remove the continue statement.
>> >
>> >I don't have 3.4 on this machine to test with, but using 3.3, I can see
>> >no evidence that `continue` is optimized away.
> The logical continue is still there -- what happens is that the
> optimizer rewrites the `else` jump at the preceding `if` condition,
> which would normally point at the `continue` statement, to the beginning
> of the loop, because it would be a jump (to the continue) to a jump (to
> the for loop header).
>
> Thus, the actual continue statement is not reached, but logically the
> code does the same, because the only way continue would have been
> reached was transformed to a continue itself.
>
To make the details more explicit, here is the source again, and the 
disassembled code, with the original source interspersed:

    a = b = c = 0
    for n in range(100):
         if n % 2:
             if n % 4:
                 a += 1
             continue
         else:
             b += 1
         c += 1
    assert a == 50 and b == 50 and c == 50

Disassembled (Python 3.4, but the same effect is visible in 2.7, 3.3, etc):

a = b = c = 0
   1           0 LOAD_CONST               0 (0)
               3 DUP_TOP
               4 STORE_NAME               0 (a)
               7 DUP_TOP
               8 STORE_NAME               1 (b)
              11 STORE_NAME               2 (c)

for n in range(100):
   2          14 SETUP_LOOP              79 (to 96)
              17 LOAD_NAME                3 (range)
              20 LOAD_CONST               1 (100)
              23 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
              26 GET_ITER
         >>   27 FOR_ITER                65 (to 95)
              30 STORE_NAME               4 (n)

     if n % 2:
   3          33 LOAD_NAME                4 (n)
              36 LOAD_CONST               2 (2)
              39 BINARY_MODULO
              40 POP_JUMP_IF_FALSE       72

         if n % 4:
   4          43 LOAD_NAME                4 (n)
              46 LOAD_CONST               3 (4)
              49 BINARY_MODULO
              50 POP_JUMP_IF_FALSE       27

             a += 1
   5          53 LOAD_NAME                0 (a)
              56 LOAD_CONST               4 (1)
              59 INPLACE_ADD
              60 STORE_NAME               0 (a)
              63 JUMP_ABSOLUTE           27

         continue
   6          66 JUMP_ABSOLUTE           27
              69 JUMP_FORWARD            10 (to 82)

         b += 1
   8     >>   72 LOAD_NAME                1 (b)
              75 LOAD_CONST               4 (1)
              78 INPLACE_ADD
              79 STORE_NAME               1 (b)

     c += 1
   9     >>   82 LOAD_NAME                2 (c)
              85 LOAD_CONST               4 (1)
              88 INPLACE_ADD
              89 STORE_NAME               2 (c)
              92 JUMP_ABSOLUTE           27
         >>   95 POP_BLOCK

assert a == 50 and b == 50 and c == 50
  10     >>   96 LOAD_NAME                0 (a)
              99 LOAD_CONST               5 (50)
             102 COMPARE_OP               2 (==)
             105 POP_JUMP_IF_FALSE      132
             108 LOAD_NAME                1 (b)
             111 LOAD_CONST               5 (50)
             114 COMPARE_OP               2 (==)
             117 POP_JUMP_IF_FALSE      132
             120 LOAD_NAME                2 (c)
             123 LOAD_CONST               5 (50)
             126 COMPARE_OP               2 (==)
             129 POP_JUMP_IF_TRUE       138
         >>  132 LOAD_GLOBAL              5 (AssertionError)
             135 RAISE_VARARGS            1
         >>  138 LOAD_CONST               6 (None)
             141 RETURN_VALUE

Notice that line 6 (the continue) is unreachable, because the else-jump 
from line 4 has been turned into a jump to bytecode offset 27 (the for 
loop), and the end of line 5 has also been turned into a jump to 27, 
rather than letting it flow to line 6.   So line 6 still exists in the 
bytecode, but is never executed, leading tracing tools to indicate that 
line 6 is never executed.

--Ned.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20140521/78088118/attachment-0001.html>


More information about the Python-ideas mailing list