From 975338797ed80793d4234fb738b7b0825fe36cca Mon Sep 17 00:00:00 2001 From: Henrique Fernandes Baggio Date: Sat, 21 Dec 2019 14:05:34 -0300 Subject: [PATCH 1/2] Improve Foreach benchmark in ImmutableArray Analysis of the generated ASM code vs the same benchmark for Array shows that the GetEnumerator call is not being inlined (the loop itself is). In the case of the ValueType ImmutableArray.GetEnumerator method, there's a call to ThrowNullRefIfNotInitialized for validation. By adding MethodImplAttribute(MethodImplOptions.AggressiveInlining) to both methods, we are able to force the JIT to inline the call and get similar results in the benchmark. Looking at the hardware counters collected in the benchmark, there are less CacheMisses and BranchMispredictions/Op when the inlining happens. Unfortunately, the same fix didn't seem to work for the other overloads of GetEnumerator, for the explicit generic implementation. That still needs more investigation. --- .../System/Collections/Immutable/ImmutableArray_1.Minimal.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Minimal.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Minimal.cs index c134fe0f78539a..8a620e704c1bad 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Minimal.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Minimal.cs @@ -8,6 +8,7 @@ using System.Diagnostics.Contracts; using System.Globalization; using System.Linq; +using System.Runtime.CompilerServices; using System.Runtime.Versioning; namespace System.Collections.Immutable @@ -283,6 +284,7 @@ public ImmutableArray.Builder ToBuilder() /// /// An enumerator. [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Enumerator GetEnumerator() { var self = this; @@ -407,6 +409,7 @@ IEnumerator IEnumerable.GetEnumerator() /// /// Throws a null reference exception if the array field is null. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void ThrowNullRefIfNotInitialized() { // Force NullReferenceException if array is null by touching its Length. From b277a2f287f664c3751faa367292e7c017435b4a Mon Sep 17 00:00:00 2001 From: Henrique Fernandes Baggio Date: Sat, 21 Dec 2019 14:11:34 -0300 Subject: [PATCH 2/2] AggressiveInline in ThrowNullRefIfNotInitialized isn't needed to inline GetEnumerator --- .../src/System/Collections/Immutable/ImmutableArray_1.Minimal.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Minimal.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Minimal.cs index 8a620e704c1bad..4fa82d95068fb4 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Minimal.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Minimal.cs @@ -409,7 +409,6 @@ IEnumerator IEnumerable.GetEnumerator() /// /// Throws a null reference exception if the array field is null. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void ThrowNullRefIfNotInitialized() { // Force NullReferenceException if array is null by touching its Length.