Too Many if Statements?

Bengt Richter bokr at oz.net
Sun Feb 12 14:27:51 EST 2006


On Sat, 11 Feb 2006 15:40:49 -0500, "Terry Reedy" <tjreedy at udel.edu> wrote:

>
>"Steve Holden" <steve at holdenweb.com> wrote in message 
>news:dsid2f$b99$1 at sea.gmane.org...
>> Clearly it would be a good idea to remove whatever problem is causing
>> the error,
>
>The problem (see my post of the com_backpatch code) is writing a compound 
>statement (here a for loop) with a body so large as to require a jump of 
>more than 64K bytes in the compiled bytecode (ie, from the test at the top 
>of the loop to the code that follows after the loop).  Until the jump limit 
>is raised (likely a long wait ;-), the OP must factor some of the code out 
>of the loop.
>
Easy example:

 >>> def test(n):
 ...     while True:
 ...         try: co = compile('if x:\n'+ n*' a=1\n','','exec')
 ...         except Exception,e: break
 ...         n += 1
 ...     print 'Stopped at n=%s due to %s: %s'%(n, e.__class__.__name__,e)
 ...

get an idea of where to start that:

 >>> import dis
 >>> n=3
 >>> dis.dis(          compile('if x:\n'+ n*' a=1\n','','exec'))
   1           0 LOAD_NAME                0 (x)
               3 JUMP_IF_FALSE           22 (to 28)
               6 POP_TOP

   2           7 LOAD_CONST               0 (1)
              10 STORE_NAME               1 (a)

   3          13 LOAD_CONST               0 (1)
              16 STORE_NAME               1 (a)

   4          19 LOAD_CONST               0 (1)
              22 STORE_NAME               1 (a)
              25 JUMP_FORWARD             1 (to 29)
         >>   28 POP_TOP
         >>   29 LOAD_CONST               1 (None)
              32 RETURN_VALUE
 >>> (2**16-7)/(13-7)
 10921

back off 1

 >>> test(10920)
 Stopped at n=10922 due to SystemError: com_backpatch: offset too large

Decided to test the exact 65536 jump with code chunks of 16 byte-codes and one
chunk at the end to make 16 with the last JUMP_FORWARD.

 >>> n=4095
 >>> dis.dis(          compile('if x:\n'+ n*' a=1+2,4  \n'+' x=0,x','','exec'))
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 SystemError: com_backpatch: offset too large
 >>> n=4094
 >>> dis.dis(          compile('if x:\n'+ n*' a=1+2,4  \n'+' x=0,x','','exec'))
   1           0 LOAD_NAME                0 (x)
               3 JUMP_IF_FALSE        65520 (to 65526)
               6 POP_TOP

   2           7 LOAD_CONST               0 (1)
              10 LOAD_CONST               1 (2)
              13 BINARY_ADD
              14 LOAD_CONST               2 (4)
              17 BUILD_TUPLE              2
              20 STORE_NAME               1 (a)

   3          23 LOAD_CONST               0 (1)

So the corner case of 2**16 is ok. Believe it or not, I once discovered a compiler
error based on optimizing a 2**16-involving loop condition as if it were 0 and the
loop didn't execute! IIRC, I bumped into it processing an array of records with a stride of
exactly 2**n and it thought it could calculate a 16-bit number of strides for end of loop.
No good for arraysize/stridesize==2**16 ;-)

If the OP really HAD to, he could always (untested) break

    if cond:
        too
        large
    else:
        two
        large,also
into
    if cond:
        too
    else:
        two
    if cond:
        large
    else:
        large,also

but that reads gawdawfully. (So, I imagine, does about any code hitting the offset limit ;-)

If it's a matter of too many elifs, the OP could break that more readably, e.g. (untested)

    done=True
    if cond:
       bla
    elif c2:
       bla 2
    ...
    elif c456:
       bla456
    else:
        done=False

    more, done = not done,True
    if more and c457:
        bla c457
    elif c458:
        bla458
    ...
    elif c1012:
        bla1012
    else:
        done = False

    more, done = not done,True
    ... etc

But the OP should find another approach I think,
because this is still disgusting code ;-)

Regards,
Bengt Richter



More information about the Python-list mailing list