Why is if True slower than if 1?
Asked Answered
S

2

38

Why is if True slower than if 1 in Python? Shouldn't if True be faster than if 1?

I was trying to learn the timeit module. Starting with the basics, I tried these:

>>> def test1():
...     if True:
...         return 1
...     else:
...         return 0

>>> print timeit("test1()", setup = "from __main__ import test1")
0.193144083023


>>> def test2():
...     if 1:
...         return 1
...     else:
...         return 0

>>> print timeit("test2()", setup = "from __main__ import test2")
0.162086009979


>>> def test3():
...     if True:
...             return True
...     else:
...             return False

>>> print timeit("test3()", setup = "from __main__ import test3")
0.214574098587

>>> def test4():
...     if 1:
...             return True
...     else:
...             return False

>>> print timeit("test4()", setup = "from __main__ import test4")
0.160849094391

I am confused by these things:

  1. According to the response from Mr. Sylvain Defresne in this question, everything is implicitly converted to a bool first and then checked. So why is if True slower than if 1?
  2. Why is test3 slower than test1 even though only the return values are different?
  3. Like Question 2, but why is test4 a little faster than test2?

NOTE: I ran timeit three times and took the average of the results, then posted the times here along with the code.

This question does not relate to how to do micro benchmarking(which I did in this example but I also understand that it is too basic) but why checking a 'True' variable is slower than a constant.

Slumlord answered 8/8, 2013 at 10:42 Comment(5)
I think your tests are too small. And the average of three runs isn't enough :pSebastian
I understand :) even the test case is too basic to think. However, we need to start somewhere right :)Slumlord
possible duplicate of How long does a microbenchmark need to run?Eisenach
@JarrodRoberson Thanks for the link :-) However, my question is not related to generic micro bench marks. It is about why does using a keyword to check for boolean is slower than check for a constant.Slumlord
True takes 4 times longer to type than the number 1Annabell
M
43

True and False are not keywords in Python 2.

They must resolve at runtime. This has been changed in Python 3

Same test on Python 3:

>>> timeit.timeit('test1()',setup="from __main__ import test1", number=10000000)
2.806439919999889
>>> timeit.timeit('test2()',setup="from __main__ import test2", number=10000000)
2.801301520000038
>>> timeit.timeit('test3()',setup="from __main__ import test3", number=10000000)
2.7952816800000164
>>> timeit.timeit('test4()',setup="from __main__ import test4", number=10000000)
2.7862537199999906

Time error is in 1%, which is acceptable.

Mosora answered 8/8, 2013 at 10:51 Comment(1)
try True,False=False,TrueMosora
J
21

Bytecode disassembly makes difference obvious.

>>> dis.dis(test1)
  2           0 LOAD_GLOBAL              0 (True)
              3 JUMP_IF_FALSE            5 (to 11)
              6 POP_TOP             

  3           7 LOAD_CONST               1 (1)
             10 RETURN_VALUE        
        >>   11 POP_TOP             

  5          12 LOAD_CONST               2 (0)
             15 RETURN_VALUE        
             16 LOAD_CONST               0 (None)
             19 RETURN_VALUE        

As Kabie mentioned, True and False are globals in Python 2. Lots of stuff is going on to access them.

>>> dis.dis(test2)
  3           0 LOAD_CONST               1 (1)
              3 RETURN_VALUE        

Python compiler was able to recognize 1 as a constantly "truthy" expression and optimize redundant condition away!

>>> dis.dis(test3)
  2           0 LOAD_GLOBAL              0 (True)
              3 JUMP_IF_FALSE            5 (to 11)
              6 POP_TOP             

  3           7 LOAD_GLOBAL              0 (True)
             10 RETURN_VALUE        
        >>   11 POP_TOP             

  5          12 LOAD_GLOBAL              1 (False)
             15 RETURN_VALUE        
             16 LOAD_CONST               0 (None)
             19 RETURN_VALUE        

Pretty much the same as test1, with one more LOAD_GLOBAL.

>>> dis.dis(test4)
  3           0 LOAD_GLOBAL              0 (True)
              3 RETURN_VALUE        

See test2. But LOAD_GLOBAL is a bit more costly than LOAD_CONST.

Justinajustine answered 8/8, 2013 at 11:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.