From 4ee10a8d63429cd27092707902907b814bded229 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 13 Feb 2019 00:48:31 +0000 Subject: [PATCH] Skip covariant checks --- .../SingleProducerSingleConsumerPool.cs | 43 ++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/src/System.IO.Pipelines/src/System/IO/Pipelines/SingleProducerSingleConsumerPool.cs b/src/System.IO.Pipelines/src/System/IO/Pipelines/SingleProducerSingleConsumerPool.cs index dfa0990392e3..b7a952c02bad 100644 --- a/src/System.IO.Pipelines/src/System/IO/Pipelines/SingleProducerSingleConsumerPool.cs +++ b/src/System.IO.Pipelines/src/System/IO/Pipelines/SingleProducerSingleConsumerPool.cs @@ -9,7 +9,7 @@ namespace System.IO.Pipelines { - internal sealed class SingleProducerSingleConsumerPool + internal sealed class SingleProducerSingleConsumerPool where T : class { // Adapted from SingleProducerSingleConsumerQueue @@ -17,7 +17,7 @@ internal sealed class SingleProducerSingleConsumerPool private const int MaxSegmentSize = 0x1000000; // this could be made as large as Int32.MaxValue / 2 /// The data stored in this segment. - private readonly T[] _array; + private readonly RefAsValueType[] _array; /// Details about the segment. private SegmentState _state; // separated out to enable StructLayout attribute to take effect @@ -31,14 +31,14 @@ public SingleProducerSingleConsumerPool(int size) Debug.Assert(MaxSegmentSize < int.MaxValue / 2, "Max segment size * 2 must be < Int32.MaxValue, or else overflow could occur."); // Initialize the pool - _array = new T[size]; + _array = new RefAsValueType[size]; } /// Enqueues an item into the queue. /// The item to enqueue. public bool TryEnqueue(T item) { - T[] array = _array; + RefAsValueType[] array = _array; int last = _state._last; // local copy to avoid multiple volatile reads // Fast path: there's obviously room @@ -74,7 +74,7 @@ private bool TryEnqueueSlow(T item) /// true if an item could be dequeued; otherwise, false. public bool TryDequeue(out T result) { - T[] array = _array; + RefAsValueType[] array = _array; int first = _state._first; // local copy to avoid multiple volatile reads // Fast path: there's obviously data available @@ -97,7 +97,7 @@ public bool TryDequeue(out T result) /// true if an item could be dequeued; otherwise, false. private bool TryDequeueSlow(out T result) { - T[] array = _array; + RefAsValueType[] array = _array; if (_state._last != _state._lastCopy) { _state._lastCopy = _state._last; @@ -143,5 +143,36 @@ private struct SegmentState /// Padding to reduce false sharing with the last and what's after the segment. internal PaddingFor32 _pad2; } + + /// + /// A simple struct we wrap reference types inside when storing in arrays to + /// bypass the CLR's covariant checks when writing to arrays. + /// + /// + /// We use as a wrapper to avoid paying the cost of covariant checks whenever + /// the underlying array that the class uses is written to. + /// We've recognized this as a perf win in ETL traces for these stack frames: + /// clr!JIT_Stelem_Ref + /// clr!ArrayStoreCheck + /// clr!ObjIsInstanceOf + /// + internal struct RefAsValueType + { + /// + /// Initializes a new instance of the struct. + /// + internal RefAsValueType(T value) + { + Value = value; + } + + /// + /// The value. + /// + internal T Value; + + public static implicit operator T(RefAsValueType value) => value.Value; + public static implicit operator RefAsValueType(T value) => new RefAsValueType(value); + } } }