|
| 1 | +<!DOCTYPE html> |
| 2 | +<html> |
| 3 | + <head> |
| 4 | + <meta charset="utf-8"> |
| 5 | + <meta name="generator" content="pandoc"> |
| 6 | + <title>Software Carpentry: Profiling</title> |
| 7 | + <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" /> |
| 8 | + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| 9 | + <link rel="stylesheet" type="text/css" href="css/bootstrap/bootstrap.css" /> |
| 10 | + <link rel="stylesheet" type="text/css" href="css/bootstrap/bootstrap-theme.css" /> |
| 11 | + <link rel="stylesheet" type="text/css" href="css/swc.css" /> |
| 12 | + <link rel="alternate" type="application/rss+xml" title="Software Carpentry Blog" href="http://software-carpentry.org/feed.xml"/> |
| 13 | + <meta charset="UTF-8" /> |
| 14 | + <!-- HTML5 shim, for IE6-8 support of HTML5 elements --> |
| 15 | + <!--[if lt IE 9]> |
| 16 | + <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> |
| 17 | + <![endif]--> |
| 18 | + </head> |
| 19 | + <body class="lesson"> |
| 20 | + <div class="container card"> |
| 21 | + <div class="banner"> |
| 22 | + <a href="http://software-carpentry.org" title="Software Carpentry"> |
| 23 | + <img alt="Software Carpentry banner" src="img/software-carpentry-banner.png" /> |
| 24 | + </a> |
| 25 | + </div> |
| 26 | + <article> |
| 27 | + <div class="row"> |
| 28 | + <div class="col-md-10 col-md-offset-1"> |
| 29 | + <a href="index.html"><h1 class="title">Profiling</h1></a> |
| 30 | + <h2 class="subtitle">Detailed runtime measurements</h2> |
| 31 | + <p>The tools from the previous section can help us decide the question whether we need to optimize in the first place (is the total run time fast enough?) and it can guide when we want to replace code by a better-performing alternative (such as a more specialized numpy function instead of a general built-in function). They do not tell us <em>what</em> to optimize, though.</p> |
| 32 | +<p>As a first example, let’s us have a look at the classical <a href="https://en.wikipedia.org/wiki/Fibonacci_number" title="Fibonacci number (wikipedia)">Fibonaci sequence</a> , where each number is the sum of the two preceding numbers. This can be directly written down in a recursive function (note that for simplicity we leave away all error checking, e.g. for negative numbers):</p> |
| 33 | +<pre class="sourceCode python"><code class="sourceCode python"><span class="kw">def</span> fibonacci(n): |
| 34 | + <span class="kw">if</span> n < <span class="dv">2</span>: |
| 35 | + <span class="kw">return</span> n <span class="co"># fibonacci(0) == 0, fibonacci(1) == 1</span> |
| 36 | + <span class="kw">else</span>: |
| 37 | + <span class="kw">return</span> fibonacci(n - <span class="dv">2</span>) + fibonacci(n - <span class="dv">1</span>)</code></pre> |
| 38 | +<p>This seems to work fine, but the runtime increases with <code>n</code> in a dramatic fashion:</p> |
| 39 | +<pre class="sourceCode python"><code class="sourceCode python">%time factorial(<span class="dv">10</span>)</code></pre> |
| 40 | +<pre><code>CPU times: user 0 ns, sys: 0 ns, total: 0 ns |
| 41 | +Wall time: 52.7 µs |
| 42 | + |
| 43 | +89</code></pre> |
| 44 | +<pre class="sourceCode python"><code class="sourceCode python">%time factorial(<span class="dv">20</span>)</code></pre> |
| 45 | +<pre><code>CPU times: user 12 ms, sys: 0 ns, total: 12 ms |
| 46 | +Wall time: 9.63 ms |
| 47 | + |
| 48 | +10946</code></pre> |
| 49 | +<pre class="sourceCode python"><code class="sourceCode python">%time factorial(<span class="dv">30</span>)</code></pre> |
| 50 | +<pre><code>CPU times: user 696 ms, sys: 0 ns, total: 696 ms |
| 51 | +Wall time: 695 ms |
| 52 | + |
| 53 | +1346269</code></pre> |
| 54 | +<pre class="sourceCode python"><code class="sourceCode python">%time factorial(<span class="dv">35</span>)</code></pre> |
| 55 | +<pre><code>CPU times: user 7.49 s, sys: 24 ms, total: 7.52 s |
| 56 | +Wall time: 7.52 s |
| 57 | + |
| 58 | +14930352</code></pre> |
| 59 | +<p>To get an idea what is going on, we can use <code>%prun</code>, which runs a command with Python’s built-in profiler:</p> |
| 60 | +<pre class="sourceCode python"><code class="sourceCode python">% prun factorial(<span class="dv">35</span>)</code></pre> |
| 61 | +<pre class="output"><code>29860706 function calls (4 primitive calls) in 10.621 seconds |
| 62 | + |
| 63 | + Ordered by: internal time |
| 64 | + |
| 65 | + ncalls tottime percall cumtime percall filename:lineno(function) |
| 66 | +29860703/1 10.621 0.000 10.621 10.621 <ipython-input-6-b471b8bf6ddb>:1(factorial) |
| 67 | + 1 0.000 0.000 10.621 10.621 {built-in method builtins.exec} |
| 68 | + 1 0.000 0.000 10.621 10.621 <string>:1(<module>) |
| 69 | + 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}</code></pre> |
| 70 | +<p>The output gives us three pieces of information for every function called during the execution of the command: the number of times the function was called (<code>ncalls</code>), the total time spend in that function itself (<code>tottime</code>) and the time spend in that function, including all time spend in functions called by that function (<code>cumtime</code>). It is not surprising that all of the time is spend in the <code>factorial</code> function (after all, that’s the only function we have) but the function got called 29860703 times! There is no way we are going to get a decent performance from this function without changing the approach fundamentally.</p> |
| 71 | +<section class="challenge panel panel-success"> |
| 72 | +<div class="panel-heading"> |
| 73 | +<h2 id="a-better-fibonacci-sequence"><span class="glyphicon glyphicon-pencil"></span>A better Fibonacci sequence</h2> |
| 74 | +</div> |
| 75 | +<div class="panel-body"> |
| 76 | +<p>Do you know of a better way to write the Fibonacci function? Can you imagine specific ways of using that function where you would prefer yet another approach?</p> |
| 77 | +</div> |
| 78 | +</section> |
| 79 | +<p>Optimizing a calculation by using a fundamentally different approach is called “algorithmic optimization” and it is the potentially most powerful way to increase the performance of a program. Whenever the runtime of a program appears to be slow, the first check should be whether there is a function that takes a lot of time and is called more often than expected (e.g. we calculate a measure on 1000 values and expect a function to be called about a 1000 times as well but it is called 1000*1000 times instead).</p> |
| 80 | +<p>TODO: Show an example with a nested loop</p> |
| 81 | +<p>TODO: Demonstrate snakeviz</p> |
| 82 | +<p>TODO: Demonstrate line profiler</p> |
| 83 | + </div> |
| 84 | + </div> |
| 85 | + </article> |
| 86 | + <div class="footer"> |
| 87 | + <a class="label swc-blue-bg" href="http://software-carpentry.org">Software Carpentry</a> |
| 88 | + <a class="label swc-blue-bg" href="https://github.com/paris-swc/python-testing-debugging-profiling">Source</a> |
| 89 | + <a class=" label swc-blue-bg" href=" mailto:[email protected]" >Contact </a> |
| 90 | + <a class="label swc-blue-bg" href="LICENSE.html">License</a> |
| 91 | + </div> |
| 92 | + </div> |
| 93 | + <!-- Javascript placed at the end of the document so the pages load faster --> |
| 94 | + <script src="http://software-carpentry.org/v5/js/jquery-1.9.1.min.js"></script> |
| 95 | + <script src="css/bootstrap/bootstrap-js/bootstrap.js"></script> |
| 96 | + <script src='https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML'></script> |
| 97 | + </body> |
| 98 | +</html> |
0 commit comments