diff --git a/src/Dependencies/Collections/Internal/SegmentedArrayHelper.cs b/src/Dependencies/Collections/Internal/SegmentedArrayHelper.cs index da94baee92d68..df82f53ae1552 100644 --- a/src/Dependencies/Collections/Internal/SegmentedArrayHelper.cs +++ b/src/Dependencies/Collections/Internal/SegmentedArrayHelper.cs @@ -16,16 +16,12 @@ internal static class SegmentedArrayHelper // Large value types may benefit from a smaller number. internal const int IntrosortSizeThreshold = 16; - /// - /// A combination of and - /// . - /// - [SuppressMessage("Documentation", "CA1200:Avoid using cref tags with a prefix", Justification = "The field is not supported in all compilation targets.")] - internal const MethodImplOptions FastPathMethodImplOptions = MethodImplOptions.AggressiveInlining | (MethodImplOptions)512; - - [MethodImpl(FastPathMethodImplOptions)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static int GetSegmentSize() { +#if NETCOREAPP3_0_OR_NEWER + return InlineCalculateSegmentSize(Unsafe.SizeOf()); +#else if (Unsafe.SizeOf() == Unsafe.SizeOf()) { return ReferenceTypeSegmentHelper.SegmentSize; @@ -34,11 +30,15 @@ internal static int GetSegmentSize() { return ValueTypeSegmentHelper.SegmentSize; } +#endif } - [MethodImpl(FastPathMethodImplOptions)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static int GetSegmentShift() { +#if NETCOREAPP3_0_OR_NEWER + return InlineCalculateSegmentShift(Unsafe.SizeOf()); +#else if (Unsafe.SizeOf() == Unsafe.SizeOf()) { return ReferenceTypeSegmentHelper.SegmentShift; @@ -47,11 +47,15 @@ internal static int GetSegmentShift() { return ValueTypeSegmentHelper.SegmentShift; } +#endif } - [MethodImpl(FastPathMethodImplOptions)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static int GetOffsetMask() { +#if NETCOREAPP3_0_OR_NEWER + return InlineCalculateOffsetMask(Unsafe.SizeOf()); +#else if (Unsafe.SizeOf() == Unsafe.SizeOf()) { return ReferenceTypeSegmentHelper.OffsetMask; @@ -60,6 +64,7 @@ internal static int GetOffsetMask() { return ValueTypeSegmentHelper.OffsetMask; } +#endif } /// @@ -121,6 +126,31 @@ private static int CalculateOffsetMask(int segmentSize) return segmentSize - 1; } + // Faster inline implementation for NETCOREAPP to avoid static constructors and non-inlineable + // generics with runtime lookups +#if NETCOREAPP3_0_OR_NEWER + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int InlineCalculateSegmentSize(int elementSize) + { + return 1 << InlineCalculateSegmentShift(elementSize); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int InlineCalculateSegmentShift(int elementSize) + { + // Default Large Object Heap size threshold + // https://github.com/dotnet/runtime/blob/c9d69e38d0e54bea5d188593ef6c3b30139f3ab1/src/coreclr/src/gc/gc.h#L111 + const uint Threshold = 85000; + return System.Numerics.BitOperations.Log2((uint)((Threshold / elementSize) - (2 * Unsafe.SizeOf()))); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int InlineCalculateOffsetMask(int elementSize) + { + return InlineCalculateSegmentSize(elementSize) - 1; + } +#endif + internal static class TestAccessor { public static int CalculateSegmentSize(int elementSize) diff --git a/src/Dependencies/Collections/SegmentedArray`1.cs b/src/Dependencies/Collections/SegmentedArray`1.cs index 7805fea208ef3..9bb6c32dc5384 100644 --- a/src/Dependencies/Collections/SegmentedArray`1.cs +++ b/src/Dependencies/Collections/SegmentedArray`1.cs @@ -32,7 +32,6 @@ namespace Microsoft.CodeAnalysis.Collections /// private static int SegmentSize { - [MethodImpl(SegmentedArrayHelper.FastPathMethodImplOptions)] get { return SegmentedArrayHelper.GetSegmentSize(); @@ -44,7 +43,6 @@ private static int SegmentSize /// private static int SegmentShift { - [MethodImpl(SegmentedArrayHelper.FastPathMethodImplOptions)] get { return SegmentedArrayHelper.GetSegmentShift(); @@ -56,7 +54,6 @@ private static int SegmentShift /// private static int OffsetMask { - [MethodImpl(SegmentedArrayHelper.FastPathMethodImplOptions)] get { return SegmentedArrayHelper.GetOffsetMask(); @@ -115,7 +112,7 @@ private SegmentedArray(int length, T[][] items) public ref T this[int index] { - [MethodImpl(SegmentedArrayHelper.FastPathMethodImplOptions)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return ref _items[index >> SegmentShift][index & OffsetMask];