Commit d7efda43 authored by Matus Valo's avatar Matus Valo Committed by GitHub

docs: Add pure Python examples to "Getting started" guide (GH-4283)

parent 13125ee7
@cython.cfunc
@cython.exceptval(-2, check=True)
def f(x: cython.double) -> cython.double:
return x ** 2 - x
cdef double f(double x) except? -2:
return x ** 2 - x
def f(x: cython.double):
return x ** 2 - x
def integrate_f(a: cython.double, b: cython.double, N: cython.int):
i: cython.int
s: cython.double
dx: cython.double
s = 0
dx = (b - a) / N
for i in range(N):
s += f(a + i * dx)
return s * dx
......@@ -4,7 +4,8 @@ def f(double x):
def integrate_f(double a, double b, int N):
cdef int i
cdef double s, dx
cdef double s
cdef double dx
s = 0
dx = (b - a) / N
for i in range(N):
......
......@@ -3,7 +3,7 @@ Building Cython code
Cython code must, unlike Python, be compiled. This happens in two stages:
- A ``.pyx`` file is compiled by Cython to a ``.c`` file, containing
- A ``.pyx`` or ``.py`` file is compiled by Cython to a ``.c`` file, containing
the code of a Python extension module.
- The ``.c`` file is compiled by a C compiler to
a ``.so`` file (or ``.pyd`` on Windows) which can be
......@@ -73,14 +73,32 @@ and load the ``Cython`` extension from within the Jupyter notebook::
%load_ext Cython
Then, prefix a cell with the ``%%cython`` marker to compile it::
Then, prefix a cell with the ``%%cython`` marker to compile it
%%cython
.. tabs::
cdef int a = 0
for i in range(10):
a += i
print(a)
.. group-tab:: Pure Python
.. code-block:: python
%%cython
a: cython.int = 0
for i in range(10):
a += i
print(a)
.. group-tab:: Cython
.. code-block:: python
%%cython
cdef int a = 0
for i in range(10):
a += i
print(a)
You can show Cython's code analysis by passing the ``--annotate`` option::
......
Faster code via static typing
=============================
.. include::
../two-syntax-variants-used
Cython is a Python compiler. This means that it can compile normal
Python code without changes (with a few obvious exceptions of some as-yet
unsupported language features, see :ref:`Cython limitations<cython-limitations>`).
......@@ -33,6 +36,7 @@ Typing Variables
Consider the following pure Python code:
.. literalinclude:: ../../examples/quickstart/cythonize/integrate.py
:caption: integrate.py
Simply compiling this in Cython merely gives a 35% speedup. This is
better than nothing, but adding some static types can make a much larger
......@@ -40,7 +44,17 @@ difference.
With additional type declarations, this might look like:
.. literalinclude:: ../../examples/quickstart/cythonize/integrate_cy.pyx
.. tabs::
.. group-tab:: Pure Python
.. literalinclude:: ../../examples/quickstart/cythonize/integrate_cy.py
:caption: integrate_cy.py
.. group-tab:: Cython
.. literalinclude:: ../../examples/quickstart/cythonize/integrate_cy.pyx
:caption: integrate_cy.pyx
Since the iterator variable ``i`` is typed with C semantics, the for-loop will be compiled
to pure C code. Typing ``a``, ``s`` and ``dx`` is important as they are involved
......@@ -55,27 +69,40 @@ Typing Functions
Python function calls can be expensive -- in Cython doubly so because
one might need to convert to and from Python objects to do the call.
In our example above, the argument is assumed to be a C double both inside f()
In our example above, the argument is assumed to be a C double both inside ``f()``
and in the call to it, yet a Python ``float`` object must be constructed around the
argument in order to pass it.
Therefore Cython provides a syntax for declaring a C-style function,
the cdef keyword:
Therefore, Cython provides a way for declaring a C-style function,
the Cython specific ``cdef`` statement, as well as the ``@cfunc`` decorator to
declare C-style functions in Python syntax. Both approaches are
equivalent and produce the same C code:
.. tabs::
.. group-tab:: Pure Python
.. literalinclude:: ../../examples/quickstart/cythonize/cdef_keyword.py
.. literalinclude:: ../../examples/quickstart/cythonize/cdef_keyword.pyx
.. group-tab:: Cython
.. literalinclude:: ../../examples/quickstart/cythonize/cdef_keyword.pyx
Some form of except-modifier should usually be added, otherwise Cython
will not be able to propagate exceptions raised in the function (or a
function it calls). The ``except? -2`` means that an error will be checked
for if ``-2`` is returned (though the ``?`` indicates that ``-2`` may also
be used as a valid return value).
be used as a valid return value). The same can be expressed using only Python
syntax with the decorator ``@exceptval(-2, check=True)``.
Alternatively, the slower ``except *`` is always
safe. An except clause can be left out if the function returns a Python
object or if it is guaranteed that an exception will not be raised
within the function call.
within the function call. Again, Cython provides the decorator ``@exceptval(check=True)``
providing the same functionality.
A side-effect of cdef is that the function is no longer available from
Python-space, as Python wouldn't know how to call it. It is also no
A side-effect of ``cdef`` (and the ``@cfunc`` decorator) is that the function is no longer
visible from Python-space, as Python wouldn't know how to call it. It is also no
longer possible to change :func:`f` at runtime.
Using the ``cpdef`` keyword instead of ``cdef``, a Python wrapper is also
......@@ -84,7 +111,8 @@ typed values directly) and from Python (wrapping values in Python
objects). In fact, ``cpdef`` does not just provide a Python wrapper, it also
installs logic to allow the method to be overridden by python methods, even
when called from within cython. This does add a tiny overhead compared to ``cdef``
methods.
methods. Again, Cython provides a ``@ccall`` decorator which provides the same
functionality as ``cpdef`` keyword.
Speedup: 150 times over pure Python.
......@@ -118,7 +146,15 @@ This report is invaluable when optimizing a function for speed,
and for determining when to :ref:`release the GIL <nogil>`:
in general, a ``nogil`` block may contain only "white" code.
.. figure:: htmlreport.png
.. tabs::
.. group-tab:: Pure Python
.. figure:: htmlreport_py.png
.. group-tab:: Cython
.. figure:: htmlreport_pyx.png
Note that Cython deduces the type of local variables based on their assignments
(including as loop variable targets) which can also cut down on the need to
......@@ -135,4 +171,3 @@ with this language feature. It can be of great help to cut down on the need to t
everything, but it also can lead to surprises. Especially if one isn't familiar with
arithmetic expressions with c types. A quick overview of those
can be found `here <https://www.eskimo.com/~scs/cclass/int/sx4cb.html>`_.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment