A preliminary benchmark shows that calling a NIF which calls Elixir to run a trivial function which returns through the NIF back to the original caller takes roughly 85 times longer than calling a function directly in Elixir: 9.5µs vs 0.11µs.
I’m pretty sure that this overhead, while not neglegible, can still be considered ‘good enough’ for many projects, as the overhead of 9.5µs will often be overshadowed if there is some actual work (or communication with other processes or IO) going on inside the function.
$ MIX_ENV=bench mix run bench/call_overhead.ex
Operating System: Linux
CPU Information: Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz
Number of Available Cores: 8
Available memory: 31.18 GB
Elixir 1.12.0
Erlang 24.0.1
Benchmark suite executing with the following configuration:
warmup: 2 s
time: 5 s
memory time: 0 ns
reduction time: 0 ns
parallel: 1
inputs: 100, 100_000, 100_000_000
Estimated total run time: 1.40 min
Benchmarking apply(fun, param) with input 100 ...
Benchmarking apply(fun, param) with input 100_000 ...
Benchmarking apply(fun, param) with input 100_000_000 ...
Benchmarking apply_elixir_fun(Pool, fun, param) with input 100 ...
Benchmarking apply_elixir_fun(Pool, fun, param) with input 100_000 ...
Benchmarking apply_elixir_fun(Pool, fun, param) with input 100_000_000 ...
Benchmarking apply_elixir_fun(Server, fun, param) with input 100 ...
Benchmarking apply_elixir_fun(Server, fun, param) with input 100_000 ...
Benchmarking apply_elixir_fun(Server, fun, param) with input 100_000_000 ...
Benchmarking fun.(param) with input 100 ...
Benchmarking fun.(param) with input 100_000 ...
Benchmarking fun.(param) with input 100_000_000 ...
##### With input 100 #####
Name ips average deviation median 99th %
apply(fun, param) 8.65 M 0.116 μs ±144.37% 0.114 μs 0.178 μs
fun.(param) 8.64 M 0.116 μs ±147.69% 0.114 μs 0.156 μs
apply_elixir_fun(Pool, fun, param) 0.104 M 9.63 μs ±222.87% 8.54 μs 27.16 μs
apply_elixir_fun(Server, fun, param) 0.101 M 9.95 μs ±233.02% 8.68 μs 27.38 μs
Comparison:
apply(fun, param) 8.65 M
fun.(param) 8.64 M - 1.00x slower +0.00003 μs
apply_elixir_fun(Pool, fun, param) 0.104 M - 83.27x slower +9.51 μs
apply_elixir_fun(Server, fun, param) 0.101 M - 86.01x slower +9.83 μs
##### With input 100_000 #####
Name ips average deviation median 99th %
apply(fun, param) 8.55 M 0.117 μs ±140.47% 0.115 μs 0.160 μs
fun.(param) 8.41 M 0.119 μs ±131.56% 0.115 μs 0.180 μs
apply_elixir_fun(Server, fun, param) 0.104 M 9.59 μs ±209.01% 8.53 μs 26.84 μs
apply_elixir_fun(Pool, fun, param) 0.0994 M 10.06 μs ±179.97% 8.80 μs 27.30 μs
Comparison:
apply(fun, param) 8.55 M
fun.(param) 8.41 M - 1.02x slower +0.00198 μs
apply_elixir_fun(Server, fun, param) 0.104 M - 82.02x slower +9.47 μs
apply_elixir_fun(Pool, fun, param) 0.0994 M - 86.05x slower +9.94 μs
##### With input 100_000_000 #####
Name ips average deviation median 99th %
fun.(param) 8.42 M 0.119 μs ±114.30% 0.115 μs 0.185 μs
apply(fun, param) 8.27 M 0.121 μs ±141.67% 0.118 μs 0.25 μs
apply_elixir_fun(Server, fun, param) 0.103 M 9.75 μs ±219.29% 8.53 μs 27.54 μs
apply_elixir_fun(Pool, fun, param) 0.0961 M 10.40 μs ±193.83% 8.94 μs 27.95 μs
Comparison:
fun.(param) 8.42 M
apply(fun, param) 8.27 M - 1.02x slower +0.00213 μs
apply_elixir_fun(Server, fun, param) 0.103 M - 82.08x slower +9.63 μs
apply_elixir_fun(Pool, fun, param) 0.0961 M - 87.54x slower +10.28 μs


















