Current section

Files

Jump to
excessibility lib telemetry_capture analyzers render_efficiency.ex
Raw

lib/telemetry_capture/analyzers/render_efficiency.ex

defmodule Excessibility.TelemetryCapture.Analyzers.RenderEfficiency do
@moduledoc """
Analyzes render efficiency by detecting wasted renders.
A "wasted render" is a render event with no state changes.
This indicates the LiveView re-rendered unnecessarily.
## Thresholds
- Warning: 3+ wasted renders
- Critical: >30% of renders are wasted
## Output
%{
findings: [
%{
severity: :critical,
message: "3 of 5 renders (60%) had no state changes",
events: [2, 4, 5],
metadata: %{wasted_count: 3, total_count: 5}
}
],
stats: %{
render_count: 5,
wasted_render_count: 3,
efficiency_ratio: 0.4
}
}
"""
@behaviour Excessibility.TelemetryCapture.Analyzer
def name, do: :render_efficiency
def default_enabled?, do: true
def analyze(%{timeline: []}, _opts) do
%{findings: [], stats: %{render_count: 0, wasted_render_count: 0, efficiency_ratio: 1.0}}
end
def analyze(%{timeline: timeline}, _opts) do
render_events = Enum.filter(timeline, &render_event?/1)
wasted = Enum.filter(render_events, &wasted_render?/1)
render_count = length(render_events)
wasted_count = length(wasted)
efficiency = if render_count > 0, do: 1 - wasted_count / render_count, else: 1.0
stats = %{
render_count: render_count,
wasted_render_count: wasted_count,
efficiency_ratio: Float.round(efficiency, 2)
}
findings = detect_issues(wasted, render_count, wasted_count)
%{findings: findings, stats: stats}
end
defp render_event?(%{event: "render"}), do: true
defp render_event?(_), do: false
defp wasted_render?(%{changes: nil}), do: false
defp wasted_render?(%{changes: changes}) when map_size(changes) == 0, do: true
defp wasted_render?(_), do: false
defp detect_issues(_wasted, render_count, _wasted_count) when render_count == 0, do: []
defp detect_issues(wasted, render_count, wasted_count) do
wasted_ratio = wasted_count / render_count
cond do
wasted_ratio > 0.3 ->
sequences = Enum.map(wasted, & &1.sequence)
[
%{
severity: :critical,
message: "#{wasted_count} of #{render_count} renders (#{round(wasted_ratio * 100)}%) had no state changes",
events: sequences,
metadata: %{wasted_count: wasted_count, total_count: render_count}
}
]
wasted_count >= 3 ->
sequences = Enum.map(wasted, & &1.sequence)
[
%{
severity: :warning,
message: "#{wasted_count} renders had no state changes - possible unnecessary re-renders",
events: sequences,
metadata: %{wasted_count: wasted_count}
}
]
true ->
[]
end
end
end