mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-20 09:59:09 +00:00
Measured on a live rig 2.5 hours after start:
RSS 1030 MB
Private_Dirty 988 MB
anonymous mappings > 10 MB 23
largest 104, 79, 66, 63, 63 MB, on 64 MB-aligned addresses
threads 9
cores 3 -> glibc ceiling = 8 x 3 = 24 arenas
23 against a ceiling of 24, all 64 MB-aligned: these are glibc's per-thread
malloc arenas, not live objects. The data the process was actually holding
accounts for perhaps 15 MB -- the widest scroll strip observed was 35,746 x 64,
about 7 MB as RGB and the same again for its numpy mirror.
It is bloat rather than a leak: sampled four times over 135 seconds, RSS sat
between 990 and 1030 MB rather than climbing. glibc gives each allocating
thread its own arena, grows them to hold peak demand, and never gives them
back. A process that builds and drops large images across several threads is
exactly the shape that produces this.
The device had 59 MB free at the time, on 1845 MB total.
MALLOC_ARENA_MAX=2 trades a little allocator concurrency for that resident
memory. It is a tuning knob rather than a fix for a defect, so the rationale
and the measurements sit next to it in the unit file, and a test asserts they
stay there -- a bare environment variable invites removal by whoever meets it
next.
Two things this is NOT, both checked rather than assumed:
- Not an OOM problem today. A grep for "oom" in the service journal returned
24 matches, all of which were the radar logging zoom=9 and zoom=7. The kernel
OOM killer has not fired: dmesg has zero matches.
- Not currently capped by the unit's MemoryMax=85% either. That directive is in
this file but absent from the unit actually installed on the rig, which
reports MemoryMax=infinity, so nothing is enforcing a ceiling there.
The saving is unmeasured on hardware: applying it needs a service restart,
which blanks the panel, so that is the user's call rather than something to do
mid-audit. If p99 frame time regresses -- it sits at 18.4 ms against a 16.7 ms
budget for 60 FPS, so there is not much headroom -- raise the value rather than
remove it.
(cherry picked from commit 446207ffbc)
53 lines
2.5 KiB
Desktop File
53 lines
2.5 KiB
Desktop File
[Unit]
|
|
Description=LED Matrix Display Service
|
|
After=network-online.target
|
|
Wants=network-online.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=root
|
|
WorkingDirectory=__PROJECT_ROOT_DIR__
|
|
Environment=PYTHONDONTWRITEBYTECODE=1
|
|
# glibc gives each allocating thread its own malloc arena, up to 8 x CPU count,
|
|
# and an arena that has grown is never handed back to the OS. This process runs
|
|
# 9 threads on a 3-core Pi, so the ceiling is 24 arenas -- and a rig measured at
|
|
# 1030 MB resident held 23 large anonymous mappings on 64 MB-aligned addresses,
|
|
# 920 MB of them, while the live data it was actually holding (widest scroll
|
|
# strip seen: 35,746 x 64) accounts for roughly 15 MB. That gap is arena bloat,
|
|
# not leaked objects: RSS was flat across repeated sampling, not climbing.
|
|
#
|
|
# Capping the arenas trades a little allocator concurrency for a large amount of
|
|
# resident memory on a device that has neither to spare. 2 is the usual value;
|
|
# raise it if frame times regress.
|
|
Environment=MALLOC_ARENA_MAX=2
|
|
ExecStart=/usr/bin/python3 __PROJECT_ROOT_DIR__/run.py
|
|
# Restart=always, not on-failure: run.py exiting 0 (a clean shutdown path taken
|
|
# for a reason that no longer applies, e.g. a config reload) would otherwise leave
|
|
# the service stopped and the panel dark indefinitely, with systemd considering
|
|
# that a successful outcome and never bringing it back.
|
|
Restart=always
|
|
RestartSec=10
|
|
# Memory ceiling as a share of physical RAM, so one unit file suits a 512 MB
|
|
# Pi Zero 2 W and an 8 GB Pi 5 alike. This is a backstop, not a tuning knob: it
|
|
# turns "the board runs out of memory, stops being able to fork, and takes sshd
|
|
# and the panel down together until someone pulls the plug" into "this one
|
|
# service restarts".
|
|
#
|
|
# NOTE: Raspberry Pi firmware boots the kernel with cgroup_disable=memory, and
|
|
# systemd accepts this setting and then silently ignores it. Verify with:
|
|
# grep memory /sys/fs/cgroup/cgroup.controllers
|
|
# If that prints nothing, add "cgroup_enable=memory cgroup_memory=1" to
|
|
# /boot/firmware/cmdline.txt (all on line 1) and reboot. first_time_install.sh
|
|
# does this for you.
|
|
MemoryMax=85%
|
|
StandardOutput=journal
|
|
StandardError=journal
|
|
SyslogIdentifier=ledmatrix
|
|
# Support for on-demand plugin filtering via environment variable
|
|
# The environment variable LEDMATRIX_ON_DEMAND_PLUGIN can be set via:
|
|
# sudo systemctl set-environment LEDMATRIX_ON_DEMAND_PLUGIN=<plugin_id>
|
|
# Or by using an EnvironmentFile (see below)
|
|
# EnvironmentFile=__PROJECT_ROOT_DIR__/config/on_demand_env.conf
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target |