Skip to content

Commit 7d3863f

Browse files
authored
Reduce memory usage in IEx autocomplete module listing (#15140)
Use :erlang.loaded/0 instead of :code.all_loaded/0 since we only need module names, not file paths. The latter copies the full path for every loaded module, which on production systems with many dependencies caused significant memory spikes (measured 2.8MB -> 172KB on a real pod). Also replace Enum.sort/1 |> Enum.dedup/1 with :lists.usort/1 and avoid list allocations.
1 parent 29f5726 commit 7d3863f

1 file changed

Lines changed: 14 additions & 9 deletions

File tree

lib/iex/lib/iex/autocomplete.ex

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -509,19 +509,25 @@ defmodule IEx.Autocomplete do
509509
end
510510

511511
defp match_elixir_modules(module, hint) do
512-
name = Atom.to_string(module)
513-
depth = length(String.split(name, ".")) + 1
514-
base = name <> "." <> hint
512+
prefix = Atom.to_string(module) <> "."
513+
prefix_size = byte_size(prefix)
514+
base = prefix <> hint
515515

516516
for mod <- match_modules(base, module == Elixir),
517-
parts = String.split(mod, "."),
518-
depth <= length(parts),
519-
name = Enum.at(parts, depth - 1),
517+
rest = binary_part(mod, prefix_size, byte_size(mod) - prefix_size),
518+
name = elixir_submodule_name(rest),
520519
valid_alias_piece?("." <> name),
521520
uniq: true,
522521
do: %{kind: :module, name: name}
523522
end
524523

524+
defp elixir_submodule_name(rest) do
525+
case :binary.match(rest, ".") do
526+
{pos, _} -> binary_part(rest, 0, pos)
527+
:nomatch -> rest
528+
end
529+
end
530+
525531
defp valid_alias_piece?(<<?., char, rest::binary>>) when char in ?A..?Z,
526532
do: valid_alias_rest?(rest)
527533

@@ -604,8 +610,7 @@ defmodule IEx.Autocomplete do
604610

605611
defp match_modules(hint, elixir_root?) do
606612
get_modules(elixir_root?)
607-
|> Enum.sort()
608-
|> Enum.dedup()
613+
|> :lists.usort()
609614
|> Enum.drop_while(&(not String.starts_with?(&1, hint)))
610615
|> Enum.take_while(&String.starts_with?(&1, hint))
611616
end
@@ -615,7 +620,7 @@ defmodule IEx.Autocomplete do
615620
end
616621

617622
defp get_modules(false) do
618-
modules = Enum.map(:code.all_loaded(), &Atom.to_string(elem(&1, 0)))
623+
modules = Enum.map(:erlang.loaded(), &Atom.to_string/1)
619624

620625
case :code.get_mode() do
621626
:interactive -> modules ++ get_modules_from_applications()

0 commit comments

Comments
 (0)