Return freed activations to the OS on Linux; recommend jemalloc (A+B)

MIMALLOC_PURGE_DELAY=0 only bites on Windows (bundled mimalloc). On a Linux
server PyTorch uses glibc ptmalloc, which keeps a BiRefNet@2048 forward's freed
activations in the arena, so RSS ratchets up across requests and the env var is
a no-op there.

A. bgfilter/memtune.py: release_freed_memory() calls glibc malloc_trim(0) at
   runtime (no-op on Windows/musl/other allocators). Wired into service.process
   (after each request) and the CLI batch loop (after each image), the two
   long-lived paths where RSS accumulates. Single-image CLI exits, so it is left
   alone.
B. DEPLOY(_ZH): document preloading jemalloc via LD_PRELOAD + MALLOC_CONF (also
   improves CPU throughput) as the production alternative, with MALLOC_ARENA_MAX/
   MALLOC_TRIM_THRESHOLD_ as an allocator-free fallback. Under jemalloc/tcmalloc
   malloc_trim simply no-ops.

Also fixes the stale off-by-default cross-check heading in DEPLOY.md.

Verified: modules import; release_freed_memory() returns False (no-op) on Windows.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-06 16:40:58 +08:00
parent ad445611ad
commit 3992f65ebf
6 changed files with 85 additions and 2 deletions
+16 -1
View File
@@ -156,7 +156,7 @@ hf download ZhengPeng7/BiRefNet_HR-matting --local-dir models/BiRefNet_HR-
Override the base directory with `BGFILTER_WEIGHTS_DIR`. `models/` is gitignored.
### Cross-check veto (optional, off by default in the service)
### Cross-check veto (on by default)
The pipeline runs a second, high-resolution matting model
(`ZhengPeng7/BiRefNet_HR-matting`, ~425 MB) as a "second opinion" that clears
@@ -291,6 +291,21 @@ service has **no authentication**. To reach it from other hosts:
balancer (each with `--workers 1`).
- Memory: one worker holds all three models (~1 GB weights + runtime). `MemoryMax=8G`
is a safe ceiling for a single CPU worker.
- **Keeping RSS flat (glibc).** After each image the service calls glibc
`malloc_trim(0)` (`bgfilter/memtune.py`) to hand a large BiRefNet@2048 forward's
freed activations back to the OS — without it, ptmalloc keeps them in the arena
and RSS ratchets up across requests. For lower memory *and* better CPU
throughput, preload **jemalloc** (PyTorch's recommendation for CPU inference)
and let it decay dirty pages aggressively:
```ini
# in the [Service] section of the systemd unit
Environment=LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2
Environment=MALLOC_CONF=background_thread:true,dirty_decay_ms:0,muzzy_decay_ms:0
```
(`apt install libjemalloc2`; tcmalloc via `libtcmalloc_minimal.so.4` works too.)
jemalloc/tcmalloc manage their own release, so `malloc_trim` simply no-ops under
them. If you cannot preload an allocator, `MALLOC_ARENA_MAX=2` and
`MALLOC_TRIM_THRESHOLD_=0` in the unit environment curb ptmalloc retention.
## 12. Troubleshooting