2016-12-16 50 views
5

我發現用Enum.map |> Enum.sum的計數比Enum.count的計算速度快得多。但爲什麼不是內置計數函數有效?爲什麼Enum.map比Elixir中的Enum.count更高效?

比較以下兩行:

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

是您的代碼依賴於枚舉代碼的性能公關?因爲除非真的如此重要,否則這會讓我以理解爲代價進行優化。我的意思是,對於維護這段代碼的人來說,通過'Enum.count'和'Enum.map |> Enum.sum'可以更容易地找出你正在做什麼。所以除非這個代碼的性能很重要,否則我會選擇前者。 –

+0

是的,我的代碼依賴於這個函數的性能。我創建了自己的count()函數,以便代碼保持可讀性。 – raacer

回答

11

這是因爲這兩個Enum.map/2

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

具有不同長度列表的測試結果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 

也就是說無論是「地圖「和」過濾器「版本在操作時會產生更多垃圾,因此可能會因擴展垃圾收集時間而消耗部分時間。這在基準中已經可見,因爲隨着列表長度的長度(以及產生的中間垃圾產生量)的增加,版本之間的相對收益會減少。

編輯:我提交了優化Enum.count/2功能是最快的一個https://github.com/elixir-lang/elixir/pull/5567