Keep notification actions and dismiss controls visible, compact and animate history, and add themed icon controls and grouping. Include notification settings, session integration and documentation, on-demand workspaces, and performance baseline notes.
218 lines
12 KiB
Markdown
218 lines
12 KiB
Markdown
# Release performance baseline
|
||
|
||
Measured 2026-09-27 at commit `08a5866`, using
|
||
`cargo build --release --locked -p ferese -p ferese-shell -p feresectl`.
|
||
|
||
Two disposable nested sessions used separate runtime/config directories, default
|
||
configuration, the bundled wallpaper, and the release shell. Performance logging
|
||
was enabled with `FERESE_TRACE_PERFORMANCE=1`. The nested output reported
|
||
1422 × 1696 physical pixels, 2× scale and 60 Hz. The parent DRM output was a
|
||
2880 × 1800 eDP panel at 120 Hz and 2× scale.
|
||
|
||
## Measurements
|
||
|
||
CPU is process CPU time from `/proc/PID/stat` divided by elapsed monotonic time,
|
||
expressed as a percentage of one core. First-run samples lasted 10 seconds;
|
||
second-run samples lasted 5 seconds. These are short observational samples,
|
||
not statistically established improvements or before/after comparisons.
|
||
|
||
| Scenario | Compositor CPU, run 1 / run 2 | Shell CPU, run 1 / run 2 |
|
||
| --- | --- | --- |
|
||
| Idle shell, no application window | 5.3% / 5.8% | 1.5% / 1.6% |
|
||
| Overview with one terminal | 18.6% / 16.2% | 1.3% / 1.4% |
|
||
| After window transitions, terminal still open | 6.6% / 6.4% | 1.5% / 1.4% |
|
||
|
||
Compositor RSS was approximately 81–82 MiB initially and 91–92 MiB after the
|
||
short exercise. This does not establish a leak or long-term memory stability.
|
||
Main-thread voluntary context switches were approximately 162–165/s at idle
|
||
and 498–505/s in overview; these are scheduling proxies, not hardware wakeups.
|
||
|
||
A five-second idle log interval contained 60 damaged frames and 531 no-damage
|
||
attempts, approximately 118 attempts/s. A later overview interval contained
|
||
591 damaged frames in 5001 ms. Nested frame scheduling and overview damage
|
||
therefore deserve investigation first. The parent runs at 120 Hz, so the
|
||
mismatch with the nested output's reported 60 Hz must be understood before
|
||
attributing these counts to duplicate scheduling.
|
||
|
||
## Behavioral exercise
|
||
|
||
Both sessions exited with status 0. The probes created a terminal, entered and
|
||
left overview, and toggled maximize, fullscreen and floating twice each.
|
||
The second session exercised `theme.typography.font_family`, wallpaper fit/fill,
|
||
and replacement image paths through filesystem-driven reloads. Logs confirmed
|
||
accepted reloads and replacement wallpaper decoding. This verifies execution
|
||
and liveness, not pixel-perfect appearance or GPU allocation-failure recovery.
|
||
The first probe used an incorrect font key; only the second probe counts as
|
||
font reload coverage.
|
||
|
||
Raw local artifacts:
|
||
|
||
- `/tmp/ferese-profile-7cbrpopn/{results.json,session.log}`
|
||
- `/tmp/ferese-profile-h2ypepn6/{results.json,session.log}`
|
||
- Probe scripts: `/tmp/ferese-profile.py` and `/tmp/ferese-profile-reloads.py`
|
||
|
||
## Limits and next work
|
||
|
||
The installed DRM compositor is release `20260927-042025-168287`; its binary hash
|
||
differs from the tested release build. A read-only host sample measured 9.8%
|
||
compositor CPU and 1.7% shell CPU, but overlapped the nested workload and is not
|
||
an idle baseline or validation of the new DRM code. No session was replaced.
|
||
Only one monitor was connected, so multi-monitor behavior remains untested.
|
||
|
||
Next investigate nested refresh scheduling and repeated overview damage, using
|
||
these scenarios for before/after measurements. Preserve client frame callbacks,
|
||
animation pacing and idle responsiveness. Status command polling remains a
|
||
secondary target; blur invalidation needs a separate translucent-material
|
||
workload before ranking its cost. New-build DRM and multi-monitor validation
|
||
remain necessary before claiming hardware performance improvements.
|
||
|
||
## Follow-up investigation
|
||
|
||
A repeat baseline (`/tmp/ferese-profile-n8437l1i`) confirmed 60 Hz after startup
|
||
but ran near 60 attempts/s rather than 118. Compositor CPU was 3.0% idle, 9.2%
|
||
in overview and 4.6% after transitions. The earlier high-rate behavior is not
|
||
consistently reproduced, so it is not sufficient evidence of duplicate redraws.
|
||
The candidate refresh-period guard was discarded after testing.
|
||
|
||
The guard run (`/tmp/ferese-profile-llcrkvm5`) measured 4.0% idle, 8.6% overview,
|
||
and 3.6% after transitions. Render attempts fell slightly below the nominal
|
||
cadence. This did not establish a benefit, so no frame-pacing change was kept.
|
||
The next investigation is unconditional thumbnail damage in overview.
|
||
|
||
## Broader profiling (visible preview)
|
||
|
||
Artifacts: `/tmp/ferese-profile-all-0lhodit4/`; script:
|
||
`/tmp/ferese-profile-all.py`. This run used the unchanged release compositor,
|
||
release shell and release Settings. The user kept the preview visible after an
|
||
earlier hidden-window run proved that host occlusion invalidated render samples.
|
||
Six-second samples are exploratory, not statistically stable benchmarks.
|
||
The captured output was 2856 × 1696 pixels; the preview was resized during the
|
||
session, so these workloads must not be compared as if output size were fixed.
|
||
|
||
| Scenario | Compositor CPU | Shell CPU | Shell waited-child CPU | Settings CPU |
|
||
| --- | ---: | ---: | ---: | ---: |
|
||
| Idle shell | 4.33% | 1.33% | 7.50% | — |
|
||
| Three terminals | 6.67% | 1.50% | 11.33% | — |
|
||
| Overview, three terminals | 17.50% | 1.17% | 8.17% | — |
|
||
| Settings open, idle | 6.17% | 1.50% | 10.83% | 0.00% |
|
||
| After capture/window transitions | 8.50% | 1.33% | 11.00% | 2.50% |
|
||
|
||
Percentages use one CPU core as 100%. Child CPU is Linux's accumulated CPU time
|
||
for reaped children, separate from the shell process. This includes the timing
|
||
wrapper: a PATH-local `timeout` wrapper invokes GNU time and the real timeout
|
||
command. Read-only PipeWire queries use the host runtime socket; no status
|
||
control actions were sent. The native installed shell cross-check, without
|
||
instrumentation, measured 2.0% self CPU plus 8.8% waited-child CPU over ten
|
||
seconds. It is an older binary and a different scene, so this corroborates the
|
||
existence of subprocess overhead rather than providing an exact comparison.
|
||
|
||
The preview recorded 273 successful status command invocations across 21 polls:
|
||
13 per poll. The three login1 capability calls consumed 1.88 seconds total wall
|
||
time, about 90 ms/poll. Three nmcli queries consumed 1.35 seconds total; two wpctl
|
||
queries consumed 0.80 seconds. GNU time's hundredth-second granularity makes
|
||
per-command CPU totals unsuitable for precise attribution. Stable capability
|
||
queries, device metadata and frequently changing values currently share one
|
||
poll cycle. Separate refresh policies or service subscriptions deserve priority.
|
||
|
||
Settings mapped in 109 ms, measured from process launch until IPC reported it
|
||
focused (not first completed paint). Idle Settings RSS was 108068 KiB, with 38
|
||
threads and 65 descriptors. Zero measured CPU over six seconds means below this
|
||
sample's resolution, not a guarantee of zero work. Startup font enumeration was
|
||
not isolated separately. Settings interaction/search/page-switch profiling is
|
||
still outstanding.
|
||
|
||
Twenty `feresectl get-outputs` calls had median process-inclusive latency of
|
||
3.05 ms and maximum 3.95 ms. Five captures took 1.13 seconds total, including
|
||
client startup, transfer and PNG encoding. Capture was enabled only in the
|
||
isolated compositor with `FERESE_ENABLE_SCREENCOPY=1`. The first screenshot was
|
||
visually inspected and showed the shell and Settings rendered. It does not
|
||
measure GPU readback independently. Window maximize/fullscreen/layout toggles
|
||
completed, but there was no instrumented input-to-photon latency measurement.
|
||
|
||
The four-second visible overview perf recording (`cpu-clock:u`, 999 Hz) collected
|
||
471 samples with no lost samples. Shared-object attribution: 47.77% Ferese
|
||
executable (including statically linked dependencies), 30.36% Mesa Gallium,
|
||
14.23% libc, 2.97% Wayland client library. Approximately 96.4% of samples were in
|
||
the main compositor thread. Texture upload/memory-copy paths appear in the
|
||
sampled stacks. This is user-space CPU sampling, not GPU timing; short samples
|
||
and incomplete frame-pointer unwinding limit function-level attribution.
|
||
|
||
The earlier broad run (`/tmp/ferese-profile-all-boiq_r30`) had a 37-second render
|
||
reporting gap while hidden, no usable perf samples, and a disabled screencopy
|
||
protocol. Its render/overview and capture results are excluded. Its bare idle
|
||
sample was 1.0% CPU, but visibility differences preclude subtracting it from the
|
||
visible shell run.
|
||
|
||
Next priority: shell status subprocesses. Then investigate sustained overview
|
||
damage and texture uploads with controlled window visibility and dimensions.
|
||
Control-center interaction, widgets, translucent-material workloads, long-term
|
||
memory growth, new-build DRM and multi-monitor runs remain unprofiled. No source
|
||
changes or profiling-note commits were made during this profiling pass.
|
||
|
||
## Status-worker optimization comparison
|
||
|
||
Compared the production status module at `08a5866` with the new single-command
|
||
brightness reader and reusable native D-Bus connections. A standalone harness
|
||
started `Service::start(None)`, consumed updates every 250 ms, and ran for 20
|
||
seconds without a UI. Both variants used the same host runtime/services and
|
||
command timing wrapper. Artifacts: `/tmp/ferese-status-compare/`.
|
||
|
||
Both completed nine polls. Notifications were available in this comparison, so
|
||
the original worker launched 15 commands/poll and the new one launched 10:
|
||
135 versus 90 invocations. Four busctl launches and one brightnessctl launch per
|
||
poll were removed. When notifications are absent, the equivalent counts are
|
||
13 versus 8. Poll frequency, fresh capability queries and post-action generation
|
||
handling remain unchanged; no status-value caching was introduced.
|
||
|
||
GNU time measured total user+system CPU, including waited children: 2.41 seconds
|
||
before, 1.90 seconds after over 20.01 seconds each (about 21% lower in this single
|
||
comparison). Timing-wrapper overhead is included, so this is not a precise
|
||
uninstrumented CPU improvement estimate. Native D-Bus adds persistent sockets
|
||
and a runtime thread pool; UI samples observed roughly 26–28 shell threads and
|
||
48 descriptors versus roughly 18 threads and 42 descriptors previously.
|
||
|
||
One UI run showed a transient self-CPU spike; a subsequent perf recording
|
||
identified tiny-skia raster work rather than D-Bus queries among its hot paths.
|
||
The UI-free comparison avoids treating variable UI activity as polling cost.
|
||
|
||
Validation: workspace suite and explicit private-dbus-daemon test passed. The
|
||
private bus exercised live capability changes, yes/challenge/no/na handling,
|
||
notification-owner appearance/disappearance, error replies, missing methods,
|
||
and a reply delayed past the configured timeout. Brightness parser tests cover
|
||
rounding, zero brightness, invalid classes and malformed/nonfinite values.
|
||
|
||
### Push status delivery measurement (b0583eb)
|
||
|
||
Compared release binaries for 4bf0757 and b0583eb with the same nested compositor,
|
||
empty isolated config and a visible preview. Binary SHA-256 hashes differ and
|
||
were checked after forcing a shell rebuild to avoid shared-target artifact reuse.
|
||
No status-command timing wrappers were active. Discarded an initial baseline
|
||
that overlapped compilation. Retained after/before/after runs, each with three
|
||
10-second samples following a three-second startup settle. Artifacts and harness:
|
||
`/tmp/ferese-status-push-measure/` (`summary.json`, `ui.py`, `latency.rs`).
|
||
|
||
Shell CPU percentages of one core:
|
||
- Before: 1.5, 1.4, 1.6 (median 1.5).
|
||
- After run 1: 0.8, 1.1, 1.1.
|
||
- After run 2: 1.0, 1.1, 7.5 (combined after median 1.1).
|
||
|
||
The 7.5% sample coincided with a large damaged-pixel burst (44.3 million in a
|
||
five-second interval), higher RSS and two additional descriptors. Its cause was
|
||
not isolated; it is retained, not silently excluded. Combined after mean is
|
||
2.1%, versus 1.5% before, so these short runs do not establish a robust average
|
||
CPU saving. Typical steady-state samples suggest a 0.4 percentage-point reduction.
|
||
Status child CPU remained roughly 6–8% of one core, dominating shell self CPU.
|
||
|
||
Steady-state damaged frames were about 12/s before versus 6.6–7.6/s in the first
|
||
after run. The repeat had similar quiet intervals plus bursts at 15.6 and 22.7/s.
|
||
Total render attempts stayed around 60/s, confirming active rather than hidden
|
||
preview sampling. No claim is made about DRM-session CPU or total idle wakeups.
|
||
|
||
An isolated transport harness used the old bounded mpsc + 250ms polling path and
|
||
an extracted copy of the new Updates::stream implementation, with 40 synthetic
|
||
updates per path at 317ms intervals. Worker-to-consumer delay:
|
||
- Before: median 129.398ms, p95 239.957ms, maximum 245.881ms.
|
||
- After: median 0.130ms, p95 0.330ms, maximum 0.425ms.
|
||
This measures delivery only, excluding status queries, UI dispatch and painting.
|
||
The preview processes exited and the temporary source worktree was removed.
|