Skip to content

Commit

Permalink
Optimize Enum.count/2 (#5567)
Browse files Browse the repository at this point in the history
reduce/3 has clauses specialized for various enumerable types, so should be
much faster than the general Enumerable.reduce/3 case, especially that we don't
need additional facilities like terminating the reduction early.

Comparing 3 implementations:

  * map based

        elements |> Enum.map(&(if check(&1), do: 1, else: 0)) |> Enum.sum

  * count/2 based

        elements |> Enum.count(&check/1)

  * filter based

        elements |> Enum.filter(&check/1) |> Enum.count

We get following results before and after the change:

len   | map       | count before | count after | filter
------|-----------|--------------|-------------|------------
10    | 2.1706 μs | 7.0754 μs    | 1.6304 μs   | 1.9765 μs
100   | 19.921 μs | 27.579 μs    | 13.057 μs   | 14.591 μs
1000  | 168.93 μs | 178.93 μs    | 130.76 μs   | 163.43 μs
10000 | 2128.9 μs | 1822.1 μs    | 1298.2 μs   | 1664.6 μs

The most idiomatic code is now also the fastest.
  • Loading branch information
michalmuskala authored and josevalim committed Dec 16, 2016
1 parent f88423d commit 9d39ebc
Showing 1 changed file with 3 additions and 3 deletions.
6 changes: 3 additions & 3 deletions lib/elixir/lib/enum.ex
Original file line number Diff line number Diff line change
Expand Up @@ -486,9 +486,9 @@ defmodule Enum do
"""
@spec count(t, (element -> as_boolean(term))) :: non_neg_integer
def count(enumerable, fun) when is_function(fun, 1) do
Enumerable.reduce(enumerable, {:cont, 0}, fn(entry, acc) ->
{:cont, if(fun.(entry), do: acc + 1, else: acc)}
end) |> elem(1)
reduce(enumerable, 0, fn(entry, acc) ->
if(fun.(entry), do: acc + 1, else: acc)
end)
end

@doc """
Expand Down

0 comments on commit 9d39ebc

Please sign in to comment.