I’ve benchmarked all of this several times and nothing beats so-called “body” recursive variant even on <10^5 lists
defmodule X do
@compile :inline_list_funcs
def increase([head | tail]) do
[head + 1 | increase tail]
end
def increase([]), do: []
def tail_increase([head | tail], acc) do
tail_increase(tail, [head + 1 | acc])
end
def tail_increase(_, acc), do: :lists.reverse(acc)
def for_increase(list) do
for i <- list, do: i + 1
end
def enum(list) do
Enum.map(list, fn x -> x + 1 end)
end
def lists(list) do
:lists.map(fn x -> x + 1 end, list)
end
end
list = Enum.to_list 1..100_000
%{
"tail" => fn -> X.tail_increase(list, []) end,
"body" => fn -> X.increase(list) end,
"for" => fn -> X.for_increase(list) end,
"enum" => fn -> X.enum(list) end,
"lists" => fn -> X.lists(list) end
}
|> tap(fn tests ->
1 = length Enum.uniq Enum.map(tests, fn {_, f} -> f.() end)
end)
|> Benchee.run(time: 10, warmup: 2)
"""
body 907.08 1.10 ms ±14.56% 1.12 ms 1.43 ms
tail 580.20 1.72 ms ±35.70% 1.79 ms 3.19 ms
enum 503.79 1.98 ms ±17.27% 1.91 ms 3.14 ms
lists 495.31 2.02 ms ±8.98% 2.04 ms 2.50 ms
for 436.56 2.29 ms ±30.84% 2.30 ms 4.31 ms
Comparison:
body 907.08
tail 580.20 - 1.56x slower +0.62 ms
enum 503.79 - 1.80x slower +0.88 ms
lists 495.31 - 1.83x slower +0.92 ms
for 436.56 - 2.08x slower +1.19 ms
"""
For bigger lists, nested lists, and filter-map for
is still 20-60% behind the recursive private functions variant.
Yeah, I might be wrong. I thought I saw this in erlang docs.