Skip to content

Commit f94fe6b

Browse files
committed
Pre-size arena to avoid unnecessary growth
1 parent bfa7692 commit f94fe6b

3 files changed

Lines changed: 57 additions & 12 deletions

File tree

include/prism/util/pm_arena.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ typedef struct {
4444
size_t block_count;
4545
} pm_arena_t;
4646

47+
/**
48+
* Ensure the arena has at least `capacity` bytes available in its current
49+
* block, allocating a new block if necessary. This allows callers to
50+
* pre-size the arena to avoid repeated small block allocations.
51+
*
52+
* @param arena The arena to pre-size.
53+
* @param capacity The minimum number of bytes to ensure are available.
54+
*/
55+
void pm_arena_reserve(pm_arena_t *arena, size_t capacity);
56+
4757
/**
4858
* Allocate memory from the arena. The returned memory is NOT zeroed. This
4959
* function is infallible — it aborts on allocation failure.

src/prism.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22173,6 +22173,14 @@ pm_parser_init(pm_arena_t *arena, pm_parser_t *parser, const uint8_t *source, si
2217322173
.warn_mismatched_indentation = true
2217422174
};
2217522175

22176+
// Pre-size the arenas based on input size to reduce the number of block
22177+
// allocations (and the kernel page zeroing they trigger). The ratios were
22178+
// measured empirically: AST arena ~3.3x input, metadata arena ~1.1x input.
22179+
// The reserve call is a no-op when the capacity is at or below the default
22180+
// arena block size, so small inputs don't waste an extra allocation.
22181+
if (size <= SIZE_MAX / 4) pm_arena_reserve(arena, size * 4);
22182+
if (size <= SIZE_MAX / 5 * 4) pm_arena_reserve(&parser->metadata_arena, size + size / 4);
22183+
2217622184
// Initialize the constant pool. We're going to completely guess as to the
2217722185
// number of constants that we'll need based on the size of the input. The
2217822186
// ratio we chose here is actually less arbitrary than you might think.

src/util/pm_arena.c

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "prism/util/pm_arena.h"
22

3+
#include <assert.h>
4+
35
/**
46
* Compute the block allocation size using offsetof so it is correct regardless
57
* of PM_FLEX_ARY_LEN.
@@ -29,6 +31,42 @@ pm_arena_next_block_size(const pm_arena_t *arena, size_t min_size) {
2931
return size > min_size ? size : min_size;
3032
}
3133

34+
/**
35+
* Allocate a new block with the given data capacity and initial usage, link it
36+
* into the arena, and return it. Aborts on allocation failure.
37+
*/
38+
static pm_arena_block_t *
39+
pm_arena_new_block(pm_arena_t *arena, size_t data_size, size_t initial_used) {
40+
assert(initial_used <= data_size);
41+
pm_arena_block_t *block = (pm_arena_block_t *) xmalloc(PM_ARENA_BLOCK_SIZE(data_size));
42+
43+
if (block == NULL) {
44+
fprintf(stderr, "prism: out of memory; aborting\n");
45+
abort();
46+
}
47+
48+
block->capacity = data_size;
49+
block->used = initial_used;
50+
block->prev = arena->current;
51+
arena->current = block;
52+
arena->block_count++;
53+
54+
return block;
55+
}
56+
57+
/**
58+
* Ensure the arena has at least `capacity` bytes available in its current
59+
* block, allocating a new block if necessary. This allows callers to
60+
* pre-size the arena to avoid repeated small block allocations.
61+
*/
62+
void
63+
pm_arena_reserve(pm_arena_t *arena, size_t capacity) {
64+
if (capacity <= PM_ARENA_INITIAL_SIZE) return;
65+
if (arena->current != NULL && (arena->current->capacity - arena->current->used) >= capacity) return;
66+
67+
pm_arena_new_block(arena, capacity, 0);
68+
}
69+
3270
/**
3371
* Allocate memory from the arena. The returned memory is NOT zeroed. This
3472
* function is infallible — it aborts on allocation failure.
@@ -51,18 +89,7 @@ pm_arena_alloc(pm_arena_t *arena, size_t size, size_t alignment) {
5189
// New blocks from xmalloc are max-aligned, so data[] starts aligned for
5290
// any C type. No padding needed at the start.
5391
size_t block_data_size = pm_arena_next_block_size(arena, size);
54-
pm_arena_block_t *block = (pm_arena_block_t *) xmalloc(PM_ARENA_BLOCK_SIZE(block_data_size));
55-
56-
if (block == NULL) {
57-
fprintf(stderr, "prism: out of memory; aborting\n");
58-
abort();
59-
}
60-
61-
block->capacity = block_data_size;
62-
block->used = size;
63-
block->prev = arena->current;
64-
arena->current = block;
65-
arena->block_count++;
92+
pm_arena_block_t *block = pm_arena_new_block(arena, block_data_size, size);
6693

6794
return block->data;
6895
}

0 commit comments

Comments
 (0)