【问题标题】:Multiprocess is less efficient多进程效率较低
【发布时间】:2016-11-10 21:35:40
【问题描述】:

我目前通过网站 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

但我最近在 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 问题的其余部分。

谢谢!

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

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

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

【问题讨论】:

  • Stream.chunk_by() 是你的朋友

标签: elixir


【解决方案1】:

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

【讨论】:

  • 我以为限制是减少 2K。
猜你喜欢
  • 1970-01-01
  • 2017-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-28
相关资源
最近更新 更多