Skip to content

Commit

Permalink
Add documentation
Browse files Browse the repository at this point in the history
...and improve examples' docs.
  • Loading branch information
Giuseppe Lumia committed Nov 25, 2021
1 parent 8b546da commit 6e45f35
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 26 deletions.
77 changes: 70 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,79 @@
# pylaprof
🚧 **Work in progress** 🚧
pylaprof is a Python library that allows you to profile functions or sections of code.

Actually already usable (check the *examples* directory) but not yet production
ready.
As a decorator:
```python
from pylaprof import profile

To install:
@profile()
def handler(event, context):
...
```

As a context manager:
```python
from pylaprof import Profiler

def main():
...
with Profiler():
# Only code inside this context will be profiled.
...
```

It is built around three main abstractions: the *profiler*, the *sampler*, and
the *storer*.

The profiler is the main component of pylaprof, it takes care of taking
snapshots of your program's stack at regular intervals of times and feeding them
to the *sampler* for processing; at the end of the profiling session, it will
then ask the *sampler* for a report and provide it to the *storer*.

Take a look at the [source](./pylaprof/__init__.py) for more documentation and
some pre-implemented samplers and storers or [here](./examples) for some
usage examples.

## Features
- Accessible: pylaprof's code is thoroughly documented and written to be read and
understood by other humans.

- Extensible: you can write your own sampler or storer to generate reports in the format
you like and store them where and how you want.

- Zero external dependencies[^1].

- Close to zero impact on performances (check [benchmark](./benchmark) for
more details).

- Production-ready: pylaprof was built with the context of long-running
applications or continuously invoked lambda functions in mind.
It will never break your code or pollute your standard output or error
with unwanted messages.

- Turn on/off profiling with an environment variable.

- Store the profiling report only if execution takes longer than a threshold.

[^1]: boto3 is optional and required only if you want to use the S3 storer.

### pylaprof-merge
`pylaprof-merge` is a simple CLI tool to merge multiple stackcollapse reports
into a single one. This might come in handy if you want to get an aggregated
overview of a function or piece of code that is executed frequently for short
periods. It is installed automatically if you get pylaprof with pip.


## Installation
```
pip install pylaprof
```

(or just copy-paste the pylaprof directory where you need it)
Or just copy-paste the pylaprof directory where you need it.


Credits:
## Credits
- This library is heavily inspired to [pprofile](
https://github.com/vpelletier/pprofile).
https://github.com/vpelletier/pprofile): thanks to its authors for writing such
accessible and well-documented code.
- Thanks to @jvns for writing and distributing some of her *wizard zines* for free:
it's what got me into the rabbit hole of profiling in the first place.
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
- Add integration tests <--- OK!
- Write a decent readme - show what we can do with pylaprof :)
(note, add info about the performance impact and other considerations to run
pylaprof in production, e.g. time required for report's upload/storage)
pylaprof in production, e.g. time required for report's upload/storage) <--- OK!
- Set-up CI/CD (Github Actions, package upload on PyPI). <--- OK!

## Wave 4 - Let's get production ready
Expand Down
6 changes: 3 additions & 3 deletions benchmark/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ handler code.
./performance_impact.py --iterations 10 --period 0.01
```

You can easily adapt it do measure the impact of pylaprof on any other function.
You can easily adapt it to measure the impact of pylaprof on any other function.


## Profile and benchmark a sampler
Expand Down Expand Up @@ -38,7 +38,7 @@ set you want, the more the better):
./process_frames.py --iterations 1000
```

Any other sampler implementation can be profiled and benchmarked in a similar way with
Any other sampler implementation can be profiled and benchmarked similarly with
smaller changes to `process_frames.py`.

## Benchmark results
Expand Down Expand Up @@ -66,7 +66,7 @@ Performance impact of pylaprof:
```

The sampler takes ~3.3 μs to process a stack snapshot and the impact of pylaprof with a
sampling period of 0.01 on handler's performance is indistinguishable from noise of
sampling period of 0.01 on handler's performance is indistinguishable from the noise of
other factors.

**Note**: in those benchmarks we use a dummy storer that ignores the report file generated
Expand Down
20 changes: 9 additions & 11 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
Just a bunch of examples of how you can use pylaprof.
# Examples
This directory contains two examples of how you can use pylaprof and the reports
it generates: [hello-lambda](./hello-lambda), an AWS lambda function, and
*launcher.py*, a dummy command-line application.

### hello-lambda
This project shows how you can use pylaprof as a decorator for your lambda
function (check `hello-lambda/handler.py`).


### launcher
## launcher.py
This "project" shows how you can use pylaprof as a context manager for
any Python function (check `launcher.py`).
any Python function.

To reproduce `flame.svg`, launch the dummy HTTP API service:
```
~/pylaprof/examples$ ./server.py
$ ./server.py
```

Run code of `handler.py` and generate a stackcollapse report in the current directory:
```
~/pylaprof/examples$ ./launcher.py --fs
$ ./launcher.py --fs
```

Finally use Brendan Gregg's [flamegraph generator](
https://github.com/brendangregg/flamegraph):
```
~/pylaprof/examples$ ../../FlameGraph/flamegraph.pl pylaprof-2021-11-21T10\:48\:51.865486+00\:00.txt > flame.svg
$ $HOME/FlameGraph/flamegraph.pl pylaprof-2021-11-21T10\:48\:51.865486+00\:00.txt > flame.svg
```
14 changes: 13 additions & 1 deletion examples/hello-lambda/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,13 @@
A minimal lambda function to test pylaprof.
# hello-lambda
A dead-simple serverless framework project with an AWS lambda function we
profile with pylaprof's decorator.

If you don't specify any storer pylaprof will instantiate and use a default
S3 storer that requires a `pylaprof` bucket on your AWS account.

Remember to give your lambda functions permissions to write on this bucket
(check lines 6-10 of [serverless.yml](./serverless.yml)), otherwise pylaprof
will not be able to store the profiling report.

You can turn on/off pylaprof once your lambda function is deployed by changing
the `PYLAPROF_DISABLE` environment variable (no need for another deploy!).
6 changes: 3 additions & 3 deletions examples/hello-lambda/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
storer = S3()


@profile(storer=storer)
@profile(period=0.001, min_time=6, storer=storer)
def handler(context, event):
print("handler: received context", context, "and event", event)
sleepy_task()
Expand All @@ -15,10 +15,10 @@ def handler(context, event):


def sleepy_task():
time.sleep(3) # ...
time.sleep(3) # ~3 seconds (captain obvious)


def cpu_intens_task():
i = 0
while i < 10 ** 6: # Will take ~3 seconds (at least on my laptop)
while i < 10 ** 6: # ~3 seconds on a i7-1165G7 @ 2.80GHz
i += 1
2 changes: 2 additions & 0 deletions examples/hello-lambda/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ provider:
Action:
- s3:PutObject
Resource: arn:aws:s3:::pylaprof/*
environment:
PYLAPROF_DISABLE: false

functions:
dummy-fun:
Expand Down

0 comments on commit 6e45f35

Please sign in to comment.