Skip to content

Cut idle session memory by 12 MB - #10

Merged
linsaftw merged 9 commits into
ArkFlame:mainfrom
infectedwarelol:idle-memory-under-10mb
Oct 28, 2026
Merged

linsaftw merged 9 commits into
ArkFlame:mainfrom
infectedwarelol:idle-memory-under-10mb

Conversation

@infectedwarelol

Copy link
Copy Markdown
Contributor

Cuts idle memory of the X11 session from 51.3 MiB to 39.2 MiB total PSS (dual 1280x800 dummy outputs, 30 s idle), with no visible change. Also drops the minimize and two-screens rows from the README screenshot table.

The 10 MB goal is not reached. What is left is mostly resident code and shared libraries, plus the desktop's wallpaper copy. Getting further needs design trade-offs, listed below.

How it was measured

python3 -I tools/flamewm_visual_tests.py --bin-dir .target/release --layout dual idle-memory

This PR adds the idle-memory command. It starts the headless session, waits 30 s, then reads /proc/<pid>/smaps for every FlameWM process. Each process gets PSS, RSS and thread count, split into binary, libs, heap, stacks, fonts and shm. Heap attribution came from heaptrack runs against a build with symbols.

Process Before PSS After PSS (mean of 3) Delta
flamewm (WM) 9403 KiB 7796 KiB -1607
flamewm-desktop 26493 KiB 17788 KiB -8705
flamewm-shell 12642 KiB 10523 KiB -2119
flamewm-shell --quick-control-host 2382 KiB 2491 KiB +109 (noise)
flamewm-session 1608 KiB 1567 KiB -41
Total 52528 KiB (51.3 MiB) 40164 KiB (39.2 MiB) -12364

The three runs after the changes came in at 39.15, 39.33 and 39.19 MiB.

Changes

  • Shared images: if a node already shows identical pixels, the document now takes the caller's new buffer and lets go of its old one. The desktop decodes the wallpaper twice at startup, so before this the old copy stayed alive next to the new one: two 8 MB root canvases. (-7.9 MB)
  • Fat LTO for release builds. opt-level stays at 3. (-1.7 MB)
  • Heap trim in the shell, 10 s after start and on the existing minute clock tick. Freed heap between live chunks never reaches the trim threshold. (-1.1 MB)
  • Icon index: base names now sit in one sorted string, looked up by binary search. Before, each name had its own boxed string in a hash map. The WM, desktop and shell each build this index. (-1.3 MB)
  • Heap trim in the WM and desktop once startup has settled. (-0.4 MB total, -0.66 MB heap)
  • Two small fixes so the checks pass again: rustfmt drift and a clippy map_unwrap_or in the shell icon cache.

Checks

  • cargo fmt --check: pass
  • cargo clippy --workspace -- -D warnings: pass
  • cargo test --workspace --no-fail-fast: everything passes except two tests that also fail on main:
    • xrender_aa::clipped_arc_tests::clipped_runs_preserve_partial_corner_coverage fails on main too.
    • flamewm-remote-fs list_cache_serves_within_ttl_and_invalidates_on_mutation is timing-flaky, 1 in 5 locally. That crate is not touched here.
  • tools/flamewm_visual_tests.py --layout both: all checks pass.
  • Idle screenshots of the 2-screen layout before and after differ only in the clock digits.

What is left (not applied, each needs a trade-off)

Option Estimated saving Trade-off
Drop the desktop's client-side wallpaper RGBA once the X pixmap exists ~8 MB The image cache evicts by entry count, so the renderer would need a pinned image or a way to ask for the pixels again. Otherwise the wallpaper can go blank.
opt-level = "s" workspace-wide ~3 MB WM CPU during a contents-while-dragging move went from 5.5% to 7.6% of a core.
Start the quick-control helper on first use ~2.5 MB The first audio/network/calendar popup opens slower (process start).
Load libpulse lazily in the WM (pulls in sndfile, vorbis, opus, FLAC, mp3lame) ~1.8 MB Needs dlopen/unsafe FFI, which means a new architecture exception.
Share one icon index across processes (mmap'd cache file) ~1.2 MB Needs unsafe mmap, which means a new architecture exception.

Everything else I looked at came in under 200 KB, which is below the run-to-run noise on resident code pages (about ±200 KB). Memory figures in the README and docs/PERFORMANCE.md are unchanged because the 10 MB target was not met.

The minimize GIF is already gone and nothing else uses two-screens.webp,
so the second row of the screenshot table goes with them.
Clippy's map_unwrap_or lint rejected the map().unwrap_or_else() chain, which
broke cargo clippy --workspace -D warnings.
Starts the headless session, waits for it to settle (30 s by default) and
prints PSS, RSS and thread count for every FlameWM process, split into
binary, libraries, heap, stacks, fonts and shared memory, plus the total.
When a node already showed identical pixels, the document kept its old Arc
and the caller kept the new one, so both copies stayed alive. The desktop
decodes the wallpaper twice at startup (initial request, then the settings
snapshot), which left two 8 MB root canvases resident on a dual 1280x800
layout. Swapping in the new Arc frees the old copy without bumping the
image revision.
Whole-program LTO trims about 1.7 MB of resident code across the idle
session (WM, desktop, shell, session) with opt-level 3 kept, so the drag
and blur paths keep their codegen.
After startup the shell kept about 1.3 MB of freed heap that sat between
live chunks, where the trim threshold never reaches it. Trim once, ten
seconds after start, and again on the existing minute clock tick so later
churn does not pile up either. No new wakeups beyond the one-shot timer.
Each base name used to be its own boxed string behind a hash table. The
WM, desktop and shell each build this index, so keeping the names in one
buffer and finding them by binary search saves roughly 0.2-0.4 MB of heap
per process.
Same one-shot trim the shell does ten seconds after start. The WM keeps
about 0.5 MB less freed heap from building the catalog and icon index, the
desktop about 0.1 MB.
@linsaftw
linsaftw merged commit 5357394 into ArkFlame:main Oct 28, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants