summaryrefslogtreecommitdiff
path: root/yjit/src
diff options
context:
space:
mode:
authorRandy Stauner <[email protected]>2024-08-27 19:24:17 -0700
committerGitHub <[email protected]>2024-08-27 22:24:17 -0400
commit942317ebf8a5e4a85189411ee4d48267f21ecca5 (patch)
tree03554f6160f2b4f86282ebdd2eeef3d8e449c6f6 /yjit/src
parent5b129c899a5cf3bf8253eaaf7fbc8331b1e55f75 (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.rs5
-rw-r--r--yjit/src/cruby_bindings.inc.rs2
-rw-r--r--yjit/src/stats.rs75
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,