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

raa*_*cer 6 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
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

源代码: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
Run Code Online (Sandbox Code Playgroud)

mic*_*ala 16

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

更令人困惑的是,实施起来更快:

elements |> Enum.filter(&check/1) |> Enum.count
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

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

编辑:我提交了一个PR,优化Enum.count/2了最快的功能https://github.com/elixir-lang/elixir/pull/5567

  • 最新的答案是“Enum.count”现在更快,因为它应该是...... (2认同)