使用 Flow,我尝试查看是否可以充分利用 CPU 内核。

处理内容是用随机数求周长比(我想不出合适的处理内容,所以很简单的问题)

随机数的产生、判断、计数组成一个三级管道。我使用 Enum/Flow 比较了每个进程。

结果

函数名 随机数发生器 判断 数数 执行时间处理时间
calc_pi_with_enum 枚举 枚举 枚举 5.065
calc_pi_with_flow 流动 流动 枚举 0.690
calc_pi_with_flow_reduce 流动 流动 流动 0.475

5.065/0.475 = 10.66

快 10 倍

执行结果详情

$ mix run bench/cutorial003_bench.exs 
Operating System: Linux
CPU Information: AMD Ryzen 7 PRO 5750G with Radeon Graphics
Number of Available Cores: 16
Available memory: 15.29 GB
Elixir 1.13.4
Erlang 24.3.4.2

Benchmark suite executing with the following configuration:
warmup: 2 s
time: 5 s
memory time: 0 ns
reduction time: 0 ns
parallel: 1
inputs: none specified
Estimated total run time: 21 s

Benchmarking Enum ...
Benchmarking Flow (count) ...
Benchmarking Flow (reduce) ...

Name                    ips        average  deviation         median         99th %
Flow (reduce)          2.10      475.37 ms     ±3.28%      474.21 ms      500.76 ms
Flow (count)           1.45      690.81 ms     ±4.28%      686.25 ms      742.75 ms
Enum                  0.197     5065.95 ms     ±0.00%     5065.95 ms     5065.95 ms

Comparison: 
Flow (reduce)          2.10
Flow (count)           1.45 - 1.45x slower +215.44 ms
Enum                  0.197 - 10.66x slower +4590.58 ms

测试程序

教程003.ex
defmodule Tutorial003 do
  def gen_rand(_) do
    {:rand.uniform(), :rand.uniform()}
  end

  def calc_pi_with_enum() do
    hit =
      1..10_000_000
      |> Enum.map(&gen_rand(&1))
      |> Enum.map(fn {x, y} -> x * x + y * y < 1 end)
      |> Enum.count(fn bool -> bool end)

    hit / 10_000_000 * 4
  end

  def calc_pi_with_flow() do
    hit =
      1..10_000_000
      |> Flow.from_enumerable()
      |> Flow.map(&gen_rand(&1))
      |> Flow.map(fn {x, y} -> x * x + y * y < 1 end)
      |> Enum.count(fn bool -> bool end)

    hit / 10_000_000 * 4
  end

  def calc_pi_with_flow_reduce() do
    hit =
      1..10_000_000
      |> Flow.from_enumerable()
      |> Flow.map(&gen_rand(&1))
      |> Flow.map(fn {x, y} -> x * x + y * y < 1 end)
      |> Flow.reduce(fn -> 0 end, fn entry, acc ->
        if(entry, do: acc + 1, else: acc)
      end)
      |> Flow.on_trigger(fn count -> {[count], count} end)
      |> Enum.sum()

    hit / 10_000_000 * 4
  end
end
tutorial003_bench.exs
Benchee.run(
  %{
    "Enum" => fn _ -> Tutorial003.calc_pi_with_enum() end,
    "Flow (count)" => fn _ -> Tutorial003.calc_pi_with_flow() end,
    "Flow (reduce)" => fn _ -> Tutorial003.calc_pi_with_flow_reduce() end
  },
  before_each: fn _ -> :rand.seed(:exsss, {100, 101, 102}) end
)

CPU 利用率

执行期间的 CPU 利用率。
由于三个进程是依次执行的,所以我用红框直观地标出了它们切换的时间。

Elixir FlowでRyzenの全コアぶん回したら10倍速かった

概括

枚举/流速比较

  • 比 Enum 和 Flow 快 10 倍
  • 我们能够确认,通过使用 Flow,CPU 能够以 80-90% 的速度运行。
  • 因为在 Enum 的情况下是 10 到 15%,所以它似乎是一个核心 (1/8) 的处理能力。
  • 在 8 个内核工作的情况下,流速度足够快。
  • 之所以比核数快,可能是线程的影响(8核16线程的CPU),或者Enum的情况下,可能有1个CPU不行的缺点。

关于流量

  • 如果您使用 Flow,您可以将处理划分并以一种很好的方式分配给每个 CPU。
  • Flow.map() 易于使用。
  • 单独使用 Flow.map() 相当快,但如果您使用 Flow.reduce 来整合结果,它可能会更快。
    * 与@zacky1972 的结果一样,加速程度似乎因 CPU 类型和版本而异。

原创声明:本文系作者授权爱码网发表,未经许可,不得转载;

原文地址:https://www.likecs.com/show-308623900.html

相关文章:

  • 2021-12-16
  • 2022-02-27
  • 2021-09-26
  • 2021-11-04
  • 2022-01-31
  • 2022-12-23
  • 2022-12-23
  • 2021-12-14
猜你喜欢
  • 2021-07-18
  • 2021-12-27
  • 2021-08-28
  • 2021-09-03
  • 2021-09-27
  • 2022-12-23
相关资源
相似解决方案