Skip to content

Input wanted before I measure: is the retained allocator slack fragmentation, or one allocation pattern in aft? #205

Description

@iceteaSA

Follow-on from #201, where the shipped malloc_trim(0) relief turned out not to reclaim anything on a long-lived Linux daemon. The free space is pinned beneath live allocations, so trim structurally can't return it.

The open question is which of two things that is:

mechanism remedy whose code
ordinary heap fragmentation allocator with decay-based purging aft's #[global_allocator]
one pathological allocation pattern fix the site aft, specific

mallinfo2 gives aggregates only. The per-arena and per-bin free-space distribution separates the two, and that's malloc_info(3) — which the crate doesn't bind today.

I've designed an experiment and I'd rather ask before building it, because you may already know the answer or want it shaped differently. Specifically:

  1. Do you have prior context on the allocation pattern? Long-lived per-root artifacts (search index, semantic index, callgraph store, symbol cache) are each individually capped but have no aggregate ceiling across roots — the daemon here held 7+. Many large, long-lived, differently-sized buffers arriving and departing on independent schedules is a textbook way to strand free space. If that's a known shape to you, the experiment may be unnecessary.

  2. Has a non-default allocator been considered and rejected? jemalloc/mimalloc with decay purging is the obvious remedy for fragmentation-pinned space, and it's a #[global_allocator] change with real consequences — binary size, the panic = "unwind" interaction with ONNX Runtime, platform matrix. If there's a standing reason not to, I'd like to know before I spend a run producing evidence for it.

  3. Would you shape the measurement differently? My rig is subc_storm_test.rs — it already drives binds, tool traffic, watcher churn, and semantic collection across N roots via AFT_STORM_SCALE, and it spawns its own ephemeral daemon so nothing production-facing is touched. One known weakness: it runs the module in-process, so malloc_info measures the whole test binary. I'm handling that with start/end deltas plus a zero-workload control arm to measure the scaffolding's own contribution, with a dedicated bin target as fallback if that control shows the scaffolding isn't constant.

Design in brief — four arms (scaffolding-only, stock, MALLOC_ARENA_MAX=2 as a negative control on the rig, decay-purging allocator), malloc_info + mallinfo2 + smaps census at start and end, predictions registered before running so the outcome can't be interpreted after the fact. Full design doc available if useful; happy to paste it here or open it as a PR against a docs path if you'd prefer it in-repo.

What I'm not doing: proposing an allocator change. That would need this evidence first, its own gate cycle, and your agreement that it's a direction worth taking at all.

Context on severity, so this is priced correctly: after #202 landed (inspect threads 88 → 8 on the same daemon) the live instance sits at ~2.9 GB swap after 12 h, against 15.3 GB at 18 h before. It's churn-bound rather than time-linear — regions and VmData go flat across idle hours. So this isn't urgent, and I'd rather spend the effort in the direction you'd actually use.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions