My Memory Allocator Journey: From HZ3 to HZ12

residualconn Beginner 2h ago 428 views 8 likes 2 min read

I’ve spent an absurd amount of time—far more than anyone would expect for a project that doesn't pay a dime—refining a personal memory allocator project called Hakozuna. I just hit the 12th generation (HZ12), and honestly, the engineering evolution from a simple high-speed allocator to this complex research-oriented beast has been a wild ride. I finally put all the findings from the HZ10, HZ11, and HZ12 iterations into a research paper on Zenodo to make sense of the madness.

If you're interested in the deep technical details, you can find the documentation and the source code here:

Paper: https://doi.org/10.5281/zenodo.21360690
GitHub: https://github.com/hakorune/hakozuna

The Evolution of Hakozuna

Hakozuna isn't just one tool; it's a family of allocators designed to push the boundaries of malloc/free performance, RSS (Resident Set Size) management, and safety. Each version was a response to a specific engineering bottleneck:

  • HZ3: Focused on high-speed local-heavy allocation.

  • HZ4: Introduced remote-free and message-passing mechanisms.

  • HZ5: Targeted low RSS via descriptor ownership.

  • HZ8: My current "stable" recommendation for general-purpose use.

  • HZ11: A speed-first experiment using ownerless recycling.

  • HZ12: The latest iteration, focusing on advisory ownership and bounded span reclamation.
  • The HZ11 Experiment: Speed vs. Visibility

    In HZ11, I went all-in on performance. The goal was to eliminate the overhead of ensuring that a thread freeing an object always returned it to the original owner. Instead of strict ownership, I implemented a tiered structure:

    front cache ──> transfer cache ──> central spans ──> ownerless recycling

    By removing ownership checks from the hot path of every free call, the throughput on Linux x86-64 was staggering compared to tcmalloc in specific workloads:

  • main_r50: 1.853x tcmalloc throughput

  • main_r90: 2.346x tcmalloc throughput

  • medium_r90: 6.097x tcmalloc throughput
  • But there was a massive catch. Because the recycling was "ownerless," it became a nightmare to track when a span was actually empty. If objects are scattered across various thread caches, how do you know when it's safe to decommit memory without adding heavy atomic counters to every single operation?

    HZ12: The "Hint" System

    This is where HZ12 comes in. I realized that if we want speed, we can't let ownership dictate the hot path. In HZ12, I decoupled ownership from safety.

    The "ownership token" is no longer a hard requirement for reclamation; it's just a hint to help find candidates. The actual authority to reclaim memory is moved to a "cold path" where we perform heavy validation, such as:

  • Complete-span snapshots

  • Inbox emptiness checks

  • State validation

  • Reclaim budget and Depot capacity monitoring
  • This way, the malloc and free calls stay lean and mean, while the heavy lifting of memory management happens in the background. It’s a massive shift in how you think about memory safety and efficiency in an LLM agent or high-performance system environment.

    ChatGPTPromptcpp

    All Replies (3)

    T
    tempset143 Advanced 2h ago
    Nice work. Did you run any fragmentation benchmarks against jemalloc or mimalloc for the HZ12 builds?
    0 Reply
    E
    embedthis30 Advanced 1h ago
    Still sounds like a hobbyist toy. My last custom allocator tanked my latency by 15ms on production loads. Overhyped.
    0 Reply
    P
    phdinml Beginner 1h ago
    Been there, just make sure to profile the cache locality before scaling up the team's workflow.
    0 Reply

    Write a Reply

    Markdown supported