diff --git a/fig/line_profiler-worked-example-notebook.png b/fig/line_profiler-worked-example-notebook.png new file mode 100644 index 0000000..76de6bc Binary files /dev/null and b/fig/line_profiler-worked-example-notebook.png differ diff --git a/fig/line_profiler-worked-example-rich.png b/fig/line_profiler-worked-example-rich.png new file mode 100644 index 0000000..187adb1 Binary files /dev/null and b/fig/line_profiler-worked-example-rich.png differ diff --git a/fig/snakeviz-worked-example-notebook.png b/fig/snakeviz-worked-example-notebook.png new file mode 100644 index 0000000..7e65991 Binary files /dev/null and b/fig/snakeviz-worked-example-notebook.png differ diff --git a/files/line_profiler-worked-example/line-profiling-worked-example.ipynb b/files/line_profiler-worked-example/line-profiling-worked-example.ipynb new file mode 100644 index 0000000..90a1a15 --- /dev/null +++ b/files/line_profiler-worked-example/line-profiling-worked-example.ipynb @@ -0,0 +1,65 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "e80c86cb-d60a-4e45-a34c-191aab11acf0", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install line_profiler\n", + "%load_ext line_profiler" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "de5e278a-6826-4f42-b1af-496f5aabf996", + "metadata": {}, + "outputs": [], + "source": [ + "def fizzbuzz(n):\n", + " for i in range(1, n + 1):\n", + " if i % 3 == 0 and i % 5 == 0:\n", + " print(\"FizzBuzz\")\n", + " elif i % 3 == 0:\n", + " print(\"Fizz\")\n", + " elif i % 5 == 0:\n", + " print(\"Buzz\")\n", + " else:\n", + " print(i)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c54f2642-617e-4a51-b3e8-1ff1ba0e8426", + "metadata": {}, + "outputs": [], + "source": [ + "%lprun -f fizzbuzz fizzbuzz(100)" + ] + } + ], + "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.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/files/snakeviz-worked-example/snakeviz-worked-example.ipynb b/files/snakeviz-worked-example/snakeviz-worked-example.ipynb new file mode 100644 index 0000000..625054d --- /dev/null +++ b/files/snakeviz-worked-example/snakeviz-worked-example.ipynb @@ -0,0 +1,89 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "83b45371-9cd9-4a8f-b0ff-ceb1bfc2ecc2", + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "\n", + "def a_1():\n", + " for i in range(3):\n", + " b_1()\n", + " time.sleep(1)\n", + " b_2()\n", + " \n", + "def b_1():\n", + " c_1()\n", + " c_2()\n", + "\n", + "def b_2():\n", + " time.sleep(1)\n", + " \n", + "def c_1():\n", + " time.sleep(0.5)\n", + "\n", + "def c_2():\n", + " time.sleep(0.3)\n", + " d_1()\n", + "\n", + "def d_1():\n", + " time.sleep(0.1)\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0fc38195-3dc4-4a7c-82f6-1167f54d3182", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install snakeviz" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c47affa0-23cc-43f8-a4f8-e73ab2f44afd", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext snakeviz" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "58040218-8578-47fc-9a18-476f4b27d25a", + "metadata": {}, + "outputs": [], + "source": [ + "%snakeviz a_1()" + ] + } + ], + "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.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/md5sum.txt b/md5sum.txt index 5e8db1c..986b4a8 100644 --- a/md5sum.txt +++ b/md5sum.txt @@ -5,8 +5,8 @@ "index.md" "3a6d3683998a6b866c134a818f1bb46e" "site/built/index.md" "2024-03-20" "links.md" "8184cf4149eafbf03ce8da8ff0778c14" "site/built/links.md" "2024-03-20" "episodes/profiling-introduction.md" "86c0d8691aae9c0aa4b6f23e216a2978" "site/built/profiling-introduction.md" "2024-03-20" -"episodes/profiling-functions.md" "806c4d44bd7b0957030044a3010c3004" "site/built/profiling-functions.md" "2024-03-20" -"episodes/profiling-lines.md" "547d98ccf5dfb92abb63feb0e02fc8a2" "site/built/profiling-lines.md" "2024-03-20" +"episodes/profiling-functions.md" "c85567f11fcc3f7b2468eb31bb2c1809" "site/built/profiling-functions.md" "2024-03-22" +"episodes/profiling-lines.md" "c00ccba171a20837aeffc9f8079d9e1d" "site/built/profiling-lines.md" "2024-03-22" "episodes/profiling-conclusion.md" "a3c2deb1bc4efaaf4a2a70f966734b71" "site/built/profiling-conclusion.md" "2024-03-20" "episodes/optimisation-introduction.md" "2c2bbafab97d4db78aa5735839516c81" "site/built/optimisation-introduction.md" "2024-03-20" "episodes/optimisation-data-structures-algorithms.md" "029ce8cc24d92f2d819fadadc06b5999" "site/built/optimisation-data-structures-algorithms.md" "2024-03-20" diff --git a/profiling-functions.md b/profiling-functions.md index 15daf64..b9e23e5 100644 --- a/profiling-functions.md +++ b/profiling-functions.md @@ -207,6 +207,42 @@ By clicking a box within the diagram, it will "zoom" making the selected box the As you hover each box, information to the left of the diagram updates specifying the location of the method and for how long it ran. +::::::::::::::::::::::::::::::::::::: callout + +## snakeviz Inside Notebooks + +If you're more familiar with writing Python inside Jupyter notebooks you can still use `snakeviz` directly from inside notebooks using the notebooks "magic" prefix (`%`) and it will automatically call `cProfile` for you. + +First `snakeviz` must be installed and it's extension loaded. + +```py +!pip install snakeviz +%load_ext snakeviz +``` + +Following this, you can either call `%snakeviz` to profile a function defined earlier in the notebook. + +```py +%snakeviz my_function() +``` + +Or, you can create a `%%snakeviz` cell, to profile the python executed within it. + +```py +%%snakeviz + +def my_function(): + print("Hello World!") + +my_function() +``` + +In both cases, the full `snakeviz` profile visualisation will appear as an output within the notebook! + +*You may wish to right click the top of the output, and select "Disable Scrolling for Outputs" to expand it's box if it begins too small.* + +::::::::::::::::::::::::::::::::::::::::::::: + ## Worked Example :::::::::::::::::::::::::::::::::: instructor @@ -310,6 +346,13 @@ The sunburst visualisation displays less text on the boxes, so it can be harder ::::::::::::::::::::::::::::::::::::::::::::: +If you followed along inside a notebook it might look like this: + +![The worked example inside a notebook.](episodes/fig/snakeviz-worked-example-notebook.png){alt="A Jupyter notebook showing the worked example profiled with snakeviz." width=80%} + +Because notebooks operate by creating temporary Python files, the filename (shown `1378276351.py` above) and line numbers are not too useful should still be helpful. The function names follow the temporary file name in parentheses, e.g. `1378276351.py(a_1)` `1378276351.py(b_1)` and so forth. + + ## Exercises The following exercises allow you to review your understanding of what has been covered in this episode. diff --git a/profiling-lines.md b/profiling-lines.md index d8239c0..e93bb0e 100644 --- a/profiling-lines.md +++ b/profiling-lines.md @@ -238,7 +238,42 @@ Therefore it can be seen in this example, how the time spent executing each line The `-r` argument passed to `kernprof` (or `line_profiler`) enables rich output, if you run the profile locally it should look similar to this. *This requires the optional package `rich`, it will have been installed if `[all]` was specified when installing `line_profiler` with `pip`.* -![Rich (highlighted) console output provided by `line_profiler` for the above FizzBuzz profile code.](episodes/fig/line_profiler-worked-example.png){alt="A screenshot of the `line_profiler` output from the previous code block, where the code within the line contents column has basic highlighting."} +![Rich (highlighted) console output provided by `line_profiler` for the above FizzBuzz profile code.](episodes/fig/line_profiler-worked-example-rich.png){alt="A screenshot of the `line_profiler` output from the previous code block, where the code within the line contents column has basic highlighting."} + +::::::::::::::::::::::::::::::::::::::::::::: + +::::::::::::::::::::::::::::::::::::: callout + +## line_profiler Inside Notebooks + +If you're more familiar with writing Python inside Jupyter notebooks you can, as with `snakeviz`, use `line_profiler` directly from inside notebooks. However it is still necessary for the code you wish to profile to be placed within a function. + +First `line_profiler` must be installed and it's extension loaded. + +```py +!pip install line_profiler +%load_ext line_profiler +``` + +Following this, you call `line_profiler` with `%lprun`. + +```py +%lprun -f profiled_function_name entry_function_call() +``` + +The functions to be line profiled are specified with `-f `, this is repeated for each individual function that you would otherwise apply the `@profile` decorator to. + +This is followed by calling the function which runs the full code to be profiled. + +For the above fizzbuzz example it would be: + +```py +%lprun -f fizzbuzz fizzbuzz(100) +``` + +This will then create an output cell with any output from the profiled code, followed by the standard output from `line_profiler`. *It is not currently possible to get the rich/coloured output from `line_profiler` within notebooks.* + +![Output provided by `line_profiler` inside a Juypter notebook for the above FizzBuzz profile code.](episodes/fig/line_profiler-worked-example-notebook.png){alt="A screenshot of the line_profiler output from the previous code block inside a Jupyter notebook."} :::::::::::::::::::::::::::::::::::::::::::::