Add variation_count on classes
Count how many "variations" each class creates. A "variation" is a a unique ordering of instance variables on a particular class. This can also be thought of as a branch in the shape tree. For example, the following Foo class will have 2 variations: ```ruby class Foo ; end Foo.new.instance_variable_set(:@a, 1) # case 1: creates one variation Foo.new.instance_variable_set(:@b, 1) # case 2: creates another variation foo = Foo.new foo.instance_variable_set(:@a, 1) # does not create a new variation foo.instance_variable_set(:@b, 1) # does not create a new variation (a continuation of the variation in case 1) ``` We will use this number to limit the amount of shapes that a class can create and fallback to using a hash iv lookup. Co-Authored-By: Aaron Patterson <tenderlove@ruby-lang.org>
This commit is contained in:
parent
f50aa19da6
commit
a3d552aedd
Notes:
git
2022-12-15 18:06:24 +00:00
@ -15,6 +15,7 @@
|
|||||||
#include "gc.h"
|
#include "gc.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "internal/array.h"
|
#include "internal/array.h"
|
||||||
|
#include "internal/class.h"
|
||||||
#include "internal/hash.h"
|
#include "internal/hash.h"
|
||||||
#include "internal/string.h"
|
#include "internal/string.h"
|
||||||
#include "internal/sanitizers.h"
|
#include "internal/sanitizers.h"
|
||||||
@ -498,6 +499,9 @@ dump_object(VALUE obj, struct dump_config *dc)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case T_CLASS:
|
case T_CLASS:
|
||||||
|
dump_append(dc, ", \"variation_count\":");
|
||||||
|
dump_append_d(dc, RCLASS_EXT(obj)->variation_count);
|
||||||
|
|
||||||
case T_MODULE:
|
case T_MODULE:
|
||||||
if (rb_class_get_superclass(obj)) {
|
if (rb_class_get_superclass(obj)) {
|
||||||
dump_append(dc, ", \"superclass\":");
|
dump_append(dc, ", \"superclass\":");
|
||||||
|
@ -53,6 +53,7 @@ struct rb_classext_struct {
|
|||||||
rb_alloc_func_t allocator;
|
rb_alloc_func_t allocator;
|
||||||
const VALUE includer;
|
const VALUE includer;
|
||||||
uint32_t max_iv_count;
|
uint32_t max_iv_count;
|
||||||
|
uint32_t variation_count;
|
||||||
#if !SHAPE_IN_BASIC_FLAGS
|
#if !SHAPE_IN_BASIC_FLAGS
|
||||||
shape_id_t shape_id;
|
shape_id_t shape_id;
|
||||||
#endif
|
#endif
|
||||||
|
39
shape.c
39
shape.c
@ -130,11 +130,15 @@ rb_shape_get_shape(VALUE obj)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static rb_shape_t*
|
static rb_shape_t*
|
||||||
get_next_shape_internal(rb_shape_t * shape, ID id, enum shape_type shape_type)
|
get_next_shape_internal(rb_shape_t * shape, ID id, enum shape_type shape_type, bool * variation_created)
|
||||||
{
|
{
|
||||||
rb_shape_t *res = NULL;
|
rb_shape_t *res = NULL;
|
||||||
RB_VM_LOCK_ENTER();
|
RB_VM_LOCK_ENTER();
|
||||||
{
|
{
|
||||||
|
bool had_edges = !!shape->edges;
|
||||||
|
|
||||||
|
*variation_created = false;
|
||||||
|
|
||||||
if (!shape->edges) {
|
if (!shape->edges) {
|
||||||
shape->edges = rb_id_table_create(0);
|
shape->edges = rb_id_table_create(0);
|
||||||
}
|
}
|
||||||
@ -142,6 +146,8 @@ get_next_shape_internal(rb_shape_t * shape, ID id, enum shape_type shape_type)
|
|||||||
// Lookup the shape in edges - if there's already an edge and a corresponding shape for it,
|
// Lookup the shape in edges - if there's already an edge and a corresponding shape for it,
|
||||||
// we can return that. Otherwise, we'll need to get a new shape
|
// we can return that. Otherwise, we'll need to get a new shape
|
||||||
if (!rb_id_table_lookup(shape->edges, id, (VALUE *)&res)) {
|
if (!rb_id_table_lookup(shape->edges, id, (VALUE *)&res)) {
|
||||||
|
*variation_created = had_edges;
|
||||||
|
|
||||||
rb_shape_t * new_shape = rb_shape_alloc(id, shape);
|
rb_shape_t * new_shape = rb_shape_alloc(id, shape);
|
||||||
|
|
||||||
new_shape->type = (uint8_t)shape_type;
|
new_shape->type = (uint8_t)shape_type;
|
||||||
@ -235,9 +241,9 @@ remove_shape_recursive(VALUE obj, ID id, rb_shape_t * shape, VALUE * removed)
|
|||||||
// We found a new parent. Create a child of the new parent that
|
// We found a new parent. Create a child of the new parent that
|
||||||
// has the same attributes as this shape.
|
// has the same attributes as this shape.
|
||||||
if (new_parent) {
|
if (new_parent) {
|
||||||
rb_shape_t * new_child = get_next_shape_internal(new_parent, shape->edge_name, shape->type);
|
bool dont_care;
|
||||||
|
rb_shape_t * new_child = get_next_shape_internal(new_parent, shape->edge_name, shape->type, &dont_care);
|
||||||
new_child->capacity = shape->capacity;
|
new_child->capacity = shape->capacity;
|
||||||
|
|
||||||
if (new_child->type == SHAPE_IVAR) {
|
if (new_child->type == SHAPE_IVAR) {
|
||||||
move_iv(obj, id, shape->next_iv_index - 1, new_child->next_iv_index - 1);
|
move_iv(obj, id, shape->next_iv_index - 1, new_child->next_iv_index - 1);
|
||||||
}
|
}
|
||||||
@ -280,7 +286,8 @@ rb_shape_transition_shape_frozen(VALUE obj)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
next_shape = get_next_shape_internal(shape, (ID)id_frozen, SHAPE_FROZEN);
|
bool dont_care;
|
||||||
|
next_shape = get_next_shape_internal(shape, (ID)id_frozen, SHAPE_FROZEN, &dont_care);
|
||||||
|
|
||||||
RUBY_ASSERT(next_shape);
|
RUBY_ASSERT(next_shape);
|
||||||
rb_shape_set_shape(obj, next_shape);
|
rb_shape_set_shape(obj, next_shape);
|
||||||
@ -294,13 +301,17 @@ rb_shape_t *
|
|||||||
rb_shape_get_next_iv_shape(rb_shape_t* shape, ID id)
|
rb_shape_get_next_iv_shape(rb_shape_t* shape, ID id)
|
||||||
{
|
{
|
||||||
RUBY_ASSERT(!is_instance_id(id) || RTEST(rb_sym2str(ID2SYM(id))));
|
RUBY_ASSERT(!is_instance_id(id) || RTEST(rb_sym2str(ID2SYM(id))));
|
||||||
return get_next_shape_internal(shape, id, SHAPE_IVAR);
|
bool dont_care;
|
||||||
|
return get_next_shape_internal(shape, id, SHAPE_IVAR, &dont_care);
|
||||||
}
|
}
|
||||||
|
|
||||||
rb_shape_t *
|
rb_shape_t *
|
||||||
rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id)
|
rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id)
|
||||||
{
|
{
|
||||||
rb_shape_t * new_shape = rb_shape_get_next_iv_shape(shape, id);
|
RUBY_ASSERT(!is_instance_id(id) || RTEST(rb_sym2str(ID2SYM(id))));
|
||||||
|
|
||||||
|
bool variation_created;
|
||||||
|
rb_shape_t * new_shape = get_next_shape_internal(shape, id, SHAPE_IVAR, &variation_created);
|
||||||
|
|
||||||
// Check if we should update max_iv_count on the object's class
|
// Check if we should update max_iv_count on the object's class
|
||||||
if (BUILTIN_TYPE(obj) == T_OBJECT) {
|
if (BUILTIN_TYPE(obj) == T_OBJECT) {
|
||||||
@ -308,6 +319,10 @@ rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id)
|
|||||||
if (new_shape->next_iv_index > RCLASS_EXT(klass)->max_iv_count) {
|
if (new_shape->next_iv_index > RCLASS_EXT(klass)->max_iv_count) {
|
||||||
RCLASS_EXT(klass)->max_iv_count = new_shape->next_iv_index;
|
RCLASS_EXT(klass)->max_iv_count = new_shape->next_iv_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (variation_created) {
|
||||||
|
RCLASS_EXT(klass)->variation_count++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new_shape;
|
return new_shape;
|
||||||
@ -317,7 +332,8 @@ rb_shape_t *
|
|||||||
rb_shape_transition_shape_capa(rb_shape_t* shape, uint32_t new_capacity)
|
rb_shape_transition_shape_capa(rb_shape_t* shape, uint32_t new_capacity)
|
||||||
{
|
{
|
||||||
ID edge_name = rb_make_temporary_id(new_capacity);
|
ID edge_name = rb_make_temporary_id(new_capacity);
|
||||||
rb_shape_t * new_shape = get_next_shape_internal(shape, edge_name, SHAPE_CAPACITY_CHANGE);
|
bool dont_care;
|
||||||
|
rb_shape_t * new_shape = get_next_shape_internal(shape, edge_name, SHAPE_CAPACITY_CHANGE, &dont_care);
|
||||||
new_shape->capacity = new_capacity;
|
new_shape->capacity = new_capacity;
|
||||||
return new_shape;
|
return new_shape;
|
||||||
}
|
}
|
||||||
@ -712,18 +728,19 @@ Init_default_shapes(void)
|
|||||||
// Make shapes for T_OBJECT
|
// Make shapes for T_OBJECT
|
||||||
for (int i = 0; i < SIZE_POOL_COUNT; i++) {
|
for (int i = 0; i < SIZE_POOL_COUNT; i++) {
|
||||||
rb_shape_t * shape = rb_shape_get_shape_by_id(i);
|
rb_shape_t * shape = rb_shape_get_shape_by_id(i);
|
||||||
#if RUBY_DEBUG
|
bool dont_care;
|
||||||
rb_shape_t * t_object_shape =
|
rb_shape_t * t_object_shape =
|
||||||
#endif
|
get_next_shape_internal(shape, id_t_object, SHAPE_T_OBJECT, &dont_care);
|
||||||
get_next_shape_internal(shape, id_t_object, SHAPE_T_OBJECT);
|
t_object_shape->edges = rb_id_table_create(0);
|
||||||
RUBY_ASSERT(rb_shape_id(t_object_shape) == (shape_id_t)(i + SIZE_POOL_COUNT));
|
RUBY_ASSERT(rb_shape_id(t_object_shape) == (shape_id_t)(i + SIZE_POOL_COUNT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool dont_care;
|
||||||
// Special const shape
|
// Special const shape
|
||||||
#if RUBY_DEBUG
|
#if RUBY_DEBUG
|
||||||
rb_shape_t * special_const_shape =
|
rb_shape_t * special_const_shape =
|
||||||
#endif
|
#endif
|
||||||
get_next_shape_internal(root, (ID)id_frozen, SHAPE_FROZEN);
|
get_next_shape_internal(root, (ID)id_frozen, SHAPE_FROZEN, &dont_care);
|
||||||
RUBY_ASSERT(rb_shape_id(special_const_shape) == SPECIAL_CONST_SHAPE_ID);
|
RUBY_ASSERT(rb_shape_id(special_const_shape) == SPECIAL_CONST_SHAPE_ID);
|
||||||
RUBY_ASSERT(SPECIAL_CONST_SHAPE_ID == (GET_VM()->next_shape_id - 1));
|
RUBY_ASSERT(SPECIAL_CONST_SHAPE_ID == (GET_VM()->next_shape_id - 1));
|
||||||
RUBY_ASSERT(rb_shape_frozen_shape_p(special_const_shape));
|
RUBY_ASSERT(rb_shape_frozen_shape_p(special_const_shape));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user