Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
{
"cells": [
{
"cell_type": "markdown",
"id": "9a298b63-cf46-4288-885f-ff767e7da837",
"metadata": {},
"source": [
"# Python built-in profiling tools\n",
"\n"
]
},
{
"cell_type": "markdown",
"id": "b1a2346b-6c27-44e8-bf23-74a92d75647c",
"metadata": {},
"source": [
"When looking into profiling tools we first should look into what python provides right out of the box cause sometimes one may be in a situation where its simply not possible to install and run more complex tools.\n",
"\n",
"## Profiling code example\n",
"\n",
"Since we need an example to illustrate how the tools work, we are going to use a 2D heatmap calculation written in python. For the sake of having less lines of code, comments and any safe guards were omitted."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "14a853ca-db59-4b6a-8a19-b9f1374908a3",
"metadata": {},
"outputs": [],
"source": [
"%run examples/fdm_2d_heat_equation.py"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1bf2f941-d160-4ad5-b901-41238dccbba2",
"metadata": {},
"outputs": [],
"source": [
"# create a solver instance\n",
"solver = HeatEquationSolver.get_default()"
]
},
{
"cell_type": "markdown",
"id": "1a8beab1-ccec-4bb3-82e9-2d7deda78347",
"metadata": {},
"source": [
"## [cProfile and profile](https://docs.python.org/3/library/profile.html)\n",
"\n",
"`cProfile` and `profile` are both deterministic profilers for Python programs and part of the standard library. To be precise, these two profilers are actually different implementations of the same profiling interface.\n",
"\n",
"As the name suggests, `cProfile` is a C extension with reasonable overhead and therefore a viable choice for programs with a longer runtime. `profile` is the (original) pure python module counterpart with significantly more overhead compared to `cProfile`. The module provided the original specification and is still maintained to be able to easily extend the profiler from python.\n",
"\n",
"Both produce statistics which can be formatted into reports using the `pstats` module."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d2f2f8e7-007e-4229-a7c9-aa56efc286f5",
"metadata": {},
"outputs": [],
"source": [
"import cProfile\n",
"cProfile.run('solver.calculate()')"
]
},
{
"cell_type": "markdown",
"id": "8baa79dd-5171-4a9e-b099-2e12d3dac51b",
"metadata": {},
"source": [
"The column headings are\n",
"- `ncalls` - number of calls\n",
"- `totime` - total time spent in the given function (excluding time in subfunctions)\n",
" - `percall` - `totime`/`ncalls`\n",
"- `cumtime` - cumulative time spent in this and all subfunctions\n",
" - `percall` - `cumtime`/`pcalls`\n",
"- `filename:lineno(function)` - function identification\n",
"\n",
"Of course `cProfile` also has a magic command which we can use instead of invoking cProfile manuall or from the terminal."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ea98aa03-af80-4c6a-9234-5e346c9d8016",
"metadata": {},
"outputs": [],
"source": [
"%prun solver.calculate()"
]
},
{
"cell_type": "markdown",
"id": "bd4720c1-e68f-40f4-822b-63c9448bca97",
"metadata": {},
"source": [
"### Analysing profile data with [pstats](https://docs.python.org/3/library/profile.html#module-pstats)\n",
"\n",
"The `pstats` module works closely together with `profile`/`cProfile`. If `cProfile.run` is used without a file name a `Stats` class (from `pstats`) is automatically created in the background and a simple profiling report is printed.\n",
"\n",
"However we can also write the results to an intermediate file and create our own profiling report programmatically."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c4f7acf1-1cee-494d-af97-eecd2ec70023",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"import cProfile\n",
"from pstats import Stats, SortKey\n",
"\n",
"filename = 'temp/profiling/heatmap.stats'\n",
"\n",
"cProfile.run('solver.calculate()', filename)\n",
"\n",
"stats = Stats(filename)\n",
"stats.strip_dirs()\n",
"stats.sort_stats(SortKey.TIME, SortKey.CALLS)\n",
"stats.print_stats()"
]
},
{
"cell_type": "markdown",
"id": "1237aafd-c282-4be0-9d12-8dfad5f2e547",
"metadata": {},
"source": [
"It is also possible to use the profile as a context, this avoids using an intermediate file while still allowing for customization."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "10753682-8815-4856-809b-07bd39027d4f",
"metadata": {},
"outputs": [],
"source": [
"import cProfile\n",
"from pstats import Stats, SortKey\n",
"\n",
"with cProfile.Profile() as profile:\n",
" profile.run('solver.calculate()')\n",
" stats = Stats(profile)\n",
" stats.strip_dirs()\n",
" stats.sort_stats(SortKey.TIME, SortKey.CALLS)\n",
" # only print top 5\n",
" stats.print_stats(5)\n",
" # getting the stats profile programmatically\n",
" print(stats.get_stats_profile())"
]
},
{
"cell_type": "markdown",
"id": "ebabe53b-74e5-4714-910d-7e689b8f0ef6",
"metadata": {},
"source": [
"## Memory profiling with [tracemalloc](https://docs.python.org/3/library/tracemalloc.html)"
]
},
{
"cell_type": "markdown",
"id": "e8a0f8a5-bb26-486d-b7bc-a9971a6212ee",
"metadata": {},
"source": [
"Another important aspect of profiling is the amount of memory used by applications, functions and expressions.\n",
"\n",
"`tracemalloc` is a standard python package and can be used to trace simple memory related problems such as for example:\n",
"- statistics of how much memory was used by which lines\n",
"- calculating differences between two snapshots to detect leaks\n",
"- traceback where objects were allocated\n",
"\n",
"and more."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f1e232e3-50d6-431f-b27e-5f3b14db0bef",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"%%writefile temp/profiling/memory_tracemalloc.py\n",
"import tracemalloc\n",
"import linecache\n",
"import os\n",
"\n",
"from fdm_2d_heat_equation import HeatEquationSolver, run_repeated\n",
"from memory_profiling import take_snapshot, display_top, display_diff, display_biggest_diff_traceback\n",
"\n",
"# create instance\n",
"solver = HeatEquationSolver.get_default()\n",
"\n",
"# start tracing\n",
"tracemalloc.start()\n",
"snap_before = take_snapshot()\n",
"\n",
"# call method\n",
"run_repeated(2, solver.calculate)\n",
"\n",
"snap_after = take_snapshot()\n",
"# stop tracing to release memory\n",
"tracemalloc.stop()\n",
"\n",
"# display some output\n",
"display_top(snap_after)\n",
"display_diff(snap_before, snap_after)\n",
"display_biggest_diff_traceback(snap_before, snap_after)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c10e7da4-dae2-4adb-9006-4955e4ca4169",
"metadata": {},
"outputs": [],
"source": [
"%%bash\n",
"export PYTHONPATH=tooling/:examples/\n",
"python3 -m temp.profiling.memory_tracemalloc"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c74281d1-73b1-4e52-b5b9-29fb3c0bb0d6",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"%%bash\n",
"export PYTHONPATH=tooling/:examples/\n",
"\n",
"# to track everything from the beginning, set PYTHONTRACEMALLOC to something >= 1\n",
"# or e.g. 10 to track 10 stack frames\n",
"export PYTHONTRACEMALLOC=10\n",
"\n",
"python3 -m temp.profiling.memory_tracemalloc"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5b64ea4e-a476-4e6c-bb76-01604039f1aa",
"metadata": {},
"outputs": [],
"source": []
}
],
"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.11.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}