why does dead code costs time?

Discussion in 'Python' started by Bruno Dupuis, Dec 5, 2012.

  1. Bruno Dupuis

    Bruno Dupuis Guest

    Hi,

    I'm interested in compilers optimizations, so I study python compilation process

    I ran that script:

    import timeit

    def f(x):
    return None

    def g(x):
    return None
    print(x)

    number = 10000

    print(timeit.timeit('f(1)',setup="from __main__ import f", number=number))
    print(timeit.timeit('g(1)',setup="from __main__ import g", number=number))

    print(dis.dis(f))
    print(dis.dis(g))

    It gives this output:

    0.003460251959040761
    0.004164454061537981
    17 0 LOAD_CONST 0 (None)
    3 RETURN_VALUE
    None
    20 0 LOAD_GLOBAL 0 (None)
    3 RETURN_VALUE

    21 4 LOAD_GLOBAL 1 (print)
    7 LOAD_FAST 0 (x)
    10 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
    13 POP_TOP
    None

    I do not understand why the dead code `print(x)` takes time (~20% in
    that case). As we see in the opcode, a call to g(1) returns immediately, so
    there should be no delay at all. Where am i wrong?

    mmhh... it comes to me now that the gap must be in function loading time...
    I'll check ceval.c

    However, isn't there a room for a slight optim here? (in this case, the
    dead code is obvious, but it may be hidden by complex loops and
    conditions)

    Cheers


    --
    Bruno Dupuis
    Bruno Dupuis, Dec 5, 2012
    #1
    1. Advertising

  2. Bruno Dupuis

    Bruno Dupuis Guest

    On Wed, Dec 05, 2012 at 04:15:59PM +0000, Neil Cerutti wrote:
    > On 2012-12-05, Bruno Dupuis <> wrote:
    > > Hi,
    > >
    > > I'm interested in compilers optimizations, so I study python
    > > compilation process
    > >
    > > I ran that script:
    > >
    > > import timeit
    > >
    > > def f(x):
    > > return None
    > >
    > > def g(x):
    > > return None
    > > print(x)
    > >
    > > number = 10000
    > >
    > > print(timeit.timeit('f(1)',setup="from __main__ import f", number=number))
    > > print(timeit.timeit('g(1)',setup="from __main__ import g", number=number))
    > >
    > > print(dis.dis(f))
    > > print(dis.dis(g))
    > >
    > > It gives this output:
    > >
    > > 0.003460251959040761
    > > 0.004164454061537981
    > > 17 0 LOAD_CONST 0 (None)
    > > 3 RETURN_VALUE
    > > None
    > > 20 0 LOAD_GLOBAL 0 (None)
    > > 3 RETURN_VALUE
    > >
    > > 21 4 LOAD_GLOBAL 1 (print)
    > > 7 LOAD_FAST 0 (x)
    > > 10 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
    > > 13 POP_TOP
    > > None
    > >
    > > I do not understand why the dead code `print(x)` takes time (~20% in
    > > that case). As we see in the opcode, a call to g(1) returns immediately, so
    > > there should be no delay at all. Where am i wrong?
    > >
    > > mmhh... it comes to me now that the gap must be in function loading time...
    > > I'll check ceval.c
    > >
    > > However, isn't there a room for a slight optim here? (in this case, the
    > > dead code is obvious, but it may be hidden by complex loops and
    > > conditions)

    >
    > Maybe it's the difference between LOAD_CONST and LOAD_GLOBAL. We
    > can wonder why g uses the latter.


    Good point! I didn't even noticed that. It's weird... Maybe the
    difference comes from a peehole optim on f which is not possible on g as
    g is to complex.

    >
    > --
    > Neil Cerutti
    > --
    > http://mail.python.org/mailman/listinfo/python-list


    --
    Bruno Dupuis
    Bruno Dupuis, Dec 5, 2012
    #2
    1. Advertising

  3. Bruno Dupuis

    Bruno Dupuis Guest

    On Wed, Dec 05, 2012 at 05:40:51PM +0100, Bruno Dupuis wrote:
    > On Wed, Dec 05, 2012 at 04:15:59PM +0000, Neil Cerutti wrote:
    > > Maybe it's the difference between LOAD_CONST and LOAD_GLOBAL. We
    > > can wonder why g uses the latter.

    >
    > Good point! I didn't even noticed that. It's weird... Maybe the
    > difference comes from a peehole optim on f which is not possible on g as
    > g is to complex.
    >


    Neil, you were right, thanks. I patched peehole.c to remove this optim, and
    now the figures are the same. I investigate to find out why the latter
    function is not optimized the same way (and if it can be, I'll propose a
    patch for that)

    --
    Bruno Dupuis
    Bruno Dupuis, Dec 5, 2012
    #3
  4. On Wed, 05 Dec 2012 16:46:39 +0100, Bruno Dupuis wrote:

    > Hi,
    >
    > I'm interested in compilers optimizations, so I study python compilation
    > process
    >
    > I ran that script:
    >
    > import timeit
    >
    > def f(x):
    > return None
    >
    > def g(x):
    > return None
    > print(x)
    >
    > number = 10000
    >
    > print(timeit.timeit('f(1)',setup="from __main__ import f",
    > number=number)) print(timeit.timeit('g(1)',setup="from __main__
    > import g", number=number))
    >
    > print(dis.dis(f))
    > print(dis.dis(g))
    >
    > It gives this output:
    >
    > 0.003460251959040761
    > 0.004164454061537981
    > 17 0 LOAD_CONST 0 (None)
    > 3 RETURN_VALUE
    > None
    > 20 0 LOAD_GLOBAL 0 (None)
    > 3 RETURN_VALUE
    >
    > 21 4 LOAD_GLOBAL 1 (print)
    > 7 LOAD_FAST 0 (x)
    > 10 CALL_FUNCTION 1 (1 positional, 0 keyword
    > pair) 13 POP_TOP
    > None
    >
    > I do not understand why the dead code `print(x)` takes time (~20% in
    > that case). As we see in the opcode, a call to g(1) returns immediately,
    > so there should be no delay at all. Where am i wrong?


    The difference is almost certain between the LOAD_CONST and the
    LOAD_GLOBAL.

    As to *why* there is such a difference, I believe that's a leftover from
    early Python days when None was not a keyword and could be reassigned.


    [steve@ando ~]$ python1.5
    Python 1.5.2 (#1, Aug 27 2012, 09:09:18) [GCC 4.1.2 20080704 (Red Hat
    4.1.2-52)] on linux2
    Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
    >>> from dis import dis
    >>>
    >>> def h():

    .... x = 1
    .... return None
    ....
    >>> dis(h)

    0 SET_LINENO 1

    3 SET_LINENO 2
    6 LOAD_CONST 1 (1)
    9 STORE_FAST 0 (x)

    12 SET_LINENO 3
    15 LOAD_GLOBAL 1 (None)
    18 RETURN_VALUE
    19 LOAD_CONST 0 (None)
    22 RETURN_VALUE
    >>> None = 42
    >>> h()

    42


    Now that None is a keyword, it should always be a LOAD_CONST.


    --
    Steven
    Steven D'Aprano, Dec 5, 2012
    #4
  5. On Wed, 05 Dec 2012 17:34:57 +0000, Steven D'Aprano wrote:

    > I believe that's a leftover from
    > early Python days when None was not a keyword and could be reassigned.


    Oops! Wrong copy and paste! Here's a simpler version:

    [steve@ando ~]$ python1.5
    Python 1.5.2 (#1, Aug 27 2012, 09:09:18) [GCC 4.1.2 20080704 (Red Hat
    4.1.2-52)] on linux2
    Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
    >>> from dis import dis
    >>> def h():

    .... return None
    ....
    >>> dis(h)

    0 SET_LINENO 1

    3 SET_LINENO 2
    6 LOAD_GLOBAL 0 (None)
    9 RETURN_VALUE
    10 LOAD_CONST 0 (None)
    13 RETURN_VALUE


    The conclusion remains the same: calling LOAD_GLOBAL None is likely a
    fossil from ancient Python before it was a keyword.


    --
    Steven
    Steven D'Aprano, Dec 5, 2012
    #5
  6. Bruno Dupuis

    Ian Kelly Guest

    On Wed, Dec 5, 2012 at 10:34 AM, Steven D'Aprano
    <> wrote:
    > The difference is almost certain between the LOAD_CONST and the
    > LOAD_GLOBAL.
    >
    > As to *why* there is such a difference, I believe that's a leftover from
    > early Python days when None was not a keyword and could be reassigned.


    I think this should even be considered a bug, not just a missing
    optimization. Consider:

    >>> globals()['None'] = 42
    >>> def f(x):

    .... return None
    .... print(x)
    ....
    >>> f('test')

    42

    The use of the LOAD_GLOBAL allows None to effectively be reassigned.

    It's also worth noting that:

    >>> def f(x):

    .... return
    .... print(x)
    ....
    >>> dis.dis(f)

    2 0 LOAD_CONST 0 (None)
    3 RETURN_VALUE

    3 4 LOAD_GLOBAL 0 (print)
    7 LOAD_FAST 0 (x)
    10 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
    13 POP_TOP

    So if you just write 'return' rather than 'return None', you get the
    correct bytecode. Additionally, the use of LOAD_GLOBAL instead of
    LOAD_CONST seems to be linked to having unreachable code at the end of
    the function. This is fine:

    >>> def f(x):

    .... if x:
    .... return None
    .... print(x)
    ....
    >>> dis.dis(f)

    2 0 LOAD_FAST 0 (x)
    3 POP_JUMP_IF_FALSE 10

    3 6 LOAD_CONST 0 (None)
    9 RETURN_VALUE

    4 >> 10 LOAD_GLOBAL 1 (print)
    13 LOAD_FAST 0 (x)
    16 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
    19 POP_TOP
    20 LOAD_CONST 0 (None)
    23 RETURN_VALUE

    But this is not. Note here that *both* loads of None become
    LOAD_GLOBAL in this case:

    >>> def f(x):

    .... if x:
    .... return None
    .... return None
    .... print(x)
    ....
    >>> dis.dis(f)

    2 0 LOAD_FAST 0 (x)
    3 POP_JUMP_IF_FALSE 13

    3 6 LOAD_GLOBAL 0 (None)
    9 RETURN_VALUE
    10 JUMP_FORWARD 0 (to 13)

    4 >> 13 LOAD_GLOBAL 0 (None)
    16 RETURN_VALUE

    5 17 LOAD_GLOBAL 1 (print)
    20 LOAD_FAST 0 (x)
    23 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
    26 POP_TOP
    Ian Kelly, Dec 5, 2012
    #6
  7. Bruno Dupuis

    Bruno Dupuis Guest

    On Wed, Dec 05, 2012 at 10:59:26AM -0700, Ian Kelly wrote:
    > On Wed, Dec 5, 2012 at 10:34 AM, Steven D'Aprano
    > <> wrote:
    > > The difference is almost certain between the LOAD_CONST and the
    > > LOAD_GLOBAL.
    > >
    > > As to *why* there is such a difference, I believe that's a leftover from
    > > early Python days when None was not a keyword and could be reassigned.

    >
    > I think this should even be considered a bug, not just a missing
    > optimization. Consider:


    This is definitely a bug

    > >>> globals()['None'] = 42
    > >>> def f(x):

    > ... return None
    > ... print(x)
    > ...
    > >>> f('test')

    > 42


    This one is pretty scary

    The difference between `return None` and `return` leads to inconsistency and
    is in contradiction with the specs, AFAIK. I'm glad we pointed this out.

    --
    Bruno Dupuis
    Bruno Dupuis, Dec 5, 2012
    #7
  8. Bruno Dupuis

    Terry Reedy Guest

    On 12/5/2012 1:24 PM, Bruno Dupuis wrote:
    > On Wed, Dec 05, 2012 at 10:59:26AM -0700, Ian Kelly wrote:
    >> On Wed, Dec 5, 2012 at 10:34 AM, Steven D'Aprano
    >> <> wrote:
    >>> The difference is almost certain between the LOAD_CONST and the
    >>> LOAD_GLOBAL.
    >>>
    >>> As to *why* there is such a difference, I believe that's a leftover from
    >>> early Python days when None was not a keyword and could be reassigned.

    >>
    >> I think this should even be considered a bug, not just a missing
    >> optimization. Consider:

    >
    > This is definitely a bug
    >
    >>>>> globals()['None'] = 42
    >>>>> def f(x):

    >> ... return None
    >> ... print(x)
    >> ...
    >>>>> f('test')

    >> 42

    >
    > This one is pretty scary
    >
    > The difference between `return None` and `return` leads to inconsistency and
    > is in contradiction with the specs, AFAIK. I'm glad we pointed this out.


    You did not specify version, but I confirmed in 3.3.0. Please open a
    tracker issue.

    --
    Terry Jan Reedy
    Terry Reedy, Dec 5, 2012
    #8
  9. Bruno Dupuis

    Chris Kaynor Guest

    On Wed, Dec 5, 2012 at 12:41 PM, Terry Reedy <> wrote:

    > On 12/5/2012 1:24 PM, Bruno Dupuis wrote:
    >
    >> On Wed, Dec 05, 2012 at 10:59:26AM -0700, Ian Kelly wrote:
    >>>
    >>> I think this should even be considered a bug, not just a missing
    >>> optimization. Consider:
    >>>

    >>
    >> This is definitely a bug
    >>
    >> globals()['None'] = 42
    >>>>>> def f(x):
    >>>>>>
    >>>>> ... return None
    >>> ... print(x)
    >>> ...
    >>>
    >>>> f('test')
    >>>>>>
    >>>>> 42
    >>>

    >>
    >> This one is pretty scary
    >>
    >> The difference between `return None` and `return` leads to inconsistency
    >> and
    >> is in contradiction with the specs, AFAIK. I'm glad we pointed this out.
    >>

    >
    > You did not specify version, but I confirmed in 3.3.0. Please open a
    > tracker issue.



    It also occurs in 2.6.4
    Chris Kaynor, Dec 5, 2012
    #9
  10. Bruno Dupuis

    Bruno Dupuis Guest

    On Wed, Dec 05, 2012 at 03:41:19PM -0500, Terry Reedy wrote:
    > On 12/5/2012 1:24 PM, Bruno Dupuis wrote:
    > >On Wed, Dec 05, 2012 at 10:59:26AM -0700, Ian Kelly wrote:
    > >>On Wed, Dec 5, 2012 at 10:34 AM, Steven D'Aprano
    > >><> wrote:
    > >>>The difference is almost certain between the LOAD_CONST and the
    > >>>LOAD_GLOBAL.
    > >>>
    > >>>As to *why* there is such a difference, I believe that's a leftover from
    > >>>early Python days when None was not a keyword and could be reassigned.
    > >>
    > >>I think this should even be considered a bug, not just a missing
    > >>optimization. Consider:

    > >
    > >This is definitely a bug
    > >
    > >>>>>globals()['None'] = 42
    > >>>>>def f(x):
    > >>... return None
    > >>... print(x)
    > >>...
    > >>>>>f('test')
    > >>42

    > >
    > >This one is pretty scary
    > >
    > >The difference between `return None` and `return` leads to inconsistency and
    > >is in contradiction with the specs, AFAIK. I'm glad we pointed this out.

    >
    > You did not specify version, but I confirmed in 3.3.0. Please open a
    > tracker issue.


    It is also in 2.7 and 3.4 head, I didn't test other versions. I forgot
    to mention here the issue I have just opened:
    http://bugs.python.org/issue16619

    --
    Bruno Dupuis
    Bruno Dupuis, Dec 5, 2012
    #10
  11. Bruno Dupuis

    Bruno Dupuis Guest

    Bruno Dupuis, Dec 5, 2012
    #11
  12. On Wednesday, 5 December 2012 22:10:51 UTC+5:30, Bruno Dupuis wrote:
    > On Wed, Dec 05, 2012 at 04:15:59PM +0000, Neil Cerutti wrote:
    >
    > > On 2012-12-05, Bruno Dupuis <> wrote:

    >
    > > > Hi,

    >
    > > >

    >
    > > > I'm interested in compilers optimizations, so I study python

    >
    > > > compilation process

    >
    > > >

    >
    > > > I ran that script:

    >
    > > >

    >
    > > > import timeit

    >
    > > >

    >
    > > > def f(x):

    >
    > > > return None

    >
    > > >

    >
    > > > def g(x):

    >
    > > > return None

    >
    > > > print(x)

    >
    > > >

    >
    > > > number = 10000

    >
    > > >

    >
    > > > print(timeit.timeit('f(1)',setup="from __main__ import f", number=number))

    >
    > > > print(timeit.timeit('g(1)',setup="from __main__ import g", number=number))

    >
    > > >

    >
    > > > print(dis.dis(f))

    >
    > > > print(dis.dis(g))

    >
    > > >

    >
    > > > It gives this output:

    >
    > > >

    >
    > > > 0.003460251959040761

    >
    > > > 0.004164454061537981

    >
    > > > 17 0 LOAD_CONST 0 (None)

    >
    > > > 3 RETURN_VALUE

    >
    > > > None

    >
    > > > 20 0 LOAD_GLOBAL 0 (None)

    >
    > > > 3 RETURN_VALUE

    >
    > > >

    >
    > > > 21 4 LOAD_GLOBAL 1 (print)

    >
    > > > 7 LOAD_FAST 0 (x)

    >
    > > > 10 CALL_FUNCTION 1 (1 positional, 0 keyword pair)

    >
    > > > 13 POP_TOP

    >
    > > > None

    >
    > > >

    >
    > > > I do not understand why the dead code `print(x)` takes time (~20% in

    >
    > > > that case). As we see in the opcode, a call to g(1) returns immediately, so

    >
    > > > there should be no delay at all. Where am i wrong?

    >
    > > >

    >
    > > > mmhh... it comes to me now that the gap must be in function loading time...

    >
    > > > I'll check ceval.c

    >
    > > >

    >
    > > > However, isn't there a room for a slight optim here? (in this case, the

    >
    > > > dead code is obvious, but it may be hidden by complex loops and

    >
    > > > conditions)

    >
    > >

    >
    > > Maybe it's the difference between LOAD_CONST and LOAD_GLOBAL. We

    >
    > > can wonder why g uses the latter.

    >
    >
    >
    > Good point! I didn't even noticed that. It's weird... Maybe the
    >
    > difference comes from a peehole optim on f which is not possible on g as
    >
    > g is to complex.
    >
    >
    >
    > >

    >
    > > --

    >
    > > Neil Cerutti

    >
    > > --

    >
    > > http://mail.python.org/mailman/listinfo/python-list

    >
    >
    >
    > --
    >
    > Bruno Dupuis


    peehole haha
    Ramchandra Apte, Dec 9, 2012
    #12
  13. On 09/12/2012 14:11, Ramchandra Apte wrote:
    >
    > peehole haha
    >


    Double spaced crap from you again not so haha.

    --
    Cheers.

    Mark Lawrence.
    Mark Lawrence, Dec 9, 2012
    #13
  14. On Sunday, 9 December 2012 22:17:09 UTC+5:30, Mark Lawrence wrote:
    > On 09/12/2012 14:11, Ramchandra Apte wrote:
    >
    > >

    >
    > > peehole haha

    >
    > >

    >
    > Double spaced crap from you again not so haha.
    >
    > --
    >
    > Cheers.
    >
    > Mark Lawrence.


    haha. What does "Cheers" mean?
    Ramchandra Apte, Dec 13, 2012
    #14
  15. On Sunday, 9 December 2012 22:17:09 UTC+5:30, Mark Lawrence wrote:
    > On 09/12/2012 14:11, Ramchandra Apte wrote:
    >
    > >

    >
    > > peehole haha

    >
    > >

    >
    > Double spaced crap from you again not so haha.
    >
    > --
    >
    > Cheers.
    >
    > Mark Lawrence.


    haha. What does "Cheers" mean?
    Ramchandra Apte, Dec 13, 2012
    #15
  16. On Wed, 12 Dec 2012 21:23:47 -0800, Ramchandra Apte wrote:

    >> Cheers.
    >>
    >> Mark Lawrence.

    >
    > haha. What does "Cheers" mean?


    It is an exclamation expressing good wishes. In particular, good wishes
    before drinking. Think of it as a variation on:

    "Good health to you"
    "Best wishes"
    "Sincerest regards"

    only less formal.


    Does the Internet not work where you are? Googling for "define:cheers" or
    "definition cheers" should have answered that question.



    --
    Steven
    Steven D'Aprano, Dec 13, 2012
    #16
  17. Bruno Dupuis

    rusi Guest

    On Dec 13, 11:01 am, Steven D'Aprano <steve
    > wrote:
    > On Wed, 12 Dec 2012 21:23:47 -0800, Ramchandra Apte wrote:
    > >> Cheers.

    >
    > >> Mark Lawrence.

    >
    > > haha. What does "Cheers" mean?

    >
    > It is an exclamation expressing good wishes. In particular, good wishes
    > before drinking. Think of it as a variation on:
    >
    > "Good health to you"
    > "Best wishes"
    > "Sincerest regards"
    >
    > only less formal.
    >
    > Does the Internet not work where you are? Googling for "define:cheers" or
    > "definition cheers" should have answered that question.
    >
    > --
    > Steven


    One line above the "cheers" (with a double-space <wink>) we find:

    > Double spaced crap from you again not so haha.


    Do you find the sentiment expressed therein consistent with any of
    your three meanings?
    rusi, Dec 13, 2012
    #17
  18. On 12Dec2012 22:27, rusi <> wrote:
    | On Dec 13, 11:01 am, Steven D'Aprano <steve
    | > wrote:
    | > On Wed, 12 Dec 2012 21:23:47 -0800, Ramchandra Apte wrote:
    | > >> Cheers.
    | > >> Mark Lawrence.
    | >
    | > > haha. What does "Cheers" mean?
    | >
    | > It is an exclamation expressing good wishes. In particular, good wishes
    | > before drinking. [...]
    |
    | One line above the "cheers" (with a double-space <wink>) we find:
    |
    | > Double spaced crap from you again not so haha.
    |
    | Do you find the sentiment expressed therein consistent with any of
    | your three meanings?

    Nope. But they're separate sentiments (weasel words:)

    I tend to end messages with "Cheers" myself. Though I also tend to strip
    it out if I am being annoyed. As you say, it is inconsistent.

    Cheers,
    --
    Cameron Simpson <>

    It looked good-natured, she thought; Still it had very long claws and a
    great many teeth, so she felt it ought to be treated with respect.
    Cameron Simpson, Dec 13, 2012
    #18
  19. Bruno Dupuis

    rusi Guest

    On Dec 13, 11:51 am, Cameron Simpson <> wrote:
    > It looked good-natured, she thought;  Still it had very long claws and a
    > great many teeth, so she felt it ought to be treated with respect.


    heh!

    If only we could respect without such coercion(s)
    rusi, Dec 13, 2012
    #19
  20. On Thu, Dec 13, 2012 at 5:59 PM, rusi <> wrote:
    > On Dec 13, 11:51 am, Cameron Simpson <> wrote:
    >> It looked good-natured, she thought; Still it had very long claws and a
    >> great many teeth, so she felt it ought to be treated with respect.

    >
    > heh!
    >
    > If only we could respect without such coercion(s)


    Even if you don't respect his claws and teeth, you should respect the
    fact that, as he says, "we're all mad here"...

    But this is quite off topic, both off the thread's subject line and
    the list's purpose.

    ChrisA
    Chris Angelico, Dec 13, 2012
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Johnny Williams
    Replies:
    0
    Views:
    444
    Johnny Williams
    Dec 2, 2003
  2. Curt_C [MVP]
    Replies:
    2
    Views:
    388
    Charlie@CBFC
    May 5, 2004
  3. Chris

    CAL and costs

    Chris, Feb 8, 2006, in forum: ASP .Net
    Replies:
    9
    Views:
    454
    Eliyahu Goldin
    Feb 9, 2006
  4. Diana Sanders
    Replies:
    0
    Views:
    367
    Diana Sanders
    Dec 2, 2003
  5. Mr. SweatyFinger
    Replies:
    2
    Views:
    1,769
    Smokey Grindel
    Dec 2, 2006
Loading...

Share This Page