Skip to content

Commit 43d6cb0

Browse files
authored
Clean up shared ArrayPool naming and add some env var config (#86109)
* Clean up shared ArrayPool naming and add some env var config We've had several requests to be able to tweak / experiment with how many arrays the shared array pool might store. This adds two environment variables, one that controls the number of partitions the shared pool uses, and one that controls the number of arrays cacheable in each partition. Previously these values were constants. As part of doing that, I needed to choose names that were a bit more palatable for external consumption, and I renamed thngs in the implementation accordingly. * Rename file to SharedArrayPool.cs * Fix naming scheme * Avoid using globalization * Avoid using Environment.GetEnvironmentVariable * Move nopool env var into Environment * Move static readonly fields to statics type
1 parent 256bf67 commit 43d6cb0

File tree

6 files changed

+240
-72
lines changed

6 files changed

+240
-72
lines changed

src/libraries/System.Buffers/tests/ArrayPool/UnitTests.cs

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33

44
using System.Collections.Concurrent;
55
using System.Collections.Generic;
6+
using System.Diagnostics;
67
using System.Diagnostics.Tracing;
8+
using System.Globalization;
79
using System.Linq;
810
using System.Numerics;
11+
using System.Reflection;
912
using System.Threading.Tasks;
1013
using Microsoft.DotNet.RemoteExecutor;
11-
using Microsoft.DotNet.XUnitExtensions;
1214
using Xunit;
1315

1416
namespace System.Buffers.ArrayPool.Tests
@@ -604,5 +606,88 @@ public static IEnumerable<object[]> BytePoolInstances()
604606
yield return new object[] { ArrayPool<byte>.Create(1024*1024, 1) };
605607
yield return new object[] { ArrayPool<byte>.Shared };
606608
}
609+
610+
[ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
611+
[InlineData("", "", "2147483647", "8")]
612+
[InlineData("0", "0", "2147483647", "8")]
613+
[InlineData("1", "2", "1", "2")]
614+
[InlineData("2", "1", "2", "1")]
615+
[InlineData("4", "123", "4", "123")]
616+
[InlineData("1000", "123", "1000", "123")]
617+
[InlineData(" 1 ", " 2 ", "1", "2")]
618+
[InlineData(
619+
" 1 ",
620+
" " +
621+
" " +
622+
" " +
623+
" " +
624+
" " +
625+
" " +
626+
"2" +
627+
" " +
628+
" " +
629+
" " +
630+
" " +
631+
" " +
632+
" " +
633+
" " +
634+
" " +
635+
" " +
636+
" " +
637+
" " +
638+
" " +
639+
" " +
640+
" ",
641+
"2147483647", "8")]
642+
public void SharedPool_SetEnvironmentVariables_ValuesRespected(
643+
string partitionCount, string maxArraysPerPartition, string expectedPartitionCount, string expectedMaxArraysPerPartition)
644+
{
645+
// This test relies on private reflection into the shared pool implementation.
646+
// If those details change, this test will need to be updated accordingly.
647+
648+
var psi = new ProcessStartInfo();
649+
psi.Environment.Add("DOTNET_SYSTEM_BUFFERS_SHAREDARRAYPOOL_MAXPARTITIONCOUNT", partitionCount);
650+
psi.Environment.Add("DOTNET_SYSTEM_BUFFERS_SHAREDARRAYPOOL_MAXARRAYSPERPARTITION", maxArraysPerPartition);
651+
652+
RemoteExecutor.Invoke((partitionCount, maxArraysPerPartition, expectedPartitionCount, expectedMaxArraysPerPartition) =>
653+
{
654+
Type staticsType = typeof(ArrayPool<>).Assembly.GetType("System.Buffers.SharedArrayPoolStatics");
655+
Assert.NotNull(staticsType);
656+
657+
FieldInfo partitionCountField = staticsType.GetField("s_partitionCount", BindingFlags.NonPublic | BindingFlags.Static);
658+
Assert.NotNull(partitionCountField);
659+
int partitionCountValue = (int)partitionCountField.GetValue(null);
660+
if (int.Parse(expectedPartitionCount) > 0)
661+
{
662+
Assert.Equal(Math.Min(int.Parse(expectedPartitionCount), Environment.ProcessorCount), partitionCountValue);
663+
}
664+
else
665+
{
666+
Assert.Equal(Environment.ProcessorCount, partitionCountValue);
667+
}
668+
669+
FieldInfo maxArraysPerPartitionField = staticsType.GetField("s_maxArraysPerPartition", BindingFlags.NonPublic | BindingFlags.Static);
670+
Assert.NotNull(maxArraysPerPartitionField);
671+
int maxArraysPerPartitionValue = (int)maxArraysPerPartitionField.GetValue(null);
672+
if (int.Parse(expectedMaxArraysPerPartition) > 0)
673+
{
674+
Assert.Equal(int.Parse(expectedMaxArraysPerPartition), maxArraysPerPartitionValue);
675+
}
676+
else
677+
{
678+
Assert.Equal(8, maxArraysPerPartitionValue);
679+
}
680+
681+
// Make sure the pool is still usable
682+
for (int i = 0; i < 2; i++)
683+
{
684+
byte[] array = ArrayPool<byte>.Shared.Rent(123);
685+
Assert.NotNull(array);
686+
Assert.InRange(array.Length, 123, int.MaxValue);
687+
ArrayPool<byte>.Shared.Return(array);
688+
}
689+
690+
}, partitionCount, maxArraysPerPartition, expectedPartitionCount, expectedMaxArraysPerPartition, new RemoteInvokeOptions() { StartInfo = psi }).Dispose();
691+
}
607692
}
608693
}

src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@
158158
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.TimeSpan.cs" />
159159
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.TimeSpan.LittleG.cs" />
160160
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.TimeSpanSplitter.cs" />
161-
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\TlsOverPerCoreLockedStacksArrayPool.cs" />
161+
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\SharedArrayPool.cs" />
162162
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Utilities.cs" />
163163
<Compile Include="$(MSBuildThisFileDirectory)System\ByReference.cs" />
164164
<Compile Include="$(MSBuildThisFileDirectory)System\Byte.cs" />

src/libraries/System.Private.CoreLib/src/System/Buffers/ArrayPool.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public abstract class ArrayPool<T>
2020
{
2121
// Store the shared ArrayPool in a field of its derived sealed type so the Jit can "see" the exact type
2222
// when the Shared property is inlined which will allow it to devirtualize calls made on it.
23-
private static readonly TlsOverPerCoreLockedStacksArrayPool<T> s_shared = new TlsOverPerCoreLockedStacksArrayPool<T>();
23+
private static readonly SharedArrayPool<T> s_shared = new SharedArrayPool<T>();
2424

2525
/// <summary>
2626
/// Retrieves a shared <see cref="ArrayPool{T}"/> instance.

0 commit comments

Comments
 (0)