5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ElixirAdvent Calendar 2024

Day 20

Elixirでフィボナッチ数列をいろいろ書いてみた Part. 2

Last updated at Posted at 2024-12-29

@mod_poppoさんのHaskellでフィボナッチ数列 〜Haskellで非実用的なコードを書いて悦に入るのはやめろ〜にインスピレーションを得て,Elixirでフィボナッチ数列をいろいろ書いてみるシリーズ記事の第2弾です.

フィボナッチ数列シリーズ

Stream版

fib_benchee.exs
Mix.install([:benchee])

defmodule Fibonacci.Stream do
  def of(n) do
    Stream.unfold([], fn
      [] -> {0, [0]}
      [0] -> {1, [1, 0]}
      [m, n] -> {m + n, [m + n, m]}
    end)
    |> Enum.at(n)
  end
end

defmodule Fibonacci.Memoization do
  use Agent

  def start_link(initial_value) when is_map(initial_value) do
    Agent.start_link(fn -> initial_value end, name: __MODULE__)
  end

  def of(n) do
    case Agent.get(__MODULE__, & &1) do
      map when is_map_key(map, n) -> 
        Map.get(map, n)

      _ -> 
        result = 
          case n do
            0 -> 0
            1 -> 1
            n -> of(n - 2) + of(n - 1)
          end

        Agent.update(__MODULE__, &(Map.put(&1, n, result)))
        result
    end
  end
end

defmodule Fibonacci do
  def of(n)
  def of(0), do: 0
  def of(1), do: 1
  def of(n), do: of(n - 2) + of(n - 1)
end

Benchee.run(
  %{
    "Very Slow Fibonacci" => fn input -> Enum.reduce(1..100, fn _, _ -> Fibonacci.of(input) end) end,
    "Fibonacci with Memoization" => 
      {
        fn input -> Enum.reduce(1..100, fn _, _ -> Fibonacci.Memoization.of(input) end) end,
        before_scenario: fn input -> 
          Fibonacci.Memoization.start_link(%{})
          input
        end
      },
    "Fibonacci by Stream" => fn input -> Enum.reduce(1..100, fn _, _ -> Fibonacci.Stream.of(input) end) end
  },
  inputs: %{
    "1" => 1,
    "2" => 2,
    "3" => 3,
    "4" => 4,
    "5" => 5,
    "6" => 6,
    "7" => 7,
    "8" => 8,
    "9" => 9
  }
)

実行結果

Operating System: macOS
CPU Information: Apple M3 Max
Number of Available Cores: 16
Available memory: 128 GB
Elixir 1.18.1
Erlang 27.2
JIT enabled: true

Benchmark suite executing with the following configuration:
warmup: 2 s
time: 5 s
memory time: 0 ns
reduction time: 0 ns
parallel: 1
inputs: 1, 2, 3, 4, 5, 6, 7, 8, 9
Estimated total run time: 3 min 9 s

Benchmarking Fibonacci by Stream with input 1 ...
Benchmarking Fibonacci by Stream with input 2 ...
Benchmarking Fibonacci by Stream with input 3 ...
Benchmarking Fibonacci by Stream with input 4 ...
Benchmarking Fibonacci by Stream with input 5 ...
Benchmarking Fibonacci by Stream with input 6 ...
Benchmarking Fibonacci by Stream with input 7 ...
Benchmarking Fibonacci by Stream with input 8 ...
Benchmarking Fibonacci by Stream with input 9 ...
Benchmarking Fibonacci with Memoization with input 1 ...
Benchmarking Fibonacci with Memoization with input 2 ...
Benchmarking Fibonacci with Memoization with input 3 ...
Benchmarking Fibonacci with Memoization with input 4 ...
Benchmarking Fibonacci with Memoization with input 5 ...
Benchmarking Fibonacci with Memoization with input 6 ...
Benchmarking Fibonacci with Memoization with input 7 ...
Benchmarking Fibonacci with Memoization with input 8 ...
Benchmarking Fibonacci with Memoization with input 9 ...
Benchmarking Very Slow Fibonacci with input 1 ...
Benchmarking Very Slow Fibonacci with input 2 ...
Benchmarking Very Slow Fibonacci with input 3 ...
Benchmarking Very Slow Fibonacci with input 4 ...
Benchmarking Very Slow Fibonacci with input 5 ...
Benchmarking Very Slow Fibonacci with input 6 ...
Benchmarking Very Slow Fibonacci with input 7 ...
Benchmarking Very Slow Fibonacci with input 8 ...
Benchmarking Very Slow Fibonacci with input 9 ...
Calculating statistics...
Formatting results...

##### With input 1 #####
Name                                 ips        average  deviation         median         99th %
Very Slow Fibonacci            1404.29 K        0.71 μs  ±1952.22%        0.67 μs        0.92 μs
Fibonacci by Stream             240.19 K        4.16 μs   ±171.21%        3.92 μs       11.92 μs
Fibonacci with Memoization       12.24 K       81.71 μs     ±4.21%       81.04 μs       95.97 μs

Comparison: 
Very Slow Fibonacci            1404.29 K
Fibonacci by Stream             240.19 K - 5.85x slower +3.45 μs
Fibonacci with Memoization       12.24 K - 114.74x slower +81.00 μs

##### With input 2 #####
Name                                 ips        average  deviation         median         99th %
Very Slow Fibonacci             931.64 K        1.07 μs  ±1275.82%           1 μs        1.42 μs
Fibonacci by Stream             208.66 K        4.79 μs   ±101.32%        4.46 μs       12.92 μs
Fibonacci with Memoization       12.11 K       82.55 μs     ±5.75%       81.96 μs       98.36 μs

Comparison: 
Very Slow Fibonacci             931.64 K
Fibonacci by Stream             208.66 K - 4.46x slower +3.72 μs
Fibonacci with Memoization       12.11 K - 76.91x slower +81.48 μs

##### With input 3 #####
Name                                 ips        average  deviation         median         99th %
Very Slow Fibonacci             690.37 K        1.45 μs   ±702.67%        1.38 μs        1.92 μs
Fibonacci by Stream             186.12 K        5.37 μs    ±99.65%        5.04 μs       13.75 μs
Fibonacci with Memoization       11.96 K       83.60 μs     ±3.94%       82.75 μs       98.42 μs

Comparison: 
Very Slow Fibonacci             690.37 K
Fibonacci by Stream             186.12 K - 3.71x slower +3.92 μs
Fibonacci with Memoization       11.96 K - 57.72x slower +82.15 μs

##### With input 4 #####
Name                                 ips        average  deviation         median         99th %
Very Slow Fibonacci             459.56 K        2.18 μs   ±484.04%        2.08 μs        2.88 μs
Fibonacci by Stream             168.18 K        5.95 μs    ±77.67%        5.75 μs       12.50 μs
Fibonacci with Memoization       12.12 K       82.49 μs     ±4.87%       82.21 μs       98.42 μs

Comparison: 
Very Slow Fibonacci             459.56 K
Fibonacci by Stream             168.18 K - 2.73x slower +3.77 μs
Fibonacci with Memoization       12.12 K - 37.91x slower +80.31 μs

##### With input 5 #####
Name                                 ips        average  deviation         median         99th %
Very Slow Fibonacci             306.73 K        3.26 μs   ±252.74%        3.13 μs        4.38 μs
Fibonacci by Stream             153.15 K        6.53 μs    ±96.25%        6.21 μs       15.08 μs
Fibonacci with Memoization       11.96 K       83.61 μs     ±5.60%       82.75 μs       99.54 μs

Comparison: 
Very Slow Fibonacci             306.73 K
Fibonacci by Stream             153.15 K - 2.00x slower +3.27 μs
Fibonacci with Memoization       11.96 K - 25.65x slower +80.35 μs

##### With input 6 #####
Name                                 ips        average  deviation         median         99th %
Very Slow Fibonacci             194.14 K        5.15 μs   ±120.03%           5 μs        6.92 μs
Fibonacci by Stream             139.24 K        7.18 μs    ±54.31%        6.83 μs       15.79 μs
Fibonacci with Memoization       12.01 K       83.26 μs     ±4.48%       82.33 μs       98.75 μs

Comparison: 
Very Slow Fibonacci             194.14 K
Fibonacci by Stream             139.24 K - 1.39x slower +2.03 μs
Fibonacci with Memoization       12.01 K - 16.16x slower +78.10 μs

##### With input 7 #####
Name                                 ips        average  deviation         median         99th %
Fibonacci by Stream             128.01 K        7.81 μs    ±56.62%        7.50 μs       16.42 μs
Very Slow Fibonacci             123.90 K        8.07 μs    ±52.85%        7.88 μs       10.92 μs
Fibonacci with Memoization       12.06 K       82.91 μs     ±4.31%       82.08 μs          98 μs

Comparison: 
Fibonacci by Stream             128.01 K
Very Slow Fibonacci             123.90 K - 1.03x slower +0.26 μs
Fibonacci with Memoization       12.06 K - 10.61x slower +75.10 μs

##### With input 8 #####
Name                                 ips        average  deviation         median         99th %
Fibonacci by Stream             115.56 K        8.65 μs    ±54.84%        8.33 μs       17.33 μs
Very Slow Fibonacci              77.14 K       12.96 μs    ±15.92%       12.63 μs       17.54 μs
Fibonacci with Memoization       12.09 K       82.71 μs     ±5.32%       81.71 μs       99.13 μs

Comparison: 
Fibonacci by Stream             115.56 K
Very Slow Fibonacci              77.14 K - 1.50x slower +4.31 μs
Fibonacci with Memoization       12.09 K - 9.56x slower +74.06 μs

##### With input 9 #####
Name                                 ips        average  deviation         median         99th %
Fibonacci by Stream             107.45 K        9.31 μs    ±34.49%        9.13 μs       15.25 μs
Very Slow Fibonacci              47.55 K       21.03 μs     ±9.72%       20.63 μs       28.33 μs
Fibonacci with Memoization       12.05 K       83.00 μs     ±5.11%       82.13 μs       99.08 μs

Comparison: 
Fibonacci by Stream             107.45 K
Very Slow Fibonacci              47.55 K - 2.26x slower +11.72 μs
Fibonacci with Memoization       12.05 K - 8.92x slower +73.70 μs
1 2 3 4 5 6 7 8 9
Very Slow Fibonacci 710 1070 1450 2180 3260 5150 8070 12960 21030
Fibonacci by Stream 4160 4790 5370 5950 6530 7180 7810 8650 9310
Fibonacci with Memoization 81710 82550 83600 82490 83610 83260 82910 82710 83000

Fibonacci by Stream

7以上でFibonacci by Streamが高速になります.Fibonacci with Memoization でStream用いても良さそうですね.トレンドラインを見ると, $y = 638.17x + 3448.1$ なので,120以上だとFibonacci with MemoizationがFibonacci by Streamより高速になる可能性があります.

StreamつきMemoization

fib_benchee.exs
Mix.install([:benchee])

defmodule Fibonacci.Stream do
  def of(n) do
    Stream.unfold([], fn
      [] -> {0, [0]}
      [0] -> {1, [1, 0]}
      [m, n] -> {m + n, [m + n, m]}
    end)
    |> Enum.at(n)
  end
end

defmodule Fibonacci.Memoization.Stream do
  use Agent

  def start_link(initial_value) when is_map(initial_value) do
    Agent.start_link(fn -> initial_value end, name: __MODULE__)
  end

  def of(n) do
    case Agent.get(__MODULE__, & &1) do
      map when is_map_key(map, n) -> 
        Map.get(map, n)

      _ -> 
        result = 
          Stream.unfold([], fn
            [] -> {0, [0]}
            [0] -> {1, [1, 0]}
            [m, n] -> {m + n, [m + n, m]}
          end)
          |> Enum.at(n)

        Agent.update(__MODULE__, &(Map.put(&1, n, result)))
        result
    end
  end
end

defmodule Fibonacci.Memoization do
  use Agent

  def start_link(initial_value) when is_map(initial_value) do
    Agent.start_link(fn -> initial_value end, name: __MODULE__)
  end

  def of(n) do
    case Agent.get(__MODULE__, & &1) do
      map when is_map_key(map, n) -> 
        Map.get(map, n)

      _ -> 
        result = 
          case n do
            0 -> 0
            1 -> 1
            n -> of(n - 2) + of(n - 1)
          end

        Agent.update(__MODULE__, &(Map.put(&1, n, result)))
        result
    end
  end
end

Benchee.run(
  %{
    "Fibonacci with Memoization" => 
      {
        fn input -> Enum.reduce(1..100, fn _, _ -> Fibonacci.Memoization.of(input) end) end,
        before_scenario: fn input -> 
          Fibonacci.Memoization.start_link(%{})
          input
        end
      },
    "Fibonacci with Memoization and Stream" => 
      {
        fn input -> Enum.reduce(1..100, fn _, _ -> Fibonacci.Memoization.Stream.of(input) end) end,
        before_scenario: fn input -> 
          Fibonacci.Memoization.Stream.start_link(%{})
          input
        end
      },
    "Fibonacci by Stream" => fn input -> Enum.reduce(1..100, fn _, _ -> Fibonacci.Stream.of(input) end) end
  },
  inputs: %{
    "120" => 120
  }
)

実行結果

Operating System: macOS
CPU Information: Apple M3 Max
Number of Available Cores: 16
Available memory: 128 GB
Elixir 1.18.1
Erlang 27.2
JIT enabled: true

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

Benchmarking Fibonacci by Stream with input 120 ...
Benchmarking Fibonacci with Memoization with input 120 ...
Benchmarking Fibonacci with Memoization and Stream with input 120 ...
Calculating statistics...
Formatting results...

##### With input 120 #####
Name                                            ips        average  deviation         median         99th %
Fibonacci with Memoization and Stream       12.72 K       78.60 μs     ±7.37%       76.58 μs       98.04 μs
Fibonacci by Stream                          8.06 K      124.04 μs     ±6.33%      121.79 μs      153.08 μs
Fibonacci with Memoization                   5.63 K      177.74 μs     ±6.33%      175.13 μs      210.29 μs

Comparison: 
Fibonacci with Memoization and Stream       12.72 K
Fibonacci by Stream                          8.06 K - 1.58x slower +45.44 μs
Fibonacci with Memoization                   5.63 K - 2.26x slower +99.15 μs

目論見通り,120の時に,Fibonacci with Memoization and Stream が Fibonacci by Stream よりも高速になりました.

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?