Skip to content

Commit f82f2c7

Browse files
pguilloryjosevalim
authored andcommitted
Optimize Enum.unzip/1 for lists (#10963)
The existing unzip implementation is generic for all enumerable types. However, large optimizations are available if the input is a list. First, instead of doing two reverse operations at the end, we can do one at the beginning. This optimization is unavailable in the existing generic implementation in order to avoid materializing an enumerable that might be generated lazily. Second, we can accumulate the two output lists in two separate parameters, rather than in the reduce function's accumulator. This avoids generating a tuple per input element that would just be matched away and discarded by Enum.reduce/3. Testing with Benchfella shows a roughly 2x to 3x performance improvement depending on the size of the list.
1 parent 63e6f24 commit f82f2c7

File tree

1 file changed

+17
-0
lines changed

1 file changed

+17
-0
lines changed

lib/elixir/lib/enum.ex

+17
Original file line numberDiff line numberDiff line change
@@ -3352,6 +3352,15 @@ defmodule Enum do
33523352
33533353
"""
33543354
@spec unzip(t) :: {[element], [element]}
3355+
3356+
def unzip([_ | _] = list) do
3357+
:lists.reverse(list) |> unzip([], [])
3358+
end
3359+
3360+
def unzip([]) do
3361+
{[], []}
3362+
end
3363+
33553364
def unzip(enumerable) do
33563365
{list1, list2} =
33573366
reduce(enumerable, {[], []}, fn {el1, el2}, {list1, list2} ->
@@ -3361,6 +3370,14 @@ defmodule Enum do
33613370
{:lists.reverse(list1), :lists.reverse(list2)}
33623371
end
33633372

3373+
defp unzip([{el1, el2} | reversed_list], list1, list2) do
3374+
unzip(reversed_list, [el1 | list1], [el2 | list2])
3375+
end
3376+
3377+
defp unzip([], list1, list2) do
3378+
{list1, list2}
3379+
end
3380+
33643381
@doc """
33653382
Returns the `enumerable` with each element wrapped in a tuple
33663383
alongside its index.

0 commit comments

Comments
 (0)