Skip to content

Instantly share code, notes, and snippets.

@amorphid
Last active November 8, 2017 01:21
Show Gist options
  • Save amorphid/fc08ac32978604f747a64051840dc095 to your computer and use it in GitHub Desktop.
Save amorphid/fc08ac32978604f747a64051840dc095 to your computer and use it in GitHub Desktop.
OTP mailbox vs Erlang queue smackdown
#!/usr/bin/env elixir
defmodule MailboxBenchmark do
def start_link(messages) do
count = Enum.at(messages, -1)
pid = spawn_link(fn -> blocked(count) end)
_ = Enum.each(messages, fn (i) -> send(pid, i) end)
{:ok, pid}
end
def blocked(count) do
receive do
{:unblocked,pid} -> receive_one(count,pid)
end
end
def receive_all(mailbox) do
me = self()
_ = send(mailbox,{:unblocked,me})
receive do
{:ok,^me} -> :ok
end
end
def receive_one(0,pid) do
send(pid, {:ok,pid})
end
def receive_one(count,pid) do
receive do
i when is_integer(i) -> receive_one(count - 1,pid)
end
end
end
defmodule QueueBenchmark do
def new(messages) do
{:ok,Enum.reduce(messages, :queue.new(), &(:queue.in(&1,&2)))}
end
def read_all(queue) do
case :queue.out(queue) do
{:empty, _} -> :ok
{_,queue2} -> read_all(queue2)
end
end
end
messages = 1..10_000_000
IO.puts("benchmarking #{Enum.at(messages, -1)} messages")
{:ok, mailbox} = MailboxBenchmark.start_link(messages)
IO.puts("mailbox created")
{:ok, queue} = QueueBenchmark.new(messages)
IO.puts("queue_time created")
{mailbox_time, _} = :timer.tc(MailboxBenchmark, :receive_all, [mailbox])
{queue_time, _} = :timer.tc(QueueBenchmark, :read_all, [queue])
IO.puts("mailbox_time: #{mailbox_time} microseconds")
IO.puts("queue_time: #{queue_time} microseconds")
:ok = if mailbox_time < queue_time do
IO.puts("mailbox_time is faster by #{queue_time - mailbox_time} microseconds")
else
IO.puts("queue_time is faster by #{queue_time - mailbox_time} microseconds")
end
defmodule OtpMailbox do
def start_link() do
{:ok, spawn_link(fn -> blocked() end)}
end
def blocked() do
receive do
:unblock -> :do_something
end
end
end
defmodule ErlangQueue do
use GenServer
def start_link, do: GenServer.start_link(__MODULE__, [])
def init(_) do
{:ok, :queue.new()}
end
def handle_info(message, queue) do
{:noreply,:queue.in(message,queue)}
end
end
messages = 1..1_000_000
{:ok, mailbox} = OtpMailbox.start_link()
{:ok, queue} = ErlangQueue.start_link()
Enum.each(messages, fn (i) ->
_ = send(mailbox, i)
_ = send(queue, i)
end)
{:memory, mailbox_size} = Process.info(mailbox, :memory)
{:memory, queue_size} = Process.info(queue, :memory)
IO.puts("mailbox_size: #{mailbox_size}")
IO.puts("queue_size: #{queue_size}")
IO.puts("mailbox_size / queue_size: #{mailbox_size / queue_size}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment