I am new to Python and figured I’d play around with problems on Project Euler to have something concrete to do meanwhile.
I came across the idea of timing different solutions to see how they rate against each other. That simple task turned out to be too complicated for my taste however. I read that the time.clock() calls are not accurate enough on unix systems (seconds resolution is simply pathetic with modern processors). Thus I stumbled upon the timeit module which seems to be the first choice for profiling tasks.
I have to say I really don’t understand why they went with such a counter-intuitive way to go about it. I can’t seem to get it to work, without needing to rewrite/restructure my code, which I find very frustrating.
Take the code below and nevermind for a second that it’s neither pretty nor particularly efficient:
import math
import sys
from timeit import Timer
def digitsum(number):
rem = 0
while number > 0:
rem += number % 10
number //= 10
return rem
def prime_form(p):
if p == 2 or p == 3 or p == 5:
return True
elif (p-1) % 6 != 0 and (p+1) % 6 != 0:
return False
elif digitsum(p) % 3 == 0:
return False
elif p % 10 == 0 or p % 10 == 5:
return False
else:
return True
def lfactor(n):
if n <= 3:
return 1
limit = int(math.sqrt(n))
if limit % 2 == 0:
limit -= 1
lfac = 1
for i in range(3,limit+1,2):
if prime_form(i):
(div,rem) = divmod(n,i)
if rem == 0:
lfac = max(lfac, max(lfactor(div) ,lfactor(i)))
return lfac if lfac != 1 else n
number = int(sys.argv[1])
t = Timer("""print lfactor(number)""", """import primefacs""")
t.timeit(100)
#print lfactor(number)
If i would like to time the line print lfactor(number) why should I go through a bunch of loops, trying to define a setup statement etc.. I understand why one would want to have debug tool that are detached from the code being tested (a la unit testing) but shouldn’t there be a simple and straightforward way to get the process time of a chunk of code without much hassle (importing/defining a setup etc)? What I am thinking here is something like the way one would do that:
long t0 = System.currentTimeInMillis();
// do something
long t = System.currentTimeInMillis() - t0;
.. or even better with MATLAB, using the tic/toc commands:
tic
x = A\b;
t(n) = toc;
Hope this doesn’t come across as a rant, I am really trying understand “the pythonian way of thinking” but honestly it doesn’t come naturally here, not at all…
When timing a statement, you want to time just that statement, not the setup. The setup could be considerably slower than the statement-under-test.
Note that
timeitruns your statement thousands of times to get a reasonable average. It does this to eliminate the effects of OS scheduling and other processes (including but not limited to disk buffer flushing, cronjob execution, memory swapping, etc); only an average time would have any meaning when comparing different code alternatives.For your case, just test
lfactor(number)directly, and just use thetimeit()function:The setup code retrieves the
lfactor()function, as well asnumbertaken fromsys.argvfrom the main script; the function and number won’t otherwise be seen.There is absolutely no point in performance testing the
printstatement, that’s not what you are trying to time. Usingtimeitis not about seeing the result of the call, just the time it takes to run it. Since the code-under-test is run thousands of times, all you’d get is thousands of prints of (presumably) the same result.Note that usually
timeitis used to compare performance characteristics of short python snippets; to find performance bottlenecks in more complex code, use profiling instead.If you want to time just one run, use the
timeit.default_timer()function to get the most accurate timer for your platform: