Improve ForEach micro-benchmark for ImmutableArray (#1183)
authorHenrique Fernandes Baggio <henrique.baggio@outlook.com>
Fri, 27 Dec 2019 17:00:45 +0000 (14:00 -0300)
committerJan Kotas <jkotas@microsoft.com>
Fri, 27 Dec 2019 17:00:45 +0000 (09:00 -0800)
* Improve Foreach benchmark in ImmutableArray<Int32>

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.

* AggressiveInline in ThrowNullRefIfNotInitialized isn't needed to inline GetEnumerator

src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Minimal.cs

index c134fe0..4fa82d9 100644 (file)
@@ -8,6 +8,7 @@ using System.Diagnostics.CodeAnalysis;
 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 @@ namespace System.Collections.Immutable
         /// </summary>
         /// <returns>An enumerator.</returns>
         [Pure]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public Enumerator GetEnumerator()
         {
             var self = this;