https://github.com/velvia/ying-profiler --- # Ying is Super Easy to Use I built this to make debugging OOMs and Memory Leaks _EASY_. ```rust use ying_profiler::YingProfiler; #[global_allocator] static YING_ALLOC: YingProfiler = YingProfiler::default(); fn main() { YING_ALLOC.start_profiling(); // ... the rest of your program } ``` Default settings: - Checks every 5 minutes - Writes flamegraphs and reports retained memory IF retained memory has changed by >= 10% - Designed to give you very succinct but useful reports --- # `ls` and you Get the Story ```bash $ ls -l ying-profiles/ total 440 -rw-r--r-- 1 evan staff 17583 Sep 9 16:45 ying.2026-09-09T16:45:05-04:00.15MB.report -rw-r--r-- 1 evan staff 50492 Sep 9 16:45 ying.2026-09-09T16:45:05-04:00.15MB.svg -rw-r--r-- 1 evan staff 16254 Sep 9 16:45 ying.2026-09-09T16:45:15-04:00.23MB.report -rw-r--r-- 1 evan staff 49262 Sep 9 16:45 ying.2026-09-09T16:45:15-04:00.23MB.svg -rw-r--r-- 1 evan staff 26162 Sep 9 16:48 ying.2026-09-09T16:48:51-04:00.28MB.report -rw-r--r-- 1 evan staff 50765 Sep 9 16:48 ying.2026-09-09T16:48:51-04:00.28MB.svg ``` - Each report/flamegraph gives you ISO timestamp and retained memory size - Filenames tell you the allocation story of the app - Easily pick out the most interesting or peak flamegraph --- # How does Ying Work? - Ying implements `GlobalAlloc` - passing on the alloc call to default System allocator - NOTE: only profiles Rust allocations through GlobalAlloc. Does not measure C++ or FFI code. - Only records a sample of new allocations - free's and reallocs are checked against existing sampled allocations - Sharded HashMap - `Arc<[RwLock]>` - equiv of parking_lot but no allocations during lock --- # Profiler Configuration ```rust pub struct YingProfiler { /// Allocation sampling ratio. Eg: 500 means 1 in 500 allocations are sampled. sampling_ratio: u32, /// Prevent and dump stack trace for giant single allocations beyond a certain size single_alloc_limit: usize, ... } ``` --- # Automatic Reporting Configuration ```rust pub struct ProfilerRunner { /// Number of seconds in between memory checks #[builder(default = "DEFAULT_CHECK_INTERVAL_SECS")] check_interval_secs: usize, /// Percent change in retained memory to trigger a report #[builder(default = "DEFAULT_PCT_CHANGE_TRIGGER")] report_pct_change_trigger: usize, /// Path to write top retained memory reports to #[builder(default)] reporting_path: String, /// Expand inlined call stack symbols with a > ? #[builder(default = "false")] expand_frames: bool, /// Generate flamegraphs at reporting_path #[builder(default = "false")] gen_flamegraphs: bool, } ``` --- # Writing a Memory Profiler: Don't Do It - Do you like pain??? :) - Implementing and testing custom GlobalAlloc is NOT for the sane - Must use `unsafe` :( - Cannot use data structures that allocate unless we protect against re-entrancy - We use an Atomic thread-local as a re-entrancy counter - Protect against all places where reentrancy could occur - Standard lazy ThreadLocals are NOT safe - must use const thread locals - Cannot use any data structure whose locking allocates --- # Thank You Very Much! .left-column[ * https://velvia.github.io/about * https://github.com/velvia * [@evanfchan](https://twitter.com/Evanfchan) * [IG: @platypus.arts](http://instagram.com/platypus.arts) ] .right-column[ ]