为什么Enum.map比Elixir中的Enum.count更有效?

时间:2016-12-16 01:09:13

标签: performance count elixir

我发现使用Enum.map |> Enum.sum进行计数的速度比Enum.count快得多。但为什么内置计数功能不高效?

比较这两行:

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

具有不同长度列表的测试结果:

len   | map(), µs | count(), µs
===============================
   10 |      0.67 |    2.55
  100 |      5.52 |    8.91
 1000 |     59.00 |   73.12
10000 |    642.50 |  734.60

源代码:https://gist.github.com/artemrizhov/fc146f7ab390f7a807be833099e5cb83

$ elixir --version
Erlang/OTP 19 [erts-8.1] [source-e7be63d] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false]

Elixir 1.3.4

1 个答案:

答案 0 :(得分:14)

这是因为Enum.map/2Enum.reduce/3(由sum使用)都针对列表进行了大量优化,而Enum.count/2只有一个通用的可枚举版本。

为了增加混乱,甚至可以更快地实施:

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

在我的机器上,使用您提供的修改基准,我得到了一致的结果:

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

那说"地图"和"过滤器"版本在运行时会产生更多垃圾,因此延长的垃圾收集时间可能会消耗一些时间。这在基准测试中已经可见,因为版本之间的相对增益随着列表长度的长度(以及产生的中间垃圾量)的增加而减少。

编辑:我提交的PR会优化Enum.count/2功能,使其成为最快的https://github.com/elixir-lang/elixir/pull/5567