Skip to content

Commit

Permalink
YJIT: Add counter to measure how often we compile "cold" ISEQs (#535)
Browse files Browse the repository at this point in the history
  • Loading branch information
maximecb authored Sep 14, 2023
1 parent d900a5e commit 5cde049
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 2 deletions.
2 changes: 1 addition & 1 deletion vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ jit_compile(rb_execution_context_t *ec)
if (body->jit_entry == NULL) {
body->jit_entry_calls++;
if (yjit_enabled) {
if (body->jit_entry_calls == rb_yjit_call_threshold()) {
if (rb_yjit_threshold_hit(iseq, body->jit_entry_calls)) {
rb_yjit_compile_iseq(iseq, ec, false);
}
}
Expand Down
2 changes: 2 additions & 0 deletions yjit.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
bool rb_yjit_enabled_p(void);
bool rb_yjit_compile_new_iseqs(void);
unsigned rb_yjit_call_threshold(void);
bool rb_yjit_threshold_hit(const rb_iseq_t *const iseq, unsigned long total_calls);
void rb_yjit_invalidate_all_method_lookup_assumptions(void);
void rb_yjit_cme_invalidate(rb_callable_method_entry_t *cme);
void rb_yjit_collect_binding_alloc(void);
Expand All @@ -50,6 +51,7 @@ void rb_yjit_tracing_invalidate_all(void);
static inline bool rb_yjit_enabled_p(void) { return false; }
static inline bool rb_yjit_compile_new_iseqs(void) { return false; }
static inline unsigned rb_yjit_call_threshold(void) { return UINT_MAX; }
static inline bool rb_yjit_threshold_hit(const rb_iseq_t *const iseq, unsigned long total_calls) { return false; }
static inline void rb_yjit_invalidate_all_method_lookup_assumptions(void) {}
static inline void rb_yjit_cme_invalidate(rb_callable_method_entry_t *cme) {}
static inline void rb_yjit_collect_binding_alloc(void) {}
Expand Down
1 change: 1 addition & 0 deletions yjit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ def _print_stats(out: $stderr) # :nodoc:
out.puts "bindings_set: " + ("%10d" % stats[:binding_set])
out.puts "compilation_failure: " + ("%10d" % compilation_failure) if compilation_failure != 0
out.puts "compiled_iseq_entry: " + ("%10d" % stats[:compiled_iseq_entry])
out.puts "compiled_entry_cold: " + ("%10d" % stats[:compiled_entry_cold])
out.puts "compiled_iseq_count: " + ("%10d" % stats[:compiled_iseq_count])
out.puts "compiled_blockid_count:" + ("%10d" % stats[:compiled_blockid_count])
out.puts "compiled_block_count: " + ("%10d" % stats[:compiled_block_count])
Expand Down
3 changes: 3 additions & 0 deletions yjit/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,9 @@ pub struct IseqPayload {
// How many blocks were compiled when we last hit a branch stub
// for this ISEQ
pub block_count_last_stub: u64,

// Number of calls when we hit the threshold for this ISEQs
pub call_count_at_threshold: u64,
}

impl IseqPayload {
Expand Down
4 changes: 3 additions & 1 deletion yjit/src/stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,10 @@ macro_rules! make_counters {

/// The list of counters that are available without --yjit-stats.
/// They are incremented only by `incr_counter!` and don't use `gen_counter_incr`.
pub const DEFAULT_COUNTERS: [&str; 9] = [
pub const DEFAULT_COUNTERS: [&str; 10] = [
"code_gc_count",
"compiled_iseq_entry",
"compiled_iseq_entry_cold",
"compiled_iseq_count",
"compiled_blockid_count",
"compiled_block_count",
Expand Down Expand Up @@ -351,6 +352,7 @@ make_counters! {
binding_set,

compiled_iseq_entry,
compiled_entry_cold,
compiled_iseq_count,
compiled_blockid_count,
compiled_block_count,
Expand Down
37 changes: 37 additions & 0 deletions yjit/src/yjit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::invariants::*;
use crate::options::*;
use crate::stats::YjitExitLocations;
use crate::stats::with_compile_time;
use crate::stats::incr_counter;

use std::os::raw;
use std::sync::atomic::{AtomicBool, Ordering};
Expand Down Expand Up @@ -51,6 +52,42 @@ pub extern "C" fn rb_yjit_call_threshold() -> raw::c_uint {
get_option!(call_threshold) as raw::c_uint
}

// Counter to serve as a proxy for execution time, total number of calls
static mut TOTAL_ENTRY_HITS: u64 = 0;

/// Test whether we are ready to compile an ISEQ or not
#[no_mangle]
pub extern "C" fn rb_yjit_threshold_hit(iseq: IseqPtr, total_calls: u64) -> bool {
let call_threshold = get_option!(call_threshold) as u64;

unsafe { TOTAL_ENTRY_HITS += 1; }

if total_calls >= call_threshold {

if total_calls == call_threshold {
let payload = get_or_create_iseq_payload(iseq);
let call_count = unsafe { TOTAL_ENTRY_HITS };
payload.call_count_at_threshold = call_count;
}

// Try to estimate the total time taken (total number of calls) to reach 20 calls to this ISEQ
// This give us a ratio of how hot/cold this ISEQ is
if total_calls == call_threshold + 20 {
let payload = get_or_create_iseq_payload(iseq);
let call_count = unsafe { TOTAL_ENTRY_HITS };
let num_calls = call_count - payload.call_count_at_threshold;

if num_calls > 50_000 {
incr_counter!(compiled_entry_cold);
}

return true;
}
}

return false;
}

/// This function is called from C code
#[no_mangle]
pub extern "C" fn rb_yjit_init_rust() {
Expand Down

0 comments on commit 5cde049

Please sign in to comment.