why does dead code costs time?

B

Bruno Dupuis

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
 
B

Bruno Dupuis

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.
 
B

Bruno Dupuis

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)
 
S

Steven D'Aprano

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.... x = 1
.... return None
....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_VALUE42


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

Steven D'Aprano

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.... return None
....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.
 
I

Ian Kelly

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)
....42

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

It's also worth noting that:
.... return
.... print(x)
....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:
.... if x:
.... return None
.... print(x)
....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:
.... if x:
.... return None
.... return None
.... print(x)
....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
 
T

Terry Reedy

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.
 
C

Chris Kaynor

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
 
B

Bruno Dupuis

On Wed, Dec 5, 2012 at 10:34 AM, Steven D'Aprano
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
 
R

Ramchandra Apte

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.

peehole haha
 
S

Steven D'Aprano

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.
 
R

rusi

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.

One line above the "cheers" (with a double-space said:
Double spaced crap from you again not so haha.

Do you find the sentiment expressed therein consistent with any of
your three meanings?
 
C

Cameron Simpson

| On Dec 13, 11:01 am, Steven D'Aprano <steve
| (e-mail address removed)> 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,
 
R

rusi

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)
 
C

Chris Angelico

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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,481
Members
44,900
Latest member
Nell636132

Latest Threads

Top