YamlDotNet icon indicating copy to clipboard operation
YamlDotNet copied to clipboard

Eliminate allocations from ReflectionUtility.GetImplementedInterfaces

Open MattKotsenas opened this issue 1 year ago • 1 comments
trafficstars

ReflectionUtility.GetImplementedInterfaces allocated an iterator object for every call to GetImplementedInterfaces. This results in an allocation per object serialized in the benchmark.

I refactored the implementation to avoid the iterator by using Type.FindIterfaces. In order to ensure there are no captures / allocations in the delegate, I made the lambda static (which necessitates the move to lang version 9).

This change saves about 2% of CPU and memory allocations in the benchmark:

Before

// * Summary *

BenchmarkDotNet v0.14.0, Windows 11 (10.0.22635.4010)
Intel Core i9-10940X CPU 3.30GHz, 1 CPU, 28 logical and 14 physical cores
.NET SDK 9.0.100-preview.7.24407.12
  [Host]                       : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX-512F+CD+BW+DQ+VL
  MediumRun-.NET 8.0           : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX-512F+CD+BW+DQ+VL
  MediumRun-.NET Framework 4.7 : .NET Framework 4.8.1 (4.8.9261.0), X64 RyuJIT VectorSize=256

IterationCount=15  LaunchCount=2  WarmupCount=10

| Method     | Job                          | Runtime            | Mean      | Error    | StdDev   | Gen0      | Gen1     | Allocated |
|----------- |----------------------------- |------------------- |----------:|---------:|---------:|----------:|---------:|----------:|
| Serializer | MediumRun-.NET 8.0           | .NET 8.0           |  51.52 ms | 0.914 ms | 1.368 ms | 2000.0000 | 500.0000 |  24.44 MB |
| Serializer | MediumRun-.NET Framework 4.7 | .NET Framework 4.7 | 114.88 ms | 5.786 ms | 8.111 ms | 7750.0000 | 500.0000 |     48 MB |

// * Warnings *
MultimodalDistribution
  SerializationBenchmarks.Serializer: MediumRun-.NET Framework 4.7 -> It seems that the distribution is bimodal (mValue = 3.85)
MinIterationTime
  SerializationBenchmarks.Serializer: MediumRun-.NET 8.0 -> The minimum observed iteration time is 97.056ms which is very small. It's recommended to increase it to at least 100ms using more operations.

// * Hints *
Outliers
  SerializationBenchmarks.Serializer: MediumRun-.NET 8.0 -> 1 outlier  was  removed, 2 outliers were detected (48.53 ms, 54.46 ms)

After

// * Summary *

BenchmarkDotNet v0.14.0, Windows 11 (10.0.22635.4010)
Intel Core i9-10940X CPU 3.30GHz, 1 CPU, 28 logical and 14 physical cores
.NET SDK 9.0.100-preview.7.24407.12
  [Host]                       : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX-512F+CD+BW+DQ+VL
  MediumRun-.NET 8.0           : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX-512F+CD+BW+DQ+VL
  MediumRun-.NET Framework 4.7 : .NET Framework 4.8.1 (4.8.9261.0), X64 RyuJIT VectorSize=256

IterationCount=15  LaunchCount=2  WarmupCount=10

| Method     | Job                          | Runtime            | Mean      | Error    | StdDev   | Gen0      | Gen1     | Gen2     | Allocated |
|----------- |----------------------------- |------------------- |----------:|---------:|---------:|----------:|---------:|---------:|----------:|
| Serializer | MediumRun-.NET 8.0           | .NET 8.0           |  50.79 ms | 1.039 ms | 1.556 ms | 2000.0000 | 500.0000 |        - |  23.83 MB |
| Serializer | MediumRun-.NET Framework 4.7 | .NET Framework 4.7 | 123.98 ms | 2.078 ms | 3.046 ms | 7600.0000 | 600.0000 | 200.0000 |  47.39 MB |

// * Warnings *
MinIterationTime
  SerializationBenchmarks.Serializer: MediumRun-.NET 8.0 -> The minimum observed iteration time is 94.463ms which is very small. It's recommended to increase it to at least 100ms using more operations.

// * Hints *
Outliers
  SerializationBenchmarks.Serializer: MediumRun-.NET Framework 4.7 -> 1 outlier  was  removed (132.06 ms)

MattKotsenas avatar Aug 15 '24 23:08 MattKotsenas