[issue2179] with should be as fast as try/finally

Jeffrey Yasskin report at bugs.python.org
Mon Mar 3 01:29:46 CET 2008


Jeffrey Yasskin added the comment:

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?

----------
keywords: +patch
Added file: http://bugs.python.org/file9589/faster_with.patch

__________________________________
Tracker <report at bugs.python.org>
<http://bugs.python.org/issue2179>
__________________________________


More information about the Python-bugs-list mailing list