This is because both Enum.map/2 and Enum.reduce/3 (used by sum ) are highly optimized for lists, and Enum.count/2 has only a common enumerated version.
To add to the confusion, there is an even faster implementation:
elements |> Enum.filter(&check/1) |> Enum.count
On my machine, using the modified control parameter that you provided, I get a consistent result:
len = 10 map: 2.1706 μs count: 7.0754 μs filter: 1.9765 μs len = 100 map: 19.921 μs count: 27.579 μs filter: 14.591 μs len = 1000 map: 168.93 μs count: 178.93 μs filter: 163.43 μs len = 10000 map: 2128.9 μs count: 1822.1 μs filter: 1664.6 μs
However, both the card and filter versions cause more garbage when they are running, so part of the time can be absorbed by the extended garbage collection time. This is already seen in the tests, since the relative profit between the versions decreases as the length of the list (and the amount of intermediate garbage) increases.
EDIT: I introduced a PR that optimizes the Enum.count/2 function as the fastest https://github.com/elixir-lang/elixir/pull/5567
source share