Skip to content

Commit 72d4319

Browse files
authored
Merge branch 'master' into optimization/messenger-shared-pool
2 parents e8dbe3c + 05d77c4 commit 72d4319

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+588
-123
lines changed

Microsoft.Toolkit.HighPerformance/Box{T}.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ public static bool TryGetFrom(object obj, [NotNullWhen(true)] out Box<T>? box)
125125
[MethodImpl(MethodImplOptions.AggressiveInlining)]
126126
public static implicit operator T(Box<T> box)
127127
{
128-
return Unsafe.Unbox<T>(box);
128+
return (T)(object)box;
129129
}
130130

131131
/// <summary>
@@ -180,7 +180,6 @@ public override int GetHashCode()
180180
/// <summary>
181181
/// Throws an <see cref="InvalidCastException"/> when a cast from an invalid <see cref="object"/> is attempted.
182182
/// </summary>
183-
[MethodImpl(MethodImplOptions.NoInlining)]
184183
private static void ThrowInvalidCastExceptionForGetFrom()
185184
{
186185
throw new InvalidCastException($"Can't cast the input object to the type Box<{typeof(T)}>");

Microsoft.Toolkit.HighPerformance/Buffers/ArrayPoolBufferWriter{T}.cs

+38-29
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ public sealed class ArrayPoolBufferWriter<T> : IBuffer<T>, IMemoryOwner<T>
3333
/// </summary>
3434
private const int DefaultInitialBufferSize = 256;
3535

36+
/// <summary>
37+
/// The <see cref="ArrayPool{T}"/> instance used to rent <see cref="array"/>.
38+
/// </summary>
39+
private readonly ArrayPool<T> pool;
40+
3641
/// <summary>
3742
/// The underlying <typeparamref name="T"/> array.
3843
/// </summary>
@@ -49,36 +54,52 @@ public sealed class ArrayPoolBufferWriter<T> : IBuffer<T>, IMemoryOwner<T>
4954
/// Initializes a new instance of the <see cref="ArrayPoolBufferWriter{T}"/> class.
5055
/// </summary>
5156
public ArrayPoolBufferWriter()
57+
: this(ArrayPool<T>.Shared, DefaultInitialBufferSize)
58+
{
59+
}
60+
61+
/// <summary>
62+
/// Initializes a new instance of the <see cref="ArrayPoolBufferWriter{T}"/> class.
63+
/// </summary>
64+
/// <param name="pool">The <see cref="ArrayPool{T}"/> instance to use.</param>
65+
public ArrayPoolBufferWriter(ArrayPool<T> pool)
66+
: this(pool, DefaultInitialBufferSize)
5267
{
53-
// Since we're using pooled arrays, we can rent the buffer with the
54-
// default size immediately, we don't need to use lazy initialization
55-
// to save unnecessary memory allocations in this case.
56-
this.array = ArrayPool<T>.Shared.Rent(DefaultInitialBufferSize);
57-
this.index = 0;
5868
}
5969

6070
/// <summary>
6171
/// Initializes a new instance of the <see cref="ArrayPoolBufferWriter{T}"/> class.
6272
/// </summary>
6373
/// <param name="initialCapacity">The minimum capacity with which to initialize the underlying buffer.</param>
64-
/// <exception cref="ArgumentException">
65-
/// Thrown when <paramref name="initialCapacity"/> is not positive (i.e. less than or equal to 0).
66-
/// </exception>
74+
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="initialCapacity"/> is not valid.</exception>
6775
public ArrayPoolBufferWriter(int initialCapacity)
76+
: this(ArrayPool<T>.Shared, initialCapacity)
6877
{
69-
if (initialCapacity <= 0)
70-
{
71-
ThrowArgumentOutOfRangeExceptionForInitialCapacity();
72-
}
78+
}
7379

74-
this.array = ArrayPool<T>.Shared.Rent(initialCapacity);
80+
/// <summary>
81+
/// Initializes a new instance of the <see cref="ArrayPoolBufferWriter{T}"/> class.
82+
/// </summary>
83+
/// <param name="pool">The <see cref="ArrayPool{T}"/> instance to use.</param>
84+
/// <param name="initialCapacity">The minimum capacity with which to initialize the underlying buffer.</param>
85+
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="initialCapacity"/> is not valid.</exception>
86+
public ArrayPoolBufferWriter(ArrayPool<T> pool, int initialCapacity)
87+
{
88+
// Since we're using pooled arrays, we can rent the buffer with the
89+
// default size immediately, we don't need to use lazy initialization
90+
// to save unnecessary memory allocations in this case.
91+
// Additionally, we don't need to manually throw the exception if
92+
// the requested size is not valid, as that'll be thrown automatically
93+
// by the array pool in use when we try to rent an array with that size.
94+
this.pool = pool;
95+
this.array = pool.Rent(initialCapacity);
7596
this.index = 0;
7697
}
7798

7899
/// <summary>
79100
/// Finalizes an instance of the <see cref="ArrayPoolBufferWriter{T}"/> class.
80101
/// </summary>
81-
~ArrayPoolBufferWriter() => this.Dispose();
102+
~ArrayPoolBufferWriter() => Dispose();
82103

83104
/// <inheritdoc/>
84105
Memory<T> IMemoryOwner<T>.Memory
@@ -182,6 +203,7 @@ public void Clear()
182203
}
183204

184205
array.AsSpan(0, this.index).Clear();
206+
185207
this.index = 0;
186208
}
187209

@@ -250,7 +272,7 @@ private void CheckAndResizeBuffer(int sizeHint)
250272
{
251273
int minimumSize = this.index + sizeHint;
252274

253-
ArrayPool<T>.Shared.Resize(ref this.array, minimumSize);
275+
this.pool.Resize(ref this.array, minimumSize);
254276
}
255277
}
256278

@@ -268,7 +290,7 @@ public void Dispose()
268290

269291
this.array = null;
270292

271-
ArrayPool<T>.Shared.Return(array);
293+
this.pool.Return(array);
272294
}
273295

274296
/// <inheritdoc/>
@@ -286,19 +308,9 @@ public override string ToString()
286308
return $"Microsoft.Toolkit.HighPerformance.Buffers.ArrayPoolBufferWriter<{typeof(T)}>[{this.index}]";
287309
}
288310

289-
/// <summary>
290-
/// Throws an <see cref="ArgumentOutOfRangeException"/> when the initial capacity is invalid.
291-
/// </summary>
292-
[MethodImpl(MethodImplOptions.NoInlining)]
293-
private static void ThrowArgumentOutOfRangeExceptionForInitialCapacity()
294-
{
295-
throw new ArgumentOutOfRangeException("initialCapacity", "The initial capacity must be a positive value");
296-
}
297-
298311
/// <summary>
299312
/// Throws an <see cref="ArgumentOutOfRangeException"/> when the requested count is negative.
300313
/// </summary>
301-
[MethodImpl(MethodImplOptions.NoInlining)]
302314
private static void ThrowArgumentOutOfRangeExceptionForNegativeCount()
303315
{
304316
throw new ArgumentOutOfRangeException("count", "The count can't be a negative value");
@@ -307,7 +319,6 @@ private static void ThrowArgumentOutOfRangeExceptionForNegativeCount()
307319
/// <summary>
308320
/// Throws an <see cref="ArgumentOutOfRangeException"/> when the size hint is negative.
309321
/// </summary>
310-
[MethodImpl(MethodImplOptions.NoInlining)]
311322
private static void ThrowArgumentOutOfRangeExceptionForNegativeSizeHint()
312323
{
313324
throw new ArgumentOutOfRangeException("sizeHint", "The size hint can't be a negative value");
@@ -316,7 +327,6 @@ private static void ThrowArgumentOutOfRangeExceptionForNegativeSizeHint()
316327
/// <summary>
317328
/// Throws an <see cref="ArgumentOutOfRangeException"/> when the requested count is negative.
318329
/// </summary>
319-
[MethodImpl(MethodImplOptions.NoInlining)]
320330
private static void ThrowArgumentExceptionForAdvancedTooFar()
321331
{
322332
throw new ArgumentException("The buffer writer has advanced too far");
@@ -325,7 +335,6 @@ private static void ThrowArgumentExceptionForAdvancedTooFar()
325335
/// <summary>
326336
/// Throws an <see cref="ObjectDisposedException"/> when <see cref="array"/> is <see langword="null"/>.
327337
/// </summary>
328-
[MethodImpl(MethodImplOptions.NoInlining)]
329338
private static void ThrowObjectDisposedException()
330339
{
331340
throw new ObjectDisposedException("The current buffer has already been disposed");

Microsoft.Toolkit.HighPerformance/Buffers/MemoryBufferWriter{T}.cs

+2-6
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,15 @@ public void Advance(int count)
106106
/// <inheritdoc/>
107107
public Memory<T> GetMemory(int sizeHint = 0)
108108
{
109-
this.ValidateSizeHint(sizeHint);
109+
ValidateSizeHint(sizeHint);
110110

111111
return this.memory.Slice(this.index);
112112
}
113113

114114
/// <inheritdoc/>
115115
public Span<T> GetSpan(int sizeHint = 0)
116116
{
117-
this.ValidateSizeHint(sizeHint);
117+
ValidateSizeHint(sizeHint);
118118

119119
return this.memory.Slice(this.index).Span;
120120
}
@@ -159,7 +159,6 @@ public override string ToString()
159159
/// <summary>
160160
/// Throws an <see cref="ArgumentOutOfRangeException"/> when the requested count is negative.
161161
/// </summary>
162-
[MethodImpl(MethodImplOptions.NoInlining)]
163162
private static void ThrowArgumentOutOfRangeExceptionForNegativeCount()
164163
{
165164
throw new ArgumentOutOfRangeException("count", "The count can't be a negative value");
@@ -168,7 +167,6 @@ private static void ThrowArgumentOutOfRangeExceptionForNegativeCount()
168167
/// <summary>
169168
/// Throws an <see cref="ArgumentOutOfRangeException"/> when the size hint is negative.
170169
/// </summary>
171-
[MethodImpl(MethodImplOptions.NoInlining)]
172170
private static void ThrowArgumentOutOfRangeExceptionForNegativeSizeHint()
173171
{
174172
throw new ArgumentOutOfRangeException("sizeHint", "The size hint can't be a negative value");
@@ -177,7 +175,6 @@ private static void ThrowArgumentOutOfRangeExceptionForNegativeSizeHint()
177175
/// <summary>
178176
/// Throws an <see cref="ArgumentOutOfRangeException"/> when the requested count is negative.
179177
/// </summary>
180-
[MethodImpl(MethodImplOptions.NoInlining)]
181178
private static void ThrowArgumentExceptionForAdvancedTooFar()
182179
{
183180
throw new ArgumentException("The buffer writer has advanced too far");
@@ -186,7 +183,6 @@ private static void ThrowArgumentExceptionForAdvancedTooFar()
186183
/// <summary>
187184
/// Throws an <see cref="ArgumentException"/> when the requested size exceeds the capacity.
188185
/// </summary>
189-
[MethodImpl(MethodImplOptions.NoInlining)]
190186
private static void ThrowArgumentExceptionForCapacityExceeded()
191187
{
192188
throw new ArgumentException("The buffer writer doesn't have enough capacity left");

Microsoft.Toolkit.HighPerformance/Buffers/MemoryOwner{T}.cs

+44-13
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ public sealed class MemoryOwner<T> : IMemoryOwner<T>
3232
private readonly int length;
3333
#pragma warning restore IDE0032
3434

35+
/// <summary>
36+
/// The <see cref="ArrayPool{T}"/> instance used to rent <see cref="array"/>.
37+
/// </summary>
38+
private readonly ArrayPool<T> pool;
39+
3540
/// <summary>
3641
/// The underlying <typeparamref name="T"/> array.
3742
/// </summary>
@@ -41,12 +46,14 @@ public sealed class MemoryOwner<T> : IMemoryOwner<T>
4146
/// Initializes a new instance of the <see cref="MemoryOwner{T}"/> class with the specified parameters.
4247
/// </summary>
4348
/// <param name="length">The length of the new memory buffer to use.</param>
49+
/// <param name="pool">The <see cref="ArrayPool{T}"/> instance to use.</param>
4450
/// <param name="mode">Indicates the allocation mode to use for the new buffer to rent.</param>
45-
private MemoryOwner(int length, AllocationMode mode)
51+
private MemoryOwner(int length, ArrayPool<T> pool, AllocationMode mode)
4652
{
4753
this.start = 0;
4854
this.length = length;
49-
this.array = ArrayPool<T>.Shared.Rent(length);
55+
this.pool = pool;
56+
this.array = pool.Rent(length);
5057

5158
if (mode == AllocationMode.Clear)
5259
{
@@ -57,20 +64,22 @@ private MemoryOwner(int length, AllocationMode mode)
5764
/// <summary>
5865
/// Initializes a new instance of the <see cref="MemoryOwner{T}"/> class with the specified parameters.
5966
/// </summary>
60-
/// <param name="array">The input <typeparamref name="T"/> array to use.</param>
6167
/// <param name="start">The starting offset within <paramref name="array"/>.</param>
6268
/// <param name="length">The length of the array to use.</param>
63-
private MemoryOwner(T[] array, int start, int length)
69+
/// <param name="pool">The <see cref="ArrayPool{T}"/> instance currently in use.</param>
70+
/// <param name="array">The input <typeparamref name="T"/> array to use.</param>
71+
private MemoryOwner(int start, int length, ArrayPool<T> pool, T[] array)
6472
{
6573
this.start = start;
6674
this.length = length;
75+
this.pool = pool;
6776
this.array = array;
6877
}
6978

7079
/// <summary>
7180
/// Finalizes an instance of the <see cref="MemoryOwner{T}"/> class.
7281
/// </summary>
73-
~MemoryOwner() => this.Dispose();
82+
~MemoryOwner() => Dispose();
7483

7584
/// <summary>
7685
/// Gets an empty <see cref="MemoryOwner{T}"/> instance.
@@ -79,7 +88,7 @@ private MemoryOwner(T[] array, int start, int length)
7988
public static MemoryOwner<T> Empty
8089
{
8190
[MethodImpl(MethodImplOptions.AggressiveInlining)]
82-
get => new MemoryOwner<T>(0, AllocationMode.Default);
91+
get => new MemoryOwner<T>(0, ArrayPool<T>.Shared, AllocationMode.Default);
8392
}
8493

8594
/// <summary>
@@ -91,19 +100,44 @@ public static MemoryOwner<T> Empty
91100
/// <remarks>This method is just a proxy for the <see langword="private"/> constructor, for clarity.</remarks>
92101
[Pure]
93102
[MethodImpl(MethodImplOptions.AggressiveInlining)]
94-
public static MemoryOwner<T> Allocate(int size) => new MemoryOwner<T>(size, AllocationMode.Default);
103+
public static MemoryOwner<T> Allocate(int size) => new MemoryOwner<T>(size, ArrayPool<T>.Shared, AllocationMode.Default);
104+
105+
/// <summary>
106+
/// Creates a new <see cref="MemoryOwner{T}"/> instance with the specified parameters.
107+
/// </summary>
108+
/// <param name="size">The length of the new memory buffer to use.</param>
109+
/// <param name="pool">The <see cref="ArrayPool{T}"/> instance currently in use.</param>
110+
/// <returns>A <see cref="MemoryOwner{T}"/> instance of the requested length.</returns>
111+
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="size"/> is not valid.</exception>
112+
/// <remarks>This method is just a proxy for the <see langword="private"/> constructor, for clarity.</remarks>
113+
[Pure]
114+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
115+
public static MemoryOwner<T> Allocate(int size, ArrayPool<T> pool) => new MemoryOwner<T>(size, pool, AllocationMode.Default);
116+
117+
/// <summary>
118+
/// Creates a new <see cref="MemoryOwner{T}"/> instance with the specified parameters.
119+
/// </summary>
120+
/// <param name="size">The length of the new memory buffer to use.</param>
121+
/// <param name="mode">Indicates the allocation mode to use for the new buffer to rent.</param>
122+
/// <returns>A <see cref="MemoryOwner{T}"/> instance of the requested length.</returns>
123+
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="size"/> is not valid.</exception>
124+
/// <remarks>This method is just a proxy for the <see langword="private"/> constructor, for clarity.</remarks>
125+
[Pure]
126+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
127+
public static MemoryOwner<T> Allocate(int size, AllocationMode mode) => new MemoryOwner<T>(size, ArrayPool<T>.Shared, mode);
95128

96129
/// <summary>
97130
/// Creates a new <see cref="MemoryOwner{T}"/> instance with the specified parameters.
98131
/// </summary>
99132
/// <param name="size">The length of the new memory buffer to use.</param>
133+
/// <param name="pool">The <see cref="ArrayPool{T}"/> instance currently in use.</param>
100134
/// <param name="mode">Indicates the allocation mode to use for the new buffer to rent.</param>
101135
/// <returns>A <see cref="MemoryOwner{T}"/> instance of the requested length.</returns>
102136
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="size"/> is not valid.</exception>
103137
/// <remarks>This method is just a proxy for the <see langword="private"/> constructor, for clarity.</remarks>
104138
[Pure]
105139
[MethodImpl(MethodImplOptions.AggressiveInlining)]
106-
public static MemoryOwner<T> Allocate(int size, AllocationMode mode) => new MemoryOwner<T>(size, mode);
140+
public static MemoryOwner<T> Allocate(int size, ArrayPool<T> pool, AllocationMode mode) => new MemoryOwner<T>(size, pool, mode);
107141

108142
/// <summary>
109143
/// Gets the number of items in the current instance
@@ -210,7 +244,7 @@ public MemoryOwner<T> Slice(int start, int length)
210244
ThrowInvalidLengthException();
211245
}
212246

213-
return new MemoryOwner<T>(array!, start, length);
247+
return new MemoryOwner<T>(start, length, this.pool, array!);
214248
}
215249

216250
/// <inheritdoc/>
@@ -227,7 +261,7 @@ public void Dispose()
227261

228262
this.array = null;
229263

230-
ArrayPool<T>.Shared.Return(array);
264+
this.pool.Return(array);
231265
}
232266

233267
/// <inheritdoc/>
@@ -251,7 +285,6 @@ public override string ToString()
251285
/// <summary>
252286
/// Throws an <see cref="ObjectDisposedException"/> when <see cref="array"/> is <see langword="null"/>.
253287
/// </summary>
254-
[MethodImpl(MethodImplOptions.NoInlining)]
255288
private static void ThrowObjectDisposedException()
256289
{
257290
throw new ObjectDisposedException(nameof(MemoryOwner<T>), "The current buffer has already been disposed");
@@ -260,7 +293,6 @@ private static void ThrowObjectDisposedException()
260293
/// <summary>
261294
/// Throws an <see cref="ArgumentOutOfRangeException"/> when the <see cref="start"/> is invalid.
262295
/// </summary>
263-
[MethodImpl(MethodImplOptions.NoInlining)]
264296
private static void ThrowInvalidOffsetException()
265297
{
266298
throw new ArgumentOutOfRangeException(nameof(start), "The input start parameter was not valid");
@@ -269,7 +301,6 @@ private static void ThrowInvalidOffsetException()
269301
/// <summary>
270302
/// Throws an <see cref="ArgumentOutOfRangeException"/> when the <see cref="length"/> is invalid.
271303
/// </summary>
272-
[MethodImpl(MethodImplOptions.NoInlining)]
273304
private static void ThrowInvalidLengthException()
274305
{
275306
throw new ArgumentOutOfRangeException(nameof(length), "The input length parameter was not valid");

0 commit comments

Comments
 (0)