Make My Python Code Faster

John Parejko, Phil Marshall and Your Name Here>

This notebook demonstrates some ways to make your python code go faster.

Step 1: Profile and improve your code

Because how can you optimize something if you haven't first evaluated it?

Step 2: Parallelize your code

Because you probably own more than one CPU.

Profiling


In [2]:
import numpy as np

In [5]:
x = np.random.randn(1000)

Inline Timing

Use %timeit in the notebook, and other commands in functions... Need examples of these!


In [6]:
%timeit np.power(x,2)


10000 loops, best of 3: 32 µs per loop

In [8]:
%timeit x**2


The slowest run took 59.22 times longer than the fastest. This could mean that an intermediate result is being cached 
1000000 loops, best of 3: 1.44 µs per loop

In [ ]:


In [11]:
import cProfile
import pstats

In [23]:
def square(x):
    for k in range(1000):
        sq = np.power(x,2)
        sq = x**2
        sq = x*x
    return

In [24]:
log = 'square.profile'
cProfile.run('square(x)',filename=log)

stats = pstats.Stats(log)
stats.strip_dirs()

stats.sort_stats('cumtime').print_stats(20)

In [29]:
# OK - so all the time is being taken by the function "square".
# We need to re-write with the lines separated into functions - 
# which is a better way to code anyway.

In [30]:
def bettersquare(x):
    
    def powersquare(x):
        return np.power(x,2)
    def justsquare(x):
        return x**2
    def selfmultiply(x):
        return x*x
    
    for k in range(1000):
        sq = powersquare(x)
        sq = justsquare(x)
        sq = selfmultiply(x)
    
    return

In [31]:
log = 'bettersquare.profile'
cProfile.run('bettersquare(x)',filename=log)

stats = pstats.Stats(log)
stats.strip_dirs()

stats.sort_stats('cumtime').print_stats(20)


Tue Sep 29 12:22:48 2015    bettersquare.profile

         3004 function calls in 0.063 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.063    0.063 <string>:1(<module>)
        1    0.004    0.004    0.063    0.063 <ipython-input-30-a566590efda6>:1(bettersquare)
     1000    0.052    0.000    0.052    0.000 <ipython-input-30-a566590efda6>:3(powersquare)
     1000    0.005    0.000    0.005    0.000 <ipython-input-30-a566590efda6>:5(justsquare)
     1000    0.003    0.000    0.003    0.000 <ipython-input-30-a566590efda6>:7(selfmultiply)
        1    0.000    0.000    0.000    0.000 {range}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


Out[31]:
<pstats.Stats instance at 0x105229c20>

In [ ]:
# Much better - you can see the cumulative time spent in each function.

In [28]:
# We could also try the line profiler, from rkern on GitHub.

!pip install --upgrade line_profiler


Collecting line-profiler
  Downloading line_profiler-1.0.tar.gz (65kB)
    100% |████████████████████████████████| 69kB 2.0MB/s 
Building wheels for collected packages: line-profiler
  Running setup.py bdist_wheel for line-profiler
  Stored in directory: /Users/pjm/Library/Caches/pip/wheels/42/e9/3a/aa5d3b6dbae0c78dd50300cb0f391f8153a3d907aacf655c87
Successfully built line-profiler
Installing collected packages: line-profiler
Successfully installed line-profiler-1.0
  • We could also run the line_profiler from the command line...

  • Which means the square function needs writing out to a file...

  • Can we do this from this notebook?

Cythonization

Something of a last resort: don't go to cython unless you know it's going to help...

We could also replace simple lines of math with the equivalent line of c...

cython -a file.pyx

makes file.c, but also file.html. The html file shows you the lines that were unwrapped into c.

Can we demo this from this notebook? Hmm.


In [ ]: