
primer_on_scientific_programming_with_python
.pdf
E.6 Evaluating Program E ciency |
683 |
|
|
This diff function gives the writer of an f function full freedom to choose positional and/or keyword arguments for the parameters. Here is an example of the G function where we let the t parameter be positional and the other parameters be keyword arguments:
def G(x, t, A=1, a=1, w=1):
return A*exp(-a*t)*sin(w*x)
A call
dGdx = diff(G, 0.5, 1E-9, 0, A=1, w=100, a=1.5)
gives the output
(0,) {’A’: 1, ’a’: 1.5, ’w’: 100}
showing that t is put in f_args and transferred as positional argument to G, while A, a, and w are put in f_kwargs and transferred as keyword arguments. We remark that in the last call to diff, h and t must be treated as positional arguments, i.e., we cannot write h=1E-9 and t=0 unless all arguments in the call are on the name=value form.
In the case we use both *f_args and **f_kwargs arguments in f and there is no need for these arguments, *f_args becomes an empty tuple and **f_kwargs becomes an empty dictionary. The example
mycos = diff(sin, 0)
shows that the tuple and dictionary are indeed empty since diff just prints out
() {}
Therefore, a variable set of positional and keyword arguments can be incorporated in a general library function such as diff without any disadvantage, just the benefit that diff works with di erent types f functions: parameters as global variables, parameters as additional positional arguments, parameters as additional keyword arguments, or parameters as instance variables (Chapter 7.1.2).
The program varargs1.py in the appendix folder implements the examples in this appendix.
E.6 Evaluating Program E ciency
E.6.1 Making Time Measurements
Time is not just “time” on a computer. The elapsed time or wall clock time is the same time as you can measure on a watch or wall clock, while CPU time is the amount of time the program keeps the central processing unit busy. The system time is the time spent on operating

684 |
E Technical Topics |
|
|
system tasks like I/O. The concept user time is the di erence between the CPU and system times. If your computer is occupied by many concurrent processes, the CPU time of your program might be very di erent from the elapsed time.
The time Module. Python has a time module with some useful functions for measuring the elapsed time and the CPU time:
import time |
|
|
e0 |
= time.time() |
# elapsed time since the epoch |
c0 |
= time.clock() |
# total CPU time spent in the program so far |
<do tasks...>
elapsed_time = time.time() - e0 cpu_time = time.clock() - c0
The term epoch means initial time (time.time() would return 0), which is 00:00:00 January 1, 1970. The time module also has numerous functions for nice formatting of dates and time, and the more recent datetime module has more functionality and an improved interface. Although the timing has a finer resolution than seconds, one should construct test cases that last some seconds to obtain reliable results.
The timeit Module. To measure the e ciency of a certain set of statements or an expression, the code should be run a large number of times so the overall CPU-time is of order seconds. The timeit module has functionality for running a code segment repeatedly. Below is an illustration of timeit for comparing the e ciency sin(1.2) versus math.sin(1.2):
>>>import timeit
>>>t = timeit.Timer(’sin(1.2)’, setup=’from math import sin’)
>>> t.timeit(10000000) # run ’sin(1.2)’ 10000000 times 11.830688953399658
>>>t = timeit.Timer(’math.sin(1.2)’, setup=’import math’)
>>>t.timeit(10000000)
16.234833955764771
The first argument to the Timer constructor is a string containing the code to execute repeatedly, while the second argument is the necessary code for initialization. From this simple test we see that math.sin(1.2) runs almost 40 percent slower than sin(1.2)!
If you want to time a function, say f, defined in the same program as where you have the timeit call, the setup procedure must import f and perhaps other variables from the program, as exemplified in
t = timeit.Timer(’f(a,b)’, setup=’from __main__ import f, a, b’)
Here, f, a, and b are names initialized in the main program. Another example is found in src/random/smart_power.py.
Hardware Information. Along with CPU-time measurements it is often convenient to print out information about the hardware on which the


686 E Technical Topics
1082 function calls (728 primitive calls) in 17.890 CPU seconds
Ordered by: internal time
List reduced from 210 to 20 due to restriction <20>
ncalls |
tottime |
percall |
cumtime |
percall |
filename:lineno(function) |
5 |
5.850 |
1.170 |
5.850 |
1.170 |
m.py:43(loop1) |
1 |
2.590 |
2.590 |
2.590 |
2.590 |
m.py:26(empty) |
5 |
2.510 |
0.502 |
2.510 |
0.502 |
m.py:32(myfunc2) |
5 |
2.490 |
0.498 |
2.490 |
0.498 |
m.py:37(init) |
1 |
2.190 |
2.190 |
2.190 |
2.190 |
m.py:13(run1) |
6 |
0.050 |
0.008 |
17.720 |
2.953 |
funcs.py:126(timer) |
... |
|
|
|
|
|
In this test, loop1 is the most expensive function, using 5.85 seconds, which is to be compared with 2.59 seconds for the next most timeconsuming function, empty. The tottime entry is the total time spent in a specific function, while cumtime reflects the total time spent in the function and all the functions it calls.
The CPU time of a Python program typically increases with a factor of about five when run under the administration of the profile module. Nevertheless, the relative CPU time among the functions are probably not much a ected by the profiler overhead.

