Skip to content

Instantly share code, notes, and snippets.

@tsangwpx
Created November 17, 2020 20:26
Show Gist options
  • Save tsangwpx/f8baecec8c6b275233dd4710e47f83c7 to your computer and use it in GitHub Desktop.
Save tsangwpx/f8baecec8c6b275233dd4710e47f83c7 to your computer and use it in GitHub Desktop.
Performance comparsion between long if-else and mapped function
def fn_ifelse(i):
if i == 0:
return i
elif i == 1:
return i
elif i == 2:
return i
elif i == 3:
return i
elif i == 4:
return i
elif i == 5:
return i
elif i == 6:
return i
else:
return i
def fn_table_build():
def sub_0(i):
return i
def sub_1(i):
return i
def sub_2(i):
return i
def sub_3(i):
return i
def sub_4(i):
return i
def sub_5(i):
return i
def sub_6(i):
return i
def sub_7(i):
return i
table = {
0: sub_0,
1: sub_1,
2: sub_2,
3: sub_3,
4: sub_4,
5: sub_5,
6: sub_6,
}
def fn_main(i):
return table.get(i, sub_7)(i)
return fn_main
fn_table = fn_table_build()
"""
Roughly 12 branches can beat mapped function
"""
from __future__ import annotations
import itertools
from timeit import timeit
from typing import List
def func_body(i):
# all are identity function
return "return i"
def fn_source(name, args, body: List[str]):
lines = ['def %s(%s):' % (name, ','.join(args))]
lines.extend([f" {s}" for s in body])
return '\n'.join(lines)
def ifelse_setup_code(n):
assert n >= 2
body = []
for i in range(n):
if i == 0:
cond = f"if i == {i}:"
elif i == n - 1:
cond = f"else:"
else:
cond = f"elif i == {i}:"
body.extend((
cond,
f' {func_body(i)}',
))
return fn_source('fn_ifelse', ('i',), body)
def map_setup_code(n):
assert n >= 2
def create_subfn(id_):
return fn_source(f'sub_{id_}', ('i',), [func_body(id_)])
lines = []
for i in range(n):
lines.extend(create_subfn(i).split('\n'))
lines.append('table = {')
for i in range(n - 1):
lines.append(f' {i}: sub_{i},')
lines.append('}')
main_body = []
main_body.append(f'return table.get(i, sub_{n - 1})(i)')
#
# main_body.extend((
# 'try:',
# ' return table[i](i)',
# 'except KeyError:',
# f' return sub_{n - 1}(i)',
# ))
lines.extend(fn_source('fn_main', ('i',), main_body).split('\n'))
lines.append('return fn_main')
s = fn_source('fn_table_build', (), lines)
return '\n'.join(itertools.chain(s.split('\n'), [
'fn_table = fn_table_build()',
]))
def main():
for n in range(10, 32):
source = '\n'.join((ifelse_setup_code(n), map_setup_code(n)))
ns = {}
exec(source, None, ns)
fn_ifelse = ns['fn_ifelse']
fn_table = ns['fn_table']
assert fn_ifelse(n // 2) == n // 2, n
assert fn_table(n // 2) == n // 2, n
number = 1_000_000
print('n', n)
# print(source)
print('ifelse', timeit('[fn(i) for i in range(n)]', 'fn = fn_ifelse', globals=locals(), number=number) / n)
print('table', timeit('[fn(i) for i in range(n)]', 'fn = fn_table', globals=locals(), number=number) / n)
print()
if __name__ == '__main__':
main()
n 10
ifelse 0.18849027999999998
table 0.20733889000000003
n 11
ifelse 0.19307480909090913
table 0.20729382727272727
n 12
ifelse 0.20221672499999999
table 0.20399962499999993
n 13
ifelse 0.20751689230769224
table 0.2013621461538461
n 14
ifelse 0.21670767857142867
table 0.1975107357142856
n 15
ifelse 0.22186927333333345
table 0.19777445333333313
n 16
ifelse 0.22301578749999984
table 0.19646908125000007
n 17
ifelse 0.2332971823529411
table 0.19414692941176442
n 18
ifelse 0.2459139055555555
table 0.19327038333333332
n 19
ifelse 0.24255543684210548
table 0.19525191578947362
n 20
ifelse 0.2588372049999997
table 0.19517728999999945
n 21
ifelse 0.26504744285714327
table 0.19126317142857172
n 22
ifelse 0.290177722727273
table 0.1923927818181813
n 23
ifelse 0.2850982608695652
table 0.18657619130434802
n 24
ifelse 0.2944755791666669
table 0.1851711708333331
n 25
ifelse 0.2965222399999999
table 0.18683115199999975
n 26
ifelse 0.3051656884615382
table 0.18965108076923143
n 27
ifelse 0.3249459555555558
table 0.1891015259259253
n 28
ifelse 0.3348370678571432
table 0.19059417500000006
n 29
ifelse 0.33812261034482805
table 0.18927325862068953
n 30
ifelse 0.3402334599999998
table 0.18482614999999972
n 31
ifelse 0.3501115806451618
table 0.18180615483870974
Process finished with exit code 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment