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

Reflection performance tweaks & cleanup #21737

Merged
merged 1 commit into from
Jan 4, 2019
Merged

Conversation

jkotas
Copy link
Member

@jkotas jkotas commented Jan 1, 2019

  • Delete internal "Unsafe" field getters/setters. They performance was very close (within 10%) of the regular getters/setters.
  • Made a few performance tweaks in reflection to make reflection faster overall. The regular field getters/setters are faster than what the unsafe ones used to be with this change.

@jkotas
Copy link
Member Author

jkotas commented Jan 1, 2019

Related to discussion in dotnet/corert#6747 (comment)

@jkotas
Copy link
Member Author

jkotas commented Jan 1, 2019

@dotnet-bot test Ubuntu x64 Checked CoreFX Tests please
@dotnet-bot test Windows_NT x64 Checked CoreFX Tests please


return m_fieldType;
Type Initialize() => m_fieldType = new Signature(this, m_declaringType).FieldType;
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't need to be NoInlining (in which case it also currently can't be local)?


Debug.Assert(cache != null);
return cache;
RuntimeTypeCache cache = Unsafe.As<RuntimeTypeCache>(GCHandle.InternalGet(m_cache));
Copy link
Member

Choose a reason for hiding this comment

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

Is the unsafe casting important for perf in this Initialize method?

@jkotas jkotas added the * NO MERGE * The PR is not ready for merge yet (see discussion for detailed reasons) label Jan 2, 2019
- Delete internal "Unsafe" field getters/setters. They performance was very close (within 10%) of the regular getters/setters.
- Made a few performance tweaks in reflection to make reflection faster overall. The regular field getters/setters are faster than what the unsafe ones used to be with this change.
@jkotas jkotas merged commit a626939 into dotnet:master Jan 4, 2019
@jkotas jkotas removed the * NO MERGE * The PR is not ready for merge yet (see discussion for detailed reasons) label Jan 4, 2019
@jkotas jkotas deleted the reflection-perf branch January 4, 2019 18:55
janvorli added a commit to janvorli/coreclr that referenced this pull request Mar 9, 2020
When there is a race calling RuntimeType.InitializeCache, each of the
racing threads creates a new GC handle using
new
RuntimeTypeHandle(this).GetGCHandle(GCHandleType.WeakTrackResurrection);
This ends up calling RuntimeTypeHandle::GetGCHandle native method that
adds the allocated handle into the handle cleanup list of the
AssemblyLoaderAllocator specific for the runtime type.
All but the winning thread then call GCHandle.InternalFree on the just
allocated handle. That frees the handle, but leaves it on the cleanup
list of the loader allocator. The same handle can be later allocated for
some
other purpose. When the AssemblyLoaderAllocator is being destroyed, all
the handles on the cleanup list are destroyed too. So it destroys also
the handle that was left on the cleanup list incorrectly. That can cause
all kinds of hard to diagnose issues, like the
dotnet/runtime#32171.

This change fixes it by adding a FreeGCHandle method on the
RuntimeTypeHandle that besides freeing the handle also removes it from
the cleanup list of the related AssemblyLoadContext.

 ## Customer impact
Hard to diagnose crashes in the runtime caused by closing of random
GC handles. The customer that has reported this issue was using
collectible assemblies and it was resulting in collecting
LoaderAllocator that was still in use and it lead to crashes at various
places.

 ## Regression?
Yes, it was introduced in 3.0. In 2.1 and 2.2, the thread that loses
the race destroys the handle only if the type was not in a collectible
assembly. Since the non-collectible assemblies LoaderAllocator is never
destroyed, the handles were never cleaned up and so no problem could occur.
It was introduced in dotnet#21737

 ##Testing
Customer affected by the issue heavily has tested a fixed version and
reported the issue doesn't occur anymore.

 ## Risk
Low, the new code is executed at single place once per process runtine
only when a thread races for allocating the GC handle with another one
and loses the race.
Anipik pushed a commit that referenced this pull request Mar 25, 2020
* Port to 3.1: Fix allocation of RuntimeTypeCache GC handle

When there is a race calling RuntimeType.InitializeCache, each of the
racing threads creates a new GC handle using
new
RuntimeTypeHandle(this).GetGCHandle(GCHandleType.WeakTrackResurrection);
This ends up calling RuntimeTypeHandle::GetGCHandle native method that
adds the allocated handle into the handle cleanup list of the
AssemblyLoaderAllocator specific for the runtime type.
All but the winning thread then call GCHandle.InternalFree on the just
allocated handle. That frees the handle, but leaves it on the cleanup
list of the loader allocator. The same handle can be later allocated for
some
other purpose. When the AssemblyLoaderAllocator is being destroyed, all
the handles on the cleanup list are destroyed too. So it destroys also
the handle that was left on the cleanup list incorrectly. That can cause
all kinds of hard to diagnose issues, like the
dotnet/runtime#32171.

This change fixes it by adding a FreeGCHandle method on the
RuntimeTypeHandle that besides freeing the handle also removes it from
the cleanup list of the related AssemblyLoadContext.

 ## Customer impact
Hard to diagnose crashes in the runtime caused by closing of random
GC handles. The customer that has reported this issue was using
collectible assemblies and it was resulting in collecting
LoaderAllocator that was still in use and it lead to crashes at various
places.

 ## Regression?
Yes, it was introduced in 3.0. In 2.1 and 2.2, the thread that loses
the race destroys the handle only if the type was not in a collectible
assembly. Since the non-collectible assemblies LoaderAllocator is never
destroyed, the handles were never cleaned up and so no problem could occur.
It was introduced in #21737

 ##Testing
Customer affected by the issue heavily has tested a fixed version and
reported the issue doesn't occur anymore.

 ## Risk
Low, the new code is executed at single place once per process runtine
only when a thread races for allocating the GC handle with another one
and loses the race.

* Fix build break - subtle differences from runtime master
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
- Delete internal "Unsafe" field getters/setters. They performance was very close (within 10%) of the regular getters/setters.
- Made a few performance tweaks in reflection to make reflection faster overall. The regular field getters/setters are faster than what the unsafe ones used to be with this change.

Commit migrated from dotnet/coreclr@a626939
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.

2 participants