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
+4
View File
@@ -6,6 +6,7 @@ import typer
from rich import print
from .config import load_settings, override_settings
from .memtune import release_freed_memory
from .pipeline import MattingPipeline, run_image
app = typer.Typer(help="Offline flat-background character matting.")
@@ -69,6 +70,9 @@ def main(
dbg = None if debug_dir is None else debug_dir / image_path.stem
result = pipeline.run_image(image_path, out_path, dbg)
print(f"[green]wrote[/green] {result['output']}")
# Free this image's activations back to the OS before the next one
# so batch RSS tracks a single image, not the whole run.
release_freed_memory()
return
if input is None or output is None: