This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author jyasskin
Recipients amaury.forgeotdarc, benjamin.peterson, christian.heimes, collinwinter, jyasskin, ncoghlan, nnorwitz, rhettinger
Date 2008-03-03.00:29:43
SpamBayes Score 0.0029841356
Marked as misclassified No
Message-id <1204504186.96.0.866163577367.issue2179@psf.upfronthosting.co.za>
In-reply-to
Content
Here's a proof-of-concept patch that keeps the __exit__ method on the
stack. It uses ROT_TWO to stuff it under the context object instead of
storing it into a temporary. (Thanks Nick for pointing out that problem
before I had to waste time on it.) test_with passes, although I need to
update several more things and maybe fix a refleak.

The patch changes the compilation of:

    def with_(l):
        with l:
            pass

from

  4           0 LOAD_FAST                0 (l)
              3 DUP_TOP             
              4 LOAD_ATTR                0 (__exit__)
              7 STORE_FAST               1 (_[1])
             10 LOAD_ATTR                1 (__enter__)
             13 CALL_FUNCTION            0
             16 POP_TOP             
             17 SETUP_FINALLY            4 (to 24)

  5          20 POP_BLOCK           
             21 LOAD_CONST               0 (None)
        >>   24 LOAD_FAST                1 (_[1])
             27 DELETE_FAST              1 (_[1])
             30 WITH_CLEANUP        
             31 END_FINALLY         
             32 LOAD_CONST               0 (None)
             35 RETURN_VALUE        

to

  4           0 LOAD_FAST                0 (l)
              3 DUP_TOP             
              4 LOAD_ATTR                0 (__exit__)
              7 ROT_TWO             
              8 LOAD_ATTR                1 (__enter__)
             11 CALL_FUNCTION            0
             14 POP_TOP             
             15 SETUP_FINALLY            4 (to 22)

  5          18 POP_BLOCK           
             19 LOAD_CONST               0 (None)
        >>   22 WITH_CLEANUP        
             23 END_FINALLY         
             24 LOAD_CONST               0 (None)
             27 RETURN_VALUE        


And speeds it up from:

$ ./python.exe -m timeit -s 'import thread; lock =
thread.allocate_lock()' 'with lock: pass'
1000000 loops, best of 3: 0.832 usec per loop

to:

$ ./python.exe -m timeit -s 'import thread; lock =
thread.allocate_lock()' 'with lock: pass'
1000000 loops, best of 3: 0.762 usec per loop


That's only half of the way to parity with try/finally:

$ ./python.exe -m timeit -s 'import thread; lock =
thread.allocate_lock()' 'lock.acquire()' 'try: pass' 'finally:
lock.release()'
1000000 loops, best of 3: 0.638 usec per loop

What's strange is that calling __enter__ and __exit__ in a try/finally
block brings the speed back to the faster 'with' speed, even though they
call the same C functions:

$ ./python.exe -m timeit -s 'import thread; lock =
thread.allocate_lock()' 'lock.__enter__()' 'try: pass' 'finally:
lock.__exit__()'
1000000 loops, best of 3: 0.754 usec per loop

Any ideas?
History
Date User Action Args
2008-03-03 00:29:47jyasskinsetspambayes_score: 0.00298414 -> 0.0029841356
recipients: + jyasskin, nnorwitz, collinwinter, rhettinger, amaury.forgeotdarc, ncoghlan, christian.heimes, benjamin.peterson
2008-03-03 00:29:46jyasskinsetspambayes_score: 0.00298414 -> 0.00298414
messageid: <1204504186.96.0.866163577367.issue2179@psf.upfronthosting.co.za>
2008-03-03 00:29:46jyasskinlinkissue2179 messages
2008-03-03 00:29:45jyasskincreate