Skip to content

Commit

Permalink
Memory breaker integration test
Browse files Browse the repository at this point in the history
Follow up to #213

Decided to inline the relevant parts of it to the test suite as
it highlighted so many problems. One of which I don't know how
to test. Moreover, with it highlighting so many problems it might
highlight them again later.

Also removed some dead code from said benchmark and added
attribution to @michalmuskala
  • Loading branch information
PragTob committed May 1, 2018
1 parent 4965096 commit ed091ad
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 55 deletions.
58 changes: 3 additions & 55 deletions samples/memory_breaker.exs
Original file line number Diff line number Diff line change
@@ -1,53 +1,8 @@
# Original code by Michal Muskala
# https://gist.github.com/michalmuskala/5ff53581b4b53adec2fff7fb4d69fd52
defmodule BenchKeyword do
@compile :inline_list_funcs

# def pop_throw(keywords, key, default \\ nil) when is_list(keywords) do
# do_pop(keywords, key, [])
# catch
# :not_found -> {default, keywords}
# end

# defp do_pop([{key, value} | rest], key, acc),
# do: {value, :lists.reverse(acc, delete(rest, key))}
# defp do_pop([{_, _} = pair | rest], key, acc),
# do: do_pop(rest, key, [pair | acc])
# defp do_pop([], _key, _acc),
# do: throw(:not_found)

# def pop_ret(keywords, key, default \\ nil) when is_list(keywords) do
# do_pop(keywords, key, [], keywords, default)
# end

# defp do_pop([{key, value} | rest], key, acc, _keywords, _default),
# do: {value, :lists.reverse(acc, delete(rest, key))}
# defp do_pop([{_, _} = pair | rest], key, acc, keywords, default),
# do: do_pop(rest, key, [pair | acc], keywords, default)
# defp do_pop([], _key, _acc, keywords, default),
# do: {default, keywords}

# def pop(keywords, key, default \\ nil) when is_list(keywords) do
# case fetch(keywords, key) do
# {:ok, value} ->
# {value, delete(keywords, key)}
# :error ->
# {default, keywords}
# end
# end

# def pop2(keywords, key, default \\ nil) when is_list(keywords) do
# case :lists.keyfind(key, 1, keywords) do
# {^key, value} -> {value, delete2(keywords, key)}
# false -> {default, keywords}
# end
# end

# def fetch(keywords, key) when is_list(keywords) and is_atom(key) do
# case :lists.keyfind(key, 1, keywords) do
# {^key, value} -> {:ok, value}
# false -> :error
# end
# end

def delete_v0(keywords, key) when is_list(keywords) and is_atom(key) do
:lists.filter(fn {k, _} -> k != key end, keywords)
end
Expand Down Expand Up @@ -123,13 +78,6 @@ defmodule BenchKeyword do
end
end

# benches = %{
# "pop_throw" => fn {kv, key} -> BenchKeyword.pop_throw(kv, key) end,
# "pop_ret" => fn {kv, key} -> BenchKeyword.pop_ret(kv, key) end,
# "pop" => fn {kv, key} -> BenchKeyword.pop(kv, key) end,
# "pop2" => fn {kv, key} -> BenchKeyword.pop2(kv, key) end
# }

benches = %{
"delete old" => fn {kv, key} -> BenchKeyword.delete_v0(kv, key) end,
"delete throw" => fn {kv, key} -> BenchKeyword.delete_v1(kv, key) end,
Expand All @@ -147,4 +95,4 @@ inputs = %{
"huge hit" => {Enum.map(1..10000, &{:"k#{&1}", &1}), :k10000}
}

Benchee.run(benches, inputs: inputs, print: [fast_warning: false], memory_time: 0.5, warmup: 0, time: 0)
Benchee.run(benches, inputs: inputs, print: [fast_warning: false], memory_time: 0.001, warmup: 0, time: 0)
29 changes: 29 additions & 0 deletions test/benchee_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,35 @@ defmodule BencheeTest do
refute output =~ ~r/ips/i # no runtime statistics displayed
assert output =~ ~r/memory.+statistics/i
end

test "the micro keyword list code from Michal does not break memory measurements #213" do
benches = %{
"delete old" => fn {kv, key} -> BenchKeyword.delete_v0(kv, key) end,
"delete reverse" => fn {kv, key} -> BenchKeyword.delete_v2(kv, key) end,
"delete keymember reverse" => fn {kv, key} -> BenchKeyword.delete_v3(kv, key) end,
}

inputs = %{
"large miss" => {Enum.map(1..100, &{:"k#{&1}", &1}), :k101},
"large hit" => {Enum.map(1..100, &{:"k#{&1}", &1}), :k100}
}

output = capture_io fn ->
Benchee.run(
benches,
inputs: inputs,
print: [fast_warning: false],
memory_time: 0.001,
warmup: 0,
time: 0
)
end

refute output =~ "N/A"
refute output =~ ~r/warning/i
assert output =~ "large hit"
assert output =~ "B" # yte
end
end

@slower_regex "\\s+- \\d+\\.\\d+x slower"
Expand Down
48 changes: 48 additions & 0 deletions test/support/bench_keyword.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Original code by Michal Muskala
# https://gist.github.com/michalmuskala/5ff53581b4b53adec2fff7fb4d69fd52
defmodule BenchKeyword do
@compile :inline_list_funcs

@moduledoc """
Together with the benchmark illustrated multiple problems in the memory measurement code.
"""

def delete_v0(keywords, key) when is_list(keywords) and is_atom(key) do
:lists.filter(fn {k, _} -> k != key end, keywords)
end

def delete_v2(keywords, key) when is_list(keywords) and is_atom(key) do
delete_v2_key(keywords, key, [])
end

defp delete_v2_key([{key, _} | tail], key, heads) do
delete_v2_key(tail, key, heads)
end

defp delete_v2_key([{_, _} = pair | tail], key, heads) do
delete_v2_key(tail, key, [pair | heads])
end

defp delete_v2_key([], _key, heads) do
:lists.reverse(heads)
end

def delete_v3(keywords, key) when is_list(keywords) and is_atom(key) do
case :lists.keymember(key, 1, keywords) do
true -> delete_v3_key(keywords, key, [])
_ -> keywords
end
end

defp delete_v3_key([{key, _} | tail], key, heads) do
delete_v3_key(tail, key, heads)
end

defp delete_v3_key([{_, _} = pair | tail], key, heads) do
delete_v3_key(tail, key, [pair | heads])
end

defp delete_v3_key([], _key, heads) do
:lists.reverse(heads)
end
end

0 comments on commit ed091ad

Please sign in to comment.