From 3eab05b06b3a54d6b726e7f99a83fc8584c03619 Mon Sep 17 00:00:00 2001 From: Dennis Ideler Date: Mon, 27 May 2019 16:45:09 +0100 Subject: [PATCH 1/7] Update elixir and erlang versions for asdf --- .tool-versions | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.tool-versions b/.tool-versions index 25caefa..1143ac5 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -erlang 21.2.6 -elixir 1.8.1-otp-21 +erlang 22.0.1 +elixir 1.8.2-otp-22 From b755032676660ae5a0fc29467d2cd216d83220e3 Mon Sep 17 00:00:00 2001 From: Dennis Ideler Date: Mon, 27 May 2019 16:47:52 +0100 Subject: [PATCH 2/7] Add step for installing the project's elixir/erlang versions --- CONTRIBUTING.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bc4e059..daff49d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,9 +6,11 @@ Thank you for contributing! Let's get you started! 1. Fork and clone the repository -2. Install dependencies: `mix deps.get` +1. Install the Elixir/Erlang versions in [`.tool-versions`](.tool-versions) -3. Write a benchmark using the following template: +1. Install dependencies: `mix deps.get` + +1. Write a benchmark using the following template: ```elixir defmodule IdiomName.Fast do @@ -37,8 +39,8 @@ Thank you for contributing! Let's get you started! IdiomName.Benchmark.benchmark() ``` -4. Run your benchmark: `mix run code//.exs` +1. Run your benchmark: `mix run code//.exs` -5. Add the output along with a description to the [README](README.md). +1. Add the output along with a description to the [README](README.md) -6. Open a Pull Request! +1. Open a Pull Request! From c85486645b8f833bcdb6e6b975737854b464094d Mon Sep 17 00:00:00 2001 From: Dennis Ideler Date: Mon, 27 May 2019 17:09:43 +0100 Subject: [PATCH 3/7] Update benchee from 0.14.0 to 1.0.1 https://github.com/bencheeorg/benchee/blob/master/CHANGELOG.md#101-2019-04-09 --- mix.exs | 12 ++---------- mix.lock | 4 ++-- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/mix.exs b/mix.exs index 5bc96f4..034e4ce 100644 --- a/mix.exs +++ b/mix.exs @@ -17,16 +17,8 @@ defmodule FastElixir.Mixfile do [applications: [:logger]] end - # Dependencies can be Hex packages: - # - # {:mydep, "~> 0.3.0"} - # - # Or git/path repositories: - # - # {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"} - # - # Type "mix help deps" for more examples and options + # Type "mix help deps" for more information defp deps do - [{:benchee, "~> 0.14"}] + [{:benchee, "~> 1.0"}] end end diff --git a/mix.lock b/mix.lock index 6590285..d3c4461 100644 --- a/mix.lock +++ b/mix.lock @@ -1,5 +1,5 @@ %{ - "benchee": {:hex, :benchee, "0.14.0", "f771f587c48b4824b497e2a3e374f75e93ef01fc329873b089a3f5dd961b80b8", [:mix], [{:deep_merge, "~> 0.1", [hex: :deep_merge, repo: "hexpm", optional: false]}], "hexpm"}, - "deep_merge": {:hex, :deep_merge, "0.2.0", "c1050fa2edf4848b9f556fba1b75afc66608a4219659e3311d9c9427b5b680b3", [:mix], [], "hexpm"}, + "benchee": {:hex, :benchee, "1.0.1", "66b211f9bfd84bd97e6d1beaddf8fc2312aaabe192f776e8931cb0c16f53a521", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}], "hexpm"}, + "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm"}, "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], []}, } From 44a5b3231bfba7f423c8d7672736ce4bd041f8d9 Mon Sep 17 00:00:00 2001 From: Dennis Ideler Date: Mon, 27 May 2019 17:10:29 +0100 Subject: [PATCH 4/7] Update minimum elixir version from 1.4 to 1.6 Benchee 1.0 is compatible with elixir ~> 1.6 --- mix.exs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/mix.exs b/mix.exs index 034e4ce..b835eb0 100644 --- a/mix.exs +++ b/mix.exs @@ -2,12 +2,14 @@ defmodule FastElixir.Mixfile do use Mix.Project def project do - [app: :fast_elixir, - version: "0.1.0", - elixir: "~> 1.4", - build_embedded: Mix.env == :prod, - start_permanent: Mix.env == :prod, - deps: deps()] + [ + app: :fast_elixir, + version: "0.1.0", + elixir: "~> 1.6", + build_embedded: Mix.env() == :prod, + start_permanent: Mix.env() == :prod, + deps: deps() + ] end # Configuration for the OTP application From 4e2958204974546bdf185a4d9c8eb8cb54b215b9 Mon Sep 17 00:00:00 2001 From: Dennis Ideler Date: Mon, 27 May 2019 17:13:33 +0100 Subject: [PATCH 5/7] Remove logger as an explicit runtime dependency The logger is not being used directly in this project. --- mix.exs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/mix.exs b/mix.exs index b835eb0..ee4925b 100644 --- a/mix.exs +++ b/mix.exs @@ -12,13 +12,6 @@ defmodule FastElixir.Mixfile do ] end - # Configuration for the OTP application - # - # Type "mix help compile.app" for more information - def application do - [applications: [:logger]] - end - # Type "mix help deps" for more information defp deps do [{:benchee, "~> 1.0"}] From 7b06914cfee667315387076b35967991dc8e26c4 Mon Sep 17 00:00:00 2001 From: Dennis Ideler Date: Mon, 27 May 2019 22:42:26 +0100 Subject: [PATCH 6/7] Add benchmarks for checking enumerable membership --- code/general/membership.exs | 68 +++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 code/general/membership.exs diff --git a/code/general/membership.exs b/code/general/membership.exs new file mode 100644 index 0000000..361f92d --- /dev/null +++ b/code/general/membership.exs @@ -0,0 +1,68 @@ +defmodule Membership.NewMapSetMember do + def check_membership(range, list, _mapset) do + find = Enum.random(range) + mapset = MapSet.new(list) + MapSet.member?(mapset, find) + end +end + +defmodule Membership.MapSetMember do + def check_membership(range, _list, mapset) do + find = Enum.random(range) + MapSet.member?(mapset, find) + end +end + +defmodule Membership.EnumMember do + def check_membership(range, list, _mapset) do + find = Enum.random(range) + Enum.member?(list, find) + end +end + +defmodule Membership.EnumAny do + def check_membership(range, list, _mapset) do + find = Enum.random(range) + Enum.any?(list, &(&1 == find)) + end +end + +defmodule Membership.In do + def check_membership(range, list, _mapset) do + find = Enum.random(range) + find in list + end +end + +defmodule Membership.Benchmark do + @inputs %{ + "Large (pass)" => {1..1_000_000, Enum.shuffle(1..1_000_000), MapSet.new(1..1_000_000)}, + "Large (fail)" => + {1_000_001..2_000_000, Enum.shuffle(1..1_000_000), MapSet.new(1..1_000_000)}, + "Medium (pass)" => {1..10_000, Enum.shuffle(1..10_000), MapSet.new(1..10_000)}, + "Medium (fail)" => {10_001..20_000, Enum.shuffle(1..10_000), MapSet.new(1..10_000)}, + "Small (pass)" => {1..100, Enum.shuffle(1..100), MapSet.new(1..100)}, + "Small (fail)" => {101..200, Enum.shuffle(1..100), MapSet.new(1..100)} + } + + def benchmark do + Benchee.run( + %{ + "New MapSet + MapSet.member?" => fn input -> bench(Membership.NewMapSetMember, input) end, + "MapSet.member?" => fn input -> bench(Membership.MapSetMember, input) end, + "Enum.member?" => fn input -> bench(Membership.EnumMember, input) end, + "Enum.any?" => fn input -> bench(Membership.EnumAny, input) end, + "x in y" => fn input -> bench(Membership.In, input) end + }, + time: 10, + inputs: @inputs, + print: [fast_warning: false] + ) + end + + defp bench(module, {range, list, mapset}) do + module.check_membership(range, list, mapset) + end +end + +Membership.Benchmark.benchmark() From 917386aa502cd1c2cd07ed8bc607648d5243ab82 Mon Sep 17 00:00:00 2001 From: Dennis Ideler Date: Mon, 27 May 2019 22:44:55 +0100 Subject: [PATCH 7/7] Reduce test cases by removing "miss" scenarios To keep the comparisons more focused. For reference, when the element is not in the list, MapSet.member?/2 is the fastest for all n. For small n, the difference is almost insignificant. But for large n, MapSet.member?/2 can be up to 1559.06x faster than the next fastest (Enum.member?/2) since the other methods are exhaustive. --- code/general/membership.exs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/code/general/membership.exs b/code/general/membership.exs index 361f92d..95086e4 100644 --- a/code/general/membership.exs +++ b/code/general/membership.exs @@ -36,13 +36,9 @@ end defmodule Membership.Benchmark do @inputs %{ - "Large (pass)" => {1..1_000_000, Enum.shuffle(1..1_000_000), MapSet.new(1..1_000_000)}, - "Large (fail)" => - {1_000_001..2_000_000, Enum.shuffle(1..1_000_000), MapSet.new(1..1_000_000)}, - "Medium (pass)" => {1..10_000, Enum.shuffle(1..10_000), MapSet.new(1..10_000)}, - "Medium (fail)" => {10_001..20_000, Enum.shuffle(1..10_000), MapSet.new(1..10_000)}, - "Small (pass)" => {1..100, Enum.shuffle(1..100), MapSet.new(1..100)}, - "Small (fail)" => {101..200, Enum.shuffle(1..100), MapSet.new(1..100)} + "Large" => {1..1_000_000, Enum.shuffle(1..1_000_000), MapSet.new(1..1_000_000)}, + "Medium" => {1..10_000, Enum.shuffle(1..10_000), MapSet.new(1..10_000)}, + "Small" => {1..100, Enum.shuffle(1..100), MapSet.new(1..100)} } def benchmark do