-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Make normal statics simpler #99183
Make normal statics simpler #99183
Conversation
- Delete DomainLocalModule and ThreadLocalModule - Replumb the JIT to use a new set of helpers (in progress) - Allocate static data on a per type basis instead of a per module basis - Thread Local Statics are now stored in the same structures that the JIT can optimize (in progress) - More scenarios can support pre-init, notably support for pre-init for cases with valuetype statics, but no Cctor - Remove ModuleForStatics concept - Remove ModuleId concept - Remove ModuleIndex concept - Remove ClassDomainID concept Work still to be done 1. Finish support for R2R, and see if we can make it backcompat with the old R2R version 2. Support for the more optimized helpers (dynamic and pinned) 3. Re-enable jit helper expansions 4. Make sure SOS and the debugger continue to work
…e a smidge faster
- GenericDictionaryExpansion re-used the DomainLocalBlck Crst type, so it now has a new one with the same Crst rules as it used to have
Use <= instead of < for TLS index compare Unallocated TLSIndex is not 0xFFFFFFFF, which will make the existing checks fall back to doing the full work for generic TLS lookups.
- While I didn't do this for most of the Microsoft maintained architectures, there isn't much evidence at the moment that the hand coded assembly actually provides any value
…cInterface14 - This is to compensate for the existing GetDomainLocalModule* api no longer working
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 new DAC API look good to me. The implementation looks good too.
…g on a preemptive thread
Static variables in CoreCLR are handled by a combination of getting the "static base", and then adjusting it by an offset to get a pointer to the actual value. | ||
We define the statics base as either non-gc or gc for each field. | ||
Currently non-gc statics are any statics which are represented by primitive types (byte, sbyte, char, int, uint, long, ulong, float, double, pointers of various forms), and enums. | ||
GC statics are any statics which are represented by classes or by non-primitive valuetypes. |
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.
Does "non-primitive" in this case mean non-enums or non-blittable or some other concept? I assume it isn't the same as C# unmanaged
.
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.
Ah, it's any valuetype which when examined via GetVerifierCorElementType
will return an CorElementType
which is not ELEMENT_TYPE_VALUETYPE
or a pointer type. Effectively, its defined as the set of valuetypes which are not eligible for being a non-gc static.
We define the statics base as either non-gc or gc for each field. | ||
Currently non-gc statics are any statics which are represented by primitive types (byte, sbyte, char, int, uint, long, ulong, float, double, pointers of various forms), and enums. | ||
GC statics are any statics which are represented by classes or by non-primitive valuetypes. | ||
For struct statics, the static variable is actually a pointer to a boxed instance of the structure. |
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 see "valuetypes" above but "struct" here. Just want to make sure we are being consistent about the topic. I personally prefer "valuetypes", but "struct" works if that is clearer.
#endif //!DACCESS_COMPILE | ||
|
||
// Do not use except in DAC and profiler scenarios | ||
inline PTR_BYTE GetNonGCThreadStaticsBasePointer(PTR_Thread pThread); | ||
inline PTR_BYTE GetGCThreadStaticsBasePointer(PTR_Thread pThread); | ||
|
||
inline DWORD IsDynamicStatics() |
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.
Are we doing this?
|
||
void MethodTable::GetStaticsOffsets(StaticsOffsetType offsetType, bool fGenericStatics, uint32_t *dwGCOffset, uint32_t *dwNonGCOffset) | ||
{ | ||
if (offsetType == StaticsOffsetType::Normal) |
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.
Contract?
One last suggestion. I would built a |
/azp list |
run runtime-coreclr gcstress0x3-gcstress0xc |
/azp run runtime-coreclr gcstress0x3-gcstress0xc |
Azure Pipelines successfully started running 1 pipeline(s). |
/azp run runtime-coreclr gcstress0x3-gcstress0xc |
Azure Pipelines successfully started running 1 pipeline(s). |
Failures in GCStress are not new, it turns out that the tests on Windows Arm64 run somewhat faster with this change under GCStress which actually makes the test failures be reported. Test failures are simply a timeout, which is difficult to reproduce locally. Also this fix now contains a change for the src/tests/JIT/Regression/CLR-x86-JIT/V1.2-M02/b138117/b138117.il test, which was testing an invalid scenario. In conjunction with Jakob, I went back to the original bug this was a regression for, and changed the test to both cover problem that bug covered as well as make it be fully legal and reliable IL. Remaining test failure in normal PR leg is a known issue. |
@@ -509,6 +509,7 @@ void BulkStaticsLogger::LogAllStatics() | |||
CONTRACTL_END; | |||
|
|||
{ | |||
// TODO: This code does not appear to find all generic instantiations of types, and thus does not log ALL statics |
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.
@davidwrighton should this be a GH issue?
…n', 'JIT_GetDynamicGCStaticBase_SingleAppDomain' after PR#99183. (#103467) * Implement `JIT_GetDynamicNonGCStaticBase_SingleAppDomain`, `JIT_GetDynamicGCStaticBase_SingleAppDomain` for LoongArch64 after PR #99183. * Also fix the build error for LA64. Change-Id: Ifb9ee599bca3971270a5bc654770dfcc7e931955
This change makes access to statics much simpler to document and also removes some performance penalties that we've had for a long time due to the old model. Most statics access should be equivalent or faster.
This change converts static variables from a model where statics are associated with the module that defined the metadata of the static to a model where each individual type allocates its statics independently. In addition, it moves the flags that indicate whether or not a type is initialized, and whether or not its statics have been allocated to the
MethodTable
structures instead of storing them in aDomainLocalModule
as was done before.Particularly notable changes
LOADERHANDLE
to keep the static alive, and a new handle type called aHNDTYPE_WEAK_INTERIOR_POINTER
which will keep the pointers to managed objects in theMethodTable
structures up to date with the latest addresses of the static variables.DomainLocalModule
no longer exists, theISOSDacInterface
has been augmented with a new api calledISOSDacInterface14
which adds the ability to query for the static base/initialization status of an individual type directly.With this change, the pointers to normal static data are located at a fixed offset from the start of the
MethodTableAuxiliaryData
, and indices for Thread Static variables are stored also stored in such a fixed offset. Concepts such as theDomainLocalModule
,ThreadLocalModule
,ModuleId
andModuleIndex
no longer exist.Lifetime management for collectible statics
DynamicStaticsInfo
structure, and when relocation occurs, if the collectible types managedLoaderAllocator
is still alive, the static field address will be relocated if the object moves. This is done by means of the new Weak Interior Pointer GC handle type.LoaderAllocator
is being cleaned up, before the WeakTrackResurrection GCHandle that points at the the managedLoaderAllocator
object is destroyed, the mapping from TLS indices to collectibleLoaderAllocator
structures shall be cleared of all relevant entries (and the current GC index shall be stored in the TLS to MethodTable mapping)MethodTable
which is in a collectible assembly, and the associatedLoaderAllocator
has been freed, then set the relevant entry to NULL.LOADERHANDLE
for each object allocated, and associate it with the TLS index on that thread.LOADERHANDLE
. If the collectible type still has a live managedLoaderAllocator
free theLOADERHANDLE
.Expected cost model for extra GC interactions associated with this change
This change adds 3 possible ways in which the GC may have to perform additional work beyond what it used to do.
Perf impact of this change
I've run the .NET Microbenchmark suite as well as a variety of ASP.NET Benchmarks. (Unfortunately the publicly visible infrastructure for running tests is incompatible with this change, so results are not public). The results are generally quite hard to interpret. ASP.NET Benchmarks are generally (very) slightly better, and the microbenchmarks are generally equivalent in performance, although there is variability in some tests that had not previously shown variability, and the differences in performance are contained within the margin of error in our perf testing for tests with any significant amount of code. When performance differences have been examined in detail, they tend to be in code which has not changed in any way due to this change, and when run in isolation the performance deltas have disappeared in all cases that I have examined. Thus, I assume they are caching side effect changes. Performance testing has led me to add a change such that all NonGC, NonCollectible statics are allocated in a separate LoaderHeap which appears to have reduced the variability in some of the tests by a small fraction, although results are not consistent enough for me to be extremely confident in that statement.