Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Commit

Permalink
Skip covariant checks
Browse files Browse the repository at this point in the history
  • Loading branch information
benaadams committed Feb 13, 2019
1 parent 2852371 commit 4ee10a8
Showing 1 changed file with 37 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@

namespace System.IO.Pipelines
{
internal sealed class SingleProducerSingleConsumerPool<T>
internal sealed class SingleProducerSingleConsumerPool<T> where T : class
{
// Adapted from SingleProducerSingleConsumerQueue

/// <summary>The maximum size to use for segments (in number of elements).</summary>
private const int MaxSegmentSize = 0x1000000; // this could be made as large as Int32.MaxValue / 2

/// <summary>The data stored in this segment.</summary>
private readonly T[] _array;
private readonly RefAsValueType[] _array;
/// <summary>Details about the segment.</summary>
private SegmentState _state; // separated out to enable StructLayout attribute to take effect

Expand All @@ -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];
}

/// <summary>Enqueues an item into the queue.</summary>
/// <param name="item">The item to enqueue.</param>
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
Expand Down Expand Up @@ -74,7 +74,7 @@ private bool TryEnqueueSlow(T item)
/// <returns>true if an item could be dequeued; otherwise, false.</returns>
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
Expand All @@ -97,7 +97,7 @@ public bool TryDequeue(out T result)
/// <returns>true if an item could be dequeued; otherwise, false.</returns>
private bool TryDequeueSlow(out T result)
{
T[] array = _array;
RefAsValueType[] array = _array;
if (_state._last != _state._lastCopy)
{
_state._lastCopy = _state._last;
Expand Down Expand Up @@ -143,5 +143,36 @@ private struct SegmentState
/// <summary>Padding to reduce false sharing with the last and what's after the segment.</summary>
internal PaddingFor32 _pad2;
}

/// <summary>
/// A simple struct we wrap reference types inside when storing in arrays to
/// bypass the CLR's covariant checks when writing to arrays.
/// </summary>
/// <remarks>
/// We use <see cref="RefAsValueType"/> as a wrapper to avoid paying the cost of covariant checks whenever
/// the underlying array that the <see cref="SingleProducerSingleConsumerPool{T}"/> 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
/// </remarks>
internal struct RefAsValueType
{
/// <summary>
/// Initializes a new instance of the <see cref="RefAsValueType"/> struct.
/// </summary>
internal RefAsValueType(T value)
{
Value = value;
}

/// <summary>
/// The value.
/// </summary>
internal T Value;

public static implicit operator T(RefAsValueType value) => value.Value;
public static implicit operator RefAsValueType(T value) => new RefAsValueType(value);
}
}
}

0 comments on commit 4ee10a8

Please sign in to comment.