Skip to content

Instantly share code, notes, and snippets.

@bryanjos
Last active July 25, 2024 19:07
Show Gist options
  • Save bryanjos/980b77f6100d676370285941b79f5930 to your computer and use it in GitHub Desktop.
Save bryanjos/980b77f6100d676370285941b79f5930 to your computer and use it in GitHub Desktop.
Absinthe.Subscription.get: Benchmark for proposed change
Operating System: macOS
CPU Information: Apple M1 Pro
Number of Available Cores: 10
Available memory: 16 GB
Elixir 1.17.0
Erlang 27.0
JIT enabled: true
Benchmark suite executing with the following configuration:
warmup: 2 s
time: 10 s
memory time: 2 s
reduction time: 0 ns
parallel: 1
inputs: none specified
Estimated total run time: 42 s
Benchmarking Using MapSet and lookup ...
Benchmarking Using list accumulator ...
Benchmarking Using select ...
Calculating statistics...
Formatting results...
Name ips average deviation median 99th %
Using list accumulator 9.25 108.11 ms ±6.78% 104.94 ms 149.45 ms
Using MapSet and lookup 9.05 110.49 ms ±3.31% 109.03 ms 126.19 ms
Using select 0.0125 79835.89 ms ±0.00% 79835.89 ms 79835.89 ms
Comparison:
Using list accumulator 9.25
Using MapSet and lookup 9.05 - 1.02x slower +2.38 ms
Using select 0.0125 - 738.49x slower +79727.79 ms
Memory usage statistics:
Name average deviation median 99th %
Using list accumulator 152.54 MB ±0.01% 152.53 MB 152.60 MB
Using MapSet and lookup 158.87 MB ±0.00% 158.87 MB 158.87 MB
Using select 31.18 MB ±0.00% 31.18 MB 31.18 MB
Comparison:
Using list accumulator 152.53 MB
Using MapSet and lookup 158.87 MB - 1.04x memory usage +6.33 MB
Using select 31.18 MB - 0.20x memory usage -121.36031 MB
# to run `elixir registry_bench.exs`
Mix.install([
{:benchee, "~> 1.0"},
{:elixir_uuid, "~> 1.2"}
])
# start registry with same settings as absinthe
{:ok, _} =
Registry.start_link(
keys: :duplicate,
name: RegistryTest,
partitions: System.schedulers_online(),
compressed: true
)
random_string = fn bytes_count ->
bytes_count |> :crypto.strong_rand_bytes() |> Base.url_encode64(padding: false)
end
# create some field keys
field_keys = Enum.flat_map(1..10, fn _ ->
field = UUID.uuid4()
Enum.map(1..3, fn _ -> {field, UUID.uuid4()} end)
end)
# create doc_ids and doc_values in the shape of what absinthe would put in
doc_ids_and_doc_values =
Enum.map(1..10_000, fn _ ->
{UUID.uuid4(), %{initial_phases: random_string.(20), source: random_string.(20)}}
end)
# add field_keys and doc_ids to registry
Enum.each(doc_ids_and_doc_values, fn {doc_id, doc_value} ->
field_keys = for _ <- 1..5, do: Enum.random(field_keys)
for field_key <- field_keys do
{:ok, _} = Registry.register(RegistryTest, field_key, doc_id)
end
{:ok, _} = Registry.register(RegistryTest, doc_id, doc_value)
end)
# current Absinthe.Subscription.get implementation
select_fun = fn key ->
RegistryTest
|> Registry.lookup(key)
|> then(fn doc_ids ->
RegistryTest
|> Registry.select(
# We compose a list of match specs that basically mean "lookup all keys
# in the doc_ids list"
for {_, doc_id} <- doc_ids,
do: {{:"$1", :_, :"$2"}, [{:==, :"$1", doc_id}], [{{:"$1", :"$2"}}]}
)
end)
|> Map.new(fn {doc_id, doc} ->
{doc_id, doc}
end)
end
#proposed implementation
map_fun = fn key ->
RegistryTest
|> Registry.lookup(key)
|> MapSet.new(fn {_pid, doc_id} -> doc_id end)
|> Enum.reduce(%{}, fn doc_id, acc ->
case Registry.lookup(RegistryTest, doc_id) do
[] ->
acc
[{_pid, doc} | _rest] ->
Map.put_new(acc, doc_id, doc)
end
end)
end
#proposed with list accumulator
map_with_list_fun = fn key ->
RegistryTest
|> Registry.lookup(key)
|> MapSet.new(fn {_pid, doc_id} -> doc_id end)
|> Enum.reduce([], fn doc_id, acc ->
case Registry.lookup(RegistryTest, doc_id) do
[] ->
acc
[{_pid, doc} | _rest] ->
[{doc_id, doc} | acc]
end
end)
|> Map.new()
end
Benchee.run(
%{
"Using select" => fn -> Enum.each(field_keys, select_fun) end,
"Using MapSet and lookup" => fn -> Enum.each(field_keys, map_fun) end,
"Using list accumulator" => fn -> Enum.each(field_keys, map_with_list_fun) end
},
time: 10,
memory_time: 2
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment