Powered by AppSignal & Oban Pro

闘魂Elixir ── Calorie Counting

calorie_counting.livemd

闘魂Elixir ── Calorie Counting

Mix.install([{:benchee, "~> 1.1"}])
:ok

About

Answers

input = """
1000
2000
3000

4000

5000
6000

7000
8000
9000

10000
"""
"1000\n2000\n3000\n\n4000\n\n5000\n6000\n\n7000\n8000\n9000\n\n10000\n"
torifukukaiou = fn input ->
  f = fn
    "" -> ""
    s -> String.to_integer(s)
  end

  sum = fn
    [""] -> 0
    list -> Enum.sum(list)
  end

  input
  |> String.split("\n")
  |> Enum.map(f)
  |> Enum.chunk_by(&(&1 == ""))
  |> Enum.map(sum)
  |> Enum.max()
end

torifukukaiou.(input)
24000
mnishiguchi = fn input ->
  input
  |> String.split("\n\n", trim: true)
  |> Enum.map(fn numbers_str ->
    numbers_str
    |> String.split(~r/\d+/, include_captures: true, trim: true)
    |> Enum.filter(&(&1 =~ ~r/\d+/))
    |> Enum.map(&String.to_integer/1)
    |> Enum.sum()
  end)
  |> Enum.max()
end

mnishiguchi.(input)
24000
defmodule MNishiguchi.Answer2 do
  def parse(binary, digits \\ [], chunk \\ [], max \\ 0)

  def parse(<<"0"::utf8, rest::binary>>, digits, chunk, max),
    do: parse(<<rest::binary>>, [0 | digits], chunk, max)

  def parse(<<"1"::utf8, rest::binary>>, digits, chunk, max),
    do: parse(<<rest::binary>>, [1 | digits], chunk, max)

  def parse(<<"2"::utf8, rest::binary>>, digits, chunk, max),
    do: parse(<<rest::binary>>, [2 | digits], chunk, max)

  def parse(<<"3"::utf8, rest::binary>>, digits, chunk, max),
    do: parse(<<rest::binary>>, [3 | digits], chunk, max)

  def parse(<<"4"::utf8, rest::binary>>, digits, chunk, max),
    do: parse(<<rest::binary>>, [4 | digits], chunk, max)

  def parse(<<"5"::utf8, rest::binary>>, digits, chunk, max),
    do: parse(<<rest::binary>>, [5 | digits], chunk, max)

  def parse(<<"6"::utf8, rest::binary>>, digits, chunk, max),
    do: parse(<<rest::binary>>, [6 | digits], chunk, max)

  def parse(<<"7"::utf8, rest::binary>>, digits, chunk, max),
    do: parse(<<rest::binary>>, [7 | digits], chunk, max)

  def parse(<<"8"::utf8, rest::binary>>, digits, chunk, max),
    do: parse(<<rest::binary>>, [8 | digits], chunk, max)

  def parse(<<"9"::utf8, rest::binary>>, digits, chunk, max),
    do: parse(<<rest::binary>>, [9 | digits], chunk, max)

  # end of chunk
  # * clear digits
  # * clear chunk
  # * update max
  def parse(<<"\n\n"::utf8, rest::binary>>, digits, chunk, max) do
    parsed_number = digits |> Enum.reverse() |> List.flatten() |> Integer.undigits()
    chunk_sum = [parsed_number | chunk] |> List.flatten() |> Enum.sum()
    parse(<<rest::binary>>, [], [], Enum.max([max, chunk_sum]))
  end

  # end of number
  # * clear digits
  # * update chunk
  def parse(<<"\n"::utf8, rest::binary>>, digits, chunk, max) do
    parsed_number = digits |> Enum.reverse() |> List.flatten() |> Integer.undigits()
    parse(<<rest::binary>>, [], [parsed_number | chunk], max)
  end

  # base case
  def parse("", _digits, _chunk, max), do: max
end

MNishiguchi.Answer2.parse(input)
24000

Bench

defmodule AdventCode1.Banch do
  def gen_line() do
    "#{:rand.uniform(10000)}"
  end

  def gen_lines(n) do
    Stream.unfold(:rand.uniform(n), fn
      0 -> nil
      n -> {gen_line(), n - 1}
    end)
    |> Enum.join("\n")
  end

  def gen_input(n) do
    Stream.unfold(:rand.uniform(n), fn
      0 -> nil
      m -> {gen_lines(n), m - 1}
    end)
    |> Enum.join("\n\n")
  end
end
{:module, AdventCode1.Banch, <<70, 79, 82, 49, 0, 0, 10, ...>>, {:gen_input, 1}}
Benchee.run(
  %{
    "torifukukaiou" => fn input -> torifukukaiou.(input) end,
    "mnishiguchi_regex" => fn input -> mnishiguchi.(input) end,
    "mnishiguchi_recursive" => fn input -> MNishiguchi.Answer2.parse(input) end
  },
  inputs: %{
    "sample1" => :sample1,
    "10" => 10,
    "100" => 100,
    "1000" => 1000
  },
  before_each: fn input ->
    case input do
      :sample1 ->
        """
        1000
        2000
        3000

        4000

        5000
        6000

        7000
        8000
        9000

        10000
        """

      n ->
        AdventCode1.Banch.gen_input(n)
    end
  end
)

:ok
Operating System: macOS
CPU Information: Apple M1 Pro
Number of Available Cores: 10
Available memory: 32 GB
Elixir 1.14.2
Erlang 25.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: 10, 100, 1000, sample1
Estimated total run time: 1.40 min

Benchmarking mnishiguchi_recursive with input 10 ...
Benchmarking mnishiguchi_recursive with input 100 ...
Benchmarking mnishiguchi_recursive with input 1000 ...
Benchmarking mnishiguchi_recursive with input sample1 ...
Benchmarking mnishiguchi_regex with input 10 ...
Benchmarking mnishiguchi_regex with input 100 ...
Benchmarking mnishiguchi_regex with input 1000 ...
Benchmarking mnishiguchi_regex with input sample1 ...
Benchmarking torifukukaiou with input 10 ...
Benchmarking torifukukaiou with input 100 ...
Benchmarking torifukukaiou with input 1000 ...
Benchmarking torifukukaiou with input sample1 ...

##### With input 10 #####
Name                            ips        average  deviation         median         99th %
mnishiguchi_recursive      131.20 K        7.62 μs    ±62.93%        7.50 μs       17.46 μs
torifukukaiou               24.54 K       40.75 μs    ±56.09%       38.92 μs       92.17 μs
mnishiguchi_regex            3.98 K      251.20 μs    ±56.14%      244.73 μs      552.60 μs

Comparison:
mnishiguchi_recursive      131.20 K
torifukukaiou               24.54 K - 5.35x slower +33.13 μs
mnishiguchi_regex            3.98 K - 32.96x slower +243.58 μs

##### With input 100 #####
Name                            ips        average  deviation         median         99th %
mnishiguchi_recursive       1820.66        0.55 ms    ±57.79%        0.54 ms        1.15 ms
mnishiguchi_regex             52.88       18.91 ms    ±59.66%       18.34 ms       40.60 ms
torifukukaiou                 34.02       29.40 ms    ±88.48%       22.59 ms       90.64 ms

Comparison:
mnishiguchi_recursive       1820.66
mnishiguchi_regex             52.88 - 34.43x slower +18.36 ms
torifukukaiou                 34.02 - 53.52x slower +28.85 ms

##### With input 1000 #####
Name                            ips        average  deviation         median         99th %
mnishiguchi_recursive         13.76       0.0727 s    ±46.23%       0.0813 s        0.127 s
mnishiguchi_regex              0.52         1.92 s    ±80.69%         1.86 s         3.81 s
torifukukaiou                0.0271        36.95 s     ±0.00%        36.95 s        36.95 s

Comparison:
mnishiguchi_recursive         13.76
mnishiguchi_regex              0.52 - 26.34x slower +1.84 s
torifukukaiou                0.0271 - 508.24x slower +36.87 s

##### With input sample1 #####
Name                            ips        average  deviation         median         99th %
mnishiguchi_recursive      415.45 K        2.41 μs   ±295.09%        2.33 μs        3.25 μs
torifukukaiou               57.54 K       17.38 μs    ±25.09%       16.38 μs       37.29 μs
mnishiguchi_regex           10.30 K       97.13 μs    ±10.55%       97.29 μs      125.37 μs

Comparison:
mnishiguchi_recursive      415.45 K
torifukukaiou               57.54 K - 7.22x slower +14.97 μs
mnishiguchi_regex           10.30 K - 40.35x slower +94.72 μs
:ok