Benchmarking Python Algorithms
I’m trying to figure out a fast way to compare the speed of different implementations of a single python algorithm. One option is the timeit module.
I’m not sure the best way to setup these comparisons, so I made a function for each implementation: divceil1
… divceil6
. Then, I made a function to run each and check its output: test1
… test6
. Finally, I’m running the test functions with timeit, e.g.: timeit.timeit(test6, number=5000)
. The output goes to STDOUT, which I’m redirecting to a CSV file, for processing in a spreadsheet tool. (I know, I know).
Here’s the implementation.
#!/usr/bin/python
""" testing various divceil algorithms """
import timeit
def divceil1(numerator, denominator):
""" Returns a division operation rounded up to the next whole number """
# E.G.: 45/20 -> 3 and 40/20 -> 2
quotient, remainder = divmod(numerator, denominator)
return quotient + (1 if remainder > 0 else 0)
def divceil2(numerator, denominator):
""" Returns a division operation rounded up to the next whole number """
# E.G.: 45/20 -> 3 and 40/20 -> 2
quotient, remainder = divmod(numerator, denominator)
return quotient + int(remainder > 0)
def divceil3(numerator, denominator):
""" Returns a division operation rounded up to the next whole number """
# E.G.: 45/20 -> 3 and 40/20 -> 2
quotient, remainder = divmod(numerator, denominator)
return quotient + (remainder > 0)
def divceil4(numerator, denominator):
""" Returns a division operation rounded up to the next whole number """
# E.G.: 45/20 -> 3 and 40/20 -> 2
quotient, remainder = divmod(numerator, denominator)
return quotient + (1 if remainder else 0)
def divceil5(numerator, denominator):
""" Returns a division operation rounded up to the next whole number """
# E.G.: 45/20 -> 3 and 40/20 -> 2
quotient, remainder = divmod(numerator, denominator)
if remainder:
return quotient + 1
return quotient
def divceil6(numerator, denominator):
""" Returns a division operation rounded up to the next whole number """
# E.G.: 45/20 -> 3 and 40/20 -> 2
quotient, remainder = divmod(numerator, denominator)
if remainder:
return quotient + 1
else:
return quotient
def test1():
""" Run divceil1, checking output """
if not (divceil1(45, 20) == 3 and divceil1(40, 20) == 2):
raise ArithmeticError("Something went awry")
return
def test2():
""" Run divceil2, checking output """
if not (divceil2(45, 20) == 3 and divceil2(40, 20) == 2):
raise ArithmeticError("Something went awry")
return
def test3():
""" Run divceil3, checking output """
if not (divceil3(45, 20) == 3 and divceil3(40, 20) == 2):
raise ArithmeticError("Something went awry")
return
def test4():
""" Run divceil4, checking output """
if not (divceil4(45, 20) == 3 and divceil4(40, 20) == 2):
raise ArithmeticError("Something went awry")
return
def test5():
""" Run divceil5, checking output """
if not (divceil5(45, 20) == 3 and divceil5(40, 20) == 2):
raise ArithmeticError("Something went awry")
return
def test6():
""" Run divceil6, checking output """
if not (divceil6(45, 20) == 3 and divceil6(40, 20) == 2):
raise ArithmeticError("Something went awry")
return
REPEAT = 5000
# note ironic use of .format
for _ in xrange(1000):
print "6,{}".format(timeit.timeit(test6, number=REPEAT))
print "5,{}".format(timeit.timeit(test5, number=REPEAT))
print "4,{}".format(timeit.timeit(test4, number=REPEAT))
print "3,{}".format(timeit.timeit(test3, number=REPEAT))
print "2,{}".format(timeit.timeit(test2, number=REPEAT))
print "1,{}".format(timeit.timeit(test1, number=REPEAT))
for _ in xrange(1000):
print "1,{}".format(timeit.timeit(test1, number=REPEAT))
print "2,{}".format(timeit.timeit(test2, number=REPEAT))
print "3,{}".format(timeit.timeit(test3, number=REPEAT))
print "4,{}".format(timeit.timeit(test4, number=REPEAT))
print "5,{}".format(timeit.timeit(test5, number=REPEAT))
print "6,{}".format(timeit.timeit(test6, number=REPEAT))
So obviously it would be between divceil5 or divceil6 here. Here are the results:
Algorithm | Total Time | Average Time |
---|---|---|
5 | 19.5343523026 | 0.0097671762 |
6 | 19.7862572670 | 0.0098931286 |
4 | 20.1976833344 | 0.0100988417 |
1 | 20.4852132797 | 0.0102426066 |
3 | 20.5143792629 | 0.0102571896 |
2 | 26.9490272999 | 0.0134745136 |
Unsurprisingly, #5 was the fastest.