Skip to content

Commit

Permalink
markdown source builds
Browse files Browse the repository at this point in the history
Auto-generated via {sandpaper}
Source  : f2233ca
Branch  : main
Author  : Robert Chisholm <[email protected]>
Time    : 2024-03-22 15:25:12 +0000
Message : Add guidance on using profilers from within Jupyter notebooks. (#24)

Co-authored-by: Neil Shephard <[email protected]>
Closes #23
  • Loading branch information
actions-user committed Mar 22, 2024
1 parent 1a4e1f4 commit 6e47732
Show file tree
Hide file tree
Showing 8 changed files with 238 additions and 4 deletions.
Binary file added fig/line_profiler-worked-example-notebook.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fig/line_profiler-worked-example-rich.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fig/snakeviz-worked-example-notebook.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -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
}
89 changes: 89 additions & 0 deletions files/snakeviz-worked-example/snakeviz-worked-example.ipynb
Original file line number Diff line number Diff line change
@@ -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
}
4 changes: 2 additions & 2 deletions md5sum.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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" "5fa15bec7b1100aa5bba5249cabb79aa" "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"
Expand Down
47 changes: 46 additions & 1 deletion profiling-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -295,6 +331,14 @@ In this simple example the execution is fairly evenly balanced between all of th

Below the icicle diagram, there is a table similar to the default output from `cProfile`. However, in this case you can sort the columns by clicking their headers and filter the rows shown by entering a filename in the search box. This allows built-in methods to be hidden, which can make it easier to highlight optimisation priorities.

**Notebooks**

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:3(a_1)`, `1378276351.py:9(b_1)` and so forth.

::::::::::::::::::::::::::::::::::::: callout

## Sunburst
Expand All @@ -306,10 +350,11 @@ This provides the same information as "Icicle", however the rows are instead cir
The sunburst visualisation displays less text on the boxes, so it can be harder to interpret. However, it increases the visibility of boxes further from the root call.

<!-- TODO: Alt text here is redundant? -->
![An sunburst visualisation provided by `snakeviz` for the worked example's Python code.](episodes/fig/snakeviz-worked-example-sunburst.png){alt="The snakeviz sunburst visualisation for the worked example Python code." width=50%}
![An sunburst visualisation provided by `snakeviz` for the worked example's Python code.](episodes/fig/snakeviz-worked-example-sunburst.png){alt="A sunburst visualisation for the worked example Python code." width=50%}

:::::::::::::::::::::::::::::::::::::::::::::


## Exercises

The following exercises allow you to review your understanding of what has been covered in this episode.
Expand Down
37 changes: 36 additions & 1 deletion profiling-lines.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <function name>`, 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."}

:::::::::::::::::::::::::::::::::::::::::::::

Expand Down

0 comments on commit 6e47732

Please sign in to comment.