-
Notifications
You must be signed in to change notification settings - Fork 19
jit: three virtual-state/registration parity gaps on the bridge path #1188
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bca594a
30d809b
0403acb
6f6262c
526b635
dc3735f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,5 @@ | ||||||||||||||||||||||||||||||||||||||||||||
| use majit_ir::IndexMapExt; | ||||||||||||||||||||||||||||||||||||||||||||
| use std::sync::atomic::{AtomicUsize, Ordering}; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| /// counter.py: JitCounter — float-based 5-way associative timetable. | ||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -17,6 +18,21 @@ const ASSOCIATIVITY: usize = 5; | |||||||||||||||||||||||||||||||||||||||||||
| /// counter.py:8 UINT32MAX = 2 ** 32 - 1 | ||||||||||||||||||||||||||||||||||||||||||||
| const UINT32MAX: u64 = 0xFFFF_FFFF; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| static MINOR_COLLECTION_STEP: AtomicUsize = AtomicUsize::new(0); | ||||||||||||||||||||||||||||||||||||||||||||
| static DECAY_GENERATION: AtomicUsize = AtomicUsize::new(0); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| /// counter.py:104-121 invoke_after_minor_collection | ||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||
| /// This runs inside a minor collection, so it must remain allocation-free and | ||||||||||||||||||||||||||||||||||||||||||||
| /// must not touch the counter table or acquire a lock. | ||||||||||||||||||||||||||||||||||||||||||||
| fn invoke_after_minor_collection() { | ||||||||||||||||||||||||||||||||||||||||||||
| let step = MINOR_COLLECTION_STEP.fetch_add(1, Ordering::Relaxed) + 1; | ||||||||||||||||||||||||||||||||||||||||||||
| if step == 32 { | ||||||||||||||||||||||||||||||||||||||||||||
| MINOR_COLLECTION_STEP.store(0, Ordering::Relaxed); | ||||||||||||||||||||||||||||||||||||||||||||
| DECAY_GENERATION.fetch_add(1, Ordering::Relaxed); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| /// One timetable entry: 5-way associative (time, subhash) pairs. | ||||||||||||||||||||||||||||||||||||||||||||
| /// counter.py:11-13 ENTRY struct. | ||||||||||||||||||||||||||||||||||||||||||||
| #[derive(Clone)] | ||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -49,11 +65,15 @@ pub struct JitCounter { | |||||||||||||||||||||||||||||||||||||||||||
| _nexthash: u64, | ||||||||||||||||||||||||||||||||||||||||||||
| /// counter.py:264 decay_by_mult — f64 (Python float). | ||||||||||||||||||||||||||||||||||||||||||||
| decay_by_mult: f64, | ||||||||||||||||||||||||||||||||||||||||||||
| /// Last `DECAY_GENERATION` this counter applied. Each counter tracks its | ||||||||||||||||||||||||||||||||||||||||||||
| /// own, so one thread's tick cannot consume another counter's decay. | ||||||||||||||||||||||||||||||||||||||||||||
| last_decay_generation: usize, | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| impl JitCounter { | ||||||||||||||||||||||||||||||||||||||||||||
| /// counter.py:84-100 __init__(self, size=DEFAULT_SIZE, translator=None) | ||||||||||||||||||||||||||||||||||||||||||||
| pub fn new(size: usize) -> Self { | ||||||||||||||||||||||||||||||||||||||||||||
| majit_gc::register_after_minor_collection_hook(invoke_after_minor_collection); | ||||||||||||||||||||||||||||||||||||||||||||
| let mut shift = 16u32; | ||||||||||||||||||||||||||||||||||||||||||||
| while (UINT32MAX >> shift) != (size as u64 - 1) { | ||||||||||||||||||||||||||||||||||||||||||||
| shift += 1; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -65,6 +85,7 @@ impl JitCounter { | |||||||||||||||||||||||||||||||||||||||||||
| timetable: vec![Entry::default(); size], | ||||||||||||||||||||||||||||||||||||||||||||
| _nexthash: 0, | ||||||||||||||||||||||||||||||||||||||||||||
| decay_by_mult: 1.0, | ||||||||||||||||||||||||||||||||||||||||||||
| last_decay_generation: DECAY_GENERATION.load(Ordering::Relaxed), | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -147,6 +168,26 @@ impl JitCounter { | |||||||||||||||||||||||||||||||||||||||||||
| /// counter.py:185-202 tick(self, hash, increment) | ||||||||||||||||||||||||||||||||||||||||||||
| #[inline(always)] | ||||||||||||||||||||||||||||||||||||||||||||
| pub fn tick(&mut self, hash: u64, increment: f64) -> bool { | ||||||||||||||||||||||||||||||||||||||||||||
| // counter.py:104-121 applies the decay synchronously inside the minor | ||||||||||||||||||||||||||||||||||||||||||||
| // collection. pyre defers it to the next tick because the metainterp | ||||||||||||||||||||||||||||||||||||||||||||
| // owns the counter table, and reaching it from inside the collector | ||||||||||||||||||||||||||||||||||||||||||||
| // would re-enter a borrow the GC does not hold. Counters are only read | ||||||||||||||||||||||||||||||||||||||||||||
| // at tick time. | ||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||
| // Each JitCounter keeps its own last-seen generation because JIT_DRIVER | ||||||||||||||||||||||||||||||||||||||||||||
| // in eval.rs is thread-local, giving each mutator thread its own counter. | ||||||||||||||||||||||||||||||||||||||||||||
| // This still saturates: if two or more 32-collection intervals elapse | ||||||||||||||||||||||||||||||||||||||||||||
| // between ticks, counter.py has applied that many decays while this | ||||||||||||||||||||||||||||||||||||||||||||
| // applies one. Carrying the elapsed count would match it, but would move | ||||||||||||||||||||||||||||||||||||||||||||
| // inline_chain_depth_typeflip's recorded jit-stats (bridges_compiled | ||||||||||||||||||||||||||||||||||||||||||||
| // 19 -> 18, guard_failures 3820 -> 3681); left for a change that can | ||||||||||||||||||||||||||||||||||||||||||||
| // re-record them on every platform. | ||||||||||||||||||||||||||||||||||||||||||||
| let generation = DECAY_GENERATION.load(Ordering::Relaxed); | ||||||||||||||||||||||||||||||||||||||||||||
| if generation != self.last_decay_generation { | ||||||||||||||||||||||||||||||||||||||||||||
| self.last_decay_generation = generation; | ||||||||||||||||||||||||||||||||||||||||||||
| self.decay_all_counters(); | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+179
to
+188
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift Apply every elapsed decay generation. If 64 minor collections occur before the next Advance Preserve each deferred decay let generation = DECAY_GENERATION.load(Ordering::Relaxed);
- if generation != self.last_decay_generation {
- self.last_decay_generation = generation;
+ while generation != self.last_decay_generation {
self.decay_all_counters();
+ self.last_decay_generation = self.last_decay_generation.wrapping_add(1);
}As per coding guidelines, “Port RPython/PyPy code with strict line-by-line structural parity; do not take shortcuts.” 📝 Committable suggestion
Suggested change
🤖 Prompt for AI AgentsSource: Coding guidelines |
||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| let index = self._get_index(hash); | ||||||||||||||||||||||||||||||||||||||||||||
| let subhash = Self::_get_subhash(hash); | ||||||||||||||||||||||||||||||||||||||||||||
| let entry = &mut self.timetable[index]; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| bridges_compiled=1 | ||
| bridges_compiled=0 | ||
| descr_set_absent=0 | ||
| descr_set_ambiguous=0 | ||
| descr_set_stale_absent=0 | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a minor collection advances the generation and
trace_next_iterationcallschange_current_fraction(..., 0.98)before this counter's nexttick, the deferred decay here is applied to the newly written boost rather than to the table state that existed when the collection occurred. With the default 0.96 multiplier, the intended 0.98 next-iteration trigger becomes about 0.9408 and can be delayed for many iterations; upstream performs the collection decay synchronously before any later boost. Drain pending decay before counter mutations such aschange_current_fraction(and before changing the decay multiplier), not only insidetick.AGENTS.md reference: AGENTS.md:L231-L233
Useful? React with 👍 / 👎.