diff options
author | Randy Stauner <[email protected]> | 2024-08-27 19:24:17 -0700 |
---|---|---|
committer | GitHub <[email protected]> | 2024-08-27 22:24:17 -0400 |
commit | 942317ebf8a5e4a85189411ee4d48267f21ecca5 (patch) | |
tree | 03554f6160f2b4f86282ebdd2eeef3d8e449c6f6 /yjit/src | |
parent | 5b129c899a5cf3bf8253eaaf7fbc8331b1e55f75 (diff) |
YJIT: Encode doubles to VALUE objects and move stat generation to rust (#11388)
* YJIT: Encode doubles to VALUE objects and move stat generation to rust
Stats that can now be generated from rust have been moved there.
* Move object_shape_count call for runtime_stats to rust
This reduces the ruby method to a single primitive.
* Change hash_aset_usize from macro to function
Notes
Notes:
Merged-By: maximecb <[email protected]>
Diffstat (limited to 'yjit/src')
-rw-r--r-- | yjit/src/cruby.rs | 5 | ||||
-rw-r--r-- | yjit/src/cruby_bindings.inc.rs | 2 | ||||
-rw-r--r-- | yjit/src/stats.rs | 75 |
3 files changed, 61 insertions, 21 deletions
diff --git a/yjit/src/cruby.rs b/yjit/src/cruby.rs index ad8209e51e..0709e2a079 100644 --- a/yjit/src/cruby.rs +++ b/yjit/src/cruby.rs @@ -116,6 +116,11 @@ extern "C" { me: *const rb_callable_method_entry_t, ci: *const rb_callinfo, ) -> *const rb_callable_method_entry_t; + + // Floats within range will be encoded without creating objects in the heap. + // (Range is 0x3000000000000001 to 0x4fffffffffffffff (1.7272337110188893E-77 to 2.3158417847463237E+77). + pub fn rb_float_new(d: f64) -> VALUE; + pub fn rb_hash_empty_p(hash: VALUE) -> VALUE; pub fn rb_yjit_str_concat_codepoint(str: VALUE, codepoint: VALUE); pub fn rb_str_setbyte(str: VALUE, index: VALUE, value: VALUE) -> VALUE; diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index 99a5024c3f..4720b4f4ea 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -976,6 +976,7 @@ extern "C" { pub fn rb_get_alloc_func(klass: VALUE) -> rb_alloc_func_t; pub fn rb_method_basic_definition_p(klass: VALUE, mid: ID) -> ::std::os::raw::c_int; pub fn rb_bug(fmt: *const ::std::os::raw::c_char, ...) -> !; + pub fn rb_float_new(d: f64) -> VALUE; pub fn rb_gc_mark(obj: VALUE); pub fn rb_gc_mark_movable(obj: VALUE); pub fn rb_gc_location(obj: VALUE) -> VALUE; @@ -1269,6 +1270,7 @@ extern "C" { file: *const ::std::os::raw::c_char, line: ::std::os::raw::c_int, ); + pub fn rb_object_shape_count() -> VALUE; pub fn rb_yjit_assert_holding_vm_lock(); pub fn rb_yjit_sendish_sp_pops(ci: *const rb_callinfo) -> usize; pub fn rb_yjit_invokeblock_sp_pops(ci: *const rb_callinfo) -> usize; diff --git a/yjit/src/stats.rs b/yjit/src/stats.rs index 353a9e3659..37ef02b440 100644 --- a/yjit/src/stats.rs +++ b/yjit/src/stats.rs @@ -709,6 +709,17 @@ pub extern "C" fn rb_yjit_incr_counter(counter_name: *const std::os::raw::c_char unsafe { *counter_ptr += 1 }; } +fn hash_aset_usize(hash: VALUE, key: &str, value: usize) { + let rb_key = rust_str_to_sym(key); + let rb_value = VALUE::fixnum_from_usize(value); + unsafe { rb_hash_aset(hash, rb_key, rb_value); } +} + +fn hash_aset_double(hash: VALUE, key: &str, value: f64) { + let rb_key = rust_str_to_sym(key); + unsafe { rb_hash_aset(hash, rb_key, rb_float_new(value)); } +} + /// Export all YJIT statistics as a Ruby hash. fn rb_yjit_gen_stats_dict() -> VALUE { // If YJIT is not enabled, return Qnil @@ -716,14 +727,6 @@ fn rb_yjit_gen_stats_dict() -> VALUE { return Qnil; } - macro_rules! hash_aset_usize { - ($hash:ident, $counter_name:expr, $value:expr) => { - let key = rust_str_to_sym($counter_name); - let value = VALUE::fixnum_from_usize($value); - rb_hash_aset($hash, key, value); - } - } - let hash = unsafe { rb_hash_new() }; unsafe { @@ -732,37 +735,39 @@ fn rb_yjit_gen_stats_dict() -> VALUE { let ocb = CodegenGlobals::get_outlined_cb(); // Inline code size - hash_aset_usize!(hash, "inline_code_size", cb.code_size()); + hash_aset_usize(hash, "inline_code_size", cb.code_size()); // Outlined code size - hash_aset_usize!(hash, "outlined_code_size", ocb.unwrap().code_size()); + hash_aset_usize(hash, "outlined_code_size", ocb.unwrap().code_size()); // GCed pages let freed_page_count = cb.num_freed_pages(); - hash_aset_usize!(hash, "freed_page_count", freed_page_count); + hash_aset_usize(hash, "freed_page_count", freed_page_count); // GCed code size - hash_aset_usize!(hash, "freed_code_size", freed_page_count * cb.page_size()); + hash_aset_usize(hash, "freed_code_size", freed_page_count * cb.page_size()); // Live pages - hash_aset_usize!(hash, "live_page_count", cb.num_mapped_pages() - freed_page_count); + hash_aset_usize(hash, "live_page_count", cb.num_mapped_pages() - freed_page_count); // Size of memory region allocated for JIT code - hash_aset_usize!(hash, "code_region_size", cb.mapped_region_size()); + hash_aset_usize(hash, "code_region_size", cb.mapped_region_size()); // Rust global allocations in bytes - hash_aset_usize!(hash, "yjit_alloc_size", GLOBAL_ALLOCATOR.alloc_size.load(Ordering::SeqCst)); + hash_aset_usize(hash, "yjit_alloc_size", GLOBAL_ALLOCATOR.alloc_size.load(Ordering::SeqCst)); // How many bytes we are using to store context data let context_data = CodegenGlobals::get_context_data(); - hash_aset_usize!(hash, "context_data_bytes", context_data.num_bytes()); - hash_aset_usize!(hash, "context_cache_bytes", crate::core::CTX_CACHE_BYTES); + hash_aset_usize(hash, "context_data_bytes", context_data.num_bytes()); + hash_aset_usize(hash, "context_cache_bytes", crate::core::CTX_CACHE_BYTES); // VM instructions count - hash_aset_usize!(hash, "vm_insns_count", rb_vm_insns_count as usize); + hash_aset_usize(hash, "vm_insns_count", rb_vm_insns_count as usize); - hash_aset_usize!(hash, "live_iseq_count", rb_yjit_live_iseq_count as usize); - hash_aset_usize!(hash, "iseq_alloc_count", rb_yjit_iseq_alloc_count as usize); + hash_aset_usize(hash, "live_iseq_count", rb_yjit_live_iseq_count as usize); + hash_aset_usize(hash, "iseq_alloc_count", rb_yjit_iseq_alloc_count as usize); + + rb_hash_aset(hash, rust_str_to_sym("object_shape_count"), rb_object_shape_count()); } // If we're not generating stats, put only default counters @@ -797,16 +802,44 @@ fn rb_yjit_gen_stats_dict() -> VALUE { rb_hash_aset(hash, key, value); } + let mut side_exits = 0; + // For each entry in exit_op_count, add a stats entry with key "exit_INSTRUCTION_NAME" // and the value is the count of side exits for that instruction. for op_idx in 0..VM_INSTRUCTION_SIZE_USIZE { let op_name = insn_name(op_idx); let key_string = "exit_".to_owned() + &op_name; let key = rust_str_to_sym(&key_string); - let value = VALUE::fixnum_from_usize(EXIT_OP_COUNT[op_idx] as usize); + let count = EXIT_OP_COUNT[op_idx]; + side_exits += count; + let value = VALUE::fixnum_from_usize(count as usize); rb_hash_aset(hash, key, value); } + hash_aset_usize(hash, "side_exit_count", side_exits as usize); + + let total_exits = side_exits + *get_counter_ptr(&Counter::leave_interp_return.get_name()); + hash_aset_usize(hash, "total_exit_count", total_exits as usize); + + // Number of instructions that finish executing in YJIT. + // See :count-placement: about the subtraction. + let retired_in_yjit = *get_counter_ptr(&Counter::yjit_insns_count.get_name()) - side_exits; + + // Average length of instruction sequences executed by YJIT + let avg_len_in_yjit: f64 = if total_exits > 0 { + retired_in_yjit as f64 / total_exits as f64 + } else { + 0_f64 + }; + hash_aset_double(hash, "avg_len_in_yjit", avg_len_in_yjit); + + // Proportion of instructions that retire in YJIT + let total_insns_count = retired_in_yjit + rb_vm_insns_count; + hash_aset_usize(hash, "total_insns_count", total_insns_count as usize); + + let ratio_in_yjit: f64 = 100.0 * retired_in_yjit as f64 / total_insns_count as f64; + hash_aset_double(hash, "ratio_in_yjit", ratio_in_yjit); + // Set method call counts in a Ruby dict fn set_call_counts( calls_hash: VALUE, |