Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which way to compare maps in Elixir

Tags:

erlang

elixir

Given two large and different maps defined as follows

Interactive Elixir (1.9.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> m1 = Map.new(1..1_000_000 |> Enum.map(&{&1, &1})); :ok
:ok
iex(2)> m2 = Map.new(2..1_000_000 |> Enum.map(&{&1, &1})); :ok
:ok

it takes a significant time difference when comparing them using ==/2 and Map.equal?/2

iex(3)> :timer.tc(fn -> m1 == m2 end)
{21, false}
iex(4)> :timer.tc(fn -> Map.equal?(m1, m2) end)
{20487, false}

What is the reason of this time difference between ==/2 and Map.equal?/2, and which to use?

Equivalently, which to use between ==/2 and ===/2? (because Map.equal?/2 calls to ===/2, see here)

Thanks

like image 632
mljrg Avatar asked Feb 06 '20 12:02

mljrg


1 Answers

Indeed, Map.equal?/2 is simply delegating to Kernel.===/2.

Kernel.===/2 is delegating to :erlang."=:="/2 and Kernel.==/2 is delegating to :erlang."=="/2. The latter compares numbers while the former compares values and types.

Consider the following example.

%{1 => 1} == %{1 => 1.0}
#⇒ true

%{1 => 1} === %{1 => 1.0}
#⇒ false

That said, Kernel.===/2 should compare all the values. OTOH, Erlang/OTP reference explicitly states that

Maps are ordered by size, two maps with the same size are compared by keys in ascending term order and then by values in key order. In maps, key order integers types are considered less than floats types.

If it was indeed true, two maps of the different size, as in your example, should have returned in the nearly same time.


The summing up, I would consider this time difference to be a bug in :erlang."=:="/2 implementation, worth to report to Erlang team.

like image 154
Aleksei Matiushkin Avatar answered Oct 05 '22 13:10

Aleksei Matiushkin