-
Notifications
You must be signed in to change notification settings - Fork 75
Optimize object forwarding for single threaded GC #1283
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
base: master
Are you sure you want to change the base?
Changes from all commits
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 |
---|---|---|
|
@@ -21,22 +21,36 @@ const FORWARDING_POINTER_MASK: usize = 0xffff_fffc; | |
/// Attempt to become the worker thread who will forward the object. | ||
/// The successful worker will set the object forwarding bits to BEING_FORWARDED, preventing other workers from forwarding the same object. | ||
pub fn attempt_to_forward<VM: VMBinding>(object: ObjectReference) -> u8 { | ||
loop { | ||
if cfg!(not(feature = "single_worker")) { | ||
loop { | ||
let old_value = get_forwarding_status::<VM>(object); | ||
if old_value != FORWARDING_NOT_TRIGGERED_YET | ||
|| VM::VMObjectModel::LOCAL_FORWARDING_BITS_SPEC | ||
.compare_exchange_metadata::<VM, u8>( | ||
object, | ||
old_value, | ||
BEING_FORWARDED, | ||
None, | ||
Ordering::SeqCst, | ||
Ordering::Relaxed, | ||
) | ||
.is_ok() | ||
{ | ||
return old_value; | ||
} | ||
} | ||
} else { | ||
let old_value = get_forwarding_status::<VM>(object); | ||
if old_value != FORWARDING_NOT_TRIGGERED_YET | ||
|| VM::VMObjectModel::LOCAL_FORWARDING_BITS_SPEC | ||
.compare_exchange_metadata::<VM, u8>( | ||
if old_value == FORWARDING_NOT_TRIGGERED_YET { | ||
unsafe { | ||
VM::VMObjectModel::LOCAL_FORWARDING_BITS_SPEC.store::<VM, u8>( | ||
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. This |
||
object, | ||
old_value, | ||
BEING_FORWARDED, | ||
None, | ||
Ordering::SeqCst, | ||
Ordering::Relaxed, | ||
) | ||
.is_ok() | ||
{ | ||
return old_value; | ||
} | ||
} | ||
old_value | ||
} | ||
} | ||
|
||
|
@@ -54,23 +68,33 @@ pub fn spin_and_get_forwarded_object<VM: VMBinding>( | |
forwarding_bits: u8, | ||
) -> ObjectReference { | ||
let mut forwarding_bits = forwarding_bits; | ||
while forwarding_bits == BEING_FORWARDED { | ||
forwarding_bits = get_forwarding_status::<VM>(object); | ||
} | ||
if cfg!(not(feature = "single_worker")) { | ||
while forwarding_bits == BEING_FORWARDED { | ||
forwarding_bits = get_forwarding_status::<VM>(object); | ||
} | ||
|
||
if forwarding_bits == FORWARDED { | ||
read_forwarding_pointer::<VM>(object) | ||
if forwarding_bits == FORWARDED { | ||
read_forwarding_pointer::<VM>(object) | ||
} else { | ||
// For some policies (such as Immix), we can have interleaving such that one thread clears | ||
// the forwarding word while another thread was stuck spinning in the above loop. | ||
// See: https://github.com/mmtk/mmtk-core/issues/579 | ||
debug_assert!( | ||
forwarding_bits == FORWARDING_NOT_TRIGGERED_YET, | ||
"Invalid/Corrupted forwarding word {:x} for object {}", | ||
forwarding_bits, | ||
object, | ||
); | ||
object | ||
} | ||
} else { | ||
// For some policies (such as Immix), we can have interleaving such that one thread clears | ||
// the forwarding word while another thread was stuck spinning in the above loop. | ||
// See: https://github.com/mmtk/mmtk-core/issues/579 | ||
debug_assert!( | ||
forwarding_bits == FORWARDING_NOT_TRIGGERED_YET, | ||
forwarding_bits != BEING_FORWARDED, | ||
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. We can just assert |
||
"Invalid/Corrupted forwarding word {:x} for object {}", | ||
forwarding_bits, | ||
object, | ||
); | ||
object | ||
read_forwarding_pointer::<VM>(object) | ||
} | ||
} | ||
|
||
|
@@ -99,12 +123,22 @@ pub fn forward_object<VM: VMBinding>( | |
let new_object = VM::VMObjectModel::copy(object, semantics, copy_context); | ||
on_after_forwarding(new_object); | ||
if let Some(shift) = forwarding_bits_offset_in_forwarding_pointer::<VM>() { | ||
VM::VMObjectModel::LOCAL_FORWARDING_POINTER_SPEC.store_atomic::<VM, usize>( | ||
object, | ||
new_object.to_raw_address().as_usize() | ((FORWARDED as usize) << shift), | ||
None, | ||
Ordering::SeqCst, | ||
) | ||
if cfg!(not(feature = "single_worker")) { | ||
VM::VMObjectModel::LOCAL_FORWARDING_POINTER_SPEC.store_atomic::<VM, usize>( | ||
object, | ||
new_object.to_raw_address().as_usize() | ((FORWARDED as usize) << shift), | ||
None, | ||
Ordering::SeqCst, | ||
) | ||
} else { | ||
unsafe { | ||
VM::VMObjectModel::LOCAL_FORWARDING_POINTER_SPEC.store::<VM, usize>( | ||
object, | ||
new_object.to_raw_address().as_usize() | ((FORWARDED as usize) << shift), | ||
None, | ||
) | ||
} | ||
} | ||
} else { | ||
write_forwarding_pointer::<VM>(object, new_object); | ||
VM::VMObjectModel::LOCAL_FORWARDING_BITS_SPEC.store_atomic::<VM, u8>( | ||
|
@@ -119,11 +153,15 @@ pub fn forward_object<VM: VMBinding>( | |
|
||
/// Return the forwarding bits for a given `ObjectReference`. | ||
pub fn get_forwarding_status<VM: VMBinding>(object: ObjectReference) -> u8 { | ||
VM::VMObjectModel::LOCAL_FORWARDING_BITS_SPEC.load_atomic::<VM, u8>( | ||
object, | ||
None, | ||
Ordering::SeqCst, | ||
) | ||
if cfg!(not(feature = "single_worker")) { | ||
VM::VMObjectModel::LOCAL_FORWARDING_BITS_SPEC.load_atomic::<VM, u8>( | ||
object, | ||
None, | ||
Ordering::SeqCst, | ||
) | ||
} else { | ||
unsafe { VM::VMObjectModel::LOCAL_FORWARDING_BITS_SPEC.load::<VM, u8>(object, None) } | ||
} | ||
} | ||
|
||
pub fn is_forwarded<VM: VMBinding>(object: ObjectReference) -> bool { | ||
|
@@ -149,12 +187,16 @@ pub fn state_is_being_forwarded(forwarding_bits: u8) -> bool { | |
/// Zero the forwarding bits of an object. | ||
/// This function is used on new objects. | ||
pub fn clear_forwarding_bits<VM: VMBinding>(object: ObjectReference) { | ||
VM::VMObjectModel::LOCAL_FORWARDING_BITS_SPEC.store_atomic::<VM, u8>( | ||
object, | ||
0, | ||
None, | ||
Ordering::SeqCst, | ||
) | ||
if cfg!(not(feature = "single_worker")) { | ||
VM::VMObjectModel::LOCAL_FORWARDING_BITS_SPEC.store_atomic::<VM, u8>( | ||
object, | ||
0, | ||
None, | ||
Ordering::SeqCst, | ||
) | ||
} else { | ||
unsafe { VM::VMObjectModel::LOCAL_FORWARDING_BITS_SPEC.store::<VM, u8>(object, 0, None) } | ||
} | ||
} | ||
|
||
/// Read the forwarding pointer of an object. | ||
|
@@ -168,15 +210,24 @@ pub fn read_forwarding_pointer<VM: VMBinding>(object: ObjectReference) -> Object | |
|
||
// We write the forwarding poiner. We know it is an object reference. | ||
unsafe { | ||
// We use "unchecked" convertion becasue we guarantee the forwarding pointer we stored | ||
// previously is from a valid `ObjectReference` which is never zero. | ||
ObjectReference::from_raw_address_unchecked(crate::util::Address::from_usize( | ||
VM::VMObjectModel::LOCAL_FORWARDING_POINTER_SPEC.load_atomic::<VM, usize>( | ||
object, | ||
Some(FORWARDING_POINTER_MASK), | ||
Ordering::SeqCst, | ||
), | ||
)) | ||
if cfg!(not(feature = "single_worker")) { | ||
// We use "unchecked" convertion becasue we guarantee the forwarding pointer we stored | ||
// previously is from a valid `ObjectReference` which is never zero. | ||
ObjectReference::from_raw_address_unchecked(crate::util::Address::from_usize( | ||
VM::VMObjectModel::LOCAL_FORWARDING_POINTER_SPEC.load_atomic::<VM, usize>( | ||
object, | ||
Some(FORWARDING_POINTER_MASK), | ||
Ordering::SeqCst, | ||
), | ||
)) | ||
} else { | ||
// We use "unchecked" convertion becasue we guarantee the forwarding pointer we stored | ||
// previously is from a valid `ObjectReference` which is never zero. | ||
ObjectReference::from_raw_address_unchecked(crate::util::Address::from_usize( | ||
VM::VMObjectModel::LOCAL_FORWARDING_POINTER_SPEC | ||
.load::<VM, usize>(object, Some(FORWARDING_POINTER_MASK)), | ||
)) | ||
} | ||
} | ||
} | ||
|
||
|
@@ -194,12 +245,22 @@ pub fn write_forwarding_pointer<VM: VMBinding>( | |
); | ||
|
||
trace!("write_forwarding_pointer({}, {})", object, new_object); | ||
VM::VMObjectModel::LOCAL_FORWARDING_POINTER_SPEC.store_atomic::<VM, usize>( | ||
object, | ||
new_object.to_raw_address().as_usize(), | ||
Some(FORWARDING_POINTER_MASK), | ||
Ordering::SeqCst, | ||
) | ||
if cfg!(not(feature = "single_worker")) { | ||
VM::VMObjectModel::LOCAL_FORWARDING_POINTER_SPEC.store_atomic::<VM, usize>( | ||
object, | ||
new_object.to_raw_address().as_usize(), | ||
Some(FORWARDING_POINTER_MASK), | ||
Ordering::SeqCst, | ||
) | ||
} else { | ||
unsafe { | ||
VM::VMObjectModel::LOCAL_FORWARDING_POINTER_SPEC.store::<VM, usize>( | ||
object, | ||
new_object.to_raw_address().as_usize(), | ||
Some(FORWARDING_POINTER_MASK), | ||
) | ||
} | ||
} | ||
} | ||
|
||
/// (This function is only used internal to the `util` module) | ||
|
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.
Having this if-else statement with
not(feature = "single_worker")
as the condition makes the code a bit harder to read. Since it is simpler to do it with a single worker, we can rewrite it in the early exit style. For example,