856 lines
73 KiB
HTML
856 lines
73 KiB
HTML
|
||
<!DOCTYPE html>
|
||
|
||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<title>The Python Profilers — Python 3.7.4 documentation</title>
|
||
<link rel="stylesheet" href="../_static/pydoctheme.css" type="text/css" />
|
||
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
|
||
|
||
<script type="text/javascript" id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
|
||
<script type="text/javascript" src="../_static/jquery.js"></script>
|
||
<script type="text/javascript" src="../_static/underscore.js"></script>
|
||
<script type="text/javascript" src="../_static/doctools.js"></script>
|
||
<script type="text/javascript" src="../_static/language_data.js"></script>
|
||
|
||
<script type="text/javascript" src="../_static/sidebar.js"></script>
|
||
|
||
<link rel="search" type="application/opensearchdescription+xml"
|
||
title="Search within Python 3.7.4 documentation"
|
||
href="../_static/opensearch.xml"/>
|
||
<link rel="author" title="About these documents" href="../about.html" />
|
||
<link rel="index" title="Index" href="../genindex.html" />
|
||
<link rel="search" title="Search" href="../search.html" />
|
||
<link rel="copyright" title="Copyright" href="../copyright.html" />
|
||
<link rel="next" title="timeit — Measure execution time of small code snippets" href="timeit.html" />
|
||
<link rel="prev" title="pdb — The Python Debugger" href="pdb.html" />
|
||
<link rel="shortcut icon" type="image/png" href="../_static/py.png" />
|
||
<link rel="canonical" href="https://docs.python.org/3/library/profile.html" />
|
||
|
||
<script type="text/javascript" src="../_static/copybutton.js"></script>
|
||
<script type="text/javascript" src="../_static/switchers.js"></script>
|
||
|
||
|
||
|
||
<style>
|
||
@media only screen {
|
||
table.full-width-table {
|
||
width: 100%;
|
||
}
|
||
}
|
||
</style>
|
||
|
||
|
||
</head><body>
|
||
|
||
<div class="related" role="navigation" aria-label="related navigation">
|
||
<h3>Navigation</h3>
|
||
<ul>
|
||
<li class="right" style="margin-right: 10px">
|
||
<a href="../genindex.html" title="General Index"
|
||
accesskey="I">index</a></li>
|
||
<li class="right" >
|
||
<a href="../py-modindex.html" title="Python Module Index"
|
||
>modules</a> |</li>
|
||
<li class="right" >
|
||
<a href="timeit.html" title="timeit — Measure execution time of small code snippets"
|
||
accesskey="N">next</a> |</li>
|
||
<li class="right" >
|
||
<a href="pdb.html" title="pdb — The Python Debugger"
|
||
accesskey="P">previous</a> |</li>
|
||
<li><img src="../_static/py.png" alt=""
|
||
style="vertical-align: middle; margin-top: -1px"/></li>
|
||
<li><a href="https://www.python.org/">Python</a> »</li>
|
||
<li>
|
||
<span class="language_switcher_placeholder">en</span>
|
||
<span class="version_switcher_placeholder">3.7.4</span>
|
||
<a href="../index.html">Documentation </a> »
|
||
</li>
|
||
|
||
<li class="nav-item nav-item-1"><a href="index.html" >The Python Standard Library</a> »</li>
|
||
<li class="nav-item nav-item-2"><a href="debug.html" accesskey="U">Debugging and Profiling</a> »</li>
|
||
<li class="right">
|
||
|
||
|
||
<div class="inline-search" style="display: none" role="search">
|
||
<form class="inline-search" action="../search.html" method="get">
|
||
<input placeholder="Quick search" type="text" name="q" />
|
||
<input type="submit" value="Go" />
|
||
<input type="hidden" name="check_keywords" value="yes" />
|
||
<input type="hidden" name="area" value="default" />
|
||
</form>
|
||
</div>
|
||
<script type="text/javascript">$('.inline-search').show(0);</script>
|
||
|
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="document">
|
||
<div class="documentwrapper">
|
||
<div class="bodywrapper">
|
||
<div class="body" role="main">
|
||
|
||
<div class="section" id="the-python-profilers">
|
||
<span id="profile"></span><h1>The Python Profilers<a class="headerlink" href="#the-python-profilers" title="Permalink to this headline">¶</a></h1>
|
||
<p><strong>Source code:</strong> <a class="reference external" href="https://github.com/python/cpython/tree/3.7/Lib/profile.py">Lib/profile.py</a> and <a class="reference external" href="https://github.com/python/cpython/tree/3.7/Lib/pstats.py">Lib/pstats.py</a></p>
|
||
<hr class="docutils" />
|
||
<div class="section" id="introduction-to-the-profilers">
|
||
<span id="profiler-introduction"></span><h2>Introduction to the profilers<a class="headerlink" href="#introduction-to-the-profilers" title="Permalink to this headline">¶</a></h2>
|
||
<p id="index-0"><a class="reference internal" href="#module-cProfile" title="cProfile"><code class="xref py py-mod docutils literal notranslate"><span class="pre">cProfile</span></code></a> and <a class="reference internal" href="#module-profile" title="profile: Python source profiler."><code class="xref py py-mod docutils literal notranslate"><span class="pre">profile</span></code></a> provide <em class="dfn">deterministic profiling</em> of
|
||
Python programs. A <em class="dfn">profile</em> is a set of statistics that describes how
|
||
often and for how long various parts of the program executed. These statistics
|
||
can be formatted into reports via the <a class="reference internal" href="#module-pstats" title="pstats: Statistics object for use with the profiler."><code class="xref py py-mod docutils literal notranslate"><span class="pre">pstats</span></code></a> module.</p>
|
||
<p>The Python standard library provides two different implementations of the same
|
||
profiling interface:</p>
|
||
<ol class="arabic simple">
|
||
<li><p><a class="reference internal" href="#module-cProfile" title="cProfile"><code class="xref py py-mod docutils literal notranslate"><span class="pre">cProfile</span></code></a> is recommended for most users; it’s a C extension with
|
||
reasonable overhead that makes it suitable for profiling long-running
|
||
programs. Based on <code class="xref py py-mod docutils literal notranslate"><span class="pre">lsprof</span></code>, contributed by Brett Rosen and Ted
|
||
Czotter.</p></li>
|
||
<li><p><a class="reference internal" href="#module-profile" title="profile: Python source profiler."><code class="xref py py-mod docutils literal notranslate"><span class="pre">profile</span></code></a>, a pure Python module whose interface is imitated by
|
||
<a class="reference internal" href="#module-cProfile" title="cProfile"><code class="xref py py-mod docutils literal notranslate"><span class="pre">cProfile</span></code></a>, but which adds significant overhead to profiled programs.
|
||
If you’re trying to extend the profiler in some way, the task might be easier
|
||
with this module. Originally designed and written by Jim Roskind.</p></li>
|
||
</ol>
|
||
<div class="admonition note">
|
||
<p class="admonition-title">Note</p>
|
||
<p>The profiler modules are designed to provide an execution profile for a given
|
||
program, not for benchmarking purposes (for that, there is <a class="reference internal" href="timeit.html#module-timeit" title="timeit: Measure the execution time of small code snippets."><code class="xref py py-mod docutils literal notranslate"><span class="pre">timeit</span></code></a> for
|
||
reasonably accurate results). This particularly applies to benchmarking
|
||
Python code against C code: the profilers introduce overhead for Python code,
|
||
but not for C-level functions, and so the C code would seem faster than any
|
||
Python one.</p>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="instant-user-s-manual">
|
||
<span id="profile-instant"></span><h2>Instant User’s Manual<a class="headerlink" href="#instant-user-s-manual" title="Permalink to this headline">¶</a></h2>
|
||
<p>This section is provided for users that “don’t want to read the manual.” It
|
||
provides a very brief overview, and allows a user to rapidly perform profiling
|
||
on an existing application.</p>
|
||
<p>To profile a function that takes a single argument, you can do:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">cProfile</span>
|
||
<span class="kn">import</span> <span class="nn">re</span>
|
||
<span class="n">cProfile</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="s1">'re.compile("foo|bar")'</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>(Use <a class="reference internal" href="#module-profile" title="profile: Python source profiler."><code class="xref py py-mod docutils literal notranslate"><span class="pre">profile</span></code></a> instead of <a class="reference internal" href="#module-cProfile" title="cProfile"><code class="xref py py-mod docutils literal notranslate"><span class="pre">cProfile</span></code></a> if the latter is not available on
|
||
your system.)</p>
|
||
<p>The above action would run <a class="reference internal" href="re.html#re.compile" title="re.compile"><code class="xref py py-func docutils literal notranslate"><span class="pre">re.compile()</span></code></a> and print profile results like
|
||
the following:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span> <span class="mi">197</span> <span class="n">function</span> <span class="n">calls</span> <span class="p">(</span><span class="mi">192</span> <span class="n">primitive</span> <span class="n">calls</span><span class="p">)</span> <span class="ow">in</span> <span class="mf">0.002</span> <span class="n">seconds</span>
|
||
|
||
<span class="n">Ordered</span> <span class="n">by</span><span class="p">:</span> <span class="n">standard</span> <span class="n">name</span>
|
||
|
||
<span class="n">ncalls</span> <span class="n">tottime</span> <span class="n">percall</span> <span class="n">cumtime</span> <span class="n">percall</span> <span class="n">filename</span><span class="p">:</span><span class="n">lineno</span><span class="p">(</span><span class="n">function</span><span class="p">)</span>
|
||
<span class="mi">1</span> <span class="mf">0.000</span> <span class="mf">0.000</span> <span class="mf">0.001</span> <span class="mf">0.001</span> <span class="o"><</span><span class="n">string</span><span class="o">></span><span class="p">:</span><span class="mi">1</span><span class="p">(</span><span class="o"><</span><span class="n">module</span><span class="o">></span><span class="p">)</span>
|
||
<span class="mi">1</span> <span class="mf">0.000</span> <span class="mf">0.000</span> <span class="mf">0.001</span> <span class="mf">0.001</span> <span class="n">re</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">212</span><span class="p">(</span><span class="nb">compile</span><span class="p">)</span>
|
||
<span class="mi">1</span> <span class="mf">0.000</span> <span class="mf">0.000</span> <span class="mf">0.001</span> <span class="mf">0.001</span> <span class="n">re</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">268</span><span class="p">(</span><span class="n">_compile</span><span class="p">)</span>
|
||
<span class="mi">1</span> <span class="mf">0.000</span> <span class="mf">0.000</span> <span class="mf">0.000</span> <span class="mf">0.000</span> <span class="n">sre_compile</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">172</span><span class="p">(</span><span class="n">_compile_charset</span><span class="p">)</span>
|
||
<span class="mi">1</span> <span class="mf">0.000</span> <span class="mf">0.000</span> <span class="mf">0.000</span> <span class="mf">0.000</span> <span class="n">sre_compile</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">201</span><span class="p">(</span><span class="n">_optimize_charset</span><span class="p">)</span>
|
||
<span class="mi">4</span> <span class="mf">0.000</span> <span class="mf">0.000</span> <span class="mf">0.000</span> <span class="mf">0.000</span> <span class="n">sre_compile</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">25</span><span class="p">(</span><span class="n">_identityfunction</span><span class="p">)</span>
|
||
<span class="mi">3</span><span class="o">/</span><span class="mi">1</span> <span class="mf">0.000</span> <span class="mf">0.000</span> <span class="mf">0.000</span> <span class="mf">0.000</span> <span class="n">sre_compile</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">33</span><span class="p">(</span><span class="n">_compile</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The first line indicates that 197 calls were monitored. Of those calls, 192
|
||
were <em class="dfn">primitive</em>, meaning that the call was not induced via recursion. The
|
||
next line: <code class="docutils literal notranslate"><span class="pre">Ordered</span> <span class="pre">by:</span> <span class="pre">standard</span> <span class="pre">name</span></code>, indicates that the text string in the
|
||
far right column was used to sort the output. The column headings include:</p>
|
||
<dl class="simple">
|
||
<dt>ncalls</dt><dd><p>for the number of calls.</p>
|
||
</dd>
|
||
<dt>tottime</dt><dd><p>for the total time spent in the given function (and excluding time made in
|
||
calls to sub-functions)</p>
|
||
</dd>
|
||
<dt>percall</dt><dd><p>is the quotient of <code class="docutils literal notranslate"><span class="pre">tottime</span></code> divided by <code class="docutils literal notranslate"><span class="pre">ncalls</span></code></p>
|
||
</dd>
|
||
<dt>cumtime</dt><dd><p>is the cumulative time spent in this and all subfunctions (from invocation
|
||
till exit). This figure is accurate <em>even</em> for recursive functions.</p>
|
||
</dd>
|
||
<dt>percall</dt><dd><p>is the quotient of <code class="docutils literal notranslate"><span class="pre">cumtime</span></code> divided by primitive calls</p>
|
||
</dd>
|
||
<dt>filename:lineno(function)</dt><dd><p>provides the respective data of each function</p>
|
||
</dd>
|
||
</dl>
|
||
<p>When there are two numbers in the first column (for example <code class="docutils literal notranslate"><span class="pre">3/1</span></code>), it means
|
||
that the function recursed. The second value is the number of primitive calls
|
||
and the former is the total number of calls. Note that when the function does
|
||
not recurse, these two values are the same, and only the single figure is
|
||
printed.</p>
|
||
<p>Instead of printing the output at the end of the profile run, you can save the
|
||
results to a file by specifying a filename to the <code class="xref py py-func docutils literal notranslate"><span class="pre">run()</span></code> function:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">cProfile</span>
|
||
<span class="kn">import</span> <span class="nn">re</span>
|
||
<span class="n">cProfile</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="s1">'re.compile("foo|bar")'</span><span class="p">,</span> <span class="s1">'restats'</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The <a class="reference internal" href="#pstats.Stats" title="pstats.Stats"><code class="xref py py-class docutils literal notranslate"><span class="pre">pstats.Stats</span></code></a> class reads profile results from a file and formats
|
||
them in various ways.</p>
|
||
<p>The file <a class="reference internal" href="#module-cProfile" title="cProfile"><code class="xref py py-mod docutils literal notranslate"><span class="pre">cProfile</span></code></a> can also be invoked as a script to profile another
|
||
script. For example:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">python</span> <span class="o">-</span><span class="n">m</span> <span class="n">cProfile</span> <span class="p">[</span><span class="o">-</span><span class="n">o</span> <span class="n">output_file</span><span class="p">]</span> <span class="p">[</span><span class="o">-</span><span class="n">s</span> <span class="n">sort_order</span><span class="p">]</span> <span class="p">(</span><span class="o">-</span><span class="n">m</span> <span class="n">module</span> <span class="o">|</span> <span class="n">myscript</span><span class="o">.</span><span class="n">py</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><span class="pre">-o</span></code> writes the profile results to a file instead of to stdout</p>
|
||
<p><code class="docutils literal notranslate"><span class="pre">-s</span></code> specifies one of the <a class="reference internal" href="#pstats.Stats.sort_stats" title="pstats.Stats.sort_stats"><code class="xref py py-func docutils literal notranslate"><span class="pre">sort_stats()</span></code></a> sort values to sort
|
||
the output by. This only applies when <code class="docutils literal notranslate"><span class="pre">-o</span></code> is not supplied.</p>
|
||
<p><code class="docutils literal notranslate"><span class="pre">-m</span></code> specifies that a module is being profiled instead of a script.</p>
|
||
<blockquote>
|
||
<div><div class="versionadded">
|
||
<p><span class="versionmodified added">New in version 3.7: </span>Added the <code class="docutils literal notranslate"><span class="pre">-m</span></code> option.</p>
|
||
</div>
|
||
</div></blockquote>
|
||
<p>The <a class="reference internal" href="#module-pstats" title="pstats: Statistics object for use with the profiler."><code class="xref py py-mod docutils literal notranslate"><span class="pre">pstats</span></code></a> module’s <a class="reference internal" href="#pstats.Stats" title="pstats.Stats"><code class="xref py py-class docutils literal notranslate"><span class="pre">Stats</span></code></a> class has a variety of methods
|
||
for manipulating and printing the data saved into a profile results file:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pstats</span>
|
||
<span class="kn">from</span> <span class="nn">pstats</span> <span class="k">import</span> <span class="n">SortKey</span>
|
||
<span class="n">p</span> <span class="o">=</span> <span class="n">pstats</span><span class="o">.</span><span class="n">Stats</span><span class="p">(</span><span class="s1">'restats'</span><span class="p">)</span>
|
||
<span class="n">p</span><span class="o">.</span><span class="n">strip_dirs</span><span class="p">()</span><span class="o">.</span><span class="n">sort_stats</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">print_stats</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The <a class="reference internal" href="#pstats.Stats.strip_dirs" title="pstats.Stats.strip_dirs"><code class="xref py py-meth docutils literal notranslate"><span class="pre">strip_dirs()</span></code></a> method removed the extraneous path from all
|
||
the module names. The <a class="reference internal" href="#pstats.Stats.sort_stats" title="pstats.Stats.sort_stats"><code class="xref py py-meth docutils literal notranslate"><span class="pre">sort_stats()</span></code></a> method sorted all the
|
||
entries according to the standard module/line/name string that is printed. The
|
||
<a class="reference internal" href="#pstats.Stats.print_stats" title="pstats.Stats.print_stats"><code class="xref py py-meth docutils literal notranslate"><span class="pre">print_stats()</span></code></a> method printed out all the statistics. You
|
||
might try the following sort calls:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">p</span><span class="o">.</span><span class="n">sort_stats</span><span class="p">(</span><span class="n">SortKey</span><span class="o">.</span><span class="n">NAME</span><span class="p">)</span>
|
||
<span class="n">p</span><span class="o">.</span><span class="n">print_stats</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The first call will actually sort the list by function name, and the second call
|
||
will print out the statistics. The following are some interesting calls to
|
||
experiment with:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">p</span><span class="o">.</span><span class="n">sort_stats</span><span class="p">(</span><span class="n">SortKey</span><span class="o">.</span><span class="n">CUMULATIVE</span><span class="p">)</span><span class="o">.</span><span class="n">print_stats</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This sorts the profile by cumulative time in a function, and then only prints
|
||
the ten most significant lines. If you want to understand what algorithms are
|
||
taking time, the above line is what you would use.</p>
|
||
<p>If you were looking to see what functions were looping a lot, and taking a lot
|
||
of time, you would do:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">p</span><span class="o">.</span><span class="n">sort_stats</span><span class="p">(</span><span class="n">SortKey</span><span class="o">.</span><span class="n">TIME</span><span class="p">)</span><span class="o">.</span><span class="n">print_stats</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>to sort according to time spent within each function, and then print the
|
||
statistics for the top ten functions.</p>
|
||
<p>You might also try:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">p</span><span class="o">.</span><span class="n">sort_stats</span><span class="p">(</span><span class="n">SortKey</span><span class="o">.</span><span class="n">FILENAME</span><span class="p">)</span><span class="o">.</span><span class="n">print_stats</span><span class="p">(</span><span class="s1">'__init__'</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This will sort all the statistics by file name, and then print out statistics
|
||
for only the class init methods (since they are spelled with <code class="docutils literal notranslate"><span class="pre">__init__</span></code> in
|
||
them). As one final example, you could try:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">p</span><span class="o">.</span><span class="n">sort_stats</span><span class="p">(</span><span class="n">SortKey</span><span class="o">.</span><span class="n">TIME</span><span class="p">,</span> <span class="n">SortKey</span><span class="o">.</span><span class="n">CUMULATIVE</span><span class="p">)</span><span class="o">.</span><span class="n">print_stats</span><span class="p">(</span><span class="o">.</span><span class="mi">5</span><span class="p">,</span> <span class="s1">'init'</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This line sorts statistics with a primary key of time, and a secondary key of
|
||
cumulative time, and then prints out some of the statistics. To be specific, the
|
||
list is first culled down to 50% (re: <code class="docutils literal notranslate"><span class="pre">.5</span></code>) of its original size, then only
|
||
lines containing <code class="docutils literal notranslate"><span class="pre">init</span></code> are maintained, and that sub-sub-list is printed.</p>
|
||
<p>If you wondered what functions called the above functions, you could now (<code class="docutils literal notranslate"><span class="pre">p</span></code>
|
||
is still sorted according to the last criteria) do:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">p</span><span class="o">.</span><span class="n">print_callers</span><span class="p">(</span><span class="o">.</span><span class="mi">5</span><span class="p">,</span> <span class="s1">'init'</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>and you would get a list of callers for each of the listed functions.</p>
|
||
<p>If you want more functionality, you’re going to have to read the manual, or
|
||
guess what the following functions do:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">p</span><span class="o">.</span><span class="n">print_callees</span><span class="p">()</span>
|
||
<span class="n">p</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s1">'restats'</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Invoked as a script, the <a class="reference internal" href="#module-pstats" title="pstats: Statistics object for use with the profiler."><code class="xref py py-mod docutils literal notranslate"><span class="pre">pstats</span></code></a> module is a statistics browser for
|
||
reading and examining profile dumps. It has a simple line-oriented interface
|
||
(implemented using <a class="reference internal" href="cmd.html#module-cmd" title="cmd: Build line-oriented command interpreters."><code class="xref py py-mod docutils literal notranslate"><span class="pre">cmd</span></code></a>) and interactive help.</p>
|
||
</div>
|
||
<div class="section" id="module-cProfile">
|
||
<span id="profile-and-cprofile-module-reference"></span><h2><a class="reference internal" href="#module-profile" title="profile: Python source profiler."><code class="xref py py-mod docutils literal notranslate"><span class="pre">profile</span></code></a> and <a class="reference internal" href="#module-cProfile" title="cProfile"><code class="xref py py-mod docutils literal notranslate"><span class="pre">cProfile</span></code></a> Module Reference<a class="headerlink" href="#module-cProfile" title="Permalink to this headline">¶</a></h2>
|
||
<span class="target" id="module-profile"></span><p>Both the <a class="reference internal" href="#module-profile" title="profile: Python source profiler."><code class="xref py py-mod docutils literal notranslate"><span class="pre">profile</span></code></a> and <a class="reference internal" href="#module-cProfile" title="cProfile"><code class="xref py py-mod docutils literal notranslate"><span class="pre">cProfile</span></code></a> modules provide the following
|
||
functions:</p>
|
||
<dl class="function">
|
||
<dt id="profile.run">
|
||
<code class="descclassname">profile.</code><code class="descname">run</code><span class="sig-paren">(</span><em>command</em>, <em>filename=None</em>, <em>sort=-1</em><span class="sig-paren">)</span><a class="headerlink" href="#profile.run" title="Permalink to this definition">¶</a></dt>
|
||
<dd><p>This function takes a single argument that can be passed to the <a class="reference internal" href="functions.html#exec" title="exec"><code class="xref py py-func docutils literal notranslate"><span class="pre">exec()</span></code></a>
|
||
function, and an optional file name. In all cases this routine executes:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">exec</span><span class="p">(</span><span class="n">command</span><span class="p">,</span> <span class="n">__main__</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">,</span> <span class="n">__main__</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>and gathers profiling statistics from the execution. If no file name is
|
||
present, then this function automatically creates a <a class="reference internal" href="#pstats.Stats" title="pstats.Stats"><code class="xref py py-class docutils literal notranslate"><span class="pre">Stats</span></code></a>
|
||
instance and prints a simple profiling report. If the sort value is specified,
|
||
it is passed to this <a class="reference internal" href="#pstats.Stats" title="pstats.Stats"><code class="xref py py-class docutils literal notranslate"><span class="pre">Stats</span></code></a> instance to control how the
|
||
results are sorted.</p>
|
||
</dd></dl>
|
||
|
||
<dl class="function">
|
||
<dt id="profile.runctx">
|
||
<code class="descclassname">profile.</code><code class="descname">runctx</code><span class="sig-paren">(</span><em>command</em>, <em>globals</em>, <em>locals</em>, <em>filename=None</em>, <em>sort=-1</em><span class="sig-paren">)</span><a class="headerlink" href="#profile.runctx" title="Permalink to this definition">¶</a></dt>
|
||
<dd><p>This function is similar to <a class="reference internal" href="#profile.run" title="profile.run"><code class="xref py py-func docutils literal notranslate"><span class="pre">run()</span></code></a>, with added arguments to supply the
|
||
globals and locals dictionaries for the <em>command</em> string. This routine
|
||
executes:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">exec</span><span class="p">(</span><span class="n">command</span><span class="p">,</span> <span class="nb">globals</span><span class="p">,</span> <span class="nb">locals</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>and gathers profiling statistics as in the <a class="reference internal" href="#profile.run" title="profile.run"><code class="xref py py-func docutils literal notranslate"><span class="pre">run()</span></code></a> function above.</p>
|
||
</dd></dl>
|
||
|
||
<dl class="class">
|
||
<dt id="profile.Profile">
|
||
<em class="property">class </em><code class="descclassname">profile.</code><code class="descname">Profile</code><span class="sig-paren">(</span><em>timer=None</em>, <em>timeunit=0.0</em>, <em>subcalls=True</em>, <em>builtins=True</em><span class="sig-paren">)</span><a class="headerlink" href="#profile.Profile" title="Permalink to this definition">¶</a></dt>
|
||
<dd><p>This class is normally only used if more precise control over profiling is
|
||
needed than what the <code class="xref py py-func docutils literal notranslate"><span class="pre">cProfile.run()</span></code> function provides.</p>
|
||
<p>A custom timer can be supplied for measuring how long code takes to run via
|
||
the <em>timer</em> argument. This must be a function that returns a single number
|
||
representing the current time. If the number is an integer, the <em>timeunit</em>
|
||
specifies a multiplier that specifies the duration of each unit of time. For
|
||
example, if the timer returns times measured in thousands of seconds, the
|
||
time unit would be <code class="docutils literal notranslate"><span class="pre">.001</span></code>.</p>
|
||
<p>Directly using the <a class="reference internal" href="#profile.Profile" title="profile.Profile"><code class="xref py py-class docutils literal notranslate"><span class="pre">Profile</span></code></a> class allows formatting profile results
|
||
without writing the profile data to a file:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">cProfile</span><span class="o">,</span> <span class="nn">pstats</span><span class="o">,</span> <span class="nn">io</span>
|
||
<span class="kn">from</span> <span class="nn">pstats</span> <span class="k">import</span> <span class="n">SortKey</span>
|
||
<span class="n">pr</span> <span class="o">=</span> <span class="n">cProfile</span><span class="o">.</span><span class="n">Profile</span><span class="p">()</span>
|
||
<span class="n">pr</span><span class="o">.</span><span class="n">enable</span><span class="p">()</span>
|
||
<span class="c1"># ... do something ...</span>
|
||
<span class="n">pr</span><span class="o">.</span><span class="n">disable</span><span class="p">()</span>
|
||
<span class="n">s</span> <span class="o">=</span> <span class="n">io</span><span class="o">.</span><span class="n">StringIO</span><span class="p">()</span>
|
||
<span class="n">sortby</span> <span class="o">=</span> <span class="n">SortKey</span><span class="o">.</span><span class="n">CUMULATIVE</span>
|
||
<span class="n">ps</span> <span class="o">=</span> <span class="n">pstats</span><span class="o">.</span><span class="n">Stats</span><span class="p">(</span><span class="n">pr</span><span class="p">,</span> <span class="n">stream</span><span class="o">=</span><span class="n">s</span><span class="p">)</span><span class="o">.</span><span class="n">sort_stats</span><span class="p">(</span><span class="n">sortby</span><span class="p">)</span>
|
||
<span class="n">ps</span><span class="o">.</span><span class="n">print_stats</span><span class="p">()</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">getvalue</span><span class="p">())</span>
|
||
</pre></div>
|
||
</div>
|
||
<dl class="method">
|
||
<dt id="profile.Profile.enable">
|
||
<code class="descname">enable</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#profile.Profile.enable" title="Permalink to this definition">¶</a></dt>
|
||
<dd><p>Start collecting profiling data.</p>
|
||
</dd></dl>
|
||
|
||
<dl class="method">
|
||
<dt id="profile.Profile.disable">
|
||
<code class="descname">disable</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#profile.Profile.disable" title="Permalink to this definition">¶</a></dt>
|
||
<dd><p>Stop collecting profiling data.</p>
|
||
</dd></dl>
|
||
|
||
<dl class="method">
|
||
<dt id="profile.Profile.create_stats">
|
||
<code class="descname">create_stats</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#profile.Profile.create_stats" title="Permalink to this definition">¶</a></dt>
|
||
<dd><p>Stop collecting profiling data and record the results internally
|
||
as the current profile.</p>
|
||
</dd></dl>
|
||
|
||
<dl class="method">
|
||
<dt id="profile.Profile.print_stats">
|
||
<code class="descname">print_stats</code><span class="sig-paren">(</span><em>sort=-1</em><span class="sig-paren">)</span><a class="headerlink" href="#profile.Profile.print_stats" title="Permalink to this definition">¶</a></dt>
|
||
<dd><p>Create a <a class="reference internal" href="#pstats.Stats" title="pstats.Stats"><code class="xref py py-class docutils literal notranslate"><span class="pre">Stats</span></code></a> object based on the current
|
||
profile and print the results to stdout.</p>
|
||
</dd></dl>
|
||
|
||
<dl class="method">
|
||
<dt id="profile.Profile.dump_stats">
|
||
<code class="descname">dump_stats</code><span class="sig-paren">(</span><em>filename</em><span class="sig-paren">)</span><a class="headerlink" href="#profile.Profile.dump_stats" title="Permalink to this definition">¶</a></dt>
|
||
<dd><p>Write the results of the current profile to <em>filename</em>.</p>
|
||
</dd></dl>
|
||
|
||
<dl class="method">
|
||
<dt id="profile.Profile.run">
|
||
<code class="descname">run</code><span class="sig-paren">(</span><em>cmd</em><span class="sig-paren">)</span><a class="headerlink" href="#profile.Profile.run" title="Permalink to this definition">¶</a></dt>
|
||
<dd><p>Profile the cmd via <a class="reference internal" href="functions.html#exec" title="exec"><code class="xref py py-func docutils literal notranslate"><span class="pre">exec()</span></code></a>.</p>
|
||
</dd></dl>
|
||
|
||
<dl class="method">
|
||
<dt id="profile.Profile.runctx">
|
||
<code class="descname">runctx</code><span class="sig-paren">(</span><em>cmd</em>, <em>globals</em>, <em>locals</em><span class="sig-paren">)</span><a class="headerlink" href="#profile.Profile.runctx" title="Permalink to this definition">¶</a></dt>
|
||
<dd><p>Profile the cmd via <a class="reference internal" href="functions.html#exec" title="exec"><code class="xref py py-func docutils literal notranslate"><span class="pre">exec()</span></code></a> with the specified global and
|
||
local environment.</p>
|
||
</dd></dl>
|
||
|
||
<dl class="method">
|
||
<dt id="profile.Profile.runcall">
|
||
<code class="descname">runcall</code><span class="sig-paren">(</span><em>func</em>, <em>*args</em>, <em>**kwargs</em><span class="sig-paren">)</span><a class="headerlink" href="#profile.Profile.runcall" title="Permalink to this definition">¶</a></dt>
|
||
<dd><p>Profile <code class="docutils literal notranslate"><span class="pre">func(*args,</span> <span class="pre">**kwargs)</span></code></p>
|
||
</dd></dl>
|
||
|
||
</dd></dl>
|
||
|
||
<p>Note that profiling will only work if the called command/function actually
|
||
returns. If the interpreter is terminated (e.g. via a <a class="reference internal" href="sys.html#sys.exit" title="sys.exit"><code class="xref py py-func docutils literal notranslate"><span class="pre">sys.exit()</span></code></a> call
|
||
during the called command/function execution) no profiling results will be
|
||
printed.</p>
|
||
</div>
|
||
<div class="section" id="the-stats-class">
|
||
<span id="profile-stats"></span><h2>The <code class="xref py py-class docutils literal notranslate"><span class="pre">Stats</span></code> Class<a class="headerlink" href="#the-stats-class" title="Permalink to this headline">¶</a></h2>
|
||
<p>Analysis of the profiler data is done using the <a class="reference internal" href="#pstats.Stats" title="pstats.Stats"><code class="xref py py-class docutils literal notranslate"><span class="pre">Stats</span></code></a> class.</p>
|
||
<span class="target" id="module-pstats"></span><dl class="class">
|
||
<dt id="pstats.Stats">
|
||
<em class="property">class </em><code class="descclassname">pstats.</code><code class="descname">Stats</code><span class="sig-paren">(</span><em>*filenames or profile</em>, <em>stream=sys.stdout</em><span class="sig-paren">)</span><a class="headerlink" href="#pstats.Stats" title="Permalink to this definition">¶</a></dt>
|
||
<dd><p>This class constructor creates an instance of a “statistics object” from a
|
||
<em>filename</em> (or list of filenames) or from a <code class="xref py py-class docutils literal notranslate"><span class="pre">Profile</span></code> instance. Output
|
||
will be printed to the stream specified by <em>stream</em>.</p>
|
||
<p>The file selected by the above constructor must have been created by the
|
||
corresponding version of <a class="reference internal" href="#module-profile" title="profile: Python source profiler."><code class="xref py py-mod docutils literal notranslate"><span class="pre">profile</span></code></a> or <a class="reference internal" href="#module-cProfile" title="cProfile"><code class="xref py py-mod docutils literal notranslate"><span class="pre">cProfile</span></code></a>. To be specific,
|
||
there is <em>no</em> file compatibility guaranteed with future versions of this
|
||
profiler, and there is no compatibility with files produced by other
|
||
profilers, or the same profiler run on a different operating system. If
|
||
several files are provided, all the statistics for identical functions will
|
||
be coalesced, so that an overall view of several processes can be considered
|
||
in a single report. If additional files need to be combined with data in an
|
||
existing <a class="reference internal" href="#pstats.Stats" title="pstats.Stats"><code class="xref py py-class docutils literal notranslate"><span class="pre">Stats</span></code></a> object, the <a class="reference internal" href="#pstats.Stats.add" title="pstats.Stats.add"><code class="xref py py-meth docutils literal notranslate"><span class="pre">add()</span></code></a> method
|
||
can be used.</p>
|
||
<p>Instead of reading the profile data from a file, a <code class="xref py py-class docutils literal notranslate"><span class="pre">cProfile.Profile</span></code>
|
||
or <a class="reference internal" href="#profile.Profile" title="profile.Profile"><code class="xref py py-class docutils literal notranslate"><span class="pre">profile.Profile</span></code></a> object can be used as the profile data source.</p>
|
||
<p><a class="reference internal" href="#pstats.Stats" title="pstats.Stats"><code class="xref py py-class docutils literal notranslate"><span class="pre">Stats</span></code></a> objects have the following methods:</p>
|
||
<dl class="method">
|
||
<dt id="pstats.Stats.strip_dirs">
|
||
<code class="descname">strip_dirs</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#pstats.Stats.strip_dirs" title="Permalink to this definition">¶</a></dt>
|
||
<dd><p>This method for the <a class="reference internal" href="#pstats.Stats" title="pstats.Stats"><code class="xref py py-class docutils literal notranslate"><span class="pre">Stats</span></code></a> class removes all leading path
|
||
information from file names. It is very useful in reducing the size of
|
||
the printout to fit within (close to) 80 columns. This method modifies
|
||
the object, and the stripped information is lost. After performing a
|
||
strip operation, the object is considered to have its entries in a
|
||
“random” order, as it was just after object initialization and loading.
|
||
If <a class="reference internal" href="#pstats.Stats.strip_dirs" title="pstats.Stats.strip_dirs"><code class="xref py py-meth docutils literal notranslate"><span class="pre">strip_dirs()</span></code></a> causes two function names to be
|
||
indistinguishable (they are on the same line of the same filename, and
|
||
have the same function name), then the statistics for these two entries
|
||
are accumulated into a single entry.</p>
|
||
</dd></dl>
|
||
|
||
<dl class="method">
|
||
<dt id="pstats.Stats.add">
|
||
<code class="descname">add</code><span class="sig-paren">(</span><em>*filenames</em><span class="sig-paren">)</span><a class="headerlink" href="#pstats.Stats.add" title="Permalink to this definition">¶</a></dt>
|
||
<dd><p>This method of the <a class="reference internal" href="#pstats.Stats" title="pstats.Stats"><code class="xref py py-class docutils literal notranslate"><span class="pre">Stats</span></code></a> class accumulates additional profiling
|
||
information into the current profiling object. Its arguments should refer
|
||
to filenames created by the corresponding version of <a class="reference internal" href="#profile.run" title="profile.run"><code class="xref py py-func docutils literal notranslate"><span class="pre">profile.run()</span></code></a>
|
||
or <code class="xref py py-func docutils literal notranslate"><span class="pre">cProfile.run()</span></code>. Statistics for identically named (re: file, line,
|
||
name) functions are automatically accumulated into single function
|
||
statistics.</p>
|
||
</dd></dl>
|
||
|
||
<dl class="method">
|
||
<dt id="pstats.Stats.dump_stats">
|
||
<code class="descname">dump_stats</code><span class="sig-paren">(</span><em>filename</em><span class="sig-paren">)</span><a class="headerlink" href="#pstats.Stats.dump_stats" title="Permalink to this definition">¶</a></dt>
|
||
<dd><p>Save the data loaded into the <a class="reference internal" href="#pstats.Stats" title="pstats.Stats"><code class="xref py py-class docutils literal notranslate"><span class="pre">Stats</span></code></a> object to a file named
|
||
<em>filename</em>. The file is created if it does not exist, and is overwritten
|
||
if it already exists. This is equivalent to the method of the same name
|
||
on the <a class="reference internal" href="#profile.Profile" title="profile.Profile"><code class="xref py py-class docutils literal notranslate"><span class="pre">profile.Profile</span></code></a> and <code class="xref py py-class docutils literal notranslate"><span class="pre">cProfile.Profile</span></code> classes.</p>
|
||
</dd></dl>
|
||
|
||
<dl class="method">
|
||
<dt id="pstats.Stats.sort_stats">
|
||
<code class="descname">sort_stats</code><span class="sig-paren">(</span><em>*keys</em><span class="sig-paren">)</span><a class="headerlink" href="#pstats.Stats.sort_stats" title="Permalink to this definition">¶</a></dt>
|
||
<dd><p>This method modifies the <a class="reference internal" href="#pstats.Stats" title="pstats.Stats"><code class="xref py py-class docutils literal notranslate"><span class="pre">Stats</span></code></a> object by sorting it according to
|
||
the supplied criteria. The argument can be either a string or a SortKey
|
||
enum identifying the basis of a sort (example: <code class="docutils literal notranslate"><span class="pre">'time'</span></code>, <code class="docutils literal notranslate"><span class="pre">'name'</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">SortKey.TIME</span></code> or <code class="docutils literal notranslate"><span class="pre">SortKey.NAME</span></code>). The SortKey enums argument have
|
||
advantage over the string argument in that it is more robust and less
|
||
error prone.</p>
|
||
<p>When more than one key is provided, then additional keys are used as
|
||
secondary criteria when there is equality in all keys selected before
|
||
them. For example, <code class="docutils literal notranslate"><span class="pre">sort_stats(SortKey.NAME,</span> <span class="pre">SortKey.FILE)</span></code> will sort
|
||
all the entries according to their function name, and resolve all ties
|
||
(identical function names) by sorting by file name.</p>
|
||
<p>For the string argument, abbreviations can be used for any key names, as
|
||
long as the abbreviation is unambiguous.</p>
|
||
<p>The following are the valid string and SortKey:</p>
|
||
<table class="docutils align-center">
|
||
<colgroup>
|
||
<col style="width: 30%" />
|
||
<col style="width: 34%" />
|
||
<col style="width: 36%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr class="row-odd"><th class="head"><p>Valid String Arg</p></th>
|
||
<th class="head"><p>Valid enum Arg</p></th>
|
||
<th class="head"><p>Meaning</p></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'calls'</span></code></p></td>
|
||
<td><p>SortKey.CALLS</p></td>
|
||
<td><p>call count</p></td>
|
||
</tr>
|
||
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">'cumulative'</span></code></p></td>
|
||
<td><p>SortKey.CUMULATIVE</p></td>
|
||
<td><p>cumulative time</p></td>
|
||
</tr>
|
||
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'cumtime'</span></code></p></td>
|
||
<td><p>N/A</p></td>
|
||
<td><p>cumulative time</p></td>
|
||
</tr>
|
||
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">'file'</span></code></p></td>
|
||
<td><p>N/A</p></td>
|
||
<td><p>file name</p></td>
|
||
</tr>
|
||
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'filename'</span></code></p></td>
|
||
<td><p>SortKey.FILENAME</p></td>
|
||
<td><p>file name</p></td>
|
||
</tr>
|
||
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">'module'</span></code></p></td>
|
||
<td><p>N/A</p></td>
|
||
<td><p>file name</p></td>
|
||
</tr>
|
||
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'ncalls'</span></code></p></td>
|
||
<td><p>N/A</p></td>
|
||
<td><p>call count</p></td>
|
||
</tr>
|
||
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">'pcalls'</span></code></p></td>
|
||
<td><p>SortKey.PCALLS</p></td>
|
||
<td><p>primitive call count</p></td>
|
||
</tr>
|
||
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'line'</span></code></p></td>
|
||
<td><p>SortKey.LINE</p></td>
|
||
<td><p>line number</p></td>
|
||
</tr>
|
||
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">'name'</span></code></p></td>
|
||
<td><p>SortKey.NAME</p></td>
|
||
<td><p>function name</p></td>
|
||
</tr>
|
||
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'nfl'</span></code></p></td>
|
||
<td><p>SortKey.NFL</p></td>
|
||
<td><p>name/file/line</p></td>
|
||
</tr>
|
||
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">'stdname'</span></code></p></td>
|
||
<td><p>SortKey.STDNAME</p></td>
|
||
<td><p>standard name</p></td>
|
||
</tr>
|
||
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'time'</span></code></p></td>
|
||
<td><p>SortKey.TIME</p></td>
|
||
<td><p>internal time</p></td>
|
||
</tr>
|
||
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">'tottime'</span></code></p></td>
|
||
<td><p>N/A</p></td>
|
||
<td><p>internal time</p></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Note that all sorts on statistics are in descending order (placing most
|
||
time consuming items first), where as name, file, and line number searches
|
||
are in ascending order (alphabetical). The subtle distinction between
|
||
<code class="docutils literal notranslate"><span class="pre">SortKey.NFL</span></code> and <code class="docutils literal notranslate"><span class="pre">SortKey.STDNAME</span></code> is that the standard name is a
|
||
sort of the name as printed, which means that the embedded line numbers
|
||
get compared in an odd way. For example, lines 3, 20, and 40 would (if
|
||
the file names were the same) appear in the string order 20, 3 and 40.
|
||
In contrast, <code class="docutils literal notranslate"><span class="pre">SortKey.NFL</span></code> does a numeric compare of the line numbers.
|
||
In fact, <code class="docutils literal notranslate"><span class="pre">sort_stats(SortKey.NFL)</span></code> is the same as
|
||
<code class="docutils literal notranslate"><span class="pre">sort_stats(SortKey.NAME,</span> <span class="pre">SortKey.FILENAME,</span> <span class="pre">SortKey.LINE)</span></code>.</p>
|
||
<p>For backward-compatibility reasons, the numeric arguments <code class="docutils literal notranslate"><span class="pre">-1</span></code>, <code class="docutils literal notranslate"><span class="pre">0</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">1</span></code>, and <code class="docutils literal notranslate"><span class="pre">2</span></code> are permitted. They are interpreted as <code class="docutils literal notranslate"><span class="pre">'stdname'</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">'calls'</span></code>, <code class="docutils literal notranslate"><span class="pre">'time'</span></code>, and <code class="docutils literal notranslate"><span class="pre">'cumulative'</span></code> respectively. If this old
|
||
style format (numeric) is used, only one sort key (the numeric key) will
|
||
be used, and additional arguments will be silently ignored.</p>
|
||
<div class="versionadded">
|
||
<p><span class="versionmodified added">New in version 3.7: </span>Added the SortKey enum.</p>
|
||
</div>
|
||
</dd></dl>
|
||
|
||
<dl class="method">
|
||
<dt id="pstats.Stats.reverse_order">
|
||
<code class="descname">reverse_order</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#pstats.Stats.reverse_order" title="Permalink to this definition">¶</a></dt>
|
||
<dd><p>This method for the <a class="reference internal" href="#pstats.Stats" title="pstats.Stats"><code class="xref py py-class docutils literal notranslate"><span class="pre">Stats</span></code></a> class reverses the ordering of the
|
||
basic list within the object. Note that by default ascending vs
|
||
descending order is properly selected based on the sort key of choice.</p>
|
||
</dd></dl>
|
||
|
||
<dl class="method">
|
||
<dt id="pstats.Stats.print_stats">
|
||
<code class="descname">print_stats</code><span class="sig-paren">(</span><em>*restrictions</em><span class="sig-paren">)</span><a class="headerlink" href="#pstats.Stats.print_stats" title="Permalink to this definition">¶</a></dt>
|
||
<dd><p>This method for the <a class="reference internal" href="#pstats.Stats" title="pstats.Stats"><code class="xref py py-class docutils literal notranslate"><span class="pre">Stats</span></code></a> class prints out a report as described
|
||
in the <a class="reference internal" href="#profile.run" title="profile.run"><code class="xref py py-func docutils literal notranslate"><span class="pre">profile.run()</span></code></a> definition.</p>
|
||
<p>The order of the printing is based on the last
|
||
<a class="reference internal" href="#pstats.Stats.sort_stats" title="pstats.Stats.sort_stats"><code class="xref py py-meth docutils literal notranslate"><span class="pre">sort_stats()</span></code></a> operation done on the object (subject to
|
||
caveats in <a class="reference internal" href="#pstats.Stats.add" title="pstats.Stats.add"><code class="xref py py-meth docutils literal notranslate"><span class="pre">add()</span></code></a> and
|
||
<a class="reference internal" href="#pstats.Stats.strip_dirs" title="pstats.Stats.strip_dirs"><code class="xref py py-meth docutils literal notranslate"><span class="pre">strip_dirs()</span></code></a>).</p>
|
||
<p>The arguments provided (if any) can be used to limit the list down to the
|
||
significant entries. Initially, the list is taken to be the complete set
|
||
of profiled functions. Each restriction is either an integer (to select a
|
||
count of lines), or a decimal fraction between 0.0 and 1.0 inclusive (to
|
||
select a percentage of lines), or a string that will interpreted as a
|
||
regular expression (to pattern match the standard name that is printed).
|
||
If several restrictions are provided, then they are applied sequentially.
|
||
For example:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">print_stats</span><span class="p">(</span><span class="o">.</span><span class="mi">1</span><span class="p">,</span> <span class="s1">'foo:'</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>would first limit the printing to first 10% of list, and then only print
|
||
functions that were part of filename <code class="file docutils literal notranslate"><span class="pre">.*foo:</span></code>. In contrast, the
|
||
command:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">print_stats</span><span class="p">(</span><span class="s1">'foo:'</span><span class="p">,</span> <span class="o">.</span><span class="mi">1</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>would limit the list to all functions having file names <code class="file docutils literal notranslate"><span class="pre">.*foo:</span></code>,
|
||
and then proceed to only print the first 10% of them.</p>
|
||
</dd></dl>
|
||
|
||
<dl class="method">
|
||
<dt id="pstats.Stats.print_callers">
|
||
<code class="descname">print_callers</code><span class="sig-paren">(</span><em>*restrictions</em><span class="sig-paren">)</span><a class="headerlink" href="#pstats.Stats.print_callers" title="Permalink to this definition">¶</a></dt>
|
||
<dd><p>This method for the <a class="reference internal" href="#pstats.Stats" title="pstats.Stats"><code class="xref py py-class docutils literal notranslate"><span class="pre">Stats</span></code></a> class prints a list of all functions
|
||
that called each function in the profiled database. The ordering is
|
||
identical to that provided by <a class="reference internal" href="#pstats.Stats.print_stats" title="pstats.Stats.print_stats"><code class="xref py py-meth docutils literal notranslate"><span class="pre">print_stats()</span></code></a>, and the
|
||
definition of the restricting argument is also identical. Each caller is
|
||
reported on its own line. The format differs slightly depending on the
|
||
profiler that produced the stats:</p>
|
||
<ul class="simple">
|
||
<li><p>With <a class="reference internal" href="#module-profile" title="profile: Python source profiler."><code class="xref py py-mod docutils literal notranslate"><span class="pre">profile</span></code></a>, a number is shown in parentheses after each caller
|
||
to show how many times this specific call was made. For convenience, a
|
||
second non-parenthesized number repeats the cumulative time spent in the
|
||
function at the right.</p></li>
|
||
<li><p>With <a class="reference internal" href="#module-cProfile" title="cProfile"><code class="xref py py-mod docutils literal notranslate"><span class="pre">cProfile</span></code></a>, each caller is preceded by three numbers: the
|
||
number of times this specific call was made, and the total and
|
||
cumulative times spent in the current function while it was invoked by
|
||
this specific caller.</p></li>
|
||
</ul>
|
||
</dd></dl>
|
||
|
||
<dl class="method">
|
||
<dt id="pstats.Stats.print_callees">
|
||
<code class="descname">print_callees</code><span class="sig-paren">(</span><em>*restrictions</em><span class="sig-paren">)</span><a class="headerlink" href="#pstats.Stats.print_callees" title="Permalink to this definition">¶</a></dt>
|
||
<dd><p>This method for the <a class="reference internal" href="#pstats.Stats" title="pstats.Stats"><code class="xref py py-class docutils literal notranslate"><span class="pre">Stats</span></code></a> class prints a list of all function
|
||
that were called by the indicated function. Aside from this reversal of
|
||
direction of calls (re: called vs was called by), the arguments and
|
||
ordering are identical to the <a class="reference internal" href="#pstats.Stats.print_callers" title="pstats.Stats.print_callers"><code class="xref py py-meth docutils literal notranslate"><span class="pre">print_callers()</span></code></a> method.</p>
|
||
</dd></dl>
|
||
|
||
</dd></dl>
|
||
|
||
</div>
|
||
<div class="section" id="what-is-deterministic-profiling">
|
||
<span id="deterministic-profiling"></span><h2>What Is Deterministic Profiling?<a class="headerlink" href="#what-is-deterministic-profiling" title="Permalink to this headline">¶</a></h2>
|
||
<p><em class="dfn">Deterministic profiling</em> is meant to reflect the fact that all <em>function
|
||
call</em>, <em>function return</em>, and <em>exception</em> events are monitored, and precise
|
||
timings are made for the intervals between these events (during which time the
|
||
user’s code is executing). In contrast, <em class="dfn">statistical profiling</em> (which is
|
||
not done by this module) randomly samples the effective instruction pointer, and
|
||
deduces where time is being spent. The latter technique traditionally involves
|
||
less overhead (as the code does not need to be instrumented), but provides only
|
||
relative indications of where time is being spent.</p>
|
||
<p>In Python, since there is an interpreter active during execution, the presence
|
||
of instrumented code is not required to do deterministic profiling. Python
|
||
automatically provides a <em class="dfn">hook</em> (optional callback) for each event. In
|
||
addition, the interpreted nature of Python tends to add so much overhead to
|
||
execution, that deterministic profiling tends to only add small processing
|
||
overhead in typical applications. The result is that deterministic profiling is
|
||
not that expensive, yet provides extensive run time statistics about the
|
||
execution of a Python program.</p>
|
||
<p>Call count statistics can be used to identify bugs in code (surprising counts),
|
||
and to identify possible inline-expansion points (high call counts). Internal
|
||
time statistics can be used to identify “hot loops” that should be carefully
|
||
optimized. Cumulative time statistics should be used to identify high level
|
||
errors in the selection of algorithms. Note that the unusual handling of
|
||
cumulative times in this profiler allows statistics for recursive
|
||
implementations of algorithms to be directly compared to iterative
|
||
implementations.</p>
|
||
</div>
|
||
<div class="section" id="limitations">
|
||
<span id="profile-limitations"></span><h2>Limitations<a class="headerlink" href="#limitations" title="Permalink to this headline">¶</a></h2>
|
||
<p>One limitation has to do with accuracy of timing information. There is a
|
||
fundamental problem with deterministic profilers involving accuracy. The most
|
||
obvious restriction is that the underlying “clock” is only ticking at a rate
|
||
(typically) of about .001 seconds. Hence no measurements will be more accurate
|
||
than the underlying clock. If enough measurements are taken, then the “error”
|
||
will tend to average out. Unfortunately, removing this first error induces a
|
||
second source of error.</p>
|
||
<p>The second problem is that it “takes a while” from when an event is dispatched
|
||
until the profiler’s call to get the time actually <em>gets</em> the state of the
|
||
clock. Similarly, there is a certain lag when exiting the profiler event
|
||
handler from the time that the clock’s value was obtained (and then squirreled
|
||
away), until the user’s code is once again executing. As a result, functions
|
||
that are called many times, or call many functions, will typically accumulate
|
||
this error. The error that accumulates in this fashion is typically less than
|
||
the accuracy of the clock (less than one clock tick), but it <em>can</em> accumulate
|
||
and become very significant.</p>
|
||
<p>The problem is more important with <a class="reference internal" href="#module-profile" title="profile: Python source profiler."><code class="xref py py-mod docutils literal notranslate"><span class="pre">profile</span></code></a> than with the lower-overhead
|
||
<a class="reference internal" href="#module-cProfile" title="cProfile"><code class="xref py py-mod docutils literal notranslate"><span class="pre">cProfile</span></code></a>. For this reason, <a class="reference internal" href="#module-profile" title="profile: Python source profiler."><code class="xref py py-mod docutils literal notranslate"><span class="pre">profile</span></code></a> provides a means of
|
||
calibrating itself for a given platform so that this error can be
|
||
probabilistically (on the average) removed. After the profiler is calibrated, it
|
||
will be more accurate (in a least square sense), but it will sometimes produce
|
||
negative numbers (when call counts are exceptionally low, and the gods of
|
||
probability work against you :-). ) Do <em>not</em> be alarmed by negative numbers in
|
||
the profile. They should <em>only</em> appear if you have calibrated your profiler,
|
||
and the results are actually better than without calibration.</p>
|
||
</div>
|
||
<div class="section" id="calibration">
|
||
<span id="profile-calibration"></span><h2>Calibration<a class="headerlink" href="#calibration" title="Permalink to this headline">¶</a></h2>
|
||
<p>The profiler of the <a class="reference internal" href="#module-profile" title="profile: Python source profiler."><code class="xref py py-mod docutils literal notranslate"><span class="pre">profile</span></code></a> module subtracts a constant from each event
|
||
handling time to compensate for the overhead of calling the time function, and
|
||
socking away the results. By default, the constant is 0. The following
|
||
procedure can be used to obtain a better constant for a given platform (see
|
||
<a class="reference internal" href="#profile-limitations"><span class="std std-ref">Limitations</span></a>).</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">profile</span>
|
||
<span class="n">pr</span> <span class="o">=</span> <span class="n">profile</span><span class="o">.</span><span class="n">Profile</span><span class="p">()</span>
|
||
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">):</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="n">pr</span><span class="o">.</span><span class="n">calibrate</span><span class="p">(</span><span class="mi">10000</span><span class="p">))</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The method executes the number of Python calls given by the argument, directly
|
||
and again under the profiler, measuring the time for both. It then computes the
|
||
hidden overhead per profiler event, and returns that as a float. For example,
|
||
on a 1.8Ghz Intel Core i5 running Mac OS X, and using Python’s time.process_time() as
|
||
the timer, the magical number is about 4.04e-6.</p>
|
||
<p>The object of this exercise is to get a fairly consistent result. If your
|
||
computer is <em>very</em> fast, or your timer function has poor resolution, you might
|
||
have to pass 100000, or even 1000000, to get consistent results.</p>
|
||
<p>When you have a consistent answer, there are three ways you can use it:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">profile</span>
|
||
|
||
<span class="c1"># 1. Apply computed bias to all Profile instances created hereafter.</span>
|
||
<span class="n">profile</span><span class="o">.</span><span class="n">Profile</span><span class="o">.</span><span class="n">bias</span> <span class="o">=</span> <span class="n">your_computed_bias</span>
|
||
|
||
<span class="c1"># 2. Apply computed bias to a specific Profile instance.</span>
|
||
<span class="n">pr</span> <span class="o">=</span> <span class="n">profile</span><span class="o">.</span><span class="n">Profile</span><span class="p">()</span>
|
||
<span class="n">pr</span><span class="o">.</span><span class="n">bias</span> <span class="o">=</span> <span class="n">your_computed_bias</span>
|
||
|
||
<span class="c1"># 3. Specify computed bias in instance constructor.</span>
|
||
<span class="n">pr</span> <span class="o">=</span> <span class="n">profile</span><span class="o">.</span><span class="n">Profile</span><span class="p">(</span><span class="n">bias</span><span class="o">=</span><span class="n">your_computed_bias</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>If you have a choice, you are better off choosing a smaller constant, and then
|
||
your results will “less often” show up as negative in profile statistics.</p>
|
||
</div>
|
||
<div class="section" id="using-a-custom-timer">
|
||
<span id="profile-timers"></span><h2>Using a custom timer<a class="headerlink" href="#using-a-custom-timer" title="Permalink to this headline">¶</a></h2>
|
||
<p>If you want to change how current time is determined (for example, to force use
|
||
of wall-clock time or elapsed process time), pass the timing function you want
|
||
to the <code class="xref py py-class docutils literal notranslate"><span class="pre">Profile</span></code> class constructor:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">pr</span> <span class="o">=</span> <span class="n">profile</span><span class="o">.</span><span class="n">Profile</span><span class="p">(</span><span class="n">your_time_func</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The resulting profiler will then call <code class="docutils literal notranslate"><span class="pre">your_time_func</span></code>. Depending on whether
|
||
you are using <a class="reference internal" href="#profile.Profile" title="profile.Profile"><code class="xref py py-class docutils literal notranslate"><span class="pre">profile.Profile</span></code></a> or <code class="xref py py-class docutils literal notranslate"><span class="pre">cProfile.Profile</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">your_time_func</span></code>’s return value will be interpreted differently:</p>
|
||
<dl>
|
||
<dt><a class="reference internal" href="#profile.Profile" title="profile.Profile"><code class="xref py py-class docutils literal notranslate"><span class="pre">profile.Profile</span></code></a></dt><dd><p><code class="docutils literal notranslate"><span class="pre">your_time_func</span></code> should return a single number, or a list of numbers whose
|
||
sum is the current time (like what <a class="reference internal" href="os.html#os.times" title="os.times"><code class="xref py py-func docutils literal notranslate"><span class="pre">os.times()</span></code></a> returns). If the
|
||
function returns a single time number, or the list of returned numbers has
|
||
length 2, then you will get an especially fast version of the dispatch
|
||
routine.</p>
|
||
<p>Be warned that you should calibrate the profiler class for the timer function
|
||
that you choose (see <a class="reference internal" href="#profile-calibration"><span class="std std-ref">Calibration</span></a>). For most machines, a timer
|
||
that returns a lone integer value will provide the best results in terms of
|
||
low overhead during profiling. (<a class="reference internal" href="os.html#os.times" title="os.times"><code class="xref py py-func docutils literal notranslate"><span class="pre">os.times()</span></code></a> is <em>pretty</em> bad, as it
|
||
returns a tuple of floating point values). If you want to substitute a
|
||
better timer in the cleanest fashion, derive a class and hardwire a
|
||
replacement dispatch method that best handles your timer call, along with the
|
||
appropriate calibration constant.</p>
|
||
</dd>
|
||
<dt><code class="xref py py-class docutils literal notranslate"><span class="pre">cProfile.Profile</span></code></dt><dd><p><code class="docutils literal notranslate"><span class="pre">your_time_func</span></code> should return a single number. If it returns integers,
|
||
you can also invoke the class constructor with a second argument specifying
|
||
the real duration of one unit of time. For example, if
|
||
<code class="docutils literal notranslate"><span class="pre">your_integer_time_func</span></code> returns times measured in thousands of seconds,
|
||
you would construct the <code class="xref py py-class docutils literal notranslate"><span class="pre">Profile</span></code> instance as follows:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">pr</span> <span class="o">=</span> <span class="n">cProfile</span><span class="o">.</span><span class="n">Profile</span><span class="p">(</span><span class="n">your_integer_time_func</span><span class="p">,</span> <span class="mf">0.001</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>As the <code class="xref py py-class docutils literal notranslate"><span class="pre">cProfile.Profile</span></code> class cannot be calibrated, custom timer
|
||
functions should be used with care and should be as fast as possible. For
|
||
the best results with a custom timer, it might be necessary to hard-code it
|
||
in the C source of the internal <code class="xref py py-mod docutils literal notranslate"><span class="pre">_lsprof</span></code> module.</p>
|
||
</dd>
|
||
</dl>
|
||
<p>Python 3.3 adds several new functions in <a class="reference internal" href="time.html#module-time" title="time: Time access and conversions."><code class="xref py py-mod docutils literal notranslate"><span class="pre">time</span></code></a> that can be used to make
|
||
precise measurements of process or wall-clock time. For example, see
|
||
<a class="reference internal" href="time.html#time.perf_counter" title="time.perf_counter"><code class="xref py py-func docutils literal notranslate"><span class="pre">time.perf_counter()</span></code></a>.</p>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||
<div class="sphinxsidebarwrapper">
|
||
<h3><a href="../contents.html">Table of Contents</a></h3>
|
||
<ul>
|
||
<li><a class="reference internal" href="#">The Python Profilers</a><ul>
|
||
<li><a class="reference internal" href="#introduction-to-the-profilers">Introduction to the profilers</a></li>
|
||
<li><a class="reference internal" href="#instant-user-s-manual">Instant User’s Manual</a></li>
|
||
<li><a class="reference internal" href="#module-cProfile"><code class="xref py py-mod docutils literal notranslate"><span class="pre">profile</span></code> and <code class="xref py py-mod docutils literal notranslate"><span class="pre">cProfile</span></code> Module Reference</a></li>
|
||
<li><a class="reference internal" href="#the-stats-class">The <code class="xref py py-class docutils literal notranslate"><span class="pre">Stats</span></code> Class</a></li>
|
||
<li><a class="reference internal" href="#what-is-deterministic-profiling">What Is Deterministic Profiling?</a></li>
|
||
<li><a class="reference internal" href="#limitations">Limitations</a></li>
|
||
<li><a class="reference internal" href="#calibration">Calibration</a></li>
|
||
<li><a class="reference internal" href="#using-a-custom-timer">Using a custom timer</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
|
||
<h4>Previous topic</h4>
|
||
<p class="topless"><a href="pdb.html"
|
||
title="previous chapter"><code class="xref py py-mod docutils literal notranslate"><span class="pre">pdb</span></code> — The Python Debugger</a></p>
|
||
<h4>Next topic</h4>
|
||
<p class="topless"><a href="timeit.html"
|
||
title="next chapter"><code class="xref py py-mod docutils literal notranslate"><span class="pre">timeit</span></code> — Measure execution time of small code snippets</a></p>
|
||
<div role="note" aria-label="source link">
|
||
<h3>This Page</h3>
|
||
<ul class="this-page-menu">
|
||
<li><a href="../bugs.html">Report a Bug</a></li>
|
||
<li>
|
||
<a href="https://github.com/python/cpython/blob/3.7/Doc/library/profile.rst"
|
||
rel="nofollow">Show Source
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="clearer"></div>
|
||
</div>
|
||
<div class="related" role="navigation" aria-label="related navigation">
|
||
<h3>Navigation</h3>
|
||
<ul>
|
||
<li class="right" style="margin-right: 10px">
|
||
<a href="../genindex.html" title="General Index"
|
||
>index</a></li>
|
||
<li class="right" >
|
||
<a href="../py-modindex.html" title="Python Module Index"
|
||
>modules</a> |</li>
|
||
<li class="right" >
|
||
<a href="timeit.html" title="timeit — Measure execution time of small code snippets"
|
||
>next</a> |</li>
|
||
<li class="right" >
|
||
<a href="pdb.html" title="pdb — The Python Debugger"
|
||
>previous</a> |</li>
|
||
<li><img src="../_static/py.png" alt=""
|
||
style="vertical-align: middle; margin-top: -1px"/></li>
|
||
<li><a href="https://www.python.org/">Python</a> »</li>
|
||
<li>
|
||
<span class="language_switcher_placeholder">en</span>
|
||
<span class="version_switcher_placeholder">3.7.4</span>
|
||
<a href="../index.html">Documentation </a> »
|
||
</li>
|
||
|
||
<li class="nav-item nav-item-1"><a href="index.html" >The Python Standard Library</a> »</li>
|
||
<li class="nav-item nav-item-2"><a href="debug.html" >Debugging and Profiling</a> »</li>
|
||
<li class="right">
|
||
|
||
|
||
<div class="inline-search" style="display: none" role="search">
|
||
<form class="inline-search" action="../search.html" method="get">
|
||
<input placeholder="Quick search" type="text" name="q" />
|
||
<input type="submit" value="Go" />
|
||
<input type="hidden" name="check_keywords" value="yes" />
|
||
<input type="hidden" name="area" value="default" />
|
||
</form>
|
||
</div>
|
||
<script type="text/javascript">$('.inline-search').show(0);</script>
|
||
|
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
<div class="footer">
|
||
© <a href="../copyright.html">Copyright</a> 2001-2019, Python Software Foundation.
|
||
<br />
|
||
The Python Software Foundation is a non-profit corporation.
|
||
<a href="https://www.python.org/psf/donations/">Please donate.</a>
|
||
<br />
|
||
Last updated on Jul 13, 2019.
|
||
<a href="../bugs.html">Found a bug</a>?
|
||
<br />
|
||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 2.0.1.
|
||
</div>
|
||
|
||
</body>
|
||
</html> |