我发现使用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
答案 0 :(得分:14)
这是因为Enum.map/2
和Enum.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