Skip to content

Commit acc3172

Browse files
authored
ZJIT: Profile each instruction at most num_profiles times (#13903)
* ZJIT: Profile each instruction at most num_profiles times * Use saturating_add for num_profiles
1 parent af1ad78 commit acc3172

File tree

5 files changed

+56
-26
lines changed

5 files changed

+56
-26
lines changed

zjit.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,18 @@ rb_zjit_profile_disable(const rb_iseq_t *iseq)
295295
}
296296
}
297297

298+
// Update a YARV instruction to a given opcode (to disable ZJIT profiling).
299+
void
300+
rb_zjit_iseq_insn_set(const rb_iseq_t *iseq, unsigned int insn_idx, enum ruby_vminsn_type bare_insn)
301+
{
302+
#if RUBY_DEBUG
303+
int insn = rb_vm_insn_addr2opcode((void *)iseq->body->iseq_encoded[insn_idx]);
304+
RUBY_ASSERT(vm_zjit_insn_to_bare_insn(insn) == (int)bare_insn);
305+
#endif
306+
const void *const *insn_table = rb_vm_get_insns_address_table();
307+
iseq->body->iseq_encoded[insn_idx] = (VALUE)insn_table[bare_insn];
308+
}
309+
298310
// Get profiling information for ISEQ
299311
void *
300312
rb_iseq_get_zjit_payload(const rb_iseq_t *iseq)

zjit/bindgen/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ fn main() {
335335
.allowlist_function("rb_zjit_get_page_size")
336336
.allowlist_function("rb_zjit_iseq_builtin_attrs")
337337
.allowlist_function("rb_zjit_iseq_inspect")
338+
.allowlist_function("rb_zjit_iseq_insn_set")
338339
.allowlist_function("rb_set_cfp_(pc|sp)")
339340
.allowlist_function("rb_c_method_tracing_currently_enabled")
340341
.allowlist_function("rb_full_cfunc_return")

zjit/src/cruby_bindings.inc.rs

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

zjit/src/options.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub static mut rb_zjit_call_threshold: u64 = 2;
1717
#[derive(Clone, Copy, Debug)]
1818
pub struct Options {
1919
/// Number of times YARV instructions should be profiled.
20-
pub num_profiles: u64,
20+
pub num_profiles: u8,
2121

2222
/// Enable debug logging
2323
pub debug: bool,
@@ -51,7 +51,7 @@ pub fn init_options() -> Options {
5151
/// Note that --help allows only 80 chars per line, including indentation. 80-char limit --> |
5252
pub const ZJIT_OPTIONS: &'static [(&str, &str)] = &[
5353
("--zjit-call-threshold=num", "Number of calls to trigger JIT (default: 2)."),
54-
("--zjit-num-profiles=num", "Number of profiled calls before JIT (default: 1)."),
54+
("--zjit-num-profiles=num", "Number of profiled calls before JIT (default: 1, max: 255)."),
5555
];
5656

5757
#[derive(Clone, Copy, Debug)]
@@ -158,7 +158,7 @@ fn update_profile_threshold(options: &Options) {
158158
rb_zjit_profile_threshold = 0;
159159
} else {
160160
// Otherwise, profile instructions at least once.
161-
rb_zjit_profile_threshold = rb_zjit_call_threshold.saturating_sub(options.num_profiles).max(1);
161+
rb_zjit_profile_threshold = rb_zjit_call_threshold.saturating_sub(options.num_profiles as u64).max(1);
162162
}
163163
}
164164
}

zjit/src/profile.rs

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// We use the YARV bytecode constants which have a CRuby-style name
22
#![allow(non_upper_case_globals)]
33

4-
use crate::{cruby::*, gc::get_or_create_iseq_payload, hir_type::{types::{Empty, Fixnum}, Type}};
4+
use crate::{cruby::*, gc::get_or_create_iseq_payload, hir_type::{types::{Empty, Fixnum}, Type}, options::get_option};
55

66
/// Ephemeral state for profiling runtime information
77
struct Profiler {
@@ -38,43 +38,49 @@ impl Profiler {
3838

3939
/// API called from zjit_* instruction. opcode is the bare (non-zjit_*) instruction.
4040
#[unsafe(no_mangle)]
41-
pub extern "C" fn rb_zjit_profile_insn(opcode: ruby_vminsn_type, ec: EcPtr) {
41+
pub extern "C" fn rb_zjit_profile_insn(bare_opcode: ruby_vminsn_type, ec: EcPtr) {
4242
with_vm_lock(src_loc!(), || {
4343
let mut profiler = Profiler::new(ec);
44-
profile_insn(&mut profiler, opcode);
44+
profile_insn(&mut profiler, bare_opcode);
4545
});
4646
}
4747

4848
/// Profile a YARV instruction
49-
fn profile_insn(profiler: &mut Profiler, opcode: ruby_vminsn_type) {
50-
match opcode {
51-
YARVINSN_opt_nil_p => profile_operands(profiler, 1),
52-
YARVINSN_opt_plus => profile_operands(profiler, 2),
53-
YARVINSN_opt_minus => profile_operands(profiler, 2),
54-
YARVINSN_opt_mult => profile_operands(profiler, 2),
55-
YARVINSN_opt_div => profile_operands(profiler, 2),
56-
YARVINSN_opt_mod => profile_operands(profiler, 2),
57-
YARVINSN_opt_eq => profile_operands(profiler, 2),
58-
YARVINSN_opt_neq => profile_operands(profiler, 2),
59-
YARVINSN_opt_lt => profile_operands(profiler, 2),
60-
YARVINSN_opt_le => profile_operands(profiler, 2),
61-
YARVINSN_opt_gt => profile_operands(profiler, 2),
62-
YARVINSN_opt_ge => profile_operands(profiler, 2),
63-
YARVINSN_opt_and => profile_operands(profiler, 2),
64-
YARVINSN_opt_or => profile_operands(profiler, 2),
49+
fn profile_insn(profiler: &mut Profiler, bare_opcode: ruby_vminsn_type) {
50+
let profile = &mut get_or_create_iseq_payload(profiler.iseq).profile;
51+
match bare_opcode {
52+
YARVINSN_opt_nil_p => profile_operands(profiler, profile, 1),
53+
YARVINSN_opt_plus => profile_operands(profiler, profile, 2),
54+
YARVINSN_opt_minus => profile_operands(profiler, profile, 2),
55+
YARVINSN_opt_mult => profile_operands(profiler, profile, 2),
56+
YARVINSN_opt_div => profile_operands(profiler, profile, 2),
57+
YARVINSN_opt_mod => profile_operands(profiler, profile, 2),
58+
YARVINSN_opt_eq => profile_operands(profiler, profile, 2),
59+
YARVINSN_opt_neq => profile_operands(profiler, profile, 2),
60+
YARVINSN_opt_lt => profile_operands(profiler, profile, 2),
61+
YARVINSN_opt_le => profile_operands(profiler, profile, 2),
62+
YARVINSN_opt_gt => profile_operands(profiler, profile, 2),
63+
YARVINSN_opt_ge => profile_operands(profiler, profile, 2),
64+
YARVINSN_opt_and => profile_operands(profiler, profile, 2),
65+
YARVINSN_opt_or => profile_operands(profiler, profile, 2),
6566
YARVINSN_opt_send_without_block => {
6667
let cd: *const rb_call_data = profiler.insn_opnd(0).as_ptr();
6768
let argc = unsafe { vm_ci_argc((*cd).ci) };
6869
// Profile all the arguments and self (+1).
69-
profile_operands(profiler, (argc + 1) as usize);
70+
profile_operands(profiler, profile, (argc + 1) as usize);
7071
}
7172
_ => {}
7273
}
74+
75+
// Once we profile the instruction num_profiles times, we stop profiling it.
76+
profile.num_profiles[profiler.insn_idx] = profile.num_profiles[profiler.insn_idx].saturating_add(1);
77+
if profile.num_profiles[profiler.insn_idx] == get_option!(num_profiles) {
78+
unsafe { rb_zjit_iseq_insn_set(profiler.iseq, profiler.insn_idx as u32, bare_opcode); }
79+
}
7380
}
7481

7582
/// Profile the Type of top-`n` stack operands
76-
fn profile_operands(profiler: &mut Profiler, n: usize) {
77-
let profile = &mut get_or_create_iseq_payload(profiler.iseq).profile;
83+
fn profile_operands(profiler: &mut Profiler, profile: &mut IseqProfile, n: usize) {
7884
let types = &mut profile.opnd_types[profiler.insn_idx];
7985
if types.len() <= n {
8086
types.resize(n, Empty);
@@ -89,11 +95,17 @@ fn profile_operands(profiler: &mut Profiler, n: usize) {
8995
pub struct IseqProfile {
9096
/// Type information of YARV instruction operands, indexed by the instruction index
9197
opnd_types: Vec<Vec<Type>>,
98+
99+
/// Number of profiled executions for each YARV instruction, indexed by the instruction index
100+
num_profiles: Vec<u8>,
92101
}
93102

94103
impl IseqProfile {
95104
pub fn new(iseq_size: u32) -> Self {
96-
Self { opnd_types: vec![vec![]; iseq_size as usize] }
105+
Self {
106+
opnd_types: vec![vec![]; iseq_size as usize],
107+
num_profiles: vec![0; iseq_size as usize],
108+
}
97109
}
98110

99111
/// Get profiled operand types for a given instruction index

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy