evennia/docs/1.0-dev/Coding/Profiling.html
2020-11-14 11:55:52 +01:00

229 lines
No EOL
15 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Profiling &#8212; Evennia 1.0-dev documentation</title>
<link rel="stylesheet" href="../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
<script src="../_static/jquery.js"></script>
<script src="../_static/underscore.js"></script>
<script src="../_static/doctools.js"></script>
<script src="../_static/language_data.js"></script>
<link rel="shortcut icon" href="../_static/favicon.ico"/>
<link rel="index" title="Index" href="../genindex.html" />
<link rel="search" title="Search" href="../search.html" />
</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="nav-item nav-item-0"><a href="../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Profiling</a></li>
</ul>
<div class="develop">develop branch</div>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<div class="section" id="profiling">
<h1>Profiling<a class="headerlink" href="#profiling" title="Permalink to this headline"></a></h1>
<p><em>This is considered an advanced topic mainly of interest to server developers.</em></p>
<div class="section" id="introduction">
<h2>Introduction<a class="headerlink" href="#introduction" title="Permalink to this headline"></a></h2>
<p>Sometimes it can be useful to try to determine just how efficient a particular piece of code is, or
to figure out if one could speed up things more than they are. There are many ways to test the
performance of Python and the running server.</p>
<p>Before digging into this section, remember Donald Knuths <a class="reference external" href="https://en.wikipedia.org/wiki/Program_optimization#When_to_optimize">words of
wisdom</a>:</p>
<blockquote>
<div><p><em>[…]about 97% of the time: Premature optimization is the root of all evil</em>.</p>
</div></blockquote>
<p>That is, dont start to try to optimize your code until you have actually identified a need to do
so. This means your code must actually be working before you start to consider optimization.
Optimization will also often make your code more complex and harder to read. Consider readability
and maintainability and you may find that a small gain in speed is just not worth it.</p>
</div>
<div class="section" id="simple-timer-tests">
<h2>Simple timer tests<a class="headerlink" href="#simple-timer-tests" title="Permalink to this headline"></a></h2>
<p>Pythons <code class="docutils literal notranslate"><span class="pre">timeit</span></code> module is very good for testing small things. For example, in order to test if it
is faster to use a <code class="docutils literal notranslate"><span class="pre">for</span></code> loop or a list comprehension you could use the following code:</p>
<div class="highlight-python notranslate"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4
5
6
7</pre></div></td><td class="code"><div class="highlight"><pre><span></span> <span class="kn">import</span> <span class="nn">timeit</span>
<span class="c1"># Time to do 1000000 for loops</span>
<span class="n">timeit</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="s2">&quot;for i in range(100):</span><span class="se">\n</span><span class="s2"> a.append(i)&quot;</span><span class="p">,</span> <span class="n">setup</span><span class="o">=</span><span class="s2">&quot;a = []&quot;</span><span class="p">)</span>
<span class="o">&lt;&lt;&lt;</span> <span class="mf">10.70982813835144</span>
<span class="c1"># Time to do 1000000 list comprehensions</span>
<span class="n">timeit</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="s2">&quot;a = [i for i in range(100)]&quot;</span><span class="p">)</span>
<span class="o">&lt;&lt;&lt;</span> <span class="mf">5.358283996582031</span>
</pre></div>
</td></tr></table></div>
<p>The <code class="docutils literal notranslate"><span class="pre">setup</span></code> keyword is used to set up things that should not be included in the time measurement,
like <code class="docutils literal notranslate"><span class="pre">a</span> <span class="pre">=</span> <span class="pre">[]</span></code> in the first call.</p>
<p>By default the <code class="docutils literal notranslate"><span class="pre">timeit</span></code> function will re-run the given test 1000000 times and returns the <em>total
time</em> to do so (so <em>not</em> the average per test). A hint is to not use this default for testing
something that includes database writes - for that you may want to use a lower number of repeats
(say 100 or 1000) using the <code class="docutils literal notranslate"><span class="pre">number=100</span></code> keyword.</p>
</div>
<div class="section" id="using-cprofile">
<h2>Using cProfile<a class="headerlink" href="#using-cprofile" title="Permalink to this headline"></a></h2>
<p>Python comes with its own profiler, named cProfile (this is for cPython, no tests have been done
with <code class="docutils literal notranslate"><span class="pre">pypy</span></code> at this point). Due to the way Evennias processes are handled, there is no point in
using the normal way to start the profiler (<code class="docutils literal notranslate"><span class="pre">python</span> <span class="pre">-m</span> <span class="pre">cProfile</span> <span class="pre">evennia.py</span></code>). Instead you start the
profiler through the launcher:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">evennia</span> <span class="o">--</span><span class="n">profiler</span> <span class="n">start</span>
</pre></div>
</div>
<p>This will start Evennia with the Server component running (in daemon mode) under cProfile. You could
instead try <code class="docutils literal notranslate"><span class="pre">--profile</span></code> with the <code class="docutils literal notranslate"><span class="pre">portal</span></code> argument to profile the Portal (you would then need to
<a class="reference internal" href="../Setup/Start-Stop-Reload.html"><span class="doc">start the Server separately</span></a>).</p>
<p>Please note that while the profiler is running, your process will use a lot more memory than usual.
Memory usage is even likely to climb over time. So dont leave it running perpetually but monitor it
carefully (for example using the <code class="docutils literal notranslate"><span class="pre">top</span></code> command on Linux or the Task Managers memory display on
Windows).</p>
<p>Once you have run the server for a while, you need to stop it so the profiler can give its report.
Do <em>not</em> kill the program from your task manager or by sending it a kill signal - this will most
likely also mess with the profiler. Instead either use <code class="docutils literal notranslate"><span class="pre">evennia.py</span> <span class="pre">stop</span></code> or (which may be even
better), use <code class="docutils literal notranslate"><span class="pre">&#64;shutdown</span></code> from inside the game.</p>
<p>Once the server has fully shut down (this may be a lot slower than usual) you will find that
profiler has created a new file <code class="docutils literal notranslate"><span class="pre">mygame/server/logs/server.prof</span></code>.</p>
</div>
<div class="section" id="analyzing-the-profile">
<h2>Analyzing the profile<a class="headerlink" href="#analyzing-the-profile" title="Permalink to this headline"></a></h2>
<p>The <code class="docutils literal notranslate"><span class="pre">server.prof</span></code> file is a binary file. There are many ways to analyze and display its contents,
all of which has only been tested in Linux (If you are a Windows/Mac user, let us know what works).</p>
<p>We recommend the
<a class="reference external" href="http://www.vrplumber.com/programming/runsnakerun/">Runsnake</a> visualizer to see the processor usage
of different processes in a graphical form. For more detailed listing of usage time, you can use
<a class="reference external" href="http://kcachegrind.sourceforge.net/html/Home.html">KCachegrind</a>. To make KCachegrind work with
Python profiles you also need the wrapper script
<a class="reference external" href="https://pypi.python.org/pypi/pyprof2calltree/">pyprof2calltree</a>. You can get pyprof2calltree via
<code class="docutils literal notranslate"><span class="pre">pip</span></code> whereas KCacheGrind is something you need to get via your package manager or their homepage.</p>
<p>How to analyze and interpret profiling data is not a trivial issue and depends on what you are
profiling for. Evennia being an asynchronous server can also confuse profiling. Ask on the mailing
list if you need help and be ready to be able to supply your <code class="docutils literal notranslate"><span class="pre">server.prof</span></code> file for comparison,
along with the exact conditions under which it was obtained.</p>
</div>
<div class="section" id="the-dummyrunner">
<h2>The Dummyrunner<a class="headerlink" href="#the-dummyrunner" title="Permalink to this headline"></a></h2>
<p>It is difficult to test “actual” game performance without having players in your game. For this
reason Evennia comes with the <em>Dummyrunner</em> system. The Dummyrunner is a stress-testing system: a
separate program that logs into your game with simulated players (aka “bots” or “dummies”). Once
connected these dummies will semi-randomly perform various tasks from a list of possible actions.
Use <code class="docutils literal notranslate"><span class="pre">Ctrl-C</span></code> to stop the Dummyrunner.</p>
<blockquote>
<div><p>Warning: You should not run the Dummyrunner on a production database. It will spawn many objects
and also needs to run with general permissions.</p>
</div></blockquote>
<p>To launch the Dummyrunner, first start your server normally (with or without profiling, as above).
Then start a new terminal/console window and active your virtualenv there too. In the new terminal,
try to connect 10 dummy players:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">evennia</span> <span class="o">--</span><span class="n">dummyrunner</span> <span class="mi">10</span>
</pre></div>
</div>
<p>The first time you do this you will most likely get a warning from Dummyrunner. It will tell you to
copy an import string to the end of your settings file. Quit the Dummyrunner (<code class="docutils literal notranslate"><span class="pre">Ctrl-C</span></code>) and follow
the instructions. Restart Evennia and try <code class="docutils literal notranslate"><span class="pre">evennia</span> <span class="pre">--dummyrunner</span> <span class="pre">10</span></code> again. Make sure to remove that
extra settings line when running a public server.</p>
<p>The actions perform by the dummies is controlled by a settings file. The default Dummyrunner
settings file is <code class="docutils literal notranslate"><span class="pre">evennia/server/server/profiling/dummyrunner_settings.py</span></code> but you shouldnt modify
this directly. Rather create/copy the default file to <code class="docutils literal notranslate"><span class="pre">mygame/server/conf/</span></code> and modify it there. To
make sure to use your file over the default, add the following line to your settings file:</p>
<div class="highlight-python notranslate"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">DUMMYRUNNER_SETTINGS_MODULE</span> <span class="o">=</span> <span class="s2">&quot;server/conf/dummyrunner_settings.py&quot;</span>
</pre></div>
</td></tr></table></div>
<blockquote>
<div><p>Hint: Dont start with too many dummies. The Dummyrunner defaults to taxing the server much more
intensely than an equal number of human players. A good dummy number to start with is 10-100.</p>
</div></blockquote>
<p>Once you have the dummyrunner running, stop it with <code class="docutils literal notranslate"><span class="pre">Ctrl-C</span></code>.</p>
<p>Generally, the dummyrunner system makes for a decent test of general performance; but it is of
course hard to actually mimic human user behavior. For this, actual real-game testing is required.</p>
</div>
</div>
<div class="clearer"></div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<p class="logo"><a href="../index.html">
<img class="logo" src="../_static/evennia_logo.png" alt="Logo"/>
</a></p>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" />
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script>
<p><h3><a href="../index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">Profiling</a><ul>
<li><a class="reference internal" href="#introduction">Introduction</a></li>
<li><a class="reference internal" href="#simple-timer-tests">Simple timer tests</a></li>
<li><a class="reference internal" href="#using-cprofile">Using cProfile</a></li>
<li><a class="reference internal" href="#analyzing-the-profile">Analyzing the profile</a></li>
<li><a class="reference internal" href="#the-dummyrunner">The Dummyrunner</a></li>
</ul>
</li>
</ul>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../_sources/Coding/Profiling.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div>
<h3>Versions</h3>
<ul>
<li><a href="Profiling.html">1.0-dev (develop branch)</a></li>
<li><a href="../../0.9.5/index.html">0.9.5 (master branch)</a></li>
</ul>
</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="nav-item nav-item-0"><a href="../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Profiling</a></li>
</ul>
<div class="develop">develop branch</div>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2020, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>