0

The examples below are from my chess game but the question is more general for runtime optimizing if-statements in Python. What type of code will run faster, and is there a huge difference? Bare in mind that in this case I will calculated this inside a minimax algorithm (thousands of times). Case 3 if of course nicer, but is it faster to run?

Another question would be if there is a difference in the order of which I put these values in? In this case the pawn option will come up the most times, is it then better to have that one first in the if-statements/dict?

# Case 1:
  
if piece_type == 'K':
    value = 20000
elif piece_type == 'Q':
    value = 900
elif piece_type == 'B':
    value = 330
elif piece_type == 'N':
    value = 320
elif piece_type == 'R':
    value = 500
elif piece_type == 'p':
    value = 100

return value

# Case 2:

if piece_type == 'K':
    return 20000
elif piece_type == 'Q':
    return 900
elif piece_type == 'B':
    return 330
elif piece_type == 'N':
    return 320
elif piece_type == 'R':
    return 500
elif piece_type == 'p':
    return 100

# Case 3:

piece_values = {'K': 20000, 'Q': 900, 'B': 330, 'N': 320, 'R': 500, 'p': 100}
return piece_values.get('K')
eligolf
  • 1,682
  • 1
  • 6
  • 22
  • `piece_values[k]` is probably faster than using the `.get` method. – kaya3 Oct 13 '20 at 05:01
  • 2
    Case 3 is O(1) and will be the fastest. https://stackoverflow.com/questions/1963507/time-complexity-of-accessing-a-python-dict – Alexander Oct 13 '20 at 05:03
  • 1
    The return in case 3 suggests that `piece_values` is regenerated inside of a function. Is it created only once? – tdelaney Oct 13 '20 at 05:13
  • Thanks @Alexander, and is there any difference for the order of the dict? E.g. put pawns first? – eligolf Oct 13 '20 at 05:30

1 Answers1

1
import timeit
piece_type = 'p'
def case1():
    if piece_type == 'K':
        value = 20000
    elif piece_type == 'Q':
        value = 900
    elif piece_type == 'B':
        value = 330
    elif piece_type == 'N':
        value = 320
    elif piece_type == 'R':
        value = 500
    elif piece_type == 'p':
        value = 100
    return value


def case2():
    if piece_type == 'K':
        return 20000
    elif piece_type == 'Q':
        return 900
    elif piece_type == 'B':
        return 330
    elif piece_type == 'N':
        return 320
    elif piece_type == 'R':
        return 500
    elif piece_type == 'p':
        return 100

piece_values = {'K': 20000, 'Q': 900, 'B': 330, 'N': 320, 'R': 500, 'p': 100}
def case3():
    return piece_values.get(piece_type)

N = 1000
r1 = 0
r2 = 0
r3 = 0
for i in range(N):
    r1 += timeit.timeit("case1()", setup="from __main__ import case1", number=100000)
    r2 += timeit.timeit("case2()", setup="from __main__ import case2", number=100000)
    r3 += timeit.timeit("case3()", setup="from __main__ import case3", number=100000)
print(r1/N)
print(r2/N)
print(r3/N)

Results

For piece_type = 'p'

0.043659273699999786
0.042489134299999745
0.025282950800000023

For peice_type = 'K'

0.017085672599999883
0.015899844999999937
0.02493290909999991

As if we see the results, accessing elements of dictionary doesn't matter where the element is located.
Accessing elements down the if-else ladder cost more time compared to the elements at the top of ladder.

So if the conditionals are less then if-else is better, if there are large number of conditionals then dictionary is better as dictionary is always O(1).

LMKR
  • 647
  • 5
  • 12
  • We also see that that ordering of the conditionals matters. There's a lot more pawns than Kings, so to optimize the if-conditionals they should come first, follwed by Knights, Rooks and Bishops (in any order), and then finally the Queen and King. – BurnNote Oct 13 '20 at 05:50
  • Thank you so much! Also showing me how to time similar things in the future :D – eligolf Oct 13 '20 at 05:53