2016-07-08 99 views
0

我目前通過網站exercism.io學習Elixir。 我有問題「總和的倍數」是:多進程效率低下

如果我們列出所有的自然數,直到但不包括20是3或5 倍數,我們得到了3,5,6和9,10,12,15,和18 這些倍數的總和是78

我解決了這個問題,此代碼:

defmodule SumOfMultiples do 
    @doc """ 
    Adds up all numbers from 1 to a given end number that are multiples of the factors provided. 
    """ 
    @spec to(non_neg_integer, [non_neg_integer]) :: non_neg_integer 
    def to(limit, factors) do 
    1..limit - 1 
    |> Enum.reduce([], fn(x,acc) -> 
     is_multiple? = factors 
     |> Enum.map(&(rem(x,&1) === 0)) 
     |> Enum.any? 
     if is_multiple?, do: [x|acc], else: acc 
    end) 
    |> Enum.sum 
    end 
end 

但我最近發現過程中藥劑所以想解決p多進程問題:

defmodule SumOfMultiples do 
    @doc """ 
    Adds up all numbers from 1 to a given end number that are multiples of the factors provided. 
    """ 
    @spec to(non_neg_integer, [non_neg_integer]) :: non_neg_integer 
    def to(limit, factors) do 
    me = self 
    1..limit - 1 
    |> Enum.map(fn(x) -> 
     spawn(SumOfMultiples, :is_multiple?, [x, factors, me]) 
    end) 
    |> Enum.map(fn(_) -> 
     receive do 
     {true, n} -> n 
     {false, n} -> 0 
     end 
    end) 
    |> Enum.sum 
    end 

    def is_multiple?(n, factors, pid) do 

    flag = factors 
    |> Enum.map(&(rem(n,&1) === 0)) 
    |> Enum.any? 
    send pid, {flag, n} 
    end 
end 

我使用並行映射來解決這個問題。是有效的,但事情是比單進程版本低4倍的性能。

如果有人能解釋我爲什麼會出現這種性能差異,這將是非常有用的,因爲我已計劃用多進程版本解決剩餘的exerciseism.io問題。

謝謝!

---------------------更新---------------------

謝謝你的回答!事實證明你是對的!感謝您的解釋!這裏是我的新實現:

defmodule SumOfMultiples do 
    @doc """ 
    Adds up all numbers from 1 to a given end number that are multiples of the factors provided. 
    """ 
    @spec to(non_neg_integer, [non_neg_integer]) :: non_neg_integer 
    def to(limit, factors) do 
    me = self 
    1..limit - 1 
    |> Stream.chunk(200, 200, Stream.cycle([0])) 
    |> Enum.map(fn(x) -> 
     spawn(SumOfMultiples, :do_to, [x, factors, me]) 
    end) 
    |> Enum.map(fn(_) -> 
     receive do 
     n -> n 
     end 
    end) 
    |> Enum.sum 
    end 

    def do_to(list, factors, pid) do 
    result = list 
     |> Enum.reduce([], fn(x,acc) -> 
     is_multiple? = factors 
     |> Enum.map(&(rem(x,&1) === 0)) 
     |> Enum.any? 
     if is_multiple?, do: [x|acc], else: acc 
    end) 
    |> Enum.sum 
    send pid, result 
    end 

end 

最大似乎是200.現在我比〜單程40%快!好極了 !

+0

Stream.chunk_by()是你的朋友 – GavinBrelstaff

回答

3

問題是你把工作分得太細。開始一個新流程的開銷比並行執行這個流程的收益要大。一個進程的一次運行(直到它將被VM重新計劃)被給出2000次減少,這或多或少對應於2000次函數調用。要看到並行化的真正好處,您應該嘗試將這種大小的塊拆分,以獲得並行化工作的最大收益。

+0

我認爲限制是2K減少。 –

+0

你說得對。更新 – michalmuskala