Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Rejit support for R2R - WIP #9298

Merged
merged 1 commit into from
Feb 14, 2017
Merged

Rejit support for R2R - WIP #9298

merged 1 commit into from
Feb 14, 2017

Conversation

noahfalk
Copy link
Member

@noahfalk noahfalk commented Feb 3, 2017

Attempting to use Rejit with R2R rapidly hits two issues:
a) The prestub never communicates to the RejitManager that new code from an R2R image is about to be used. This causes pending requests to rejit a method never
to be honored. Fixed in prestub.cpp

b) ICorProfilerInfo6::EnumNgenMethodsInliningThisMethod didn't work with R2R images because R2R images did not carry the inline tracking table. The majority of the changes address this part. Currently only intra-module inlining is supported. Cross module inlining via NonVersionableAttribute won't be tracked by design.

This change isn't ready to be checked in yet, but it does demonstrate the proposed file format, the layout of the code, and works well enough to pass really trivial functional tests on windows. Most of my planned work at this point is going to be subjecting it to broader testing (still on windows), fixing issues, and responding to PR feedback. Linux is on the roadmap and may work as-is, but verifying that is not part of this PR. I'm going to look into putting a better guard in place so that anyone adding cross module inlining in the future will realize the implications for the profiler. I wasn't planning to do perf investigation for size on disk as this format should be strictly smaller than NGEN's and we already accepted the perf overhead in that case.

@noahfalk
Copy link
Member Author

noahfalk commented Feb 3, 2017

@jkotas @fadimounir - PTAL

@gkhanna79 - Gaurav are there additional people on your team that should be included on this PR?

Thanks!

@fadimounir
Copy link

The prestub never communicates to the RejitManager that new code from an R2R image is about to be used. This causes pending requests to rejit a method never
to be honored

@noahfalk could you please give me some context on what this rejit feature is? What do we rejit and why? Is it because we can't use AOT code from the R2R image for some reason? etc...

@gkhanna79
Copy link
Member

CC @rahku @jkotas

@@ -57,6 +57,7 @@ enum ReadyToRunSectionType
// 107 used by an older format of READYTORUN_SECTION_AVAILABLE_TYPES
READYTORUN_SECTION_AVAILABLE_TYPES = 108,
READYTORUN_SECTION_INSTANCE_METHOD_ENTRYPOINTS = 109,
READYTORUN_SECTION_INLINING_INFO = 110

Choose a reason for hiding this comment

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

READYTORUN_SECTION_INLINING_INFO [](start = 4, length = 32)

@jkotas Does this need a change the the R2R minor version?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, I think it would be a good idea to bump the R2R minor version for this.

Copy link
Member Author

Choose a reason for hiding this comment

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

Will do

{
LIMITED_METHOD_DAC_CONTRACT;
return m_persistentInlineTrackingMap;
#ifdef FEATURE_READYTORUN
if(IsReadyToRun() && GetReadyToRunInfo()->GetInlineTrackingMap() != NULL) return TRUE;

Choose a reason for hiding this comment

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

return TRUE; [](start = 78, length = 12)

Nit: new line :)

Copy link
Member Author

Choose a reason for hiding this comment

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

Will do

@fadimounir
Copy link

You should have a test (at least a manual one before merging the PR) where you crossgen an assembly to produce a R2R using an old crossgen without any of your changes, and see how this loads with your runtime changes.
And vice versa, you should ensure that new R2R images with your changes either work with an older runtime, or get rejected by it (but not crash it)


// This type know how to serialize and deserialize the inline tracking map format within an R2R image. See
// above for a description of the format.
class PersistentInlineTrackingMapR2R

Choose a reason for hiding this comment

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

PersistentInlineTrackingMapR2R [](start = 6, length = 30)

Maybe this should be under the FEATURE_READYTORUN?

Copy link
Member Author

Choose a reason for hiding this comment

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

Will do

@@ -40,6 +41,9 @@ class ReadyToRunInfo
Crst m_Crst;
PtrHashMap m_entryPointToMethodDescMap;

PersistentInlineTrackingMapR2R m_persistentInlineTrackingMap;
PTR_PersistentInlineTrackingMapR2R m_pPersistentInlineTrackingMap;

Choose a reason for hiding this comment

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

I'm personally not a big fan of this... Maybe you should make the .TryLoad function a static one that returns a pointer to a heap allocated map. Feels a bit like a hack to me

Copy link
Member Author

Choose a reason for hiding this comment

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

Mostly I just did it because the machinery to make new allocations looks like it requires some additional arguments to be passed around and the map datastructure is only a few pointers in size, however if I am breaking form I can certainly do a seperate allocation.


#ifndef DACCESS_COMPILE
COUNT_T PersistentInlineTrackingMap::GetInliners(PTR_Module inlineeOwnerMod, mdMethodDef inlineeTkn, COUNT_T inlinersSize, MethodInModule inliners[], BOOL *incompleteData)
// Going through last 10 inliners to check if a given inliner has recently been registered.

Choose a reason for hiding this comment

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

10 [](start = 26, length = 2)

What is this number based on? just curious :). Any data?

Copy link
Member Author

Choose a reason for hiding this comment

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

This number didn't actually come from me, I was simply rearranging the file a bit so that methods on different types wouldn't be intermingled. The original author was Eugene and I think he came up with this number and several others empirically based on some ad-hoc experiements/guessing reasonable values.

int SizeOfInlineeIndex;
};

#ifdef FEATURE_NATIVE_IMAGE_GENERATION

Choose a reason for hiding this comment

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

FEATURE_NATIVE_IMAGE_GENERATION [](start = 7, length = 31)

shouldn't we also have FEATURE_READYTORUN here?

Copy link
Member Author

Choose a reason for hiding this comment

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

will do

entry->SortAndDeduplicate();
MethodInModule inlinee = entry->m_inlinee;
InlineSArray<MethodInModule, 3> &inliners = entry->m_inliners;
COUNT_T tatalInlinersCount = inliners.GetCount();

Choose a reason for hiding this comment

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

tatalInlinersCount [](start = 12, length = 18)

typo

Copy link
Member Author

Choose a reason for hiding this comment

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

will do

@fadimounir
Copy link

InlineTrackingEntry **inlinees = new InlineTrackingEntry *[runtimeMapCount];

We should probably check for null allocations (out of memory cases)


Refers to: src/vm/inlinetracking.cpp:382 in c761c11. [](commit_id = c761c11, deletion_comment = False)

// Sort records from runtimeMap, because we need to make sure
// we save everything in deterministic order. Hashtable iteration is not deterministic.
COUNT_T runtimeMapCount = runtimeMap->GetCount();
InlineTrackingEntry **inlinees = new InlineTrackingEntry *[runtimeMapCount];

Choose a reason for hiding this comment

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

new InlineTrackingEntry *[runtimeMapCount]; [](start = 37, length = 43)

check for null allocation (OOM)

Copy link
Member Author

Choose a reason for hiding this comment

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

Will do (though if it alarms anyone this is a pre-existing bug in the NGEN variant of the code that got replicated here)

Copy link
Member Author

Choose a reason for hiding this comment

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

Looking around at numerous other allocation sites in NGEN/R2R paths I am not seeing any that do NULL checks. It appears the common pattern is "new (image->GetHeap()) InlineTrackingEntry *[runtimeMapCount]" that will throw an out of memory exception automatically on failure to allocate.


InliningHeader header;
header.MajorVersion = 1;
header.MinorVersion = 0;

Choose a reason for hiding this comment

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

It would be nice to have these versions declared in #define directives so that updating them in the future would be easier (including reading them in TryLoad)

Copy link
Member Author

Choose a reason for hiding this comment

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

will do

inlineeIndex->Append(record);
}

void PersistentInlineTrackingMapR2R::Save(SBuffer* pSaveTarget, InlineTrackingMap* runtimeMap)

Choose a reason for hiding this comment

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

Save [](start = 37, length = 4)

Nit: Ideally, we should share the pre-saving logic between this R2R and the NGEN persistent inline tracking map, and just have the Save method do the acutal writing (the pre-saving logic is the same in both case)

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure, I'll confirm that I can merge a bit more than we've got so far.

BOOL PersistentInlineTrackingMapR2R::TryLoad(Module* pModule, const BYTE* pBuffer, DWORD cbBuffer)
{
InliningHeader* pHeader = (InliningHeader*)pBuffer;
if(pHeader->MajorVersion != 1)

Choose a reason for hiding this comment

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

MajorVersion [](start = 16, length = 12)

Have you considered using the R2R major and minor versions instead of declaring new ones? (also seems like MinorVersion is not used)

Copy link
Member Author

@noahfalk noahfalk Feb 3, 2017

Choose a reason for hiding this comment

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

Is there a document or comments somewhere that describe the r2r versioning model? I'm not at all attached to these version checks if I can piggy back on a broader versioning boundary.

Choose a reason for hiding this comment

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

@gkhanna79 or @jkotas could you please point Noah to the r2r design doc? I can't remember where it was

Copy link
Member Author

Choose a reason for hiding this comment

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

I made some changes for versioning already guessing that we are using the standard major = breaking, minor = non-breaking and added comments to that effect. If that all looks good then I don't need the doc, but if I guessed incorrectly then yes the doc could be very useful.

@fadimounir
Copy link

The general direction looks good to me.

Do you think it would be better to have the 2 implementations for NGEN and R2R separated by #ifdefs and avoid having the NGEN and R2R suffixes to class names?

@noahfalk
Copy link
Member Author

noahfalk commented Feb 3, 2017

Historical background on rejit:

Rejit is a profiler feature that we introduced in .Net 4.0 (or 4.5 I can't recall?). The intention is to allow a profiler writer to change the implementation of a method at any time during the execution of a process. The profiler does this by indicating which Modules/MethodTokens need to be updated in ICorProfilerInfo4::RequestRejit, then providing a new IL body in IProfilerControl::SetILFunctionBody. The IL is usually a close derivative of the previous IL, but it doesn't have to be. The runtime takes this IL, uses the JIT to compile it and places a jump instruction on the beginning of the old method body to send execution to the new method body. A simple scenario where this might be useful - a developer is running an app in production and they want to log the value of a local variable in the middle of a function without rebuilding and redeploying a new application. Using rejit the developer could ask the profiler to change the implementation so the IL includes a call to a logging statement that was not there before.

In 4.5.1 (I think?) we added support for rejit in NGEN images, previously it was only supported for runtime jitted code. Part of that additional work was exposing the inlining information. This was required because if you wanted to change the implementation of method A, and the IL of method A has already been inlined inside method B, then you need to recompile B in order to get the new behavior.

When R2R was created nobody hooked it up to the rejit mechanism in the same way that NGEN was, so scenario-wise profilers lost the ability to change a bunch of framework code when moving from desktop to .Net Core. This feature aims to restore that ability.

@noahfalk
Copy link
Member Author

noahfalk commented Feb 3, 2017

Re: testing comment above - yes I plan to do both the forward and back-compat tests (new image/old runtime) + (old image/new runtime)

@noahfalk
Copy link
Member Author

noahfalk commented Feb 3, 2017

Do you think it would be better to have the 2 implementations for NGEN and R2R separated by #ifdefs and avoid having the NGEN and R2R suffixes to class names?

I don't think we can. We need to support both the mscorlib image which is NGEN'ed and the other framework images which are R2R using the same build of coreclr.

@noahfalk
Copy link
Member Author

noahfalk commented Feb 7, 2017

@fadimounir - I've taken a stab at addressing all the feedback, but keep it coming as needed. I still have a few small changes of my own planned and testing is continuing. Thanks!

*
* It can be used to lift limitation on inlining for ReJIT.
*
* NOTE: If the inlinee method is decorated with the System.Runtime.Versioning.NonVersionable attribute then
Copy link

Choose a reason for hiding this comment

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

I would more precisely call out that either NonVersionable or the profiler author explicitly disables all native images.

Copy link
Member Author

Choose a reason for hiding this comment

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

hmm, not sure I understand your suggestion? Are you suggesting adding a comment like this: "Inlining results for methods marked NonVersionable may not be complete. If you need to get an full accounting you may avoid the issue by disabling the use of all native images"

Copy link

Choose a reason for hiding this comment

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

Correct, since I'm assuming this doc will go on MSDN.

Copy link
Member Author

Choose a reason for hiding this comment

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

Updated

@mattwarren
Copy link

Rejit is a profiler feature that we introduced in .Net 4.0 (or 4.5 I can't recall?).

According to David Broman's blog ReJIT was added in .NET 4.5

@noahfalk
Copy link
Member Author

Unless I hear otherwise by Monday afternoon I'm going to assume this PR is in good shape. I'll squash and merge at that time. Thanks all!

Two changes:
a) R2R code wasn't being reported to the Rejit Manager when it was used, this is a simple fix in prestub.cpp. This makes the ReJit API work.
b) The bulk of the changes handle adding support for an inlining table to R2R so that ICorProfilerInfo6::EnumNgenMethodsInliningThisMethod can supply that information to profilers.

This was only tested on Windows thus far, but there is no apparent reason this change would be OS specific.
@noahfalk noahfalk merged commit 3cd73fb into dotnet:master Feb 14, 2017
@fadimounir
Copy link

LGTM

@karelz karelz modified the milestone: 2.0.0 Aug 28, 2017
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants