多进程效率较低

时间:2016-07-08 10:35:59

标签: elixir

我目前通过网站exercism.io学习Elixir。 我遇到了问题" Sum Of Multiples"这是:

  

如果我们列出所有自然数字,但不包括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

但我最近在Elixir中发现了进程,所以我想用多进程解决问题:

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倍。

如果有人能解释为什么会有这样的性能差异,那将是非常有用的,因为我已计划用多进程版本来解决剩余的exercism.io问题。

谢谢!

--------------------- update ---------------------

感谢您的回答!事实证明你是对的!谢谢你的解释!这是我的新实现:

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%! YAY!

1 个答案:

答案 0 :(得分:3)

问题在于你将工作分得太薄了。启动新流程的开销大于并行执行此操作的收益。一次进程(直到它将由VM重新安排)被给予2000次减少,这或多或少地对应于2000次函数调用。要看到并行化的真正好处,您应该尝试将该工作拆分为该大小的块,以便从并行化工作中获得最大收益。