A deep dive into optimizing the garbage collector for Plush, a toy Lox-like language with actor-based parallelism. The original copying GC used a Rust HashMap to track forwarding pointers instead of traditional forwarding pointers embedded in object headers, which turned out to be extremely slow due to Rust's secure (HashDoS-resistant) hashing and poor cache locality. Switching to FxHashMap doubled speed (117ms to 43ms for one million objects), but rewriting the GC to use the traditional Cheney algorithm with forwarding pointers brought collection time down to 7ms, a 16.7x improvement overall. Also covered: using mmap with PROT_NONE and mprotect to create resizable, pointer-stable memory regions for actor message buffers, eliminating the previous 16MB message size cap.

12m read timeFrom pointersgonewild.com
Post cover image

Questions this post answers

Why is Rust's default HashMap slower than expected for performance-critical code?

Rust's default HashMap uses a secure hashing function (SipHash) designed to protect against HashDoS attacks, which adds overhead compared to non-cryptographic hashing. Swapping in FxHashMap from the rustc_hash crate, a drop-in replacement maintained by the rust-lang project, more than doubled throughput in one garbage collector benchmark, cutting collection time from 117ms to 43ms for a million objects. Developers chasing hot-path performance regressions in Rust can find writeups like this on daily.dev.

How can I resize a large buffer in Rust without invalidating existing pointers into it?

Use mmap to reserve a large chunk of virtual address space with MAP_PRIVATE | MAP_ANONYMOUS and PROT_NONE, then mprotect individual pages to PROT_READ | PROT_WRITE as needed. Since virtual address space is roughly 128TB on macOS and Linux, reservations like 512GB cost nothing until touched, letting you grow or shrink a vector-like structure while keeping all existing pointers valid. daily.dev surfaces low-level memory tricks like this for engineers designing custom allocators.

Why is a traditional Cheney copying garbage collector faster than one using a hash map for forwarding pointers?

A hash map storing object-to-copy correspondences is slower because hash functions distribute entries in a quasi-random order, causing unpredictable, cache-unfriendly memory access, and because the hash table itself can consume more memory than the data being copied. Reverting to the classic Cheney design, which embeds forwarding pointers directly in object headers and uses the to-space as a linear work list, reduced garbage collection time for one million live objects from 43ms to 7ms. Engineers optimizing runtime internals can track deep GC design tradeoffs like these on daily.dev.

35.1K Impressions