Enum.xxx functions vs Hand rolled recursion

137 views
Skip to first unread message

Charles Okwuagwu

unread,
Dec 6, 2015, 4:57:58 AM12/6/15
to elixir-lang-talk
Most times it seems sensible to make use of functions from Enum.xxx, but it would seem there are performance gains to be had from hand rolling recursion. Case in point:


My questions: 

1) Why is the hand rolled recursive version more performant?, when it seems to be doing a lot more, hence more lines of code.
2) Is there a rule of thumb that can point us to writing better code, i.e. when should we reach for Enum.xxx vs hand rolling our own recursion?


defmodule Example do
  def to_indexed_map(list, offset \\ 0)
      when is_list(list)
      and is_integer(offset),
    do: for {v, k} <- list |> Enum.with_index,
      into: %{},
      do: {k+offset, v}
end


Example usage:


iex> list = ~w[dog cat sheep]
["dog", "cat", "sheep"]
iex> Example.to_indexed_map(list)
%{0 => "dog", 1 => "cat", 2 => "sheep"}


Minor Update: A less concise, but more performant version (roughly 2x faster) is shown below.


defmodule Example do
  def to_indexed_map(list, offset \\ 0)
      when is_list(list)
      and is_integer(offset),
    do: to_indexed_map(list, offset, [])

  defp to_indexed_map([], _k, acc),
    do: :maps.from_list(acc)
  defp to_indexed_map([v | vs], k, acc),
    do: to_indexed_map(vs, k+1, [{k, v} | acc])
end

Booker Bense

unread,
Dec 6, 2015, 1:07:02 PM12/6/15
to elixir-lang-talk
Enum is a general purpose library that works on anything that implements the Enumerable protocol. 
There are more things that are Enumerable than just lists. 

There is no free lunch, ease of use and general applicability almost always comes with a price. 
For instance if I pipe File.Stream! into your function it will fail. 

However, unless you've done your timings with consolidated protocols, the case for code in production
may not be as bad as it initially appears. Trying running your tests with 

env MIX_ENV=prod mix ... 

The way that protocols work requires some indirection that makes them slower in development.

There are also some changes in 1.2 that should help speed up things in the default case. 

It's important to remember the Elixir/Erlang mantra, 

Make it work, Make it pretty, and then only after you've profiled the code and found the
parts where it really is slow, make it fast. 

- Booker C. Bense

Charles Okwuagwu

unread,
Dec 6, 2015, 3:33:58 PM12/6/15
to elixir-lang-talk
Thanks, i'll remember that
Reply all
Reply to author
Forward
0 new messages