Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > why does dead code costs time?

Reply
Thread Tools

why does dead code costs time?

 
 
Bruno Dupuis
Guest
Posts: n/a
 
      12-05-2012
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
 
Reply With Quote
 
 
 
 
Bruno Dupuis
Guest
Posts: n/a
 
      12-05-2012
On Wed, Dec 05, 2012 at 04:15:59PM +0000, Neil Cerutti wrote:
> On 2012-12-05, Bruno Dupuis <(E-Mail Removed)> 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
 
Reply With Quote
 
 
 
 
Bruno Dupuis
Guest
Posts: n/a
 
      12-05-2012
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
 
Reply With Quote
 
Steven D'Aprano
Guest
Posts: n/a
 
      12-05-2012
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:1 [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
 
Reply With Quote
 
Steven D'Aprano
Guest
Posts: n/a
 
      12-05-2012
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:1 [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
 
Reply With Quote
 
Ian Kelly
Guest
Posts: n/a
 
      12-05-2012
On Wed, Dec 5, 2012 at 10:34 AM, Steven D'Aprano
<(E-Mail Removed)> 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
 
Reply With Quote
 
Bruno Dupuis
Guest
Posts: n/a
 
      12-05-2012
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
> <(E-Mail Removed)> 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
 
Reply With Quote
 
Terry Reedy
Guest
Posts: n/a
 
      12-05-2012
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
>> <(E-Mail Removed)> 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

 
Reply With Quote
 
Chris Kaynor
Guest
Posts: n/a
 
      12-05-2012
On Wed, Dec 5, 2012 at 12:41 PM, Terry Reedy <(E-Mail Removed)> 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

 
Reply With Quote
 
Bruno Dupuis
Guest
Posts: n/a
 
      12-05-2012
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
> >><(E-Mail Removed)> 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
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
FAQ 5.38 Why does Perl let me delete read-only files? Why does "-i" clobber protected files? Isn't this a bug in Perl? PerlFAQ Server Perl Misc 0 02-11-2011 05:00 AM
Dead TCP/IP Stack = DEAD VISTA !! Skybuck Flying Windows 64bit 15 09-23-2007 07:54 PM
why why why why why Mr. SweatyFinger ASP .Net 4 12-21-2006 01:15 PM
findcontrol("PlaceHolderPrice") why why why why why why why why why why why Mr. SweatyFinger ASP .Net 2 12-02-2006 03:46 PM
Dead... the damn thing's dead... David Dean Computer Support 9 01-30-2004 02:54 AM



Advertisments