Skip to content

Instantly share code, notes, and snippets.

@mgartner
Last active May 18, 2019 00:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mgartner/8d7040fff1f383a21fa56f006c88c660 to your computer and use it in GitHub Desktop.
Save mgartner/8d7040fff1f383a21fa56f006c88c660 to your computer and use it in GitHub Desktop.
Benchmark for different forms of checking list uniqueness in Elixir.
WARMUP: 8.65
WARMUP: 12.806
WARMUP: 5.377
unique_length? @ints_uniq: 7.888
unique_map? @ints_uniq: 11.046
unique_recursion? @ints_uniq: 5.157
unique_length? @ints_not_uniq: 14.374
unique_map? @ints_not_uniq: 11.662
unique_recursion? @ints_not_uniq: 5.25
unique_length? @ints_not_uniq_short_circuit: 7.969
unique_map? @ints_not_uniq_short_circuit: 4.005
unique_recursion? @ints_not_uniq_short_circuit: 0.961
unique_length? @strings_uniq: 16.442
unique_map? @strings_uniq: 17.162
unique_recursion? @strings_uniq: 10.873
unique_length? @strings_not_uniq: 14.242
unique_map? @strings_not_uniq: 17.612
unique_recursion? @strings_not_uniq: 11.152
unique_length? @strings_not_uniq_short_circuit: 13.779
unique_map? @strings_not_uniq_short_circuit: 3.986
unique_recursion? @strings_not_uniq_short_circuit: 1.116
defmodule Test do
@loops 10000
@ints_uniq [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
@ints_not_uniq [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 4]
@ints_not_uniq_short_circuit [1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
@strings_uniq ["hello", "goodbye", "a", "b", "c", "d", "e", "f"]
@strings_not_uniq ["hello", "goodbye", "a", "b", "c", "d", "e", "f", "hello"]
@strings_not_uniq_short_circuit ["hello", "hello", "goodbye", "a", "b", "c", "d", "e", "f"]
def run do
time(&unique_length?/1, "WARMUP", @ints_uniq)
time(&unique_map?/1, "WARMUP", @ints_uniq)
time(&unique_recursion?/1, "WARMUP", @ints_uniq)
IO.puts("\n")
time(&unique_length?/1, "unique_length? @ints_uniq", @ints_uniq)
time(&unique_map?/1, "unique_map? @ints_uniq", @ints_uniq)
time(&unique_recursion?/1, "unique_recursion? @ints_uniq", @ints_uniq)
IO.puts("\n")
time(&unique_length?/1, "unique_length? @ints_not_uniq", @ints_not_uniq)
time(&unique_map?/1, "unique_map? @ints_not_uniq", @ints_not_uniq)
time(&unique_recursion?/1, "unique_recursion? @ints_not_uniq", @ints_not_uniq)
IO.puts("\n")
time(&unique_length?/1, "unique_length? @ints_not_uniq_short_circuit", @ints_not_uniq_short_circuit)
time(&unique_map?/1, "unique_map? @ints_not_uniq_short_circuit", @ints_not_uniq_short_circuit)
time(&unique_recursion?/1, "unique_recursion? @ints_not_uniq_short_circuit", @ints_not_uniq_short_circuit)
IO.puts("\n")
time(&unique_length?/1, "unique_length? @strings_uniq", @strings_uniq)
time(&unique_map?/1, "unique_map? @strings_uniq", @strings_uniq)
time(&unique_recursion?/1, "unique_recursion? @strings_uniq", @strings_uniq)
IO.puts("\n")
time(&unique_length?/1, "unique_length? @strings_not_uniq", @strings_not_uniq)
time(&unique_map?/1, "unique_map? @strings_not_uniq", @strings_not_uniq)
time(&unique_recursion?/1, "unique_recursion? @strings_not_uniq", @strings_not_uniq)
IO.puts("\n")
time(&unique_length?/1, "unique_length? @strings_not_uniq_short_circuit", @strings_not_uniq_short_circuit)
time(&unique_map?/1, "unique_map? @strings_not_uniq_short_circuit", @strings_not_uniq_short_circuit)
time(&unique_recursion?/1, "unique_recursion? @strings_not_uniq_short_circuit", @strings_not_uniq_short_circuit)
end
def time(fun, label, list) do
loop_fun = fn ->
Enum.each(1..@loops, fn _ ->
fun.(list)# |> IO.inspect
end)
end
{time, _} = :timer.tc(loop_fun)
IO.inspect(time / 1000.0, label: label)
end
def unique_length?(list) do
unique_list = Enum.uniq(list)
length(unique_list) == length(list)
end
def unique_map?(list) do
{result, _} = Enum.reduce_while(list, {true, %{}}, fn item, {_, set} ->
case set do
%{^item => true} -> {:halt, {false, set}}
%{} -> {:cont, {true, Map.put(set, item, true)}}
end
end)
result
end
# Based on https://github.com/elixir-lang/elixir/blob/v1.8.2/lib/elixir/lib/enum.ex#L3286-L3297
def unique_recursion?(list), do: unique_recursion?(list, %{})
def unique_recursion?([], _set), do: true
def unique_recursion?([head | tail], set) do
case set do
%{^head => true} -> false
%{} -> unique_recursion?(tail, Map.put(set, head, true))
end
end
end
Test.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment