Enum fusion?

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.

1 Like