Skip to content

Commit d69c078

Browse files
Merge pull request #234044 from vmagelo/freshness-work4
Freshness work on memory profiler article.
2 parents e867d89 + b254180 commit d69c078

File tree

1 file changed

+116
-38
lines changed

1 file changed

+116
-38
lines changed

articles/azure-functions/python-memory-profiler-reference.md

Lines changed: 116 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
---
2-
title: Memory profiling on Python apps in Azure Functions
3-
description: Learn how to profile Python apps memory usage and identify memory bottleneck.
2+
title: Memory profiling of Python apps in Azure Functions
3+
description: Learn how to profile the memory usage of Python apps and identify memory bottleneck.
44
ms.topic: how-to
5-
ms.date: 3/22/2021
5+
ms.date: 4/11/2023
66
ms.devlang: python
77
ms.custom: devx-track-python, py-fresh-zinc
88
---
99
# Profile Python apps memory usage in Azure Functions
1010

11-
During development or after deploying your local Python function app project to Azure, it's a good practice to analyze for potential memory bottlenecks in your functions. Such bottlenecks can decrease the performance of your functions and lead to errors. The following instruction show you how to use the [memory-profiler](https://pypi.org/project/memory-profiler) Python package, which provides line-by-line memory consumption analysis of your functions as they execute.
11+
During development or after deploying your local Python function app project to Azure, it's a good practice to analyze for potential memory bottlenecks in your functions. Such bottlenecks can decrease the performance of your functions and lead to errors. The following instructions show you how to use the [memory-profiler](https://pypi.org/project/memory-profiler) Python package, which provides line-by-line memory consumption analysis of your functions as they execute.
1212

1313
> [!NOTE]
14-
> Memory profiling is intended only for memory footprint analysis on development environment. Please do not apply the memory profiler on production function apps.
14+
> Memory profiling is intended only for memory footprint analysis in development environments. Please do not apply the memory profiler on production function apps.
1515
1616
## Prerequisites
1717

1818
Before you start developing a Python function app, you must meet these requirements:
1919

20-
* [Python 3.6.x or above](https://www.python.org/downloads/release/python-374/). To check the full list of supported Python versions in Azure Functions, please visit [Python developer guide](functions-reference-python.md#python-version).
20+
* [Python 3.7 or above](https://www.python.org/downloads). To check the full list of supported Python versions in Azure Functions, see the [Python developer guide](functions-reference-python.md#python-version).
2121

22-
* The [Azure Functions Core Tools](functions-run-local.md#v2) version 3.x.
22+
* The [Azure Functions Core Tools](functions-run-local.md#v2), version 4.x or greater. Check your version with `func --version`. To learn about updating, see [Azure Functions Core Tools on GitHub](https://github.com/Azure/azure-functions-core-tools).
2323

2424
* [Visual Studio Code](https://code.visualstudio.com/) installed on one of the [supported platforms](https://code.visualstudio.com/docs/supporting/requirements#_platforms).
2525

@@ -29,9 +29,9 @@ Before you start developing a Python function app, you must meet these requireme
2929

3030
## Memory profiling process
3131

32-
1. In your requirements.txt, add `memory-profiler` to ensure the package will be bundled with your deployment. If you are developing on your local machine, you may want to [activate a Python virtual environment](create-first-function-cli-python.md#create-venv) and do a package resolution by `pip install -r requirements.txt`.
32+
1. In your requirements.txt, add `memory-profiler` to ensure the package is bundled with your deployment. If you're developing on your local machine, you may want to [activate a Python virtual environment](create-first-function-cli-python.md#create-venv) and do a package resolution by `pip install -r requirements.txt`.
3333

34-
2. In your function script (usually \_\_init\_\_.py), add the following lines above the `main()` function. This will ensure the root logger reports the child logger names, so that the memory profiling logs are distinguishable by the prefix `memory_profiler_logs`.
34+
2. In your function script (for example, *\_\_init\_\_.py* for the Python v1 programming model and *function_app.py* for the v2 model), add the following lines above the `main()` function. These lines ensure the root logger reports the child logger names, so that the memory profiling logs are distinguishable by the prefix `memory_profiler_logs`.
3535

3636
```python
3737
import logging
@@ -40,51 +40,64 @@ Before you start developing a Python function app, you must meet these requireme
4040
root_logger.handlers[0].setFormatter(logging.Formatter("%(name)s: %(message)s"))
4141
profiler_logstream = memory_profiler.LogFile('memory_profiler_logs', True)
4242

43-
3. Apply the following decorator above any functions that need memory profiling. This does not work directly on the trigger entrypoint `main()` method. You need to create subfunctions and decorate them. Also, due to a memory-profiler known issue, when applying to an async coroutine, the coroutine return value will always be None.
43+
3. Apply the following decorator above any functions that need memory profiling. The decorator doesn't work directly on the trigger entrypoint `main()` method. You need to create subfunctions and decorate them. Also, due to a memory-profiler known issue, when applying to an async coroutine, the coroutine return value is always `None`.
4444

4545
```python
4646
@memory_profiler.profile(stream=profiler_logstream)
4747

48-
4. Test the memory profiler on your local machine by using azure Functions Core Tools command `func host start`. This should generate a memory usage report with file name, line of code, memory usage, memory increment, and the line content in it.
48+
4. Test the memory profiler on your local machine by using Azure Functions Core Tools command `func host start`. When you invoke the functions, they should generate a memory usage report. The report contains file name, line of code, memory usage, memory increment, and the line content in it.
4949

50-
5. To check the memory profiling logs on an existing function app instance in Azure, you can query the memory profiling logs in recent invocations by pasting the following Kusto queries in Application Insights, Logs.
50+
5. To check the memory profiling logs on an existing function app instance in Azure, you can query the memory profiling logs for recent invocations with [Kusto](/azure/azure-monitor/logs/log-query-overview) queries in Application Insights, Logs.
5151

52-
:::image type="content" source="media/python-memory-profiler-reference/application-insights-query.png" alt-text="Query memory usage of a Python app in Application Insights":::
52+
:::image type="content" source="media/python-memory-profiler-reference/application-insights-query.png" alt-text="Screenshot showing the query memory usage of a Python app in Application Insights.":::
5353

54-
```text
55-
traces
56-
| where timestamp > ago(1d)
57-
| where message startswith_cs "memory_profiler_logs:"
58-
| parse message with "memory_profiler_logs: " LineNumber " " TotalMem_MiB " " IncreMem_MiB " " Occurences " " Contents
59-
| union (
54+
```kusto
6055
traces
6156
| where timestamp > ago(1d)
62-
| where message startswith_cs "memory_profiler_logs: Filename: "
63-
| parse message with "memory_profiler_logs: Filename: " FileName
64-
| project timestamp, FileName, itemId
65-
)
66-
| project timestamp, LineNumber=iff(FileName != "", FileName, LineNumber), TotalMem_MiB, IncreMem_MiB, Occurences, Contents, RequestId=itemId
67-
| order by timestamp asc
68-
```
69-
57+
| where message startswith_cs "memory_profiler_logs:"
58+
| parse message with "memory_profiler_logs: " LineNumber " " TotalMem_MiB " " IncreMem_MiB " " Occurrences " " Contents
59+
| union (
60+
traces
61+
| where timestamp > ago(1d)
62+
| where message startswith_cs "memory_profiler_logs: Filename: "
63+
| parse message with "memory_profiler_logs: Filename: " FileName
64+
| project timestamp, FileName, itemId
65+
)
66+
| project timestamp, LineNumber=iff(FileName != "", FileName, LineNumber), TotalMem_MiB, IncreMem_MiB, Occurrences, Contents, RequestId=itemId
67+
| order by timestamp asc
68+
```
69+
7070
## Example
7171

72-
Here is an example of performing memory profiling on an asynchronous and a synchronous HTTP triggers, named "HttpTriggerAsync" and "HttpTriggerSync" respectively. We will build a Python function app that simply sends out GET requests to the Microsoft's home page.
72+
Here's an example of performing memory profiling on an asynchronous and a synchronous HTTP trigger, named "HttpTriggerAsync" and "HttpTriggerSync" respectively. We'll build a Python function app that simply sends out GET requests to the Microsoft's home page.
7373

7474
### Create a Python function app
7575

7676
A Python function app should follow Azure Functions specified [folder structure](functions-reference-python.md#folder-structure). To scaffold the project, we recommend using the Azure Functions Core Tools by running the following commands:
7777

78+
# [v1](#tab/v1)
79+
7880
```bash
7981
func init PythonMemoryProfilingDemo --python
8082
cd PythonMemoryProfilingDemo
8183
func new -l python -t HttpTrigger -n HttpTriggerAsync -a anonymous
8284
func new -l python -t HttpTrigger -n HttpTriggerSync -a anonymous
8385
```
8486

87+
# [v2](#tab/v2)
88+
89+
```bash
90+
func init PythonMemoryProfilingDemov2 --python -m v2
91+
cd PythonMemoryProfilingDemov2
92+
```
93+
94+
For the Python V2 programming model, triggers and bindings are created as decorators within the Python file itself, the *function_app.py* file. For information on how to create a new function with the new programming model, see the [Azure Functions Python developer guide](https://aka.ms/pythonprogrammingmodel). `func new` isn't supported for the preview of the V2 Python programming model.
95+
96+
---
97+
8598
### Update file contents
8699

87-
The requirements.txt defines the packages that will be used in our project. Besides the Azure Functions SDK and memory-profiler, we introduce `aiohttp` for asynchronous HTTP requests and `requests` for synchronous HTTP calls.
100+
The *requirements.txt* defines the packages that are used in our project. Besides the Azure Functions SDK and memory-profiler, we introduce `aiohttp` for asynchronous HTTP requests and `requests` for synchronous HTTP calls.
88101

89102
```text
90103
# requirements.txt
@@ -95,7 +108,11 @@ aiohttp
95108
requests
96109
```
97110

98-
We also need to rewrite the asynchronous HTTP trigger `HttpTriggerAsync/__init__.py` and configure the memory profiler, root logger format, and logger streaming binding.
111+
Create the asynchronous HTTP trigger.
112+
113+
# [v1](#tab/v1)
114+
115+
Replace the code in the asynchronous HTTP trigger *HttpTriggerAsync/\_\_init\_\_.py* with the following code, which configures the memory profiler, root logger format, and logger streaming binding.
99116

100117
```python
101118
# HttpTriggerAsync/__init__.py
@@ -114,7 +131,7 @@ profiler_logstream = memory_profiler.LogFile('memory_profiler_logs', True)
114131
async def main(req: func.HttpRequest) -> func.HttpResponse:
115132
await get_microsoft_page_async('https://microsoft.com')
116133
return func.HttpResponse(
117-
f"Microsoft Page Is Loaded",
134+
f"Microsoft page loaded.",
118135
status_code=200
119136
)
120137

@@ -128,7 +145,49 @@ async def get_microsoft_page_async(url: str):
128145
# GitHub Issue: https://github.com/pythonprofilers/memory_profiler/issues/289
129146
```
130147

131-
For synchronous HTTP trigger, please refer to the following `HttpTriggerSync/__init__.py` code section:
148+
# [v2](#tab/v2)
149+
150+
Replace the code in the *function_app.py* file with the following code, which configures the memory profiler, root logger format, and logger streaming binding.
151+
152+
```python
153+
# function_app.py
154+
import azure.functions as func
155+
import logging
156+
import aiohttp
157+
import requests
158+
import memory_profiler
159+
160+
app = func.FunctionApp()
161+
162+
# Update root logger's format to include the logger name. Ensure logs generated
163+
# from memory profiler can be filtered by "memory_profiler_logs" prefix.
164+
root_logger = logging.getLogger()
165+
root_logger.handlers[0].setFormatter(logging.Formatter("%(name)s: %(message)s"))
166+
profiler_logstream = memory_profiler.LogFile('memory_profiler_logs', True)
167+
168+
@app.function_name(name="HttpTriggerAsync")
169+
@app.route(route="HttpTriggerAsync", auth_level=func.AuthLevel.ANONYMOUS)
170+
async def test_function(req: func.HttpRequest) -> func.HttpResponse:
171+
await get_microsoft_page_async('https://microsoft.com')
172+
return func.HttpResponse(f"Microsoft page loaded.")
173+
174+
@memory_profiler.profile(stream=profiler_logstream)
175+
async def get_microsoft_page_async(url: str):
176+
async with aiohttp.ClientSession() as client:
177+
async with client.get(url) as response:
178+
await response.text()
179+
# @memory_profiler.profile does not support return for coroutines.
180+
# All returns become None in the parent functions.
181+
# GitHub Issue: https://github.com/pythonprofilers/memory_profiler/issues/289
182+
```
183+
184+
---
185+
186+
Create the synchronous HTTP trigger.
187+
188+
# [v1](#tab/v1)
189+
190+
Replace the code in the asynchronous HTTP trigger *HttpTriggerSync/\_\_init\_\_.py* with the following code.
132191

133192
```python
134193
# HttpTriggerSync/__init__.py
@@ -147,7 +206,7 @@ profiler_logstream = memory_profiler.LogFile('memory_profiler_logs', True)
147206
def main(req: func.HttpRequest) -> func.HttpResponse:
148207
content = profile_get_request('https://microsoft.com')
149208
return func.HttpResponse(
150-
f"Microsoft Page Response Size: {len(content)}",
209+
f"Microsoft page response size: {len(content)}",
151210
status_code=200
152211
)
153212

@@ -157,21 +216,40 @@ def profile_get_request(url: str):
157216
return response.content
158217
```
159218

219+
# [v2](#tab/v2)
220+
221+
Add this code to the bottom of the existing *function_app.py* file.
222+
223+
```python
224+
@app.function_name(name="HttpTriggerSync")
225+
@app.route(route="HttpTriggerSync", auth_level=func.AuthLevel.ANONYMOUS)
226+
def test_function(req: func.HttpRequest) -> func.HttpResponse:
227+
content = profile_get_request('https://microsoft.com')
228+
return func.HttpResponse(f"Microsoft Page Response Size: {len(content)}")
229+
230+
@memory_profiler.profile(stream=profiler_logstream)
231+
def profile_get_request(url: str):
232+
response = requests.get(url)
233+
return response.content
234+
```
235+
236+
---
237+
160238
### Profile Python function app in local development environment
161239

162-
After making all the above changes, there are a few more steps to initialize a Python virtual envionment for Azure Functions runtime.
240+
After you make the above changes, there are a few more steps to initialize a Python virtual environment for Azure Functions runtime.
163241

164242
1. Open a Windows PowerShell or any Linux shell as you prefer.
165243
2. Create a Python virtual environment by `py -m venv .venv` in Windows, or `python3 -m venv .venv` in Linux.
166-
3. Activate the Python virutal environment with `.venv\Scripts\Activate.ps1` in Windows PowerShell or `source .venv/bin/activate` in Linux shell.
167-
4. Restore the Python dependencies with `pip install requirements.txt`
244+
3. Activate the Python virtual environment with `.venv\Scripts\Activate.ps1` in Windows PowerShell or `source .venv/bin/activate` in Linux shell.
245+
4. Restore the Python dependencies with `pip install -r requirements.txt`
168246
5. Start the Azure Functions runtime locally with Azure Functions Core Tools `func host start`
169247
6. Send a GET request to `https://localhost:7071/api/HttpTriggerAsync` or `https://localhost:7071/api/HttpTriggerSync`.
170-
7. It should show a memory profiling report similiar to below section in Azure Functions Core Tools.
248+
7. It should show a memory profiling report similar to the following section in Azure Functions Core Tools.
171249

172250
```text
173251
Filename: <ProjectRoot>\HttpTriggerAsync\__init__.py
174-
Line # Mem usage Increment Occurences Line Contents
252+
Line # Mem usage Increment Occurrences Line Contents
175253
============================================================
176254
19 45.1 MiB 45.1 MiB 1 @memory_profiler.profile
177255
20 async def get_microsoft_page_async(url: str):

0 commit comments

Comments
 (0)