Expand description
Shuyi Engine’s heap counter (ADR-0046 §9): CountingAlloc, a global
allocator around another that counts what is asked of it, and with the
global feature, the global module: the one counting allocator of
every debug build of a game on shuyi-app and of its tool program.
What it counts:
- the bytes asked for, each
Layout’s size: never the inner allocator’s own overhead, so a reading is what the program asked for, the same on every machine; - live bytes, allocated and not yet freed, and their peak, the
most at once since the start or a
CountingAlloc::reset_peak; - calls to
alloc,alloc_zeroedandrealloc, taken: each read (CountingAlloc::take_allocs) gives the calls since the last.
A global allocator counts the whole process: every thread’s allocations count, not only the game’s.
Debug builds count; release builds don’t. shuyi-app and
shuyi-cli turn on global, so a game’s debug build and its tool
program’s run on global’s counter, and App puts its counts in
each frame’s record (shuyi_ui::perf::FrameRecord::heap). A game with
a #[global_allocator] of its own opts out, since a binary has one:
in its .cargo/config.toml,
[build]
rustflags = ["--cfg", "shuyi_no_heap_count"]Without std the crate is no_std: a console’s backend wraps the
SDK’s own allocator in a CountingAlloc.
Memory tags (ADR-0046 §6) say what the bytes are for. A
CountingAlloc::with_tags also counts each block under the tag
current on its thread when it was allocated: the frame’s phase
(update, draw, …), the game’s span, or the asset kind being
decoded, the innermost one entered winning (tags). Its
rows give each tag’s live bytes and blocks,
the bytes ever allocated under it and its peak (TagCounts). Tags
come from a process-wide table of 256: tag 0 is other, what is
allocated outside any tag, and tag 255 overflow, which every name
past the 254th shares (tags::overflowed counts them). A realloc
moves its block to the current tag, so a list that grows in update
counts in update.
The cost: each block carries its tag in a header in front of it,
of its alignment or 8 bytes if that is more (Tagged), and each call
counts twice. The global counter has tags, in debug builds only; a
CountingAlloc::new has none, and no header.
Modules§
- global
- The one counting allocator of a debug build (the
globalfeature): a#[global_allocator]crate::CountingAlloc::with_tagsoverstd::alloc::System, andreadingandrows, which read it. What it counts goes under the tags the program enters (crate::tags):shuyi-appandshuyi-clienter the frame’s phases, the game’s spans and each asset kind’s decode. - tags
- Memory tags (ADR-0046 §6): what a
CountingAlloc::with_tagscounts each block under. A tag is a number,0to255, for a name in a process-wide table: the frame’s phase, the game’s span, or the asset kind being decoded. Code tags what it allocates by entering a tag around it, and the innermost tag entered wins:
Structs§
- Counting
Alloc - A global allocator around
innerthat counts what is asked of it: the bytes live, their peak, and the calls toalloc,alloc_zeroedandrealloc. It counts eachLayout’s size, never the inner allocator’s own overhead. - Counts
- What a
CountingAlloccounted, read at once (CountingAlloc::counts). Build it withCounts::new(ADR-0039). - TagCounts
- One memory tag’s counts in a
CountingAlloc::with_tags(CountingAlloc::rows), in bytes asked for. Build it withTagCounts::new(ADR-0039). - Tagged
- What a
CountingAlloc::with_tagscounts by tag: for each of theTAGS, its live bytes and blocks, the bytes ever allocated under it, and the peak of its live bytes. - Untagged
- What a
CountingAlloc::newcounts by tag: nothing, and no header in front of a block.