Skip to content

Don't pin objects permanently in engine.cpp #89

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

Merged

Conversation

d-netto
Copy link
Collaborator

@d-netto d-netto commented Jun 16, 2025

Creates a side table of objects pinned by inference engine.

Adds/removes objects to the table at the start/end of inference.

@d-netto
Copy link
Collaborator Author

d-netto commented Jun 16, 2025

Should fix mmtk/mmtk-julia#246.

@d-netto d-netto force-pushed the dcn-optimize-pinning-in-inference-engine branch from 7e323b1 to 388036b Compare June 17, 2025 02:45
@d-netto d-netto force-pushed the dcn-optimize-pinning-in-inference-engine branch from 388036b to e19c0da Compare June 17, 2025 16:26
@d-netto d-netto marked this pull request as draft June 17, 2025 16:27
@d-netto
Copy link
Collaborator Author

d-netto commented Jun 17, 2025

Sharing some fragmentation time-series for the inference benchmark, similar to those we discussed on Slack:

  • Stock GC:

stock_gc_inference_benchmark_fragmentation

mmtk_immix_inference_benchmark_fragmentation

@d-netto d-netto marked this pull request as ready for review June 17, 2025 22:51
@d-netto d-netto requested review from qinsoon and udesou June 17, 2025 22:52
@d-netto d-netto changed the title WIP: Don't pin objects permanently in engine.cpp Don't pin objects permanently in engine.cpp Jun 17, 2025
@udesou
Copy link

udesou commented Jun 18, 2025

I still wonder whether pin/unpin is the right approach here - i.e., whether we could actually move those objects.
Could we instead:
(1) potentially traverse these objects and add the references from InferKey as roots - we might want to use something similar but perhaps slightly different than the vm specific roots in

JL_DLLEXPORT void jl_gc_scan_vm_specific_roots(RootsWorkClosure* closure)
otherwise they'll be pinned for every GC as well.
(2) adapt your code to capture the address of these objects into objects_pinned_by_inference_engine and give those objects to the GC as roots?

@d-netto
Copy link
Collaborator Author

d-netto commented Jun 18, 2025

Your suggestions might allow us to move more objects, but I'm not sure if they are necessary at the moment.

If we find that my approach introduces unnecessary pinning, then we can optimize it further.

But this PR is already a huge performance win, and seems "good enough" as it is.

@udesou
Copy link

udesou commented Jun 18, 2025

Your suggestions might allow us to move more objects, but I'm not sure if they are necessary at the moment.

If we find that my approach introduces unnecessary pinning, then we can optimize it further.

But this PR is already a huge performance win, and seems "good enough" as it is.

I agree, but I don't see the need to pin/unpin these. While pinning/unpinning is the direction we should take for conservative stack pinning since we don't know whether those references are or are not actual roots, these are roots. A simple change is to remove the code for pinning/unpinning and actually process those roots in jl_gc_scan_vm_specific_roots:

for (size_t i = 0; i < gc_pinned_objects.len; i++) {
      void *obj = gc_pinned_objects.items[i];
      add_node_to_roots_buffer(closure, &buf, &len, obj);
}

You might want to rename the variable gc_pinned_objects etc.

@d-netto
Copy link
Collaborator Author

d-netto commented Jun 18, 2025

Wait, are these pinned_refs stored in the C++ data structures traced in the current implementation?

@udesou
Copy link

udesou commented Jun 18, 2025

Wait, are these pinned_refs stored in the C++ data structures traced in the current implementation?

They are indefinitely pinned AFAIK. But that was just a conservative solution that we used for all the dominated roots that are not traced by the GC.

@d-netto
Copy link
Collaborator Author

d-netto commented Jun 18, 2025

Ok, so here is the reasoning behind my implementation choice of pinning them, but not rooting them:

  • Current implementation permanently pins them, but doesn't register them as roots. So there must be other part of the code rooting them.
  • This other part of the code is unchanged in my PR, and will still continue rooting them.

Am I missing something?

@udesou
Copy link

udesou commented Jun 18, 2025

My suggestions (untested) according to the point I've made above: https://github.com/mmtk/julia/compare/dcn-optimize-pinning-in-inference-engine...udesou:udesou-minor-refactoring?expand=1.

Ok, so here is the reasoning behind my implementation choice of pinning them, but not rooting them:

  • Current implementation permanently pins them, but doesn't register them as roots. So there must be other part of the code rooting them.
  • This other part of the code is unchanged in my PR, and will still continue rooting them.

Am I missing something?

But that's why we had this problem in the first place right? (i.e., we have a root that is not being treated as a root because it's being kept alive by some other reference somewhere else)
And what we're trying to do is to make rooting explicit, at least in the long run, I'm not saying that this should be necessarily the case in this PR alone. Note that pinning does not make them roots. It works as a conservative solution (a "hack") and as Yi mentioned above, that macro serves as documentation to let us know the places where this happens. The solution above should not only work the same way but it also does the "right" thing, it treats those objects as roots. Furthermore it simplifies your code removing the need to pin/unpin those objects.

@qinsoon
Copy link
Member

qinsoon commented Jun 18, 2025

Ok, so here is the reasoning behind my implementation choice of pinning them, but not rooting them:

* Current implementation permanently pins them, but doesn't register them as roots. So there must be other part of the code rooting them.

* This other part of the code is unchanged in my PR, and will still continue rooting them.

Am I missing something?

You can look at our talk (we shared a link on Slack), the second part that talks about moving GCs. We explained why the current 'rooting' in Julia is insufficient. The definition of 'rooting' in Julia is flawed.

@d-netto
Copy link
Collaborator Author

d-netto commented Jun 18, 2025

Yes, I understand what you are saying.

I agree we should root these objects in the C++ data structure at some point, and stop relying on them getting rooted by some other part of the code.

I believe that chasing the C++ data structures and other spots in the code that this may happen is a topic for another PR, though.

@qinsoon
Copy link
Member

qinsoon commented Jun 18, 2025

chasing the C++ data structures and other spots in the code that this may happen is a topic for another PR

Those places where we use jl_pinned_ref are the roots we identified. We use pinning just for simplicity. This PR removes an occurrence of jl_pinned_ref. Ideally, we could just trace it as a root node, rather than a different mechanism for pinning/unpinning for GC. But I think the current PR is fine. We can tidy it up later.

@udesou
Copy link

udesou commented Jun 18, 2025

Sounds good to me.

@d-netto d-netto merged commit 5e33341 into mmtk-support-moving-upstream Jun 18, 2025
3 checks passed
qinsoon added a commit that referenced this pull request Jun 27, 2025
Following the suggestion in #89 (comment), trace recently added pinned objects as roots. When we pass those objects as 'nodes' to MMTk, MMTk does not know the slots so MMTk cannot update the reference, thus MMTk will not move those objects. This is essentially the same as pinning those objects before a GC, and unpinning after a GC.
udesou pushed a commit to udesou/julia that referenced this pull request Jul 30, 2025
Following the suggestion in mmtk#89 (comment), trace recently added pinned objects as roots. When we pass those objects as 'nodes' to MMTk, MMTk does not know the slots so MMTk cannot update the reference, thus MMTk will not move those objects. This is essentially the same as pinning those objects before a GC, and unpinning after a GC.
udesou pushed a commit to udesou/julia that referenced this pull request Aug 1, 2025
Following the suggestion in mmtk#89 (comment), trace recently added pinned objects as roots. When we pass those objects as 'nodes' to MMTk, MMTk does not know the slots so MMTk cannot update the reference, thus MMTk will not move those objects. This is essentially the same as pinning those objects before a GC, and unpinning after a GC.
udesou pushed a commit to udesou/julia that referenced this pull request Aug 5, 2025
Following the suggestion in mmtk#89 (comment), trace recently added pinned objects as roots. When we pass those objects as 'nodes' to MMTk, MMTk does not know the slots so MMTk cannot update the reference, thus MMTk will not move those objects. This is essentially the same as pinning those objects before a GC, and unpinning after a GC.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants