-
Notifications
You must be signed in to change notification settings - Fork 76
Add PolicyProcessEdges #570
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
Conversation
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.
The significance of this PR is that it eliminated plan-specific ProcessEdgesWork
implementations. The crux of the problem is still dynamically dispatching the trace_object(obj)
call into the appropriate space. With "policy process edges", the Plan
implements get_target_space
and fallback_trace
, expressing the dispatching of trace_object
in a more declarative way.
However, I think the current implementation on the plan side is not concise enough. Personally, I think get_target_space
and fallback_trace
is no better than the old if-elseif-elseif-... statement:
if obj is in space1
space1.trace_object(obj)
else if obj is in space2
space2.trace_object(obj)
else if obj is in space3
...
We may consider a subsequent refactoring to make it even more declarative.
fn create_scan_work<E: ProcessEdgesWork<VM = VM>>( | ||
&'static self, | ||
nodes: Vec<ObjectReference>, | ||
) -> Box<dyn GCWork<VM>> { | ||
Box::new(crate::scheduler::gc_work::ScanObjects::<E>::new( | ||
nodes, false, | ||
)) | ||
} |
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.
This method can be lifted to the ProcessEdgesWork
trait because ProcessEdgesWork::flush()
contains an equivalent code sequence:
// ProcessEdgesWork::flush()
let scan_objects_work = ScanObjects::<Self>::new(self.pop_nodes(), false);
self.new_scan_work(Box::new(scan_objects_work));
It can use this create_scan_work
, too:
// ProcessEdgesWork::flush()
let scan_objects_work = Self::create_scan_work::<Self>(self.pop_nodes, false);
self.new_scan_work(scan_objects_work);
A more concise way is to let the space provide an associated type
type ScanObjectsWorkType: GCWork = ScanObjects;
which can be overridden. But there seem to be a problem doing this in stable Rust.
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.
I have made some changes to this:
- add
create_scan_work()
inProcessEdgesWork
. - rename
new_scan_work()
inProcessEdgesWork
tostart_scan_work()
to avoid confusion. - remove the override
flush()
impl inPolicyProcessEdges
, and instead overridecreate_scan_work()
and forward the call toSupportPolicyProcessEdges
.
@@ -113,6 +113,9 @@ impl<VM: VMBinding> WorkBucket<VM> { | |||
pub fn add<W: GCWork<VM>>(&self, work: W) { | |||
self.add_with_priority(Self::DEFAULT_PRIORITY, Box::new(work)); | |||
} | |||
pub fn add_boxed(&self, work: Box<dyn GCWork<VM>>) { |
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.
If convenient, I would prefer that add
always takes a Box<dyn GCWork<VM>>
as its parameter. That is, requiring the user to box the work first before giving to the scheduler.
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.
It is an easy refactoring. But it may not be worth it. If we only keep the 'boxed' version, we need to add Box::new()
to ~27 callsites. In that case, you probably would like to extract the common pattern and have the unboxed version.
Actually I am thinking about something like this: trait TraceObject {
fn trace_object(obj: ObjectReference, ...) -> ObjectReference;
}
#[derive(TraceObject)]
struct SemiSpace {
#[space_field] from_space: CopySpace,
#[space_field] to_space: CopySpace,
#[fallback_trace] common: CommonSpace,
...
} So that we can use a procedural macro to automatically generate (i.e. "derive") an implementation of |
The problem is not 'dynamic dispatch'. Using the if-else pattern to dispatch The problem before this PR is that there are policy-specific code in the plan. For example, how Immix traces objects in different traces, what scan packet Immix uses, etc. Now those code are moved to the policy, and the code that remains in the plan is actually plan-specific. |
This looks like a good idea. I have no experience with procedural macros, but I am interested to give it a try (now or at some point). In this PR, different plans now use different ways to dispatch
Currently I am not seeing many merits from |
I am closing this PR. Following Kunshan's suggestion in #570 (comment), I have submitted another PR #575 to solve the issue. |
This PR adds another general process edges work packet,
PolicyProcessEdges
. For policies that may have different trace kinds and cannot useSFTProcessEdges
, they can usePolicyProcessEdges
. This further removes policy-specific code from plans (#258).struct PolicyProcessEdges
, which is similar toSFTProcessEdges
. However, it allows multiple traces for the policy (specified byTraceKind
). To usePolicyProcessEdges
,SupportPolicyProcessEdges
. andUsePolicyProcessEdges
.PolicyProcessEdges
.