tmbb

tmbb

Incendium

Easy profiling for your Phoenix controller actions (and other functions) using flamegraphs.

Example flamegraph: Example flamegraph — Incendium v0.5.0

Documentation can be found at https://hexdocs.pm/incendium.

Rationale

Profiling Elixir code is easy using the default Erlang tools, such as fprof.

These tools produce a lot of potentially useful data, but visualizing and interpreting all that data is not easy.

The erlang tool eflame contains some utilities to generate flamegraphs from the results of profiling your code

But… eflame expects you to manually run a bash script, which according to my reading seems to call a perl (!) script that generates an interactive SVG.

And although the generated SVGs support some minimal interaction, it’s possible to do better.

When developping a web application, one can take advantage of the web browser as a highly dynamic tool for visualization using SVG, HTML and Javascript.

Fortunately there is a very good javascrtip library to generate flamegraphs: d3-flame-graph.

By reading :eflame stacktrace samples and converting them into a format that d3-flame-graph can understand, we can render the flamegraph in a webpage.

That way, instead of the manual steps above you can just visit an URL in your web application.

Usage

To use incendium in your web application, you need to follow these steps:

1. Add Incendium as a dependency

def deps do
  [
    {:incendium, "~> 0.2.0"}
  ]
end

You can make it a :dev only dependency if you wish, but Incendium will only decorate your functions if you’re in :dev mode. Incendium decorators won’t decorate your functions in :prod (profilers such as eflame should never be used in :prod because they add a very significant overhead; your code will be ~10-12 times slower)

2. Create an Incendium Controller for your application

# lib/my_app_web/controllers/incencdium_controller.ex
defmodule MyApp.IncendiumController do
  use Incendium.Controller,
    routes_module: MyAppWeb.Router.Helpers,
    otp_app: :my_app
end

There is no need to define an accompanying view.

Currently the controller is not extensible (and there aren’t many natural extension points anyway).
Upon compilation, the controller will automcatically add the files incendium.js and incendium.css to your priv/static directory so that those static files will be served using the normal Phoenix mechanisms.
On unusual Phoenix apps which have static files in other places, this might not work as expected.
Currently there isn’t a way to override the place where the static files should be added.

3. Add the controller to your Router

# lib/my_app_web/controllers/router.ex

  require Incendium

  scope "/incendium", MyAppWeb do
    Incendium.routes(IncendiumController)
  end

4. Decorate the functions you want to profile

Incendium decorators depend on the decorator package.

defmodule MyAppWeb.MyContext.ExampleController do
  use MyAppWeb.Mandarin, :controller
  # Activate the incendium decorators
  use Incendium.Decorator

  # Each invocation of the `index/2` function will be traced and profiled.
  @decorate incendium_profile_with_tracing()
  def index(conn, params) do
    resources = MyContext.list_resources(params)
    render(conn, "index.html", resources: resources)
  end
end

Currently incendium only supports tracing profilers (which are very slow and not practical in production).
In the future we may support better options such as sampling profilers.

5. Visit the /incendium route to see the generated flamegraph

Each time you run a profiled function, a new stacktrace will be generated. Stacktraces are not currently saves, you can only access the latest one. In the future we might add a persistence layer that stores a number of stacktraces instead of keeping just the last one.

Here you can find an example flamegraph with explanations about how to interact with it .

Showing Posts 1 to 10

tmbb

tmbb OP

Some questions “for the audience”…

  1. Should I add a way of saving profiler stacktraces for multiple function runs and aggregate them all in the same flamegraph?

  2. Should I add a way to compare flamegraphs between different implementations of the same function? How should I do it? I can’t find a good way of comparing flamegraphs…

  3. Any suggestions for a good sampling profilers to use? (as opposed to a “tracing” profiler)

lud

lud

This is nice :slight_smile: But how could it be used if an app does not use phoenix framework ?

tmbb

tmbb OP

Well, you can use the private API to generate HTML files and dump them somewhere.

Or maybe I could change things so that Incendium was a standalone phoenix app locally (listening to a different port, of course). That would make it independent of the user’s app, and it would allow you to profile any Elixir code running locally.

That’s probably the easiest way

tmbb

tmbb OP

PS. This was written as a very simple way of profiling routes in a Phoenix app, and although I’ve tried to make it generic and usable from other apps, I’ve always thought of making it a phoenix controller instead of a standalone phoenix app

tmbb

tmbb OP

Incendium can now (v0.3.0) integrate with Benchee to generate reproducible benchmarks with performance data displayed as flamegraphs (using samples from multiple iterations, of course).

The logical way would be to create an Incendium formatter for benchee, but unfortunately that’s impossible. Incendium needs to run the benchmark twice (one without profiling so that we get accurate runtimes and another with profiling so that we get flamegraphs). This means I need control before execution (to set up profiling and create some processes that will gather the relevant metrics generated by the profiler), during execution (so that I can run the benchmark suite tiwce) and after execution (as a normal benchee formatter, to generate the HTML report

The limitations above mean that you need to call Incendium.run(benchmark, options) instead of Benchee.run(benchmark, options_with_incendim_specific_parts).

Example HTML report here: https://hexdocs.pm/incendium/0.3.0/assets/Example.html
Note that the flamegraph width is scaled according to the scenario runtime (longer runtimes create wider flamegraphs). The scaling of flamegraph widths can be disabled with an option.

Pinging @PragTob because he might be interested in this. I’d appreciate a way of running this with Benchee.run/2 instead of having to call Incendum.run/2.

The old Pheonix-based features still work as before.

PragTob

PragTob

:wave:

Thanks for the ping.

Sad things first, I’ve been battling hand/arm problems for close to 5 months and haven’t done any OSS or gaming or general fun things in the time, hence benchee might appear stale… and there’s also a lot of unreleased stuff on main from even before that :cry:

We should definitely have that, and we might already have it!

There is a feature that has been on main forever (thanks to idea from @josevalim and implementation by @pablocostass ) to have profile_after which runs builtin profilers after benchmarks initial PR

It should be semi easy to extend it to support non builtin profilers/that you can give it custom args. Maybe it already does, can’t check right now.

That said, I worry about my hands and if the past is an indicator of the future won’t get to make a new release or changes any time soon. Sorry :cry: It’s scary for me so I’m minimizing what I can to have a chance to be “good” again. Once I am, fixing that damn mac bug in nano time measurements releasing a new versiona nd brushing up benchee (and simplecov) is top of the priority list.

tmbb

tmbb OP

Oh, hope you get better soon!

Does the profiler have access to the results of the original suite so that I can report the original runtimes and do some sanity checks on whether the profilers have distorted the relationship between the runtimes of different scenarios?

PragTob

PragTob

Profile is run last - it has access to everything. However, a quick look at Profile shows that we are only calling the profilers themselves with the functions. So, some further work would be needed there.

That said, one of the easiest things might be for you for now to do:

Benche.run(..)
|> YourModule.your_call

Benchee funs always return the suite which has all the aggregated information throughout the benchmark run.

tmbb

tmbb OP

If calling the profiler on the suite is all I need, then I don’t think I’ll need anything else

tmbb

tmbb OP

Another question: do you think that scaling the flamegraph widths should be the default, so that longer runtimes result in wider flamegraphs? Or should all the flamegraphs have the same width by default?

This will be configurable, of course, I’m just asking you what the default should be

Where Next? Top

Trending in Announcing Top

wojtekmach
Hey everyone! Req is an HTTP client for Elixir that I’ve been working on for quite some time. There is already a lot of HTTP clients out...
New
handnot2
Samly can be used to enable SAML 2.0 Single Sign On in a Plug/Phoenix application. This library uses Erlang esaml to provide plug enabl...
New
woylie
Flop is an Elixir library that applies filtering, ordering and pagination parameters to your Ecto queries. offset-based pagination with...
New
restlessronin
The repo is at GitHub - cyberchitta/openai_ex: Community maintained Elixir library for OpenAI API · GitHub. Docs are at OpenaiEx User Gu...
152 11030 135
New
shahryarjb
The Chelekom project is a library of Phoenix and LiveView components generated via Mix tasks to fit developer needs seamlessly. One of i...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
woylie
Phoenix components for pagination, sortable tables and filter forms with Flop and (optionally) Ecto. pagination cursor pagination sorta...
New

Other Trending Topics Top

mudasobwa
I am seeing a lot of aplications of Argumentum ad Vericundiam in software discussions. They do link some piece of writing and point us to...
New
bartblast
Hey folks, I just published a post about Hologram’s funding and where the project goes next - the short version: Curiosum as Main Spons...
New
alexslade
Fly’s CEO posted this recently - Turn And Face The Strange · The Fly Blog It says that Fly is going all-in on sprites, which is a worry ...
New
Herve37
We’re evaluating API mocking tools for OpenAPI-based projects and would love to hear what other teams are using. We’re particularly inte...
New
sorenone
Today we’re releasing Oban for Python. Not an Oban client in Python. Not a pythonx wrapper embedded in Elixir. Nope, it’s a fully operati...
New
lawik
I was thinking since Goatmire Elixir turned out pretty good I should maybe do another one. 30th of Sep - 2nd of Oct this year./ The firs...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews