-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Further clean up usages of LowLevelList<T> #109114
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
|
Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas |
| /// <param name="offset">The offset at which we need to write the bitfield.</param> | ||
| public void WriteToBitfield(LowLevelList<bool> bitfield, int offset) | ||
| /// <returns>The result bitfield, may or may not be the input array.</returns> | ||
| public bool[] WriteToBitfield(bool[] bitfield, int offset) |
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'm not sure how relevant is this. I can only find one caller of this method which builds ArrayType.
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 am not sure what you mean by "this"
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 mean the WriteToBitfield method and its descendants. I can only find one caller and its input argument is always empty. The GCLayout structure represents a mergeable bit vector, but I can't find the callers fully leveraging it.
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.
Feel free to simplify it
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.
Taking more insight on GCLayout, it's converting two representations back and forth. I'm not fully understood about GC info encoding and not sure whether it should be converted further.
| #if TYPE_LOADER_IMPLEMENTATION | ||
| [System.Runtime.CompilerServices.ForceDictionaryLookups] | ||
| #endif | ||
| internal class LowLevelList<T> |
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.
Should the type be moved under TypeLoader since it's only required for it?
Additional question: is ForceDictionaryLookups only applicable for reference types because of generic sharing, or also applicable for value types?
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.
Should the type be moved under TypeLoader since it's only required for it?
It is very likely that there is code in CoreLib that is used by the TypeLoader indirectly that would need to be annotated with this attribute if we ever resurrected this (#85184 (comment)).
I think it would be ok to delete ForceDictionaryLookups and its few remaining uses. It is likely heavily bitrotten.
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.
Yeah, in this PR I was using Array.Resize which is generic. It seems that the real foreseeing solution is to limit TypeLoader usage of CoreLib, according to the linked discussion. It also means that TypeLoader needs its specialized collection. If we resurrected the generic lookup work, is it expected to use the attribute, or just do some specialized handling for TypeLoader?
The original question is that, would there be any place else (not involved with TypeLoader) unable to use the CoreLib collection? I assume no.
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.
Per #85184 (comment), if we ever resurrected the lazy generic lookup work, we would need likely ended up with TypeLoader in its own partition that does not perform the lazy generic lookups. It would be very large refactoring.
| /// The dictionary's value is a list of (resourcename,index) tuples. | ||
| /// </summary> | ||
| private static volatile LowLevelDictionary<string, LowLevelList<ResourceInfo>> s_extractedResourceDictionary; | ||
| private static volatile LowLevelDictionary<string, List<ResourceInfo>> s_extractedResourceDictionary; |
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.
In avialonia-linux, all interfaces on the List<ResourceInfo> are preserved. This is causing the majority of size difference.
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.
Appearantly the usual List and Dictionary do have higher size impact than LowLevelList and LowLevelDictionary, but they are very likely to be used by other part of application, especially Dictionary<__Canon, __Canon>.
|
Triaging the status of this PR: we should first make the footprint for |
| private static uint s_genericFunctionPointerNextIndex; | ||
| private const uint c_genericDictionaryChunkSize = 1024; | ||
| private static LowLevelList<IntPtr> s_genericFunctionPointerCollection = new LowLevelList<IntPtr>(); | ||
| private static readonly List<IntPtr> s_genericFunctionPointerCollection = new List<IntPtr>(); |
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 s_genericFunctionPointerCollection pool looks like a premature optimization. I think it would be fine to allocate GenericMethodDescriptorInfos one at a time.
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 this mean there will be memory leak per function pointer, instead of per method instantiation? This would become unbounded instead of bounded.
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.
s_genericFunctionPointerCollection does not enable any GenericMethodDescriptor reuse. There is s_genericFunctionPointerDictionary for that. I am not suggesting deleting s_genericFunctionPointerDictionary.
s_genericFunctionPointerCollection reduces frequency of NativeMemory.Alloc calls. We allocate spaces for c_genericDictionaryChunkSize GenericMethodDescriptors at a time using NativeMemory.Alloc and then hand them out one at a time.
That's very hard. I would split this PR into multiple smaller ones that are easier to discuss, review and merge:
|
| { | ||
| if (s_extractedResourceDictionary == null) | ||
| { | ||
| // Lazily create the extracted resource dictionary. If two threads race here, we may construct two dictionaries |
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 think the question to ask here is whether we need to create this global cache in the first place.
|
I'm going to move this to draft for now. Can be resubmitted with more granular changes. |
|
Draft Pull Request was automatically closed for 30 days of inactivity. Please let us know if you'd like to reopen it. |
Use
List<T>outside type loader, and replace the usage ofLowLevelList<T>tobool[]for representing field GC layout.Separated with
LowLevelDictionary<T>, which may introduce more concern about code size and speed.