Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-32949: Better bytecodes for "with" statement. #5112

Closed
wants to merge 6 commits into from

Conversation

markshannon
Copy link
Member

@markshannon markshannon commented Jan 6, 2018

This PR cleans up the interpreter by generating two different code paths for exiting a with statement. One for normal exits and another for the exceptional case.

For this function:

def foo():
     with a:
        b

The bytecode generated changes from:

 2           0 LOAD_GLOBAL              0 (a)
             2 SETUP_WITH              10 (to 14)
             4 POP_TOP

 3           6 LOAD_GLOBAL              1 (b)
             8 POP_TOP
             10 POP_BLOCK
             12 BEGIN_FINALLY
        >>   14 WITH_CLEANUP_START
             16 WITH_CLEANUP_FINISH
             18 END_FINALLY
             20 LOAD_CONST               0 (None)
             22 RETURN_VALUE

to

 2           0 LOAD_GLOBAL              0 (a)
             2 SETUP_WITH    20 (to 24)
             4 POP_TOP
 3           6 LOAD_GLOBAL              1 (b)
              8 POP_TOP
             10 POP_BLOCK
             12 LOAD_CONST               0 (None)
             14 DUP_TOP
             16 DUP_TOP
             18 CALL_FUNCTION            3
             20 POP_TOP
             22 JUMP_FORWARD            16 (to 40)
        >>   24 WITH_EXCEPT_START
             26 POP_JUMP_IF_TRUE        30
             28 RERAISE
        >>   30 POP_TOP
             32 POP_TOP
             34 POP_TOP
             36 POP_EXCEPT
             38 POP_TOP
        >>   40 LOAD_CONST               0 (None)
             42 RETURN_VALUE

Although this superficially appears worse, consider the non-exceptional path, after the POP_BLOCK bytecode.
before:

             12 BEGIN_FINALLY
        >>   14 WITH_CLEANUP_START
             16 WITH_CLEANUP_FINISH
             18 END_FINALLY

after:

             12 LOAD_CONST               0 (None)
             14 DUP_TOP
             16 DUP_TOP
             18 CALL_FUNCTION            3
             20 POP_TOP
             22 JUMP_FORWARD            16 (to 40)

we have replaced several complex and inefficient bytecodes with a few more simpler and faster bytecodes.

https://bugs.python.org/issue32949

@serhiy-storchaka
Copy link
Member

Please open an issue for the discussion. I was going to make different changes in with-related opcodes after merging #5006, but your approach may be better. Needed some microbenchmarks for comparing implementations.

You can simplify and speed up the bytecode (get rid of LOAD_CONST and 2 DUP_TOPs) if make WITH_SETUP pushing 3 Nones after pushing a block.

@markshannon markshannon changed the title Better bytecodes for "with" statement. bpo-32949: Better bytecodes for "with" statement. Mar 13, 2018
@serhiy-storchaka
Copy link
Member

@markshannon, are you aware that tests are failed with your current code? And merging with master will add failures in new tests.

@markshannon
Copy link
Member Author

I've decided that generating new bytecodes for the with statement is best done at the same time as for try-finally. There is a lot of similarity between with and try-finally at the bytecode level.
See #6641 which replaces this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants