<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Performance Archive - Leichte Dienstplanung - MiaPlan</title>
	<atom:link href="https://www.miaplan.de/tag/performance/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.miaplan.de/tag/performance/</link>
	<description></description>
	<lastBuildDate>Thu, 02 Feb 2023 11:15:00 +0000</lastBuildDate>
	<language>de</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9</generator>

<image>
	<url>https://www.miaplan.de/wp-content/uploads/2021/08/miaplan_logo-150x150.png</url>
	<title>Performance Archive - Leichte Dienstplanung - MiaPlan</title>
	<link>https://www.miaplan.de/tag/performance/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Aus Python-Code C-Code generieren mit einem Dekorator</title>
		<link>https://www.miaplan.de/aus-python-code-c-code-generieren-mit-einem-dekorator/</link>
		
		<dc:creator><![CDATA[miaplan]]></dc:creator>
		<pubDate>Thu, 02 Feb 2023 10:38:46 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[C-Code]]></category>
		<category><![CDATA[Numba]]></category>
		<category><![CDATA[Performance]]></category>
		<guid isPermaLink="false">https://www.miaplan.de/?p=1591</guid>

					<description><![CDATA[<p>C-Code aus Python-Code zu generieren ist eine schwierige Aufgabe, da die beiden Sprachen unterschiedliche Syntax und Funktionen haben. Es ist jedoch möglich, die Kluft zwischen den beiden Sprachen mit Hilfe eines einfachen Dekorators zu überbrücken. Numba ist ein Paket für Python das in der Lage ist, Python-Code direkt in C-Code zu übersetzen. Dabei steht Numba&#8230;&#160;<a href="https://www.miaplan.de/aus-python-code-c-code-generieren-mit-einem-dekorator/" class="" rel="bookmark">Weiterlesen &#187;<span class="screen-reader-text">Aus Python-Code C-Code generieren mit einem Dekorator</span></a></p>
<p>Der Beitrag <a href="https://www.miaplan.de/aus-python-code-c-code-generieren-mit-einem-dekorator/">Aus Python-Code C-Code generieren mit einem Dekorator</a> erschien zuerst auf <a href="https://www.miaplan.de">Leichte Dienstplanung - MiaPlan</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>C-Code aus Python-Code zu generieren ist eine schwierige Aufgabe, da die beiden Sprachen unterschiedliche Syntax und Funktionen haben. Es ist jedoch möglich, die Kluft zwischen den beiden Sprachen mit Hilfe eines einfachen <a href="https://peps.python.org/pep-0318/">Dekorators</a> zu überbrücken.</p>
<p><span id="more-1591"></span></p>
<p><a href="https://numba.readthedocs.io/en/stable/user/5minguide.html">Numba</a> ist ein Paket für Python das in der Lage ist, Python-Code direkt in C-Code zu übersetzen. Dabei steht Numba unter der BSD-Lizenz und darf für kommerzielle Zwecke genutzt werden. Die Installation erfolgt mit Pip.</p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-bash" data-lang="Bash"><code>pip install numba</code></pre>
</div>
<p>In der Praxis bedeutet das, dass ganz normaler Python-Code entwickelt werden kann, und durch das ergänzen einer einzigen Zeile wird eine Funktion unter den richtigen Voraussetzungen um eine Vielfaches schneller. Spoiler: Im Praxisbeispiel weiter unten erreichen wir eine Beschleunigung um <strong>300%</strong>.</p>
<p>Numba kann seine Stärken am besten ausspielen, wenn Schleifen oder NumPy-Funktionen im Spiel sind. Wurde im bestehenden Python-Projekt eine langsame Funktion identifiziert, z. B. mit Hilfe des <a href="https://github.com/pyutils/line_profiler">line profilers</a>, und diese ist Schleifen- oder NumPy-lastig, kommt sie zur Optimierung mit Numba in Frage.</p>
<p>Schauen wir uns die folgende Funktion zur Berechnung von Matrix-Intervallen an.</p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-python" data-lang="Python"><code>def get_matrix_intervals(matrix, flag_or_value, flag=True):
    ''' Start and end indices for matrix preset flags '''
    presetBoolMat = ((matrix &amp; flag_or_value).astype(bool)) if flag else matrix == flag_or_value
    fPad = np.zeros((presetBoolMat.shape[0], 1))
    presetBoolMatDiff = np.diff(np.hstack((fPad, presetBoolMat, fPad)).astype(int))
    starts = np.where(presetBoolMatDiff == 1)
    ends = np.where(presetBoolMatDiff == -1)
    return starts, ends
</code></pre>
</div>
<p><em>get_matrix_intervals</em> berechnet für eine Matrix mit aufeinanderfolgenden, identischen Werten die Start- und End-Indizes. So werden z. B. für die Sequenz <strong>[0,1,1,1,1,0,1,1]</strong> die Intervalle <strong>(1 bis 4)</strong> und <strong>(6 bis 7)</strong> berechnet.</p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-python" data-lang="Python"><code>&gt;&gt;&gt; get_matrix_intervals(np.array([[0,1,1,1,1,0,1,1]]), 1, flag=False)
((array([0, 0]), array([1, 6])), (array([0, 0]), array([5, 8])))</code></pre>
</div>
<p>Wie schnell ist diese Funktion, wenn sie allein durch den Python-Interpreter ausgeführt wird?</p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-python" data-lang="Python"><code>&gt;&gt;&gt; timeit.timeit('get_matrix_intervals(np.array([[0,1,1,1,1,0,1,1]]), 1, flag=False)', 'from __main__ import ' + ', '.join(globals()))
11.411816049000663</code></pre>
</div>
<p>Sie braucht gut 11 Sekunden für eine Millionen Iterationen. Nun wenden wir den Dekorator von Numba an und verwenden Datentypen, die Numba versteht.</p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-python" data-lang="Python"><code>from numba import jit
from numba.types import bool_, int_

@jit(nopython=True)
def get_matrix_intervals(matrix, flag_or_value, flag=True):
    ''' Start and end indices for matrix preset flags '''
    presetBoolMat = ((matrix &amp; flag_or_value).astype(bool_)) if flag else matrix == flag_or_value
    fPad = np.zeros((presetBoolMat.shape[0], 1))
    presetBoolMatDiff = np.diff(np.hstack((fPad, presetBoolMat, fPad)).astype(int_))
    starts = np.where(presetBoolMatDiff == 1)
    ends = np.where(presetBoolMatDiff == -1)
    return starts, ends

timeit.timeit('get_matrix_intervals(np.array([[0,1,1,1,1,0,1,1]]), 1, flag=False)', 'from __main__ import ' + ', '.join(globals()))
5.79446492399984</code></pre>
</div>
<p>Die Funktion ist etwa doppelt so schnell wie vorher. Nicht schlecht dafür, dass wir fast nichts getan haben. Aber das ist nicht das Ende der Fahnenstange. Wendet man zusätzlich die <a href="https://numba.readthedocs.io/en/stable/user/performance-tips.html#performance-tips">Performance-Tips</a> von Numba an, stellt man fest, dass die Funktion ungünstig implementiert wurde. Zum einen verschwendet sie durch zu großzügige Datentypen Arbeitsspeicher und zum anderen ist sie durch den <em>flag</em>-Parameter unnötig komplex, wodurch der generierte C-Code ineffizient wird. Wendet man das Wissen an, was man über die Eingabe-Parameter hat, kann man die Funktion wie folgt verbessern.</p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-python" data-lang="Python"><code>from numba import jit
from numba.types import bool_, int8, int64, uint16, UniTuple

@jit(UniTuple(UniTuple(int64[:], 2), 2)(uint16[:,:], uint16), nopython=True)
def get_matrix_intervals(matrix, flag):
    ''' Start and end indices for matrix preset flags '''
    presetBoolMat = (matrix &amp; flag).astype(bool_)
    fPad = np.zeros((presetBoolMat.shape[0], 1), dtype=bool_)
    presetBoolMatDiff = np.diff(np.hstack((fPad, presetBoolMat, fPad)).astype(int8))
    starts = np.where(presetBoolMatDiff == 1)
    ends = np.where(presetBoolMatDiff == -1)
    return starts, ends

timeit.timeit('get_matrix_intervals(np.array([[0,1,1,1,1,0,1,1]], dtype="u2"), 1)', 'from __main__ import ' + ', '.join(globals()))
3.575752114000352</code></pre>
</div>
<p><em>get_matrix_intervals</em> ist nun mehr als 3 Mal so schnell wie vorher. Wir haben dafür den <em>flag</em>-Parameter entfernt und geben Numba Informationen über die Datentypen der Ein-und Ausgaben. Dadurch muss der durch Numba genutzte <a href="https://de.wikipedia.org/wiki/Just-in-time-Kompilierung">JIT</a> diese Typen nicht selbst zur Laufzeit bestimmen. Zusätzlich kann man mit dem Parameter <em>cache=True</em> dafür sorgen, dass die einmal kompilierte Funktion im Dateisystem-Ordner <em>__pycache__</em> hinterlegt wird und dadurch nicht bei jeder Ausführung neu erzeugt werden muss.</p><p>Der Beitrag <a href="https://www.miaplan.de/aus-python-code-c-code-generieren-mit-einem-dekorator/">Aus Python-Code C-Code generieren mit einem Dekorator</a> erschien zuerst auf <a href="https://www.miaplan.de">Leichte Dienstplanung - MiaPlan</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
