From a09720d0853fa49c91d0ab921e87bca32551fbc4 Mon Sep 17 00:00:00 2001 From: preciz Date: Sat, 13 Jun 2026 22:04:40 +0200 Subject: [PATCH 1/3] Optimize MapSet.symmetric_difference/2 when sizes mismatched By folding over the smaller set and using the larger set as the starting accumulator, the time complexity is reduced from O(large) to O(small) iterations. This provides a over 100x speedup when set sizes are mismatched. --- lib/elixir/lib/map_set.ex | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/elixir/lib/map_set.ex b/lib/elixir/lib/map_set.ex index fbfd9c303c1..0e6aba1274f 100644 --- a/lib/elixir/lib/map_set.ex +++ b/lib/elixir/lib/map_set.ex @@ -173,16 +173,20 @@ defmodule MapSet do def symmetric_difference(%MapSet{map: set1} = map_set1, %MapSet{map: set2} = _map_set2) do {small, large} = if :sets.size(set1) <= :sets.size(set2), do: {set1, set2}, else: {set2, set1} - disjointer_fun = fn elem, {small, acc} -> - if :sets.is_element(elem, small) do - {:sets.del_element(elem, small), acc} - else - {small, [elem | acc]} - end - end - - {new_small, list} = :sets.fold(disjointer_fun, {small, []}, large) - %{map_set1 | map: :sets.union(new_small, :sets.from_list(list, version: 2))} + map = + :sets.fold( + fn elem, acc -> + if :sets.is_element(elem, acc) do + :sets.del_element(elem, acc) + else + :sets.add_element(elem, acc) + end + end, + large, + small + ) + + %{map_set1 | map: map} end @doc """ From 68a89c94ada891f52a0e20187cb2b47f3e6e1171 Mon Sep 17 00:00:00 2001 From: preciz Date: Sun, 14 Jun 2026 10:43:13 +0200 Subject: [PATCH 2/3] Further optimize MapSet.symmetric_difference --- lib/elixir/lib/map_set.ex | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/lib/elixir/lib/map_set.ex b/lib/elixir/lib/map_set.ex index 0e6aba1274f..b036e313fd0 100644 --- a/lib/elixir/lib/map_set.ex +++ b/lib/elixir/lib/map_set.ex @@ -171,20 +171,15 @@ defmodule MapSet do @doc since: "1.14.0" @spec symmetric_difference(t(val1), t(val2)) :: t(val1 | val2) when val1: value, val2: value def symmetric_difference(%MapSet{map: set1} = map_set1, %MapSet{map: set2} = _map_set2) do - {small, large} = if :sets.size(set1) <= :sets.size(set2), do: {set1, set2}, else: {set2, set1} - map = - :sets.fold( - fn elem, acc -> - if :sets.is_element(elem, acc) do - :sets.del_element(elem, acc) - else - :sets.add_element(elem, acc) - end - end, - large, - small - ) + if :sets.is_disjoint(set1, set2) do + :sets.union(set1, set2) + else + {small, large} = + if :sets.size(set1) <= :sets.size(set2), do: {set1, set2}, else: {set2, set1} + + :sets.union(:sets.subtract(large, small), :sets.subtract(small, large)) + end %{map_set1 | map: map} end From 17c2c9cd8080d28707c0a0026224487015d3f0d5 Mon Sep 17 00:00:00 2001 From: preciz Date: Sun, 14 Jun 2026 11:00:35 +0200 Subject: [PATCH 3/3] No comparison required --- lib/elixir/lib/map_set.ex | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/elixir/lib/map_set.ex b/lib/elixir/lib/map_set.ex index b036e313fd0..8826ba8cff1 100644 --- a/lib/elixir/lib/map_set.ex +++ b/lib/elixir/lib/map_set.ex @@ -175,10 +175,7 @@ defmodule MapSet do if :sets.is_disjoint(set1, set2) do :sets.union(set1, set2) else - {small, large} = - if :sets.size(set1) <= :sets.size(set2), do: {set1, set2}, else: {set2, set1} - - :sets.union(:sets.subtract(large, small), :sets.subtract(small, large)) + :sets.union(:sets.subtract(set1, set2), :sets.subtract(set2, set1)) end %{map_set1 | map: map}