Rnj
Rnj

Reputation: 1187

Is max() slower than if()?

I am preparing for competitive programming in python. I was guessing if I should use max() at all with consideration of performance impact. So I quickly tried following:

import time

def compare(i, j, num_iterations):
    start = time.process_time_ns()
    for i in range(num_iterations):
        m = max(i,j)
    end = time.process_time_ns()
    print('t(max): ' + str(end - start))

    start = time.process_time_ns()
    for i in range(num_iterations):
        if i < j:
            m = j
        else:
            m = i
    end = time.process_time_ns()
    print('t(if): ' + str(end - start))

    start = time.process_time_ns()
    for i in range(num_iterations):
        m = j if i < j else i
    end = time.process_time_ns()
    print('t(?:): ' + str(end - start))

compare(0,1,10000)
print()
compare(0,1,100000)
print()
compare(0,1,1000000)
print()
compare(0,1,100000000)

The output was:

t(max): 0
t(if): 0
t(?:): 0

t(max): 0
t(if):  15625000
t(?:):  0

t(max): 109375000
t(if):  31250000
t(?:):  31250000

t(max): 13765625000
t(if):  5265625000
t(?:):  4953125000

Seems that max() is indeed slower than the simple if-else. Is it the case?

Upvotes: 1

Views: 893

Answers (2)

2e0byo
2e0byo

Reputation: 5944

Yes:

def _if(i, j):
    return i if i > j else j

In [328]: %timeit max(1, 0)
200 ns ± 5.73 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

%timeit max(0, 1)
195 ns ± 3.07 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

%timeit _if(0, 1)
136 ns ± 1.39 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

In [367]: %timeit _if(1, 0)
127 ns ± 0.47 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

Why?

Obviously enough because of _if(0,1,2) (which is illegal). The _if function here isn't a replacement for max at all, and in this special case (picking the biggest of two operands) a simple comparison is (currently) cheaper (although nothing prevents an implementation optimising max for this case).

That said a few ns is unlikely to dominate. When you get to that kind of worry you start avoiding functions at all, and you're arguably better off with a compiled language where you can let the compiler worry about things like inlining.

I highly doubt any programming competition focused on 'performance' will be decided on things like this (more likely on getting your higher-level algorithms right for the problem at hand). But what do I know: there's always a counter-example.

Upvotes: 1

Killian Fortman
Killian Fortman

Reputation: 106

This if/else statement is only comparing 1 and 0. Yes you are doing many iterations, but this wouldnt be a practical case for using max.

your if/else would be much slower when getting the max value from a list of 100000 random numbers between 0-100000. The alternative to using max is to sort and get the last element.

import random

@profile
def main():
    rand_list = random.sample(range(0,100000), 100000)
    print(max(rand_list))

@profile
def main2():
    rand_list = random.sample(range(0,100000), 100000)
    print(sorted(rand_list)[-1])

@profile
def main3():
    rand_list = random.sample(range(0,100000), 100000)
    count = 0
    for i in rand_list:
        if count < i:
            count = i
    print(count)

main()
main2()
main3()

Running

python3 -m kernprof -l -v testing.py 
99999
99999
99999
Wrote profile results to testing.py.lprof
Timer unit: 1e-06 s

Total time: 0.149179 s
File: testing.py
Function: main at line 3

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     3                                           @profile
     4                                           def main():
     5         1     147971.0 147971.0     99.2      rand_list = random.sample(range(0,100000), 100000)
     6         1       1208.0   1208.0      0.8      print(max(rand_list))

Total time: 0.164449 s
File: testing.py
Function: main2 at line 8

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     8                                           @profile
     9                                           def main2():
    10         1     150492.0 150492.0     91.5      rand_list = random.sample(range(0,100000), 100000)
    11         1      13957.0  13957.0      8.5      print(sorted(rand_list)[-1])

Total time: 0.193634 s
File: testing.py
Function: main3 at line 13

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
    13                                           @profile
    14                                           def main3():
    15         1     150456.0 150456.0     77.7      rand_list = random.sample(range(0,100000), 100000)
    16         1          1.0      1.0      0.0      count = 0
    17    100001      20864.0      0.2     10.8      for i in rand_list:
    18    100000      22296.0      0.2     11.5          if count < i:
    19        13          2.0      0.2      0.0              count = i
    20         1         15.0     15.0      0.0      print(count)

In a practical use case for max it will be faster.

Upvotes: 1

Related Questions