Skip to content
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

partial fix 44948 #47269

Closed
wants to merge 1 commit into from
Closed

Conversation

y-yamshchikov
Copy link
Contributor

@y-yamshchikov y-yamshchikov commented Jan 21, 2021

This code simply traverses through assemblies in the application
domain. For each assembly (module) it realizes is it Ready To Run and is
it in the same bubble with (does it deliberately bubbling the) module
from which generic function originates. If so, it makes request for code
is Ready To Run and hopes there is some in the module. If the request
succeeds it proceeds with found pointer to the bare native code.

This PR fixes a part of 44948 issue:

Generic instantiations like System.Collections.Concurrent.ConcurrentDictionary`2[System.Int32,System.__Canon] contained within same version bubble are either a bug in crossgen2 or should be taken care of by having PGO data.

Now such methods use their Ready To Run code.

This PR fixes #46160

@jkotas @alpencolt @gbalykov @t-mustafin

Also we have got significant performance gain on startup, that measures between 4.5% to 10% depending on an app.

@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label.

{
AppDomain::AssemblyIterator it = SystemDomain::System()->DefaultDomain()->IterateAssembliesEx((AssemblyIterationFlags)(kIncludeLoaded|kIncludeExecution));
CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
while (it.Next(pDomainAssembly.This()))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do see important applications that load 1000+ assemblies. I suspect that iterating through 1000+ assemblies before JITing each method will show up as unacceptable performance regression.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@y-yamshchikov A hashmap might reduce looping time. Please refer to FnV code.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HJLeee for creating hasmap we have to make global table and add new entries on every assembly loading. It will take time (may be not even big) and increase memory consumption (otherwise we'll have to change R2R format). Will recheck FNV if current approach will not give enough gain.

In case of Large Version Bubble we should to search compiled instantiation of external generic method in assembly where it's being called first. It should help with many case related to #46160. So now we're implementing this logic in ExternalMethodFixupWorker.

@jkotas
Copy link
Member

jkotas commented Jan 21, 2021

@davidwrighton Do you have thoughts about the best way to address this?

Base automatically changed from master to main March 1, 2021 09:07
@y-yamshchikov
Copy link
Contributor Author

There is an improvement of the previous approach. We eliminated iteration through modules in an appdomain. We guess that if we have a call from a method defined in some module A to a generic method defined in module B then in version bubble scenario highly likely the R2R code resides in module A, and ExternalMethodFixupWorker provides us the pointer. We pull that pointer to MethodDesc::GetPrecompiledR2RCode so it can do a probe.

Here we add a way to MethodDesc::GetPrecompiledR2RCode from ExternalMethodFixupWorker. In original ExternalMethodFixupWorker returns to assembler stub, then in some way goes to PreStubWorker -> ... -> PreStubWorker_Preemptive -> MethodDesc::DoPrestub -> ... -> MethodDesc::GetPrecompiledR2RCode. Now it goes just from ExternalMethodFixupWorker to a copy of PreStubWorker, passing to it additional parameter pModule, then goes the same way through MethodDesc::DoPrestub, pulling this parameter to it's destination in MethodDesc::GetPrecompiledR2RCode. Here in some cases we do probes of R2R code which can not be successful in original.

@jkotas
Copy link
Member

jkotas commented Mar 5, 2021

A lot of tests are failing. I think the problem is the recursive call of PreStubWorker main entrypoint. Passing the extra module as a hint sounds reasonable, but I do think that the recursive call of PreStubWorker logic is going to work well.

@mangod9
Copy link
Member

mangod9 commented Mar 29, 2021

Hi @y-yamshchikov, since this is not quite ready to merge could you please convert to draft or close till a full fix is available?

@y-yamshchikov
Copy link
Contributor Author

y-yamshchikov commented Apr 7, 2021

I've recently pushed an improved version of the feature. This PR uses the ability of the runtime to determine caller site via TransitionBlock structure. Having a pointer to caller site (e.g. return address) we call ExecutionManager::FindReadyToRunModule to get the pModule. Then pass it forward as we do.

Dear colleagues @jkotas, @mangod9, @alpencolt, please take a look.

Module* pModule = NULL;
FrameWithCookie<ExternalMethodFrame> frame(pTransitionBlock);
ExternalMethodFrame * pEMFrame = &frame;
pModule = ExecutionManager::FindReadyToRunModule((TADDR)(((BYTE*)pEMFrame->GetReturnAddress())-1));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is again a linear search through all loaded assemblies before JITing every method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be a suitable approach to improve FindReadyToRunModule to do O(log(N)) or even O(1) search on some structured cache?

@y-yamshchikov y-yamshchikov marked this pull request as draft April 12, 2021 09:54
@ghost ghost closed this May 12, 2021
@ghost
Copy link

ghost commented May 12, 2021

Draft Pull Request was automatically closed for inactivity. Please let us know if you'd like to reopen it.

@karelz karelz added this to the 6.0.0 milestone May 20, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Jun 19, 2021
This pull request was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Version Bubble implementation flaw: some methods are forced to rejit
6 participants