I noticed that if I pass a captured function (e.g. &logger/4
) as an argument to :telemetry.attach/4
, I get a warning:
[info] The function passed as a handler with ID “…” is a local function.
This means that it is either an anonymous function or a capture of a function without a module specified. That may cause a performance penalty when calling that handler. For more details see the note intelemetry:attach/4
documentation.
telemetry — telemetry v1.2.1
… but if I change this to &__MODULE__.logger/4
, I no longer get that warning. The link says:
Note: due to how anonymous functions are implemented in the Erlang VM, it is best to use function captures (i.e.
fun mod:fun/4
in Erlang or&Mod.fun/4
in Elixir) as event handlers to achieve maximum performance. In other words, avoid using literal anonymous functions (fun(...) -> ... end
orfn ... -> ... end
) or local function captures (fun handle_event/4
or&handle_event/4
) as event handlers.
Is there more documentation about what “due to how anonymous functions are implemented in the Erlang VM” is alluding to? I read the documentation for &/1 but it doesn’t go into detail about why &foo/n is different from &__MODULE__.foo/n
, and I am curious!
The Erlang documentation on functions makes it sound like foo()
and m:foo()
should have the same performance (Functions — Erlang System Documentation v27.0):
This is a rough hierarchy of the performance of the different types of function calls:
- Calls to local or external functions (
foo()
,m:foo()
) are the fastest calls.- Calling or applying a fun (
Fun()
,apply(Fun, [])
) is just a little slower than external calls.- Applying an exported function (
Mod:Name()
,apply(Mod, Name, [])
) where the number of arguments is known at compile time is next.- Applying an exported function (
apply(Mod, Name, Args)
) where the number of arguments is not known at compile time is the least efficient.
Are local function captures (without the module name) compiled into Erlang Fun()
s instead of local/external functions? If so, why?