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: cdef double f(double x) except? -2:
return x ** 2 - x 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): ...@@ -4,7 +4,8 @@ def f(double x):
def integrate_f(double a, double b, int N): def integrate_f(double a, double b, int N):
cdef int i cdef int i
cdef double s, dx cdef double s
cdef double dx
s = 0 s = 0
dx = (b - a) / N dx = (b - a) / N
for i in range(N): for i in range(N):
......
...@@ -3,7 +3,7 @@ Building Cython code ...@@ -3,7 +3,7 @@ Building Cython code
Cython code must, unlike Python, be compiled. This happens in two stages: 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 code of a Python extension module.
- The ``.c`` file is compiled by a C compiler to - The ``.c`` file is compiled by a C compiler to
a ``.so`` file (or ``.pyd`` on Windows) which can be a ``.so`` file (or ``.pyd`` on Windows) which can be
...@@ -73,14 +73,32 @@ and load the ``Cython`` extension from within the Jupyter notebook:: ...@@ -73,14 +73,32 @@ and load the ``Cython`` extension from within the Jupyter notebook::
%load_ext Cython %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 .. group-tab:: Pure Python
for i in range(10):
a += i .. code-block:: python
print(a)
%%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:: You can show Cython's code analysis by passing the ``--annotate`` option::
......
Faster code via static typing Faster code via static typing
============================= =============================
.. include::
../two-syntax-variants-used
Cython is a Python compiler. This means that it can compile normal 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 Python code without changes (with a few obvious exceptions of some as-yet
unsupported language features, see :ref:`Cython limitations<cython-limitations>`). unsupported language features, see :ref:`Cython limitations<cython-limitations>`).
...@@ -33,6 +36,7 @@ Typing Variables ...@@ -33,6 +36,7 @@ Typing Variables
Consider the following pure Python code: Consider the following pure Python code:
.. literalinclude:: ../../examples/quickstart/cythonize/integrate.py .. literalinclude:: ../../examples/quickstart/cythonize/integrate.py
:caption: integrate.py
Simply compiling this in Cython merely gives a 35% speedup. This is 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 better than nothing, but adding some static types can make a much larger
...@@ -40,7 +44,17 @@ difference. ...@@ -40,7 +44,17 @@ difference.
With additional type declarations, this might look like: 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 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 to pure C code. Typing ``a``, ``s`` and ``dx`` is important as they are involved
...@@ -55,27 +69,40 @@ Typing Functions ...@@ -55,27 +69,40 @@ Typing Functions
Python function calls can be expensive -- in Cython doubly so because 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. 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 and in the call to it, yet a Python ``float`` object must be constructed around the
argument in order to pass it. argument in order to pass it.
Therefore Cython provides a syntax for declaring a C-style function, Therefore, Cython provides a way for declaring a C-style function,
the cdef keyword: 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 Some form of except-modifier should usually be added, otherwise Cython
will not be able to propagate exceptions raised in the function (or a 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 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 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 Alternatively, the slower ``except *`` is always
safe. An except clause can be left out if the function returns a Python 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 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 A side-effect of ``cdef`` (and the ``@cfunc`` decorator) is that the function is no longer
Python-space, as Python wouldn't know how to call it. It is also no 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. longer possible to change :func:`f` at runtime.
Using the ``cpdef`` keyword instead of ``cdef``, a Python wrapper is also 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 ...@@ -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 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 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`` 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. Speedup: 150 times over pure Python.
...@@ -118,7 +146,15 @@ This report is invaluable when optimizing a function for speed, ...@@ -118,7 +146,15 @@ This report is invaluable when optimizing a function for speed,
and for determining when to :ref:`release the GIL <nogil>`: and for determining when to :ref:`release the GIL <nogil>`:
in general, a ``nogil`` block may contain only "white" code. 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 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 (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 ...@@ -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 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 arithmetic expressions with c types. A quick overview of those
can be found `here <https://www.eskimo.com/~scs/cclass/int/sx4cb.html>`_. 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