RubyVM.stat constant cache metrics (#5766)

Before the new constant cache behavior, caches were invalidated by a
single global variable. You could inspect the value of this variable
with RubyVM.stat(:global_constant_state). This was mostly useful to
verify the behavior of the VM or to test constant loading like in Rails.

With the new constant cache behavior, we introduced
RubyVM.stat(:constant_cache) which returned a hash with symbol keys and
integer values that represented the number of live constant caches
associated with the given symbol. Additionally, we removed the old
RubyVM.stat(:global_constant_state).

This was proven to be not very useful, so it doesn't help you diagnose
constant loading issues. So, instead we added the global constant state
back into the RubyVM output. However, that number can be misleading as
now when you invalidate something like `Foo::Bar::Baz` you're actually
invalidating 3 different lists of inline caches.

This commit attempts to get the best of both worlds. We remove
RubyVM.stat(:global_constant_state) like we did originally, as it
doesn't have the same semantic meaning and it could be confusing going
forward. Instead we add RubyVM.stat(:constant_cache_invalidations) and
RubyVM.stat(:constant_cache_misses). These two metrics should provide
enough information to diagnose any constant loading issues, as well as
provide a replacement for the old global constant state.
This commit is contained in:
Kevin Newton 2022-04-05 16:37:00 -04:00 коммит произвёл GitHub
Родитель 5571ca3aa2
Коммит 8ee4a82e8c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 15 добавлений и 34 удалений

Просмотреть файл

@ -1044,6 +1044,7 @@ opt_getinlinecache
JUMP(dst);
}
else {
ruby_vm_constant_cache_misses++;
val = Qnil;
}
}

43
vm.c
Просмотреть файл

@ -428,7 +428,8 @@ rb_event_flag_t ruby_vm_event_flags;
rb_event_flag_t ruby_vm_event_enabled_global_flags;
unsigned int ruby_vm_event_local_num;
rb_serial_t ruby_vm_global_constant_state = 1;
rb_serial_t ruby_vm_constant_cache_invalidations = 0;
rb_serial_t ruby_vm_constant_cache_misses = 0;
rb_serial_t ruby_vm_class_serial = 1;
rb_serial_t ruby_vm_global_cvar_state = 1;
@ -496,16 +497,6 @@ rb_dtrace_setup(rb_execution_context_t *ec, VALUE klass, ID id,
return FALSE;
}
// Iterator function to loop through each entry in the constant cache and add
// its associated size into the given Hash.
static enum rb_id_table_iterator_result
vm_stat_constant_cache_i(ID id, VALUE table, void *constant_cache)
{
st_index_t size = ((st_table *) table)->num_entries;
rb_hash_aset((VALUE) constant_cache, ID2SYM(id), LONG2NUM(size));
return ID_TABLE_CONTINUE;
}
/*
* call-seq:
* RubyVM.stat -> Hash
@ -517,8 +508,10 @@ vm_stat_constant_cache_i(ID id, VALUE table, void *constant_cache)
* This hash includes information about method/constant caches:
*
* {
* :constant_cache=>{:RubyVM=>3},
* :class_serial=>9029
* :constant_cache_invalidations=>2,
* :constant_cache_misses=>14,
* :class_serial=>546,
* :global_cvar_state=>27
* }
*
* The contents of the hash are implementation specific and may be changed in
@ -529,7 +522,7 @@ vm_stat_constant_cache_i(ID id, VALUE table, void *constant_cache)
static VALUE
vm_stat(int argc, VALUE *argv, VALUE self)
{
static VALUE sym_global_constant_state, sym_constant_cache, sym_class_serial, sym_global_cvar_state;
static VALUE sym_constant_cache_invalidations, sym_constant_cache_misses, sym_class_serial, sym_global_cvar_state;
VALUE arg = Qnil;
VALUE hash = Qnil, key = Qnil;
@ -547,8 +540,8 @@ vm_stat(int argc, VALUE *argv, VALUE self)
}
#define S(s) sym_##s = ID2SYM(rb_intern_const(#s))
S(global_constant_state);
S(constant_cache);
S(constant_cache_invalidations);
S(constant_cache_misses);
S(class_serial);
S(global_cvar_state);
#undef S
@ -559,26 +552,12 @@ vm_stat(int argc, VALUE *argv, VALUE self)
else if (hash != Qnil) \
rb_hash_aset(hash, sym_##name, SERIALT2NUM(attr));
SET(global_constant_state, ruby_vm_global_constant_state);
SET(constant_cache_invalidations, ruby_vm_constant_cache_invalidations);
SET(constant_cache_misses, ruby_vm_constant_cache_misses);
SET(class_serial, ruby_vm_class_serial);
SET(global_cvar_state, ruby_vm_global_cvar_state);
#undef SET
// Here we're going to set up the constant cache hash that has key-value
// pairs of { name => count }, where name is a Symbol that represents the
// ID in the cache and count is an Integer representing the number of inline
// constant caches associated with that Symbol.
if (key == sym_constant_cache || hash != Qnil) {
VALUE constant_cache = rb_hash_new();
rb_id_table_foreach(GET_VM()->constant_cache, vm_stat_constant_cache_i, (void *) constant_cache);
if (key == sym_constant_cache) {
return constant_cache;
} else {
rb_hash_aset(hash, sym_constant_cache, constant_cache);
}
}
if (!NIL_P(key)) { /* matched key should return above */
rb_raise(rb_eArgError, "unknown key: %"PRIsVALUE, rb_sym2str(key));
}

Просмотреть файл

@ -14,7 +14,8 @@
MJIT_SYMBOL_EXPORT_BEGIN
RUBY_EXTERN VALUE ruby_vm_const_missing_count;
RUBY_EXTERN rb_serial_t ruby_vm_global_constant_state;
RUBY_EXTERN rb_serial_t ruby_vm_constant_cache_invalidations;
RUBY_EXTERN rb_serial_t ruby_vm_constant_cache_misses;
RUBY_EXTERN rb_serial_t ruby_vm_class_serial;
RUBY_EXTERN rb_serial_t ruby_vm_global_cvar_state;

Просмотреть файл

@ -144,10 +144,10 @@ rb_clear_constant_cache_for_id(ID id)
if (rb_id_table_lookup(vm->constant_cache, id, (VALUE *) &ics)) {
st_foreach(ics, rb_clear_constant_cache_for_id_i, (st_data_t) NULL);
ruby_vm_constant_cache_invalidations += ics->num_entries;
}
rb_yjit_constant_state_changed();
ruby_vm_global_constant_state++;
}
static void