{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Ipython and the \"compile_at_import\" mode\n", "\n", "Here, we show how easy it is to work with modules using Pythran from IPython. We also show a little bit of how it works internally." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import time\n", "from pathlib import Path\n", "\n", "from transonic import set_compile_at_import\n", "\n", "# an internal object to get more information on what is going on under the hood\n", "from transonic.aheadoftime import modules" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We define a small function to display the state of a Transonic object representing a module:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "def get_ts_mod_display_state(name):\n", " ts = modules[name]\n", " print(\n", " \"(is_transpiled, is_compiling, is_compiled) =\",\n", " (ts.is_transpiled, ts.is_compiling, ts.is_compiled),\n", " )\n", " return ts" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We first write in a small Python module which defines a function using Transonic. We are going to run this module and to modify it during the Ipython session." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "src = \"\"\"\n", "from transonic import boost\n", "\n", "@boost\n", "def func(n: int):\n", " return 2 * n\n", "\n", "if __name__ == \"__main__\":\n", " print(\"result func(1):\", func(1))\n", "\"\"\"\n", "\n", "with open(\"tmp.py\", \"w\") as file:\n", " file.write(src)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's first clean all Transonic cache files..." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "paths = Path(\"__pythran__\").glob(\"tmp*.*\")\n", "for path in paths:\n", " path.unlink()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We first use Transonic in the default mode for which Pythran is not used at run time." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "set_compile_at_import(False)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
WARNING  Module /home/pierre/Dev/transonic/doc/ipynb/executed/tmp.py has not been compiled   \n",
       "         for Transonic-Pythran                                                               \n",
       "
\n" ], "text/plain": [ "\u001b[31mWARNING \u001b[0m Module \u001b[35m/home/pierre/Dev/transonic/doc/ipynb/executed/\u001b[0m\u001b[95mtmp.py\u001b[0m has not been compiled \n", " for Transonic-Pythran \n" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "result func(1): 2\n" ] } ], "source": [ "run tmp.py" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "result func(1): 2\n" ] } ], "source": [ "run tmp.py" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's now switch on the Transonic mode with Pythran compilations at import time." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "set_compile_at_import(True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We then rerun the tmp.py module:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Running transonic on file /home/pierre/Dev/transonic/doc/ipynb/executed/tmp.py... WARNING 1 files created or updated needs to be pythranized \n", "Done!\n", "Launching Pythran to compile a new extension...\n", "result func(1): 2\n" ] } ], "source": [ "%run tmp.py" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Pythranizing /home/pierre/Dev/transonic/doc/ipynb/executed/__pythran__/tmp.py\n", "tmp.py tmp.pythran tmp_889c4908cf3164087423cd3cf32fc2f5.lock __pycache__/\n" ] } ], "source": [ "ls --color=never __pythran__" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that we used `ls --color=never` just because colors in the terminal are not well rendered in html produced by Jupyter nbconvert... " ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(is_transpiled, is_compiling, is_compiled) = (True, True, False)\n" ] } ], "source": [ "ts_mod = get_ts_mod_display_state(\"tmp\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see that Transonic created a Pythran file at import and launch the compilation. We can still rerun the module and it works as without Transonic:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "result func(1): 2\n" ] } ], "source": [ "%run tmp.py" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "While it's compiling, let's wait and call the function." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2,2,2,File /home/pierre/Dev/transonic/doc/ipynb/executed/__pythran__/tmp_889c4908cf3164087423cd3cf32fc2f5.cpython-39-x86_64-linux-gnu.so created by pythran\n", "2," ] } ], "source": [ "while ts_mod.is_compiling:\n", " print(func(1), end=\",\")\n", " time.sleep(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ok so here, we see that Pythran compilations are quite long! In particular compared to Numba, PyPy or Julia JIT compilations! But after these few seconds, it should very efficient..." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(is_transpiled, is_compiling, is_compiled) = (True, False, True)\n" ] } ], "source": [ "ts_mod = get_ts_mod_display_state(\"tmp\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now the compilation is done. Let's check that there is a new extension file:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tmp.py\n", "tmp.pythran\n", "tmp_889c4908cf3164087423cd3cf32fc2f5.cpython-39-x86_64-linux-gnu.so\n", "__pycache__/\n" ] } ], "source": [ "ls --color=never __pythran__" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "use the compiled functions (now it should be faster): 2\n" ] } ], "source": [ "print(\"use the compiled functions (now it should be faster):\", func(1))" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "result func(1): 2\n" ] } ], "source": [ "%run tmp.py" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "use the compiled functions again: 2\n" ] } ], "source": [ "print(\"use the compiled functions again:\", func(1))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's update the code of the module and see how Transonic is going to update the Pythran file and the extension!" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "src1 = \"\"\"\n", "from transonic import boost\n", "\n", "@boost\n", "def func(n: int):\n", " return 4 * n\n", "\n", "if __name__ == \"__main__\":\n", " print(\"result func(1):\", func(1))\n", "\"\"\"\n", "\n", "with open(\"tmp.py\", \"w\") as file:\n", " file.write(src1)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Running transonic on file /home/pierre/Dev/transonic/doc/ipynb/executed/tmp.py... WARNING 1 files created or updated needs to be pythranized \n", "Done!\n", "Launching Pythran to compile a new extension...\n", "result func(1): 4\n" ] } ], "source": [ "%run tmp.py" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "result func(1): 4\n" ] } ], "source": [ "%run tmp.py" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(is_transpiled, is_compiling, is_compiled) = (True, True, False)\n" ] } ], "source": [ "ts_mod = get_ts_mod_display_state(\"tmp\")" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "4,Pythranizing /home/pierre/Dev/transonic/doc/ipynb/executed/__pythran__/tmp.py\n", "4,4,File /home/pierre/Dev/transonic/doc/ipynb/executed/__pythran__/tmp_5a0e1960a66e86684449b88438bf6462.cpython-39-x86_64-linux-gnu.so created by pythran\n", "4," ] } ], "source": [ "while ts_mod.is_compiling:\n", " print(func(1), end=\",\")\n", " time.sleep(1)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tmp.py\n", "tmp.pythran\n", "tmp_5a0e1960a66e86684449b88438bf6462.cpython-39-x86_64-linux-gnu.so\n", "tmp_889c4908cf3164087423cd3cf32fc2f5.cpython-39-x86_64-linux-gnu.so\n", "__pycache__/\n" ] } ], "source": [ "ls --color=never __pythran__" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(is_transpiled, is_compiling, is_compiled) = (True, False, True)\n", "use the compiled functions: 4\n" ] } ], "source": [ "ts_mod = get_ts_mod_display_state(\"tmp\")\n", "\n", "print(\"use the compiled functions:\", func(1))" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "result func(1): 4\n" ] } ], "source": [ "%run tmp.py" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We now update the module such that the Pythran functions are the same as in the first example." ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "src2 = \"\"\"\n", "\n", "from transonic import boost\n", "\n", "\n", "@boost\n", "def func(n: int):\n", " return 4 * n\n", "\n", "\n", "def func_hello():\n", " print(\"hello\")\n", "\n", "\n", "if __name__ == \"__main__\":\n", " print(\"result func(1):\", func(1))\n", "\"\"\"\n", "\n", "\n", "with open(\"tmp.py\", \"w\") as file:\n", " file.write(src2)" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Running transonic on file /home/pierre/Dev/transonic/doc/ipynb/executed/tmp.py... WARNING Code in file /home/pierre/Dev/transonic/doc/ipynb/executed/__pythran__/tmp.py already up-to-date. \n", "Done!\n", "result func(1): 4\n" ] } ], "source": [ "%run tmp.py" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(is_transpiled, is_compiling, is_compiled) = (True, False, True)\n" ] } ], "source": [ "ts_mod = get_ts_mod_display_state(\"tmp\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Great! No need for compilation because the extension has been cached!" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [], "source": [ "while ts_mod.is_compiling:\n", " print(func(1), end=\",\")\n", " time.sleep(1)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tmp.py\n", "tmp.pythran\n", "tmp_5a0e1960a66e86684449b88438bf6462.cpython-39-x86_64-linux-gnu.so\n", "tmp_889c4908cf3164087423cd3cf32fc2f5.cpython-39-x86_64-linux-gnu.so\n", "__pycache__/\n" ] } ], "source": [ "ls --color=never __pythran__" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(is_transpiled, is_compiling, is_compiled) = (True, False, True)\n", "use the compiled functions: 4\n" ] } ], "source": [ "ts_mod = get_ts_mod_display_state(\"tmp\")\n", "\n", "print(\"use the compiled functions:\", func(1))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Summary\n", "\n", "With the \"compile_at_import\" mode (set with the function `set_compile_at_import` or by the environment variable `TRANSONICIZE_AT_IMPORT`), we can just work interactively modifying Python files and running them in IPython as without Transonic. Transonic automatically takes care of creating Pythran files and calling Pythran to create the extensions." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "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.9.6" } }, "nbformat": 4, "nbformat_minor": 4 }