Skip to content
Advertisement

Python Match Case (Switch) Performance

I was expecting the Python match/case to have equal time access to each case, but seems like I was wrong. Any good explanation why?

Lets use the following example:

def match_case(decimal):
    match decimal:
      case '0':
        return "000"
      case '1':
        return "001"
      case '2':
        return "010"
      case '3':
        return "011"
      case '4':
        return "100"
      case '5':
        return "101"
      case '6':
        return "110"
      case '7':
        return "111"
      case _:
        return "NA"

And define a quick tool to measure the time:

import time
def measure_time(funcion):
    def measured_function(*args, **kwargs):
        init = time.time()
        c = funcion(*args, **kwargs)
        print(f"Input: {args[1]} Time: {time.time() - init}")
        return c
    return measured_function

@measure_time
def repeat(function, input):
    return [function(input) for i in range(10000000)]

If we run each 10000000 times each case, the times are the following:

for i in range(8):
    repeat(match_case, str(i))

# Input: 0 Time: 2.458001136779785
# Input: 1 Time: 2.36093807220459
# Input: 2 Time: 2.6832823753356934
# Input: 3 Time: 2.9995620250701904
# Input: 4 Time: 3.5054492950439453
# Input: 5 Time: 3.815168857574463
# Input: 6 Time: 4.164452791213989
# Input: 7 Time: 4.857251167297363

Just wondering why the access times are different. Isn’t this optimised with perhaps a lookup table?. Note that I’m not interested in other ways of having equals access times (i.e. with dictionaries).

Advertisement

Answer

PEP 622 The “matchcase” functionality is developed to replace the code like this:

def is_tuple(node):
if isinstance(node, Node) and node.children == [LParen(), RParen()]:
    return True
return (isinstance(node, Node)
        and len(node.children) == 3
        and isinstance(node.children[0], Leaf)
        and isinstance(node.children[1], Node)
        and isinstance(node.children[2], Leaf)
        and node.children[0].value == "("
        and node.children[2].value == ")")

with code like this:

def is_tuple(node: Node) -> bool:
match node:
    case Node(children=[LParen(), RParen()]):
        return True
    case Node(children=[Leaf(value="("), Node(), Leaf(value=")")]):
        return True
    case _:
        return False

While it may be equivalent to a dict lookup in the most primitive cases, in general it is not so. Case patterns are designed to look like normal python code but actually they conceal isinsance and len calls and don’t execute what you’d expect to be executed when you see code like Node().

Essentially this is equivalent to a chain of if … elif … else statements. Note that unlike for the previously proposed switch statement, the pre-computed dispatch dictionary semantics does not apply here.

User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement