Move classpath to rb_classext_t
This commit moves the classpath (and tmp_classpath) from instance variables to the rb_classext_t. This improves performance as we no longer need to set an instance variable when assigning a classpath to a class. I benchmarked with the following script: ```ruby name = :MyClass puts(Benchmark.measure do 10_000_000.times do |i| Object.const_set(name, Class.new) Object.send(:remove_const, name) end end) ``` Before this patch: ``` 5.440119 0.025264 5.465383 ( 5.467105) ``` After this patch: ``` 4.889646 0.028325 4.917971 ( 4.942678) ```
This commit is contained in:
parent
d86833e717
commit
abff5f6203
Notes:
git
2023-01-11 16:07:21 +00:00
4
gc.c
4
gc.c
@ -7259,6 +7259,8 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
|
|||||||
gc_mark(objspace, RCLASS_IVPTR(obj)[i]);
|
gc_mark(objspace, RCLASS_IVPTR(obj)[i]);
|
||||||
}
|
}
|
||||||
mark_const_tbl(objspace, RCLASS_CONST_TBL(obj));
|
mark_const_tbl(objspace, RCLASS_CONST_TBL(obj));
|
||||||
|
|
||||||
|
gc_mark(objspace, RCLASS_EXT(obj)->classpath);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_ICLASS:
|
case T_ICLASS:
|
||||||
@ -10559,6 +10561,8 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
|
|||||||
|
|
||||||
update_class_ext(objspace, RCLASS_EXT(obj));
|
update_class_ext(objspace, RCLASS_EXT(obj));
|
||||||
update_const_tbl(objspace, RCLASS_CONST_TBL(obj));
|
update_const_tbl(objspace, RCLASS_CONST_TBL(obj));
|
||||||
|
|
||||||
|
UPDATE_IF_MOVED(objspace, RCLASS_EXT(obj)->classpath);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_ICLASS:
|
case T_ICLASS:
|
||||||
|
@ -59,6 +59,8 @@ struct rb_classext_struct {
|
|||||||
#endif
|
#endif
|
||||||
uint32_t max_iv_count;
|
uint32_t max_iv_count;
|
||||||
unsigned char variation_count;
|
unsigned char variation_count;
|
||||||
|
bool permanent_classpath;
|
||||||
|
VALUE classpath;
|
||||||
};
|
};
|
||||||
typedef struct rb_classext_struct rb_classext_t;
|
typedef struct rb_classext_struct rb_classext_t;
|
||||||
|
|
||||||
@ -73,8 +75,10 @@ struct RClass {
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct rb_subclass_entry rb_subclass_entry_t;
|
#if RCLASS_EXT_EMBEDDED
|
||||||
typedef struct rb_classext_struct rb_classext_t;
|
// Assert that classes can be embedded in size_pools[2] (which has 160B slot size)
|
||||||
|
STATIC_ASSERT(sizeof_rb_classext_t, sizeof(struct RClass) + sizeof(rb_classext_t) <= 4 * RVALUE_SIZE);
|
||||||
|
#endif
|
||||||
|
|
||||||
#if RCLASS_EXT_EMBEDDED
|
#if RCLASS_EXT_EMBEDDED
|
||||||
# define RCLASS_EXT(c) ((rb_classext_t *)((char *)(c) + sizeof(struct RClass)))
|
# define RCLASS_EXT(c) ((rb_classext_t *)((char *)(c) + sizeof(struct RClass)))
|
||||||
@ -184,4 +188,14 @@ RCLASS_SET_SUPER(VALUE klass, VALUE super)
|
|||||||
return super;
|
return super;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
RCLASS_SET_CLASSPATH(VALUE klass, VALUE classpath, bool permanent)
|
||||||
|
{
|
||||||
|
assert(BUILTIN_TYPE(klass) == T_CLASS || BUILTIN_TYPE(klass) == T_MODULE);
|
||||||
|
assert(classpath == 0 || BUILTIN_TYPE(classpath) == T_STRING);
|
||||||
|
|
||||||
|
RB_OBJ_WRITE(klass, &(RCLASS_EXT(klass)->classpath), classpath);
|
||||||
|
RCLASS_EXT(klass)->permanent_classpath = permanent;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* INTERNAL_CLASS_H */
|
#endif /* INTERNAL_CLASS_H */
|
||||||
|
@ -993,13 +993,19 @@ describe "CApiObject" do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "calls the callback function for each cvar and ivar on a class" do
|
it "calls the callback function for each cvar and ivar on a class" do
|
||||||
|
exp = [:@@cvar, :foo, :@@cvar2, :bar, :@ivar, :baz]
|
||||||
|
exp.unshift(:__classpath__, 'CApiObjectSpecs::CVars') if RUBY_VERSION < "3.3"
|
||||||
|
|
||||||
ary = @o.rb_ivar_foreach(CApiObjectSpecs::CVars)
|
ary = @o.rb_ivar_foreach(CApiObjectSpecs::CVars)
|
||||||
ary.should == [:__classpath__, 'CApiObjectSpecs::CVars', :@@cvar, :foo, :@@cvar2, :bar, :@ivar, :baz]
|
ary.should == exp
|
||||||
end
|
end
|
||||||
|
|
||||||
it "calls the callback function for each cvar and ivar on a module" do
|
it "calls the callback function for each cvar and ivar on a module" do
|
||||||
|
exp = [:@@mvar, :foo, :@@mvar2, :bar, :@ivar, :baz]
|
||||||
|
exp.unshift(:__classpath__, 'CApiObjectSpecs::MVars') if RUBY_VERSION < "3.3"
|
||||||
|
|
||||||
ary = @o.rb_ivar_foreach(CApiObjectSpecs::MVars)
|
ary = @o.rb_ivar_foreach(CApiObjectSpecs::MVars)
|
||||||
ary.should == [:__classpath__, 'CApiObjectSpecs::MVars', :@@mvar, :foo, :@@mvar2, :bar, :@ivar, :baz]
|
ary.should == exp
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
65
variable.c
65
variable.c
@ -46,7 +46,7 @@ RUBY_EXTERN rb_serial_t ruby_vm_global_cvar_state;
|
|||||||
typedef void rb_gvar_compact_t(void *var);
|
typedef void rb_gvar_compact_t(void *var);
|
||||||
|
|
||||||
static struct rb_id_table *rb_global_tbl;
|
static struct rb_id_table *rb_global_tbl;
|
||||||
static ID autoload, classpath, tmp_classpath;
|
static ID autoload;
|
||||||
|
|
||||||
// This hash table maps file paths to loadable features. We use this to track
|
// This hash table maps file paths to loadable features. We use this to track
|
||||||
// autoload state until it's no longer needed.
|
// autoload state until it's no longer needed.
|
||||||
@ -75,10 +75,6 @@ Init_var_tables(void)
|
|||||||
rb_global_tbl = rb_id_table_create(0);
|
rb_global_tbl = rb_id_table_create(0);
|
||||||
generic_iv_tbl_ = st_init_numtable();
|
generic_iv_tbl_ = st_init_numtable();
|
||||||
autoload = rb_intern_const("__autoload__");
|
autoload = rb_intern_const("__autoload__");
|
||||||
/* __classpath__: fully qualified class path */
|
|
||||||
classpath = rb_intern_const("__classpath__");
|
|
||||||
/* __tmp_classpath__: temporary class path which contains anonymous names */
|
|
||||||
tmp_classpath = rb_intern_const("__tmp_classpath__");
|
|
||||||
|
|
||||||
autoload_mutex = rb_mutex_new();
|
autoload_mutex = rb_mutex_new();
|
||||||
rb_obj_hide(autoload_mutex);
|
rb_obj_hide(autoload_mutex);
|
||||||
@ -109,17 +105,16 @@ rb_namespace_p(VALUE obj)
|
|||||||
* Ruby level APIs that can change a permanent +classpath+.
|
* Ruby level APIs that can change a permanent +classpath+.
|
||||||
*/
|
*/
|
||||||
static VALUE
|
static VALUE
|
||||||
classname(VALUE klass, int *permanent)
|
classname(VALUE klass, bool *permanent)
|
||||||
{
|
{
|
||||||
*permanent = 0;
|
*permanent = false;
|
||||||
|
|
||||||
VALUE classpathv = rb_ivar_lookup(klass, classpath, Qnil);
|
VALUE classpath = RCLASS_EXT(klass)->classpath;
|
||||||
if (RTEST(classpathv)) {
|
if (classpath == 0) return Qnil;
|
||||||
*permanent = 1;
|
|
||||||
return classpathv;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rb_ivar_lookup(klass, tmp_classpath, Qnil);;
|
*permanent = RCLASS_EXT(klass)->permanent_classpath;
|
||||||
|
|
||||||
|
return classpath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -132,7 +127,7 @@ classname(VALUE klass, int *permanent)
|
|||||||
VALUE
|
VALUE
|
||||||
rb_mod_name(VALUE mod)
|
rb_mod_name(VALUE mod)
|
||||||
{
|
{
|
||||||
int permanent;
|
bool permanent;
|
||||||
return classname(mod, &permanent);
|
return classname(mod, &permanent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +153,7 @@ make_temporary_path(VALUE obj, VALUE klass)
|
|||||||
typedef VALUE (*fallback_func)(VALUE obj, VALUE name);
|
typedef VALUE (*fallback_func)(VALUE obj, VALUE name);
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
rb_tmp_class_path(VALUE klass, int *permanent, fallback_func fallback)
|
rb_tmp_class_path(VALUE klass, bool *permanent, fallback_func fallback)
|
||||||
{
|
{
|
||||||
VALUE path = classname(klass, permanent);
|
VALUE path = classname(klass, permanent);
|
||||||
|
|
||||||
@ -171,11 +166,11 @@ rb_tmp_class_path(VALUE klass, int *permanent, fallback_func fallback)
|
|||||||
path = Qfalse;
|
path = Qfalse;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int perm;
|
bool perm;
|
||||||
path = rb_tmp_class_path(RBASIC(klass)->klass, &perm, fallback);
|
path = rb_tmp_class_path(RBASIC(klass)->klass, &perm, fallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*permanent = 0;
|
*permanent = false;
|
||||||
return fallback(klass, path);
|
return fallback(klass, path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -183,7 +178,7 @@ rb_tmp_class_path(VALUE klass, int *permanent, fallback_func fallback)
|
|||||||
VALUE
|
VALUE
|
||||||
rb_class_path(VALUE klass)
|
rb_class_path(VALUE klass)
|
||||||
{
|
{
|
||||||
int permanent;
|
bool permanent;
|
||||||
VALUE path = rb_tmp_class_path(klass, &permanent, make_temporary_path);
|
VALUE path = rb_tmp_class_path(klass, &permanent, make_temporary_path);
|
||||||
if (!NIL_P(path)) path = rb_str_dup(path);
|
if (!NIL_P(path)) path = rb_str_dup(path);
|
||||||
return path;
|
return path;
|
||||||
@ -204,7 +199,7 @@ no_fallback(VALUE obj, VALUE name)
|
|||||||
VALUE
|
VALUE
|
||||||
rb_search_class_path(VALUE klass)
|
rb_search_class_path(VALUE klass)
|
||||||
{
|
{
|
||||||
int permanent;
|
bool permanent;
|
||||||
return rb_tmp_class_path(klass, &permanent, no_fallback);
|
return rb_tmp_class_path(klass, &permanent, no_fallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,21 +221,18 @@ build_const_path(VALUE head, ID tail)
|
|||||||
void
|
void
|
||||||
rb_set_class_path_string(VALUE klass, VALUE under, VALUE name)
|
rb_set_class_path_string(VALUE klass, VALUE under, VALUE name)
|
||||||
{
|
{
|
||||||
VALUE str;
|
bool permanent = true;
|
||||||
ID pathid = classpath;
|
|
||||||
|
|
||||||
|
VALUE str;
|
||||||
if (under == rb_cObject) {
|
if (under == rb_cObject) {
|
||||||
str = rb_str_new_frozen(name);
|
str = rb_str_new_frozen(name);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int permanent;
|
|
||||||
str = rb_tmp_class_path(under, &permanent, make_temporary_path);
|
str = rb_tmp_class_path(under, &permanent, make_temporary_path);
|
||||||
str = build_const_pathname(str, name);
|
str = build_const_pathname(str, name);
|
||||||
if (!permanent) {
|
|
||||||
pathid = tmp_classpath;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
rb_ivar_set(klass, pathid, str);
|
RCLASS_SET_CLASSPATH(klass, str, permanent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -311,7 +303,7 @@ rb_class_name(VALUE klass)
|
|||||||
const char *
|
const char *
|
||||||
rb_class2name(VALUE klass)
|
rb_class2name(VALUE klass)
|
||||||
{
|
{
|
||||||
int permanent;
|
bool permanent;
|
||||||
VALUE path = rb_tmp_class_path(rb_class_real(klass), &permanent, make_temporary_path);
|
VALUE path = rb_tmp_class_path(rb_class_real(klass), &permanent, make_temporary_path);
|
||||||
if (NIL_P(path)) return NULL;
|
if (NIL_P(path)) return NULL;
|
||||||
return RSTRING_PTR(path);
|
return RSTRING_PTR(path);
|
||||||
@ -3217,17 +3209,21 @@ set_namespace_path_i(ID id, VALUE v, void *payload)
|
|||||||
{
|
{
|
||||||
rb_const_entry_t *ce = (rb_const_entry_t *)v;
|
rb_const_entry_t *ce = (rb_const_entry_t *)v;
|
||||||
VALUE value = ce->value;
|
VALUE value = ce->value;
|
||||||
int has_permanent_classpath;
|
|
||||||
VALUE parental_path = *((VALUE *) payload);
|
VALUE parental_path = *((VALUE *) payload);
|
||||||
if (!rb_is_const_id(id) || !rb_namespace_p(value)) {
|
if (!rb_is_const_id(id) || !rb_namespace_p(value)) {
|
||||||
return ID_TABLE_CONTINUE;
|
return ID_TABLE_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool has_permanent_classpath;
|
||||||
classname(value, &has_permanent_classpath);
|
classname(value, &has_permanent_classpath);
|
||||||
if (has_permanent_classpath) {
|
if (has_permanent_classpath) {
|
||||||
return ID_TABLE_CONTINUE;
|
return ID_TABLE_CONTINUE;
|
||||||
}
|
}
|
||||||
set_namespace_path(value, build_const_path(parental_path, id));
|
set_namespace_path(value, build_const_path(parental_path, id));
|
||||||
rb_attr_delete(value, tmp_classpath);
|
|
||||||
|
if (!RCLASS_EXT(value)->permanent_classpath) {
|
||||||
|
RCLASS_SET_CLASSPATH(value, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
return ID_TABLE_CONTINUE;
|
return ID_TABLE_CONTINUE;
|
||||||
}
|
}
|
||||||
@ -3244,7 +3240,8 @@ set_namespace_path(VALUE named_namespace, VALUE namespace_path)
|
|||||||
|
|
||||||
RB_VM_LOCK_ENTER();
|
RB_VM_LOCK_ENTER();
|
||||||
{
|
{
|
||||||
rb_class_ivar_set(named_namespace, classpath, namespace_path);
|
RCLASS_SET_CLASSPATH(named_namespace, namespace_path, true);
|
||||||
|
|
||||||
if (const_table) {
|
if (const_table) {
|
||||||
rb_id_table_foreach(const_table, set_namespace_path_i, &namespace_path);
|
rb_id_table_foreach(const_table, set_namespace_path_i, &namespace_path);
|
||||||
}
|
}
|
||||||
@ -3304,24 +3301,24 @@ const_set(VALUE klass, ID id, VALUE val)
|
|||||||
* and avoid order-dependency on const_tbl
|
* and avoid order-dependency on const_tbl
|
||||||
*/
|
*/
|
||||||
if (rb_cObject && rb_namespace_p(val)) {
|
if (rb_cObject && rb_namespace_p(val)) {
|
||||||
int val_path_permanent;
|
bool val_path_permanent;
|
||||||
VALUE val_path = classname(val, &val_path_permanent);
|
VALUE val_path = classname(val, &val_path_permanent);
|
||||||
if (NIL_P(val_path) || !val_path_permanent) {
|
if (NIL_P(val_path) || !val_path_permanent) {
|
||||||
if (klass == rb_cObject) {
|
if (klass == rb_cObject) {
|
||||||
set_namespace_path(val, rb_id2str(id));
|
set_namespace_path(val, rb_id2str(id));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int parental_path_permanent;
|
bool parental_path_permanent;
|
||||||
VALUE parental_path = classname(klass, &parental_path_permanent);
|
VALUE parental_path = classname(klass, &parental_path_permanent);
|
||||||
if (NIL_P(parental_path)) {
|
if (NIL_P(parental_path)) {
|
||||||
int throwaway;
|
bool throwaway;
|
||||||
parental_path = rb_tmp_class_path(klass, &throwaway, make_temporary_path);
|
parental_path = rb_tmp_class_path(klass, &throwaway, make_temporary_path);
|
||||||
}
|
}
|
||||||
if (parental_path_permanent && !val_path_permanent) {
|
if (parental_path_permanent && !val_path_permanent) {
|
||||||
set_namespace_path(val, build_const_path(parental_path, id));
|
set_namespace_path(val, build_const_path(parental_path, id));
|
||||||
}
|
}
|
||||||
else if (!parental_path_permanent && NIL_P(val_path)) {
|
else if (!parental_path_permanent && NIL_P(val_path)) {
|
||||||
ivar_set(val, tmp_classpath, build_const_path(parental_path, id));
|
RCLASS_SET_CLASSPATH(val, build_const_path(parental_path, id), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user