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

Keep native order of get members call from Type #69506

Merged
merged 9 commits into from
May 31, 2022

Conversation

buyaa-n
Copy link
Member

@buyaa-n buyaa-n commented May 18, 2022

Keep native order (declaration order) of members call in Type, for example GetProperties()

The issue causing this behavior is the member cache logic in the managed code, it caches members by first come first added order. The native call returns members in declared order as needed, but if an individual members called before all members call
the native (declared) order will be lost

After investigating the behavior and discussing the solution with the team we decided to keep the native call results order in the cache instead of adding an reordering logic by token or something else. The main goal is to not regress current performance. And that goal is achieved

The entire caching logic is in MergeWithGlobalList(T[] list) method and it is used only in Insert(ref T[] list, string? name, MemberListType listType) method.

The method merges members loaded from native with cache depending on MemberListType. By my investigation the ordering is matter only for MemberListType.All case, all other cases are loading only one member or a group of member with same name which would keep the order correctly.

So I have created a MergeWithGlobalListInOrder(T[] list) method for MemberListType.All case, the performance test result shows the new method has about the same perf as existing one for MemberListType.All case, but not good for all other cases. But as I mentioned we don't need to change order in other cases.

Fixes #46272

@ghost
Copy link

ghost commented May 18, 2022

Tagging subscribers to this area: @dotnet/area-system-reflection
See info in area-owners.md if you want to be subscribed.

Issue Details

Keep native order (declaration order) of members call in Type, for example GetProperties()

The issue causing this behavior is the member cache logic in the managed code, it caches members by first come first added order. The native call returns members in declared order as needed, but if an individual members called before all members call
the native (declared) order will be lost

After investigating the behavior and discussing the solution with the team we decided to keep the native call results order in the cache instead of adding an reordering logic by token or something else. The main goal is to not regress current performance.

The entire caching logic is in MergeWithGlobalList(T[] list) method and it is used only in Insert(ref T[] list, string? name, MemberListType listType) method.

The method merges members loaded from native with cache depending on MemberListType. By my investigation the ordering is matter only for MemberListType.All case, all other cases are loading only one member or a group of member with same name which would keep the order correctly.

So I have created a MergeWithGlobalListInOrder(T[] list) method for MemberListType.All case, the performance test result shows the new method has about the same perf as existing one for MemberListType.All case, but not good for all other cases. But as I mentioned we don't need to change order in other cases.

Fixes #46272

Author: buyaa-n
Assignees: -
Labels:

area-System.Reflection

Milestone: -

@jkotas
Copy link
Member

jkotas commented May 19, 2022

Could you please try a test case with a few hundred methods? I do not think that the VM method iterators that this using under the covers guarantee order. A test case with a few hundred methods should be large enough to prove or disprove whether it is the case.

@buyaa-n
Copy link
Member Author

buyaa-n commented May 20, 2022

Could you please try a test case with a few hundred methods? I do not think that the VM method iterators that this using under the covers guarantee order. A test case with a few hundred methods should be large enough to prove or disprove whether it is the case.

Sure, will add a test

@buyaa-n
Copy link
Member Author

buyaa-n commented May 20, 2022

I do not think that the VM method iterators that this using under the covers guarantee order.

Yes, you are right, it is divided into chunks and returns methods starting from last chunk 😢

@buyaa-n
Copy link
Member Author

buyaa-n commented May 20, 2022

it is divided into chunks and returns methods starting from last chunk

Seems we need to go back to the reordering approach, do not see any other/better way

@jkotas
Copy link
Member

jkotas commented May 20, 2022

Seems we need to go back to the reordering approach, do not see any other/better way

We do not need to. The runtime can iterate methods in native order, using the same low-level metadata iterators that are used for properties. We need to switch to use these order-preserving iterators for the MemberListType.All case.

Alternatively, we may be able to fix the runtime to keep the methods in chunks in native order and keep using the existing FCalls/QCalls.

@buyaa-n
Copy link
Member Author

buyaa-n commented May 23, 2022

We do not need to. The runtime can iterate methods in native order, using the same low-level metadata iterators that are used for properties. We need to switch to use these order-preserving iterators for the MemberListType.All case.

I was able to load methods in order using this approach, but there is other test failures with System.InvalidOperationException : Could not execute the method because either the method itself or the containing type is not fully instantiated..

The low-level metadata iterators that are used for properties are returns array of metadata tokens, from which I am populating the methods using:

public RuntimeMethodHandle ResolveMethodHandle(int methodToken) => ResolveMethodHandle(methodToken, null, null);

apparently methods are not getting populated completely with this approach, any suggestions @jkotas?

Alternatively, we may be able to fix the runtime to keep the methods in chunks in native order and keep using the existing FCalls/QCalls.

This option seems needs quite a lot change in native code, I would try it out if the above option would not work

@jkotas
Copy link
Member

jkotas commented May 23, 2022

apparently methods are not getting populated completely with this approach, any suggestions @jkotas?

It looks like a problem with generic instantiations. I am not able to tell where the problem is by just looking at the code.

This option seems needs quite a lot change in native code

I do not think it would be a lot of code change. The change to start would be to change

_ASSERTE(pNewChunk->GetNextChunk() == NULL);
pNewChunk->SetNextChunk(GetChunks());
SetChunks(pNewChunk);
to add the new chunk to the end of the chunks linked list (ie walk the link list to the end of first and then add it).

@buyaa-n
Copy link
Member Author

buyaa-n commented May 24, 2022

I do not think it would be a lot of code change. The change to start would be to change

_ASSERTE(pNewChunk->GetNextChunk() == NULL);
pNewChunk->SetNextChunk(GetChunks());
SetChunks(pNewChunk);

to add the new chunk to the end of the chunks linked list (ie walk the link list to the end of first and then add it).

Thanks! that worked well 💯, I was thinking to add another pointer for opposite direction and a new iterator looping through them 😛 😆

src/coreclr/vm/class.cpp Outdated Show resolved Hide resolved
src/coreclr/vm/class.cpp Outdated Show resolved Hide resolved
src/coreclr/vm/class.cpp Outdated Show resolved Hide resolved
src/coreclr/vm/class.cpp Outdated Show resolved Hide resolved
Copy link
Member

@jkotas jkotas left a comment

Choose a reason for hiding this comment

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

LGTM. Thank you!

@jkotas
Copy link
Member

jkotas commented May 26, 2022

/azp run runtime-extra-platforms

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@jkotas
Copy link
Member

jkotas commented May 26, 2022

I have triggered extra-plaforms to verify that the new tests are passing everywhere.

@buyaa-n
Copy link
Member Author

buyaa-n commented May 27, 2022

I have triggered extra-plaforms to verify that the new tests are passing everywhere.

All of them failed with Nuget error, requesting rerun not working, not sure what should do

error NU1301: Failed to retrieve information about 'Microsoft.NETCore.App.Ref' from remote source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/d1622942-d16f-48e5-bc83-96f4539e7601/nuget/v3/flat2/microsoft.netcore.app.ref/index.json'.

CC @ViktorHofer

@ViktorHofer
Copy link
Member

@buyaa-n that was due to an AzDO change. I mail was sent out for it. The issue should be mitigated by now.

@jkotas
Copy link
Member

jkotas commented May 27, 2022

/azp run runtime-extra-platforms

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@buyaa-n
Copy link
Member Author

buyaa-n commented May 31, 2022

Still some tests failing with similar error, rerunning did not help, will try one more time

@buyaa-n
Copy link
Member Author

buyaa-n commented May 31, 2022

/azp run runtime-extra-platforms

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@jkotas
Copy link
Member

jkotas commented May 31, 2022

Still some tests failing with similar error, rerunning did not help, will try one more time

Is there an issue opened for these error? It is sufficient to note the know issues that are tracking the failures in the PR comment. You do not have to keep rerunning until you hit green.

@buyaa-n
Copy link
Member Author

buyaa-n commented May 31, 2022

Is there an issue opened for these error? It is sufficient to note the know issues that are tracking the failures in the PR comment. You do not have to keep rerunning until you hit green.

All unsuccessful builds on runtime-extra-platforms have failed at the send to Helix step not a real test failure, usually I rerun the test in such cases and it succeeds in next run, though did not this time.

@buyaa-n
Copy link
Member Author

buyaa-n commented May 31, 2022

All failures does not look related so merging the PR, most of the failures looks has already reported: #60705, #70005 etc, created one new issue: #70056

@buyaa-n buyaa-n merged commit 64e05d8 into dotnet:main May 31, 2022
@buyaa-n buyaa-n deleted the declaration_order branch May 31, 2022 22:14
@ghost ghost locked as resolved and limited conversation to collaborators Jul 1, 2022
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.

Allow GetProperties to order results by declaration order
5 participants