Skip to content

Arena pattern support#134

Open
sureshkrishnan-ai wants to merge 2 commits intoz-libs:mainfrom
sureshkrishnan-ai:rc-arena
Open

Arena pattern support#134
sureshkrishnan-ai wants to merge 2 commits intoz-libs:mainfrom
sureshkrishnan-ai:rc-arena

Conversation

@sureshkrishnan-ai
Copy link
Contributor

No description provided.

@Burnett01
Copy link
Contributor

Very nice! I wanted to contribute an arena allocator myself but you beat me to it 👍🏾
Looks good

Copy link
Contributor

@Burnett01 Burnett01 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did a code review and after the suggested changes it works:

Image

So please incorporate the suggestions :)

std/arena.zc Outdated

fn alloc<T>(self) -> T* {
let ptr: T* = (T*)self.alloc_bytes(sizeof(T));
if (ptr != 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use NULL because 0 lead to segfault when I tested your code, because it will compare with strcmp.

out.c: In function ‘Arena__alloc’:
out.c:165:6: warning: argument 2 null where non-null expected [-Wnonnull]
  165 | if ((strcmp(ptr, 0) != 0))     {
      |      ^~~~~~
Suggested change
if (ptr != 0) {
if (ptr != NULL) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

std/arena.zc Outdated
fn alloc_n<T>(self, count: usize) -> T* {
let size = sizeof(T) * count;
let ptr: T* = (T*)self.alloc_bytes(size);
if (ptr != 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use NULL because 0 leads to segfault because it will compare with strcmp.

Suggested change
if (ptr != 0) {
if (ptr != NULL) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

std/arena.zc Outdated
fn dup_str(self, src: char*) -> char* {
let len = strlen(src);
let ptr: char* = (char*)self.alloc_bytes(len + 1);
if (ptr != 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use NULL because 0 leads to segfault because it will compare with strcmp.

Suggested change
if (ptr != 0) {
if (ptr != NULL) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

let len = strlen(src);
let ptr: char* = (char*)self.alloc_bytes(len + 1);
if (ptr != 0) {
strcpy(ptr, src);

This comment was marked as resolved.

std/arena.zc Outdated
}

fn free(self) {
if (self.data != 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use NULL because 0 leads to segfault because it will compare with strcmp.

Suggested change
if (self.data != 0) {
if (self.data != NULL) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

let mark = arena.save();
printf("Savepoint at: %zu\n", mark);

let extra: int* = arena.alloc_n<int>(100);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This prints a warning because extra is not used. So it's fine to change it to:

Suggested change
let extra: int* = arena.alloc_n<int>(100);
let _: int* = arena.alloc_n<int>(100);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Copy link
Contributor

@Burnett01 Burnett01 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add the following file in tests/std/test_arena.zc

import "std.zc"
import "std/arena.zc"

test "test_std_arena" {

    let arena = Arena::new(4096);

    assert(arena.bytes_used() == 0, "Arena should not have used any bytes yet")
    assert(arena.bytes_free() == 4096, "Arena bytes_free should be 4096 bytes")

    // Allocate integers
    let arr: int* = arena.alloc_n<int>(10);
    for (let i = 0; i < 10; i = i + 1) {
        arr[i] = i * i;
    }

    assert(arena.bytes_used() == 40, "Arena should have used 40 bytes")
    assert(arena.bytes_free() == 4056, "Arena bytes_free should be 4056 bytes")

    // Copy string
    let _ = arena.dup_str("Hello, Arena!");

    assert(arena.bytes_used() == 54, "Arena should have used 54 bytes")
    assert(arena.bytes_free() == 4042, "Arena bytes_free should be 4002 bytes")

    // Save/restore
    let mark = arena.save();

    assert(mark == 54, "Arena mark (savepoint) should be at 54 bytes")

    arena.alloc_n<int>(100);

    assert(arena.bytes_used() == 456, "Arena should have used 456 bytes")
    assert(arena.bytes_free() == 3640, "Arena bytes_free should be 3640 bytes")

    arena.restore(mark);

    assert(arena.bytes_used() == 54, "Arena after savepoint restore should have used 54 bytes")
    assert(arena.bytes_free() == 4042, "Arena bytes_free should be 4042 bytes")

    arena.reset();

    assert(arena.bytes_used() == 0, "Arena after reset should have used 0 bytes")
    assert(arena.bytes_free() == 4096, "Arena bytes_free should be 4096 bytes")

    arena.free();
}

Copy link
Contributor

@Burnett01 Burnett01 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job on the changes!

Final review and decision is left to Zuhaitz :)

@kyle-github
Copy link

You make the assumption that an int is four bytes. At least in C, this is not at all guaranteed. Use sizeof or similar to get the size of one int and then use that to compare the amount used in the arena. The main README doc for Zen-C says that int is the platform int. So the C rules apply here.

There is also the issue of alignment. If you align to single bytes, which is what this appears to do, then you will not be able to return pointers to values that have a larger alignment if the pointer is not aligned. At least on some architectures (Aarch64, IIRC). So you probably want to add an alignment param to the API. Malloc usually punts and returns pointers that are aligned on 16-byte boundaries. You could do that here too if you do not want the complexity of passing an alignment at every call site. It will waste a bit of memory on some allocations, but that may be preferable to making all callers deal with alignment.

All of this makes it a trickier to determine the amount of space that should be used in an allocation in a generic way that is platform agnostic.

These are things I've run into implementing arenas in C. I have the bruises and singed knuckles to prove it :-(

It is really great to see all this hard work creating such a nice foundations for Zen-C!

@Burnett01
Copy link
Contributor

Thank you for your great review insights @kyle-github !

This is very valuable especially when it comes to portability.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants