Stream: platform development

Topic: Zig allocator


view this post on Zulip Oskar Hahn (Dec 17 2023 at 09:54):

When I learned zig (to write roc platforms) I learned about the different zig-allocators. So I think it is a pity, that the roc-zig-platforms do not use them, and instead use extern fn malloc.

For my AoC platform, I tried to use the the zig GeneralPurposeAllocatorr: https://github.com/ostcar/aoc2023/blob/f657d50aa2b393b5dc26b49baeb6f740ef405908/platform/host.zig#L30-L50

This worked fine, until I had to free memory for Day 16 (my solution for day 16 seems very memory inefficient).

How would I write roc_dealloc when roc does not call it with a size argument? Must the platform save the size of every allocation? Is there a trick, how to do this? My native solution would be, to use a global hashmap. But this seems inefficient and would need a lock, if the platform runs in parallel.

Why does roc_realloc has the argument old_size? If it is the responsibility of the platform to save the size, then it could also be used for roc_realloc.

What about the alignment argument? It is ignored in all examples and it does not seem to be needed. In what situation should the platform use it?

view this post on Zulip Brian Carroll (Dec 17 2023 at 11:20):

How would I write roc_dealloc when roc does not call it with a size argument?

Yeah the current API is really annoying to use with anything other than C malloc and free as the implementation.
The only way I know to deal with it is to slightly expand each allocation so that you can store the size just in front of the allocated bytes. It's definitely a pity that the definition of roc_dealloc forces this. I see this as a design mistake that we haven't fixed. I'm not aware of any strong reason to do it this way.
If we added a size argument, we could make some compiler changes to pass it in from compiled Roc. It would also require changes to every platform that currently exists.

view this post on Zulip Brian Carroll (Dec 17 2023 at 11:27):

Why does roc_realloc has the argument old_size? If it is the responsibility of the platform to save the size, then it could also be used for roc_realloc.

Good point, I have no idea. As far as I can tell this is an old decision and undoing it involves a lot of friction because every single platform would have to be redone. I know there are other changes planned to the platform API (changing Effects into a sort of state machine). If we are making other changes then we'd definitely want to batch them together so that there's only one upgrade to do.

view this post on Zulip Brian Carroll (Dec 17 2023 at 11:31):

What about the alignment argument? It is ignored in all examples and it does not seem to be needed. In what situation should the platform use it?

Yeah I think in theory every roc_alloc should ensure that the allocated memory has the requested alignment. The compiler is careful to ensure that Roc code supplies the right value. But nobody has written a platform yet that uses this argument. As far as I can tell, this is basically a bug in all our platforms that has been copied and pasted lots of times. But in practice maybe malloc already aligns to 16 bytes or something and we never ask for more alignment than that so everything is OK? That might be different in a custom allocator.

view this post on Zulip Richard Feldman (Dec 17 2023 at 13:12):

we should be using aligned_alloc over malloc in examples but aren't :sweat_smile:

view this post on Zulip Richard Feldman (Dec 17 2023 at 13:13):

in C examples at least; in Rust ones we should be using Rust's global allocator, but that requires knowing the size when we dealloc

view this post on Zulip Richard Feldman (Dec 17 2023 at 13:13):

we want to pass that but currently we can't because seamless slices don't know it

view this post on Zulip Richard Feldman (Dec 17 2023 at 13:14):

there's a project to change that, but it requires changing how every List and Str are represented in memory - @Brendan Hansknecht knows more

view this post on Zulip Brendan Hansknecht (Dec 17 2023 at 15:36):

But in practice maybe malloc already aligns to 16 bytes or something and we never ask for more alignment than that so everything is OK? That might be different in a custom allocator.

100% this. That so why roc alignment never matters currently.

view this post on Zulip Brendan Hansknecht (Dec 17 2023 at 15:49):

As for passing size to dealloc. One simple solution would be to store an extra usize and the heap for every list and string. This is quite wasteful, especially given many allocators already store this information internal (that is why malloc doesn't need the information for example).

From the last chat Richard and I had about this, we probably want roughly this API:

fn roc_alloc(alignment: u8, size: usize, roc_tracked_sized: bool) -> *void;

fn roc_realloc(ptr: *void, alignment: u8, requested_size: usize, old_size: Option<usize>) -> *void;

fn roc_dealloc(ptr: *void, size: Option<usize>);

For all types that have a size that roc can determine at compile time (records, tags, etc), roc will pass the size in. For types with a size that we can't determine at compile time (list, str), roc won't pass in a size. Most importantly with this, roc will let the platform know this info. As such, if the platform uses malloc, which already records the size, the platform can just ignore this info. If the platform uses some other for of allocator that needs to know the size, it is the platforms job to store the size somewhere (probably right before the roc allocation) when roc can't know the size at compile time.

view this post on Zulip Brendan Hansknecht (Dec 17 2023 at 15:49):

This minimizes repeated storage of the allocation size overall.

view this post on Zulip Brendan Hansknecht (Dec 17 2023 at 15:57):

Otherwise, every single list and string would require and extra usize no matter the allocator due to seamless slices not knowing the original capacity of the allocation.

view this post on Zulip Richard Feldman (Dec 17 2023 at 21:12):

also note that in a future where Roc supports simd, 16 may not be enough anymore! So malloc happens to always work today, but may stop working in the future

view this post on Zulip Brendan Hansknecht (Dec 17 2023 at 21:50):

Very true

view this post on Zulip Eli Dowling (Dec 22 2023 at 11:02):

This is wonderfully convenient, I came here to ask exactly this question after hitting this same issue trying to get a custom allocator to work for an embedded rust platform


Last updated: Jul 05 2025 at 12:14 UTC