Skip to content

Commit d361793

Browse files
committed
Drop unused process info maps early
- avoid keeping process info for all processes in memory - avoid .sort, .take, and .map on extremely long lists - remains fast for hundreds of thousands of processes - only slow (3s) showing 5000 processes out of hundreds of thousands
1 parent 46b14cb commit d361793

File tree

1 file changed

+47
-10
lines changed

1 file changed

+47
-10
lines changed

lib/phoenix/live_dashboard/system_info.ex

+47-10
Original file line numberDiff line numberDiff line change
@@ -281,21 +281,58 @@ defmodule Phoenix.LiveDashboard.SystemInfo do
281281
@doc false
282282
def processes_callback(search, sort_by, sort_dir, limit, prev_reductions) do
283283
multiplier = sort_dir_multiplier(sort_dir)
284+
constants = {prev_reductions, search, sort_by, multiplier, limit}
285+
reduce_processes(Process.list(), constants)
286+
end
284287

285-
processes =
286-
for pid <- Process.list(),
287-
info = process_info(pid, prev_reductions[pid]),
288-
show_process?(info, search) do
289-
sorter = info[sort_by] * multiplier
290-
{sorter, info}
288+
defp reduce_processes(pids, {prev_reductions, search, sort_by, multiplier, limit}) do
289+
reduce_processes(pids, {prev_reductions, search, sort_by, multiplier, limit}, {[], 0, %{}})
290+
end
291+
292+
defp reduce_processes([], _, {processes, count, next_state}) do
293+
{processes |> Enum.map(&elem(&1, 1)) |> Enum.reverse(), count, next_state}
294+
end
295+
296+
defp reduce_processes(
297+
[pid | pids],
298+
{prev_reductions, search, sort_by, multiplier, limit} = constants,
299+
{processes, count, next_state}
300+
) do
301+
acc =
302+
if info = process_info(pid, prev_reductions[pid]) do
303+
sorter = info[sort_by] * multiplier * -1
304+
show_process? = show_process?(info, search)
305+
count = if show_process?, do: count + 1, else: count
306+
next_state = Map.put(next_state, info[:pid], info[:reductions])
307+
308+
processes =
309+
case {count, limit, show_process?} do
310+
{_, _, false} -> processes
311+
{0, limit, _} when limit > 0 -> insert_sorted({sorter, info}, [])
312+
{count, limit, _} when count > limit -> tl(insert_sorted({sorter, info}, processes))
313+
{_, _, _} -> insert_sorted({sorter, info}, processes)
314+
end
315+
316+
{processes, count, next_state}
317+
else
318+
{processes, count, next_state}
291319
end
292320

293-
next_state = for {_sorter, info} <- processes, into: %{}, do: {info[:pid], info[:reductions]}
321+
reduce_processes(pids, constants, acc)
322+
end
323+
324+
defp insert_sorted({key, val}, []), do: [{key, val}]
325+
defp insert_sorted({key, val}, list), do: insert_sorted({key, val}, list, [])
326+
defp insert_sorted(nil, list, []), do: list
327+
defp insert_sorted(nil, list, [hd | tl]), do: insert_sorted(nil, [hd | list], tl)
328+
defp insert_sorted({key, val}, [], hd), do: insert_sorted(nil, [{key, val}], hd)
294329

295-
count = if search, do: length(processes), else: :erlang.system_info(:process_count)
296-
processes = processes |> Enum.sort() |> Enum.take(limit) |> Enum.map(&elem(&1, 1))
330+
defp insert_sorted({key, val}, [{low_key, _} | _] = list, hd) when key < low_key do
331+
insert_sorted(nil, [{key, val} | list], hd)
332+
end
297333

298-
{processes, count, next_state}
334+
defp insert_sorted({key, val}, [{low_key, low_val} | tl], hd) when key >= low_key do
335+
insert_sorted({key, val}, tl, [{low_key, low_val} | hd])
299336
end
300337

301338
defp process_info(pid, prev_reductions) do

0 commit comments

Comments
 (0)