Skip to content

Instantly share code, notes, and snippets.

@asakura
Last active June 8, 2023 07:13
Show Gist options
  • Save asakura/8aabd3fed8859b36949f2df9d02e685a to your computer and use it in GitHub Desktop.
Save asakura/8aabd3fed8859b36949f2df9d02e685a to your computer and use it in GitHub Desktop.

Bandit.HTTP1.Adapter.read/5 benchmark

Mix.install([
  {:benchee_dsl, "~> 0.5"}
  # {:benchee_markdown, "~> 0.3"}
])

Section

defmodule ThousandIsland.Socket do
  def recv(_socket, _read_size, _read_timeout) do
    {:ok, unquote(String.duplicate("a", 1024))}
  end
end
defmodule Read do
  def read(socket, to_read, opts \\ [], _bytes_read \\ 0, already_read \\ []) do
    read_size = min(to_read, Keyword.get(opts, :read_length, 1_000_000))
    read_timeout = Keyword.get(opts, :read_timeout)
    remaining_bytes = max(to_read, 1)
    do_read(socket, read_size, read_timeout, to_read, already_read)
  end

  defp do_read(socket, read_size, read_timeout, remaining_bytes, already_read)
       when remaining_bytes > 0 do
    case ThousandIsland.Socket.recv(socket, read_size, read_timeout) do
      {:ok, chunk} ->
        remaining_bytes = remaining_bytes - byte_size(chunk)
        read_size = min(read_size, remaining_bytes)

        do_read(socket, read_size, read_timeout, remaining_bytes, [
          already_read | chunk
        ])

      err ->
        err
    end
  end

  defp do_read(_socket, _read_size, _read_timeout, _remaining_bytes, already_read),
    do: {:ok, already_read}
end
defmodule ReadWith do
  def read(socket, to_read, opts \\ [], bytes_read \\ 0, already_read \\ []) do
    read_size = min(to_read, Keyword.get(opts, :read_length, 1_000_000))
    read_timeout = Keyword.get(opts, :read_timeout)

    with {:ok, chunk} <- ThousandIsland.Socket.recv(socket, read_size, read_timeout) do
      remaining_bytes = to_read - byte_size(chunk)
      bytes_read = bytes_read + byte_size(chunk)

      if remaining_bytes > 0 do
        read(socket, remaining_bytes, opts, bytes_read, [already_read | chunk])
      else
        {:ok, [already_read | chunk]}
      end
    end
  end
end
{:module, name, _binary, _bindings} =
  defmodule ReadBenchmark do
    use BencheeDsl.Benchmark

    config(
      warmup: 1,
      time: 3,
      memory_time: 1,
      reduction_time: 1,
      pre_check: true,
      print: [configuration: false]
    )

    inputs(%{
      "Micro" => {10240, read_length: 1024, read_timeout: 1000},
      "Small" => {102_400, read_length: 1024, read_timeout: 1000},
      "Bigger" => {1_024_000, read_length: 1024, read_timeout: 1000}
    })

    job(read_with({to_read, opts})) do
      ReadWith.read(_socket = nil, to_read, opts)
    end

    job(read({to_read, opts})) do
      Read.read(_socket = nil, to_read, opts)
    end
  end

BencheeDsl.Livebook.benchee_config() |> name.run() |> BencheeDsl.Livebook.render()
Benchmarking read with input Bigger ...
Benchmarking read with input Micro ...
Benchmarking read with input Small ...
Benchmarking read_with with input Bigger ...
Benchmarking read_with with input Micro ...
Benchmarking read_with with input Small ...
##### With input Bigger #####
Name ips average deviation median 99th %
read 14.48 K 69.05 μs ±16.52% 66.80 μs 88.02 μs
read_with 5.87 K 170.47 μs ±11.91% 163.60 μs 263.02 μs
Comparison:
read 14.48 K
read_with 5.87 K - 2.47x slower +101.42 μs
Memory usage statistics:
Name Memory usage
read 15.65 KB
read_with 15.65 KB - 1.00x memory usage +0 KB
**All measurements for memory usage were the same**
Reduction count statistics:
Name Reduction count
read 5.62 K
read_with 12.50 K - 2.22x reduction count +6.88 K
**All measurements for reduction count were the same**
##### With input Micro #####
Name ips average deviation median 99th %
read 792.89 K 1.26 μs ±1345.57% 1.18 μs 2.42 μs
read_with 452.71 K 2.21 μs ±1583.25% 1.98 μs 3.94 μs
Comparison:
read 792.89 K
read_with 452.71 K - 1.75x slower +0.95 μs
Memory usage statistics:
Name Memory usage
read 184 B
read_with 184 B - 1.00x memory usage +0 B
**All measurements for memory usage were the same**
Reduction count statistics:
Name Reduction count
read 63
read_with 122 - 1.94x reduction count +59
**All measurements for reduction count were the same**
##### With input Small #####
Name ips average deviation median 99th %
read 132.48 K 7.55 μs ±147.68% 7.19 μs 10.85 μs
read_with 57.07 K 17.52 μs ±43.68% 16.60 μs 29.46 μs
Comparison:
read 132.48 K
read_with 57.07 K - 2.32x slower +9.97 μs
Memory usage statistics:
Name Memory usage
read 1.59 KB
read_with 1.59 KB - 1.00x memory usage +0 KB
**All measurements for memory usage were the same**
Reduction count statistics:
Name Reduction count
read 0.53 K
read_with 1.22 K - 2.30x reduction count +0.69 K
**All measurements for reduction count were the same**
{:function, :do_read, 5, 11,
[
{:line, 1},
{:label, 10},
{:func_info, {:atom, Test}, {:atom, :do_read}, 5},
{:label, 11},
{:test, :is_lt, {:f, 13}, [integer: 0, x: 3]},
{:allocate, 6, 5},
{:init_yregs, {:list, [y: 0]}},
{:move, {:x, 4}, {:y, 1}},
{:move, {:x, 3}, {:y, 2}},
{:move, {:x, 2}, {:y, 3}},
{:move, {:x, 1}, {:y, 4}},
{:move, {:x, 0}, {:y, 5}},
{:line, 2},
{:call_ext, 3, {:extfunc, ThousandIsland.Socket, :recv, 3}},
{:test, :is_tagged_tuple, {:f, 12}, [{:x, 0}, 2, {:atom, :ok}]},
{:get_tuple_element, {:x, 0}, 1, {:y, 0}},
{:line, 3},
{:gc_bif, :byte_size, {:f, 0}, 0, [y: 0], {:x, 0}},
{:gc_bif, :-, {:f, 0}, 1,
[{:y, 2}, {:tr, {:x, 0}, {{:t_integer, :any}, 0, 288230376151711743}}],
{:y, 2}},
{:move, {:y, 2}, {:x, 1}},
{:move, {:y, 4}, {:x, 0}},
{:init_yregs, {:list, [y: 4]}},
{:call_ext, 2, {:extfunc, :erlang, :min, 2}},
{:test_heap, 2, 1},
{:put_list, {:y, 1}, {:y, 0}, {:x, 4}},
{:move, {:y, 3}, {:x, 2}},
{:move, {:x, 0}, {:x, 1}},
{:move, {:y, 2}, {:x, 3}},
{:move, {:y, 5}, {:x, 0}},
{:call_last, 5, {Test, :do_read, 5}, 6},
{:label, 12},
{:deallocate, 6},
:return,
{:label, 13},
{:test_heap, 3, ...},
{:put_tuple2, ...},
:return
]}
{:function, :read, 5, 21,
[
{:line, 4},
{:label, 20},
{:func_info, {:atom, Test}, {:atom, :read}, 5},
{:label, 21},
{:allocate, 4, 5},
{:move, {:x, 4}, {:y, 0}},
{:move, {:x, 2}, {:y, 1}},
{:move, {:x, 1}, {:y, 2}},
{:move, {:x, 0}, {:y, 3}},
{:move, {:atom, :read_length}, {:x, 1}},
{:move, {:x, 2}, {:x, 0}},
{:move, {:integer, 1000000}, {:x, 2}},
{:line, 5},
{:call_ext, 3, {:extfunc, Keyword, :get, 3}},
{:move, {:x, 0}, {:x, 1}},
{:move, {:y, 2}, {:x, 0}},
{:call_ext, 2, {:extfunc, :erlang, :min, 2}},
{:swap, {:y, 1}, {:x, 0}},
{:move, {:atom, :read_timeout}, {:x, 1}},
{:line, 6},
{:call_ext, 2, {:extfunc, Keyword, :get, 2}},
{:move, {:x, 0}, {:x, 2}},
{:move, {:y, 0}, {:x, 4}},
{:move, {:y, 1}, {:x, 1}},
{:move, {:y, 2}, {:x, 3}},
{:move, {:y, 3}, {:x, 0}},
{:call_last, 5, {Test, :do_read, 5}, 4}
]}
{:function, :read, 5, 17,
[
{:line, 1},
{:label, 16},
{:func_info, {:atom, Test2}, {:atom, :read}, 5},
{:label, 17},
{:allocate, 6, 5},
{:init_yregs, {:list, [y: 0]}},
{:move, {:x, 4}, {:y, 1}},
{:move, {:x, 3}, {:y, 2}},
{:move, {:x, 2}, {:y, 3}},
{:move, {:x, 1}, {:y, 4}},
{:move, {:x, 0}, {:y, 5}},
{:move, {:atom, :read_length}, {:x, 1}},
{:move, {:x, 2}, {:x, 0}},
{:move, {:integer, 1000000}, {:x, 2}},
{:line, 2},
{:call_ext, 3, {:extfunc, Keyword, :get, 3}},
{:move, {:x, 0}, {:x, 1}},
{:move, {:y, 4}, {:x, 0}},
{:call_ext, 2, {:extfunc, :erlang, :min, 2}},
{:move, {:x, 0}, {:y, 0}},
{:move, {:y, 3}, {:x, 0}},
{:move, {:atom, :read_timeout}, {:x, 1}},
{:line, 3},
{:call_ext, 2, {:extfunc, Keyword, :get, 2}},
{:move, {:y, 0}, {:x, 1}},
{:move, {:x, 0}, {:x, 2}},
{:init_yregs, {:list, [y: 0]}},
{:move, {:y, 5}, {:x, 0}},
{:line, 4},
{:call_ext, 3, {:extfunc, ThousandIsland.Socket, :recv, 3}},
{:test, :is_tagged_tuple, {:f, 19}, [{:x, 0}, 2, {:atom, :ok}]},
{:get_tuple_element, {:x, 0}, 1, {:x, 0}},
{:line, 5},
{:gc_bif, :byte_size, {:f, 0}, 1, [x: 0], {:x, 1}},
{:gc_bif, :-, {:f, 0}, 2,
[{:y, 4}, {:tr, {:x, 1}, {{:t_integer, :any}, 0, 288230376151711743}}],
{:x, 2}},
{:line, 6},
{:gc_bif, :+, {:f, 0}, 3,
[{:y, 2}, {:tr, {:x, 1}, {{:t_integer, :any}, 0, 288230376151711743}}],
{:x, 1}},
{:test, :is_lt, {:f, 18},
[{:integer, 0}, {:tr, {:x, 2}, {:number, 0, 18446744073709551615}}]},
{:test_heap, 2, 3},
{:put_list, {:y, 1}, {:x, 0}, {:x, 4}},
{:move, {:x, 1}, {:x, 3}},
{:move, {:x, 2}, {:x, 1}},
{:move, {:y, 3}, {:x, 2}},
{:move, {:y, 5}, {:x, 0}},
{:call_last, 5, {Test2, :read, 5}, 6},
{:label, 18},
{:test_heap, 5, 1},
{:put_list, {:y, 1}, {:x, 0}, {:x, 0}},
{:put_tuple2, {:x, 0}, {:list, [atom: :ok, x: 0]}},
{:deallocate, 6},
:return,
{:label, 19},
{:deallocate, 6},
:return
]}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment