diff options
author | Jean Boussier <byroot@ruby-lang.org> | 2023-10-10 15:32:12 +0200 |
---|---|---|
committer | Jean Boussier <jean.boussier@gmail.com> | 2023-10-23 09:33:15 +0200 |
commit | e5364ea496a193560c0dc339f82a67674d26fee2 (patch) | |
tree | c680de57367698155a75d9c4219b3243d97ba1b1 /shape.c | |
parent | e7d845b1d0154651962f34eefb37ffb0ac0c4e0a (diff) |
rb_shape_transition_shape_capa: use optimal sizes transitions
Previously the growth was 3(embed), 6, 12, 24, ...
With this change it's now 3(embed), 8, 16, 32, 64, ... by default.
However, since power of two isn't the best size for all allocators,
if `malloc_usable_size` is vailable, we use it to discover the best
offset.
On Linux/glibc 2.35 for instance, the growth will be 3(embed), 7, 15, 31
to avoid wasting 8B per object.
Test program:
```c
size_t test(size_t slots) {
size_t allocated = slots * VALUE_SIZE;
void *test_ptr = malloc(allocated);
size_t wasted = malloc_usable_size(test_ptr) - allocated;
free(test_ptr);
fprintf(stderr, "slots = %lu, wasted_bytes = %lu\n", slots, wasted);
return wasted;
}
int main(int argc, char *argv[]) {
size_t best_padding = 0;
size_t padding = 0;
for (padding = 0; padding <= 2; padding++) {
size_t wasted = test(8 - padding);
if (wasted == 0) {
best_padding = padding;
break;
}
}
size_t index = 0;
fprintf(stderr, "=============== naive ================\n");
size_t list_size = 4;
for (index = 0; index < 10; index++) {
test(list_size);
list_size *= 2;
}
fprintf(stderr, "=============== auto-padded (-%lu) ================\n", best_padding);
list_size = 4;
for (index = 0; index < 10; index ++) {
test(list_size - best_padding);
list_size *= 2;
}
fprintf(stderr, "\n\n");
return 0;
}
```
```
===== glibc ======
slots = 8, wasted_bytes = 8
slots = 7, wasted_bytes = 0
=============== naive ================
slots = 4, wasted_bytes = 8
slots = 8, wasted_bytes = 8
slots = 16, wasted_bytes = 8
slots = 32, wasted_bytes = 8
slots = 64, wasted_bytes = 8
slots = 128, wasted_bytes = 8
slots = 256, wasted_bytes = 8
slots = 512, wasted_bytes = 8
slots = 1024, wasted_bytes = 8
slots = 2048, wasted_bytes = 8
=============== auto-padded (-1) ================
slots = 3, wasted_bytes = 0
slots = 7, wasted_bytes = 0
slots = 15, wasted_bytes = 0
slots = 31, wasted_bytes = 0
slots = 63, wasted_bytes = 0
slots = 127, wasted_bytes = 0
slots = 255, wasted_bytes = 0
slots = 511, wasted_bytes = 0
slots = 1023, wasted_bytes = 0
slots = 2047, wasted_bytes = 0
```
```
========== jemalloc =======
slots = 8, wasted_bytes = 0
=============== naive ================
slots = 4, wasted_bytes = 0
slots = 8, wasted_bytes = 0
slots = 16, wasted_bytes = 0
slots = 32, wasted_bytes = 0
slots = 64, wasted_bytes = 0
slots = 128, wasted_bytes = 0
slots = 256, wasted_bytes = 0
slots = 512, wasted_bytes = 0
slots = 1024, wasted_bytes = 0
slots = 2048, wasted_bytes = 0
=============== auto-padded (-0) ================
slots = 4, wasted_bytes = 0
slots = 8, wasted_bytes = 0
slots = 16, wasted_bytes = 0
slots = 32, wasted_bytes = 0
slots = 64, wasted_bytes = 0
slots = 128, wasted_bytes = 0
slots = 256, wasted_bytes = 0
slots = 512, wasted_bytes = 0
slots = 1024, wasted_bytes = 0
slots = 2048, wasted_bytes = 0
```
Diffstat (limited to 'shape.c')
-rw-r--r-- | shape.c | 10 |
1 files changed, 6 insertions, 4 deletions
@@ -418,19 +418,21 @@ rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id) } static inline rb_shape_t * -rb_shape_transition_shape_capa_create(rb_shape_t* shape, uint32_t new_capacity) +rb_shape_transition_shape_capa_create(rb_shape_t* shape, size_t new_capacity) { + RUBY_ASSERT(new_capacity < (size_t)MAX_IVARS); + ID edge_name = rb_make_temporary_id(new_capacity); bool dont_care; rb_shape_t * new_shape = get_next_shape_internal(shape, edge_name, SHAPE_CAPACITY_CHANGE, &dont_care, true, false); - new_shape->capacity = new_capacity; + new_shape->capacity = (uint32_t)new_capacity; return new_shape; } rb_shape_t * rb_shape_transition_shape_capa(rb_shape_t* shape) { - return rb_shape_transition_shape_capa_create(shape, shape->capacity * 2); + return rb_shape_transition_shape_capa_create(shape, rb_malloc_grow_capa(shape->capacity, sizeof(VALUE))); } bool @@ -833,7 +835,7 @@ Init_default_shapes(void) // Shapes by size pool for (int i = 1; i < SIZE_POOL_COUNT; i++) { - uint32_t capa = (uint32_t)((rb_size_pool_slot_size(i) - offsetof(struct RObject, as.ary)) / sizeof(VALUE)); + size_t capa = ((rb_size_pool_slot_size(i) - offsetof(struct RObject, as.ary)) / sizeof(VALUE)); rb_shape_t * new_shape = rb_shape_transition_shape_capa_create(root, capa); new_shape->type = SHAPE_INITIAL_CAPACITY; new_shape->size_pool_index = i; |