{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# High Performance/Throughput Computing" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def f(x):\n", " y = 0.0\n", " for i in range(100000):\n", " y += x\n", " return y" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "999.9999999992356" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f(0.01)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "100 loops, best of 3: 5.24 ms per loop\n" ] } ], "source": [ "%timeit f(0.01)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Cython\n", "\n", "[Cython](http://cython.org/) ist ein Compiler,\n", "der Cython Code in C/C++ Code übersetzt und mit der Python C-Bibliothek verbindet.\n", "Dies eignet sich hervorragend dazu,\n", "um langsame Teile eines größeren Python Programmes selektiv zu beschleunigen.\n", "Dies wird dadurch erreicht, dass einzelne Funktionen oder Klassen in die Sprache \"*Cython*\" umgeschrieben werden.\n", "Dies ist nicht besonders schwierig, da Cython eine Obermenge einer Untermenge der Python Sprache ist:\n", "das heißt, einige komplexere Programmstrukturen gibt es nicht, dafür aber Erweiterungen wie z.B. native Typenbezeichnungen.\n", "\n", "Die einzige wirkliche Herausforderung ist,\n", "genau zu verstehen welche Datentypen im Spiel sind und\n", "wie aufwändig die eventuelle Konvertierung der einzelnen Objekte zwischen C/C++ und Python ist.\n", "In der [Dokumentation](http://docs.cython.org/) gibt es dazu viele Beispiele mit anschaulichen Erklärungen.\n", "\n", "Im folgenden wird nun Cython durch die `cythonmagic` Extension geladen,\n", "die eingangs vorgestellte Python Funktion nach Cython umgeschrieben,\n", "und in der Zelle mittels des `%%cython` Magic-Commandos ausgeführt.\n", "\n", "Von der im Hintergrund ablaufenden Compilierung nach C,\n", "das Linken mit den Python Bibliotheken,\n", "und das anschließende dynamische laden in den aktuellen Python-Kernel bekommt man nichts mit --\n", "es dauert nur ein paar Sekunden, die sich dann später durch die beschleunigte Ausführungszeit wieder leicht einholen lassen ;-)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%load_ext Cython" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%%cython\n", "cimport cython\n", "@cython.boundscheck(False)\n", "cpdef f_cy(float x):\n", " cdef double y = 0.0\n", " cdef int i\n", " for 0 <= i <= 100000:\n", " y += x\n", " return y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Kontrolle, dass das Ergebnis stimmt und anschließendes Benchmark mit `%timeit`" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "1000.0099776480347" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f_cy(0.01)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1000 loops, best of 3: 214 µs per loop\n" ] } ], "source": [ "%timeit f_cy(0.01)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Die `--annotate` Option zeigt in gelb genauer an,\n", "was wo Konvertierungen oder Python-Objekte noch im Spiel sind (die das Programm langam machen):" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", " \n", " Cython: _cython_magic_22a304d0c206e4ff00eb6647f587e3bb.pyx\n", " \n", " \n", "\n", "\n", "

Generated by Cython 0.24

\n", "

\n", " Yellow lines hint at Python interaction.
\n", " Click on a line that starts with a \"+\" to see the C code that Cython generated for it.\n", "

\n", "
 1: cimport cython
\n", "
 2: @cython.boundscheck(False)
\n", "
+3: cpdef f_cy(float x):
\n", "
static PyObject *__pyx_pw_46_cython_magic_22a304d0c206e4ff00eb6647f587e3bb_1f_cy(PyObject *__pyx_self, PyObject *__pyx_arg_x); /*proto*/\n",
       "static PyObject *__pyx_f_46_cython_magic_22a304d0c206e4ff00eb6647f587e3bb_f_cy(float __pyx_v_x, CYTHON_UNUSED int __pyx_skip_dispatch) {\n",
       "  double __pyx_v_y;\n",
       "  CYTHON_UNUSED int __pyx_v_i;\n",
       "  PyObject *__pyx_r = NULL;\n",
       "  __Pyx_RefNannyDeclarations\n",
       "  __Pyx_RefNannySetupContext(\"f_cy\", 0);\n",
       "/* … */\n",
       "  /* function exit code */\n",
       "  __pyx_L1_error:;\n",
       "  __Pyx_XDECREF(__pyx_t_1);\n",
       "  __Pyx_AddTraceback(\"_cython_magic_22a304d0c206e4ff00eb6647f587e3bb.f_cy\", __pyx_clineno, __pyx_lineno, __pyx_filename);\n",
       "  __pyx_r = 0;\n",
       "  __pyx_L0:;\n",
       "  __Pyx_XGIVEREF(__pyx_r);\n",
       "  __Pyx_RefNannyFinishContext();\n",
       "  return __pyx_r;\n",
       "}\n",
       "\n",
       "/* Python wrapper */\n",
       "static PyObject *__pyx_pw_46_cython_magic_22a304d0c206e4ff00eb6647f587e3bb_1f_cy(PyObject *__pyx_self, PyObject *__pyx_arg_x); /*proto*/\n",
       "static PyObject *__pyx_pw_46_cython_magic_22a304d0c206e4ff00eb6647f587e3bb_1f_cy(PyObject *__pyx_self, PyObject *__pyx_arg_x) {\n",
       "  float __pyx_v_x;\n",
       "  PyObject *__pyx_r = 0;\n",
       "  __Pyx_RefNannyDeclarations\n",
       "  __Pyx_RefNannySetupContext(\"f_cy (wrapper)\", 0);\n",
       "  assert(__pyx_arg_x); {\n",
       "    __pyx_v_x = __pyx_PyFloat_AsFloat(__pyx_arg_x); if (unlikely((__pyx_v_x == (float)-1) && PyErr_Occurred())) __PYX_ERR(0, 3, __pyx_L3_error)\n",
       "  }\n",
       "  goto __pyx_L4_argument_unpacking_done;\n",
       "  __pyx_L3_error:;\n",
       "  __Pyx_AddTraceback(\"_cython_magic_22a304d0c206e4ff00eb6647f587e3bb.f_cy\", __pyx_clineno, __pyx_lineno, __pyx_filename);\n",
       "  __Pyx_RefNannyFinishContext();\n",
       "  return NULL;\n",
       "  __pyx_L4_argument_unpacking_done:;\n",
       "  __pyx_r = __pyx_pf_46_cython_magic_22a304d0c206e4ff00eb6647f587e3bb_f_cy(__pyx_self, ((float)__pyx_v_x));\n",
       "\n",
       "  /* function exit code */\n",
       "  __Pyx_RefNannyFinishContext();\n",
       "  return __pyx_r;\n",
       "}\n",
       "\n",
       "static PyObject *__pyx_pf_46_cython_magic_22a304d0c206e4ff00eb6647f587e3bb_f_cy(CYTHON_UNUSED PyObject *__pyx_self, float __pyx_v_x) {\n",
       "  PyObject *__pyx_r = NULL;\n",
       "  __Pyx_RefNannyDeclarations\n",
       "  __Pyx_RefNannySetupContext(\"f_cy\", 0);\n",
       "  __Pyx_XDECREF(__pyx_r);\n",
       "  __pyx_t_1 = __pyx_f_46_cython_magic_22a304d0c206e4ff00eb6647f587e3bb_f_cy(__pyx_v_x, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 3, __pyx_L1_error)\n",
       "  __Pyx_GOTREF(__pyx_t_1);\n",
       "  __pyx_r = __pyx_t_1;\n",
       "  __pyx_t_1 = 0;\n",
       "  goto __pyx_L0;\n",
       "\n",
       "  /* function exit code */\n",
       "  __pyx_L1_error:;\n",
       "  __Pyx_XDECREF(__pyx_t_1);\n",
       "  __Pyx_AddTraceback(\"_cython_magic_22a304d0c206e4ff00eb6647f587e3bb.f_cy\", __pyx_clineno, __pyx_lineno, __pyx_filename);\n",
       "  __pyx_r = NULL;\n",
       "  __pyx_L0:;\n",
       "  __Pyx_XGIVEREF(__pyx_r);\n",
       "  __Pyx_RefNannyFinishContext();\n",
       "  return __pyx_r;\n",
       "}\n",
       "
+4:     cdef double y = 0.0
\n", "
  __pyx_v_y = 0.0;\n",
       "
 5:     cdef int i
\n", "
+6:     for 0 <= i <= 100000:
\n", "
  for (__pyx_v_i = 0; __pyx_v_i <= 0x186A0; __pyx_v_i++) {\n",
       "
+7:         y += x
\n", "
    __pyx_v_y = (__pyx_v_y + __pyx_v_x);\n",
       "  }\n",
       "
+8:     return y
\n", "
  __Pyx_XDECREF(__pyx_r);\n",
       "  __pyx_t_1 = PyFloat_FromDouble(__pyx_v_y); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 8, __pyx_L1_error)\n",
       "  __Pyx_GOTREF(__pyx_t_1);\n",
       "  __pyx_r = __pyx_t_1;\n",
       "  __pyx_t_1 = 0;\n",
       "  goto __pyx_L0;\n",
       "
" ], "text/plain": [ "" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%%cython --annotate\n", "cimport cython\n", "@cython.boundscheck(False)\n", "cpdef f_cy(float x):\n", " cdef double y = 0.0\n", " cdef int i\n", " for 0 <= i <= 100000:\n", " y += x\n", " return y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Cython & OpenMP\n", "\n", "OpenMP ist ein älteres Programmierhilfsmittel, um Schleifen und parallelisierbare Datenstrukturen im Code einfach ausdrücken zu können.\n", "Die Arbeit wird hierfür auf alle CPU-Kerne verteilt.\n", "In dem hier vorgestellten Beispiel ist es die Funktion `prange`, welche diese in Kombination mit der Addition der Ypsilons erledigt.\n", "Implizit wird hier auf die [Nebenläufigkeit](http://de.wikipedia.org/wiki/Nebenl%C3%A4ufigkeit) der Berechnung und dem anschließenden zusammenführen der Ergebnisse aufgepasst!" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%%cython --compile-args=-fopenmp --link-args=-fopenmp\n", "\n", "cimport cython\n", "from cython.parallel import parallel, prange\n", "\n", "@cython.boundscheck(False)\n", "cpdef f_cy_omp(float x):\n", " cdef double y = 0.0\n", " cdef Py_ssize_t i\n", " with nogil:\n", " for i in prange(100000):\n", " y += x\n", " return y" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "999.9999776482582" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f_cy_omp(0.01)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10 loops, best of 3: 47.3 ms per loop\n" ] } ], "source": [ "%timeit f_cy_omp(0.01)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Anaconda (Python 3)", "language": "python", "name": "anaconda3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.1" } }, "nbformat": 4, "nbformat_minor": 0 }