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

Make `Activator.CreateInstance<T>` 50% faster #8000

Merged
merged 3 commits into from Feb 22, 2020

Conversation

@MichalStrehovsky
Copy link
Member

MichalStrehovsky commented Feb 22, 2020

Ratio
new 1.0
Activator.CreateInstance old 2.36
Activator.CreateInstance new 1.26

The existing implementation was a bit awkward because of how the intrinsic was implemented in .NET Native (the intrinsic in .NET Native did allocate+call default constructor - in CoreRT we have a separate intrinsic for "get default constructor").

.NET Native required an awkward dance around "uh-oh, this type doesn't have a default constructor, what should I call now". None of that is needed in CoreRT, but we had it there so that we don't diverge too much. It was 3x faster than CoreCLR anyway. We no longer have to worry about diverging and it seems like CoreCLR might be getting a perf boost here for .NET 5, so let's not stay behind.

In this commit:

  • Removing the s_createInstanceMissingDefaultConstructor flag - we can do this check quickly by comparing the retrieved default constructor with DefaultConstructorOf<ClassWithMissingConstructor>.
  • Removing upfront checks for arrays/strings/interfaces. We no longer have to worry about accidentally allocating them. We detect them as not having a default ctor.
  • For an additional perf boost, exposing AllocatorOf<T>. This is a new intrinsic that expands to the existing AllocatorOf dictionary entry that represents the optimized allocator method for T. This entry is normally only used in universal shared code, but it's valid to use it outside of that.
  • Extra compiler support for dictionary entries that are double indirections. AllocatorOf is doubly-indirect for USG reasons. We should probably keep that for universal shared code anyway.

There's still room left for improvement - RyuJIT currently doesn't have support for the RawCalli intrinsic and generates a normal calli for them. Normal calli needs to check for fat function pointers. These pointers are never fat pointers.

|                              | Ratio |
|------------------------------|------:|
| new                          |   1.0 |
| Activator.CreateInstance old |  2.36 |
| Activator.CreateInstance new |  1.26 |

The existing implementation was a bit awkward because of how the intrinsic was implemented in .NET Native (the intrinsic in .NET Native did allocate+call default constructor - in CoreRT we have a separate intrinsic for "get default constructor").

.NET Native required an awkward dance around "uh-oh, this type doesn't have a default constructor, what should I call now". None of that is needed in CoreRT, but we had it there so that we don't diverge too much. It was 3x faster than CoreCLR anyway. We no longer have to worry about diverging and it seems like CoreCLR might be getting a perf boost here for .NET 5, so let's not stay behind.

In this commit:
* Removing the `s_createInstanceMissingDefaultConstructor` flag - we can do this check quickly by comparing the retrieved default constructor with `DefaultConstructorOf<ClassWithMissingConstructor>`.
* Removing upfront checks for arrays/strings/interfaces. We no longer have to worry about accidentally allocating them. We detect them as not having a default ctor.
* For an additional perf boost, exposing `AllocatorOf<T>`. This is a new intrinsic that expands to the existing `AllocatorOf` dictionary entry that represents the optimized allocator method for T. This entry is normally only used in universal shared code, but it's valid to use it outside of that.
* Extra compiler support for dictionary entries that are double indirections. `AllocatorOf` is doubly-indirect for USG reasons. We should probably keep that for universal shared code anyway.

There's still room left for improvement - RyuJIT currently doesn't have support for the `RawCalli` intrinsic and generates a normal calli for them. Normal calli needs to check for fat function pointers. These pointers are never fat pointers.
@jkotas
jkotas approved these changes Feb 22, 2020
Copy link
Member

jkotas left a comment

Nice :-)

@jkotas jkotas merged commit 9d81d82 into dotnet:master Feb 22, 2020
12 checks passed
12 checks passed
WIP Ready for review
Details
corert-ci Build #20200222.8 succeeded
Details
corert-ci (Build Linux x64 debug and CoreCLR tests) Build Linux x64 debug and CoreCLR tests succeeded
Details
corert-ci (Build Linux x64 debug and CoreFX tests) Build Linux x64 debug and CoreFX tests succeeded
Details
corert-ci (Build Linux x64 release) Build Linux x64 release succeeded
Details
corert-ci (Build OSX x64 debug and CoreCLR tests) Build OSX x64 debug and CoreCLR tests succeeded
Details
corert-ci (Build OSX x64 debug and CoreFX tests) Build OSX x64 debug and CoreFX tests succeeded
Details
corert-ci (Build OSX x64 release) Build OSX x64 release succeeded
Details
corert-ci (Build Windows_NT x64 debug and CoreCLR tests) Build Windows_NT x64 debug and CoreCLR tests succeeded
Details
corert-ci (Build Windows_NT x64 debug and CoreFX tests) Build Windows_NT x64 debug and CoreFX tests succeeded
Details
corert-ci (Build Windows_NT x64 release) Build Windows_NT x64 release succeeded
Details
license/cla All CLA requirements met.
Details
@MichalStrehovsky MichalStrehovsky deleted the MichalStrehovsky:fastActivator branch Feb 23, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

2 participants
You can’t perform that action at this time.