Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 688
alrevuelta Avatar asked Jan 22 '26 14:01

alrevuelta


1 Answers

I come late to the party, but I feel like I can add something useful to this thread.
A while back, I published an article on Medium about Python's match-case performance, analyzing the generated byte code and performing benchmarks and comparisons. If you want, you can read the article here. I think it's worth your time.
However, I'm going to summarize it here.

Many programming languages implement their switch-case statements as if they were an if-else sequence. Sometimes the switch-case can be optimized into an efficient lookup table, but that's not always the case.

In Python, this optimization is never performed, thus always resulting in a series of condition checks.

From the article, a speed comparison between if-else and match-case:

Average time for match_case:  0.00424 seconds
Average time for if_else:     0.00413 seconds

As you can see, they are almost equal.
Plus, if you disassembled the two statements, you would find that they generate nearly identical byte code.

Just a difference to point out, if-else checks call the objects' __eq__() method while match-case does the comparisons internally.

Lastly, here's a benchmark that also includes a hash table (dictionary):

Average time for match_case:  0.00438 seconds
Average time for if_else:     0.00433 seconds
Average time for dict_lookup: 0.00083 seconds

So, if you're keen on performance, you should use hash tables. Although match-case is similar to a C-style switch-case, it's not: match-case is meant to be used for structural pattern matching, and not for replacing performant hash and lookup tables.

like image 176
Nicholas Obert Avatar answered Jan 24 '26 05:01

Nicholas Obert