When using defdelegate, does the compiler skip the call to the local function to only call the target function?
Also the documentation for defdelegate mentions “to another module” but can it not be “to another function of the same module” also?
defdelegate basically does:
def function do
And yes, you can definitely delegate to a different function in the same module.
OK, so then the documentation should be updated because “other module” is misleading. Just saying.
But the heart of my question is: is there 2 calls or only 1? In other words, is there a “penalty” for delegating or is the compiler brilliant enough to only call the target function?
Because of the possibility of hot code loading I don’t think function calls that go across module boundaries can be elided. However due to last call optimization it won’t be allocating any additional stack frames or having to shuffle registers, so the overhead will be minimal.
The good thing about open source is, of course, you can see exactly what it does at https://github.com/elixir-lang/elixir/blob/master/lib/elixir/lib/kernel.ex#L4755-L4819 (which is basically what I described above). I think the documentation describes pretty well what it does - but it doesn’t describe how it does it and I’m personally not sure thats the purpose of the documentation.
Of course if you want to delegate without re-exporting the function then
importing the function would be more appropriate and it does dispatch directly to the target module.
In general I wouldn’t worry about the performance of
defelegate. Function calls in Elixir are very fast and low overhead. You should be benchmarking a real system to get an idea of where you should spend time optimizing.
The Erlang compiler is very conservative with inlining, because it affects semantics for hot code upgrades and exceptions, without improving performance much. It won’t inline calls between modules, and only performs inlining within a module if you ask it to via directives like
There was some discussion on rewriting delegate calls in the Elixir compiler at some point: https://github.com/elixir-lang/elixir/issues/948
But I don’t think something came out of it.
If you want a
defdelegate that has no intermediary call then just define your own
defmacro that inlines the other call to whoever calls it.
Which introduces additional boilerplate on the callers side, as the caller has to require the delegating module then.
Maybe add a
use ... too (gets heavier and heavier it seems, we need more lisp’y style macros in elixir instead…).