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

System.Reflection.MetadataLoadContext performance #30886

Open
jonathanpeppers opened this issue Sep 18, 2019 · 4 comments
Open

System.Reflection.MetadataLoadContext performance #30886

jonathanpeppers opened this issue Sep 18, 2019 · 4 comments

Comments

@jonathanpeppers
Copy link
Member

I have a benchmark here: https://github.com/jonathanpeppers/Benchmarks/blob/7db49fb3d272c5b07deda166dd4f5a5112258bbe/Benchmarks/Cecil.cs#L90-L111

And I am getting "not so great" results for SR.MetadataLoadContext:

// * Summary *

BenchmarkDotNet=v0.11.3, OS=Windows 10.0.18362
Intel Core i9-9900K CPU 3.60GHz, 1 CPU, 16 logical and 8 physical cores
  [Host]     : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.8.4010.0
  DefaultJob : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.8.4010.0


                                Method |        Mean |      Error |     StdDev |      Median | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op |
-------------------------------------- |------------:|-----------:|-----------:|------------:|------------:|------------:|------------:|--------------------:|
            System.Reflection.Metadata |    32.84 ms |  0.1200 ms |  0.1123 ms |    32.85 ms |   3000.0000 |     62.5000 |           - |            15.12 MB |
                            Mono.Cecil |   351.81 ms |  7.1738 ms | 21.1522 ms |   339.38 ms |  16000.0000 |  12000.0000 |   4000.0000 |           103.57 MB |
 System.Reflection.MetadataLoadContext | 2,058.91 ms | 19.9863 ms | 18.6952 ms | 2,051.50 ms |  97000.0000 |  90000.0000 |   6000.0000 |           544.84 MB |

The performance compared to using raw SRM or Mono.Cecil is drastically worse.

I expected it to be somewhere in the middle of using SRM and Mono.Cecil. Is there something I'm doing here that would explain the poor performance?

Thanks!

@steveharter steveharter self-assigned this Sep 27, 2019
@msftgits msftgits transferred this issue from dotnet/corefx Feb 1, 2020
@msftgits msftgits added this to the 5.0 milestone Feb 1, 2020
@maryamariyan maryamariyan added the untriaged New issue has not been triaged by the area owner label Feb 23, 2020
@steveharter steveharter removed the untriaged New issue has not been triaged by the area owner label Mar 4, 2020
@jkotas jkotas added the tenet-performance Performance related issue label Mar 6, 2020
@steveharter
Copy link
Member

Moving to future due to scheduling + priority.

@steveharter steveharter modified the milestones: 5.0.0, Future Aug 10, 2020
@steveharter steveharter removed their assignment Aug 10, 2020
@steveharter steveharter modified the milestones: Future, 6.0.0 Nov 13, 2020
@krwq krwq modified the milestones: 6.0.0, 7.0.0 Jul 8, 2021
@steveharter steveharter self-assigned this Jul 16, 2021
@ghost ghost added the needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration label Jul 16, 2021
@buyaa-n buyaa-n removed the needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration label Jul 21, 2021
@steveharter steveharter removed their assignment Sep 23, 2021
@ghost ghost added the needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration label Sep 23, 2021
@buyaa-n buyaa-n removed the needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration label Sep 28, 2021
@buyaa-n buyaa-n modified the milestones: 7.0.0, 8.0.0 Jun 1, 2022
@steveharter steveharter modified the milestones: 8.0.0, 9.0.0 Jul 24, 2023
@steveharter
Copy link
Member

Moving to 9; we should at least perform some traces to see where the perf hit is; MLC does have extensive caching but perhaps there are temporary allocs that are being done based on the alloc numbers above.

@steveharter steveharter modified the milestones: 9.0.0, 10.0.0 Jul 24, 2024
@jonathanpeppers
Copy link
Member Author

If anyone looks into this in the future, I think it's specifically iterating over methods that is slow:

Someone else did a comparison, and MLC performs somewhere in between Mono.Cecil and SRM if you remove these lines in all benchmarks.

@jpobst
Copy link
Contributor

jpobst commented Sep 20, 2024

Indeed, resolving assemblies and iterating types has excellent performance, but digging any deeper into a Type tanks performance (GetNestedTypes (), GetConstructors (), GetMethods (), etc.)

Modifying @jonathanpeppers test case to simply iterate type names and nothing else shows performance very close to System.Relection.Metadata, however iterating type names and nested type names performs ~100x worse:

Method Mean Error StdDev Gen0 Gen1 Gen2 Allocated
S.R.M - Types 5.026 ms 0.0978 ms 0.2418 ms 39.0625 - - 1.11 MB
S.R.M - Types+Nested 5.662 ms 0.1092 ms 0.1531 ms 70.3125 - - 1.81 MB
S.R.MLC - Types 7.273 ms 0.1367 ms 0.2358 ms 140.6250 125.0000 - 3.38 MB
S.R.MLC - Types+Nested 674.997 ms 13.3861 ms 14.3229 ms 16000.0000 15000.0000 3000.0000 324.81 MB

Benchmark code:

[Benchmark (Description = "MetadataLoadContext - Types")]
public void SystemReflectionMetadataLoadContext ()
{
	var resolver = new SimpleResolver (assemblies);
	using (var context = new MetadataLoadContext (resolver)) {
		foreach (var assemblyFile in assemblies) {
			var assembly = context.LoadFromAssemblyPath (assemblyFile);
			foreach (var t in assembly.GetTypes ()) {
				var name = t.Name;
			}
		}
	}
}

[Benchmark (Description = "MetadataLoadContext - Types + Nested Types")]
public void SystemReflectionMetadataLoadContext2 ()
{
	var resolver = new SimpleResolver (assemblies);
	using (var context = new MetadataLoadContext (resolver)) {
		foreach (var assemblyFile in assemblies) {
			var assembly = context.LoadFromAssemblyPath (assemblyFile);
			foreach (var t in assembly.GetTypes ()) {
				var name = t.Name;
				foreach (var m in t.GetNestedTypes ()) {
					var mname = m.Name;
				}
			}
		}
	}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
Development

No branches or pull requests

8 participants