YJIT: implement two-step call threshold (#8839)

* YJIT: implement two-step call threshold

Automatically switch call threshold to a larger value for
larger, production-sized apps, while still allowing smaller apps
and command-line programs to start with a lower threshold.

* Update yjit/src/options.rs

Co-authored-by: Alan Wu <XrXr@users.noreply.github.com>

* Make the new variables constants

* Check that a custom call threshold was not specified

---------

Co-authored-by: Alan Wu <XrXr@users.noreply.github.com>
This commit is contained in:
Maxime Chevalier-Boisvert 2023-11-03 17:07:13 -04:00 committed by GitHub
parent 38bdb9d0da
commit 6e38076b16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 20 additions and 3 deletions

View File

@ -2,17 +2,26 @@ use std::{ffi::{CStr, CString}, ptr::null};
use crate::backend::current::TEMP_REGS;
use std::os::raw::{c_char, c_int, c_uint};
// Call threshold for small deployments and command-line apps
pub static SMALL_CALL_THRESHOLD: u64 = 30;
// Call threshold for larger deployments and production-sized applications
pub static LARGE_CALL_THRESHOLD: u64 = 120;
// Number of live ISEQs after which we consider an app to be large
pub static LARGE_ISEQ_COUNT: u64 = 40_000;
// This option is exposed to the C side a a global variable for performance, see vm.c
// Number of method calls after which to start generating code
// Threshold==1 means compile on first execution
#[no_mangle]
static mut rb_yjit_call_threshold: u64 = 30;
pub static mut rb_yjit_call_threshold: u64 = SMALL_CALL_THRESHOLD;
// This option is exposed to the C side a a global variable for performance, see vm.c
// Number of execution requests after which a method is no longer
// considered hot. Raising this results in more generated code.
#[no_mangle]
static mut rb_yjit_cold_threshold: u64 = 200_000;
pub static mut rb_yjit_cold_threshold: u64 = 200_000;
// Command-line options
#[derive(Clone, PartialEq, Eq, Debug)]
@ -92,7 +101,7 @@ static YJIT_OPTIONS: [(&str, &str); 8] = [
("--yjit-trace-exits", "Record Ruby source location when exiting from generated code"),
("--yjit-trace-exits-sample-rate", "Trace exit locations only every Nth occurrence"),
("--yjit-exec-mem-size=num", "Size of executable memory block in MiB (default: 128)"),
("--yjit-call-threshold=num", "Number of calls to trigger JIT (default: 30)"),
("--yjit-call-threshold=num", "Number of calls to trigger JIT"),
("--yjit-cold-threshold=num", "Global call after which ISEQs not compiled (default: 200K)"),
("--yjit-max-versions=num", "Maximum number of versions per basic block (default: 4)"),
("--yjit-perf", "Enable frame pointers and perf profiling"),

View File

@ -134,6 +134,14 @@ pub extern "C" fn rb_yjit_iseq_gen_entry_point(iseq: IseqPtr, ec: EcPtr, jit_exc
return std::ptr::null();
}
// If a custom call threshold was not specified on the command-line and
// this is a large application (has very many ISEQs), switch to
// using the call threshold for large applications after this entry point
use crate::stats::rb_yjit_live_iseq_count;
if unsafe { rb_yjit_call_threshold } == SMALL_CALL_THRESHOLD && unsafe { rb_yjit_live_iseq_count } > LARGE_ISEQ_COUNT {
unsafe { rb_yjit_call_threshold = LARGE_CALL_THRESHOLD; };
}
let maybe_code_ptr = with_compile_time(|| { gen_entry_point(iseq, ec, jit_exception) });
match maybe_code_ptr {