2016-07-08 3 views
0

私は現在、ウェブサイトexerciseism.ioでエリクシルを学習しています。 私は問題である「倍数の和」を持っていた:マルチプロセスの効率が悪い

、我々は3、5、6を得るまでが、3または5のいずれかの 倍数である20を含んでいない、我々はすべての自然数を一覧表示した場合と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 

最大は、1つのプロセスよりも40%高速です!わーい !

+0

Stream.chunk_by()はあなたの友だち – GavinBrelstaff

答えて

3

問題は、あまりにも細い作品を分割することです。新しいプロセスを開始するオーバーヘッドは、これを並行して行うよりも大きかった。プロセスの1回の実行(VMによって再スケジュールされるまで)は2000回の削減が行われ、2000回の関数呼び出しに相当します。並列化の実際の利点を確認するには、作業を並列化することで最大の利益を得るために、そのサイズの塊で作業を分割しようとする必要があります。

+0

私は制限が2K削減であると思っていました。 –

+0

あなたは正しいです。更新しました – michalmuskala

関連する問題