Skip to content

Commit f1ad1b9

Browse files
authored
Delete custom FastRandom from ThreadPool (#47338)
* Delete custom FastRandom from ThreadPool - Consolidates 128/256-bit variants of Xoshiro** into the same class name - Uses that from ThreadPool instead of its custom xorshift-based algorithm Throughput stays the same (~2.5ns per next random value) and incurs just one additional object allocation per thread pool thread. * Consolidate TARGET_64/32BIT DefineConstants
1 parent f89e6b1 commit f1ad1b9

File tree

9 files changed

+33
-59
lines changed

9 files changed

+33
-59
lines changed

src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,18 @@
5757
<PropertyGroup Condition="'$(Platform)' == 'x64'">
5858
<PlatformTarget>x64</PlatformTarget>
5959
<Prefer32Bit>false</Prefer32Bit>
60-
<DefineConstants>TARGET_64BIT;TARGET_AMD64;$(DefineConstants)</DefineConstants>
60+
<DefineConstants>$(DefineConstants);TARGET_AMD64</DefineConstants>
6161
</PropertyGroup>
6262
<PropertyGroup Condition="'$(Platform)' == 'x86'">
6363
<PlatformTarget>x86</PlatformTarget>
64-
<DefineConstants>TARGET_32BIT;$(DefineConstants)</DefineConstants>
6564
</PropertyGroup>
6665
<PropertyGroup Condition="'$(Platform)' == 'arm'">
6766
<PlatformTarget>arm</PlatformTarget>
68-
<DefineConstants>TARGET_32BIT;TARGET_ARM;$(DefineConstants)</DefineConstants>
67+
<DefineConstants>$(DefineConstants);TARGET_ARM</DefineConstants>
6968
</PropertyGroup>
7069
<PropertyGroup Condition="'$(Platform)' == 'arm64'">
7170
<PlatformTarget>AnyCPU</PlatformTarget>
72-
<DefineConstants>TARGET_64BIT;TARGET_ARM64;$(DefineConstants)</DefineConstants>
71+
<DefineConstants>$(DefineConstants);TARGET_ARM64</DefineConstants>
7372
</PropertyGroup>
7473

7574
<!-- Configuration specific properties -->

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414
<SupportsArmIntrinsics Condition="'$(Platform)' == 'arm64'">true</SupportsArmIntrinsics>
1515
<SupportsX86Intrinsics Condition="'$(Platform)' == 'x64' or ('$(Platform)' == 'x86' and '$(TargetsUnix)' != 'true')">true</SupportsX86Intrinsics>
1616
<ILLinkSharedDirectory>$(MSBuildThisFileDirectory)ILLink\</ILLinkSharedDirectory>
17+
<Is64Bit Condition="'$(Platform)' == 'arm64' or '$(Platform)' == 'x64'">true</Is64Bit>
18+
</PropertyGroup>
19+
<PropertyGroup>
20+
<DefineConstants Condition="'$(Is64Bit)' != 'true'">$(DefineConstants);TARGET_32BIT</DefineConstants>
21+
<DefineConstants Condition="'$(Is64Bit)' == 'true'">$(DefineConstants);TARGET_64BIT</DefineConstants>
1722
</PropertyGroup>
1823
<PropertyGroup>
1924
<DefineConstants Condition="'$(TargetsUnix)' == 'true'">$(DefineConstants);TARGET_UNIX</DefineConstants>
@@ -29,8 +34,8 @@
2934
<ItemGroup>
3035
<ILLinkSubstitutionsXmls Include="$(ILLinkSharedDirectory)ILLink.Substitutions.Shared.xml" />
3136
<ILLinkSubstitutionsXmls Include="$(ILLinkSharedDirectory)ILLink.Substitutions.LittleEndian.xml" Condition="'$(Platform)' == 'wasm' or '$(Platform)' == 'arm' or '$(Platform)' == 'arm64' or '$(Platform)' == 'x86' or '$(Platform)' == 'x64'" />
32-
<ILLinkSubstitutionsXmls Include="$(ILLinkSharedDirectory)ILLink.Substitutions.32bit.xml" Condition="'$(Platform)' == 'wasm' or '$(Platform)' == 'arm' or '$(Platform)' == 'x86'" />
33-
<ILLinkSubstitutionsXmls Include="$(ILLinkSharedDirectory)ILLink.Substitutions.64bit.xml" Condition="'$(Platform)' == 'arm64' or '$(Platform)' == 'x64'" />
37+
<ILLinkSubstitutionsXmls Include="$(ILLinkSharedDirectory)ILLink.Substitutions.32bit.xml" Condition="'$(Is64Bit)' != 'true'" />
38+
<ILLinkSubstitutionsXmls Include="$(ILLinkSharedDirectory)ILLink.Substitutions.64bit.xml" Condition="'$(Is64Bit)' == 'true'" />
3439
<ILLinkSubstitutionsXmls Include="$(ILLinkSharedDirectory)ILLink.Substitutions.NoArmIntrinsics.xml" Condition="'$(SupportsArmIntrinsics)' != 'true'" />
3540
<ILLinkSubstitutionsXmls Include="$(ILLinkSharedDirectory)ILLink.Substitutions.NoX86Intrinsics.xml" Condition="'$(SupportsX86Intrinsics)' != 'true'" />
3641
<ILLinkLinkAttributesXmls Include="$(ILLinkSharedDirectory)ILLink.LinkAttributes.Shared.xml" />
@@ -485,8 +490,8 @@
485490
<Compile Include="$(MSBuildThisFileDirectory)System\Random.cs" />
486491
<Compile Include="$(MSBuildThisFileDirectory)System\Random.ImplBase.cs" />
487492
<Compile Include="$(MSBuildThisFileDirectory)System\Random.LegacyImpl.cs" />
488-
<Compile Include="$(MSBuildThisFileDirectory)System\Random.Xoshiro128StarStarImpl.cs" />
489-
<Compile Include="$(MSBuildThisFileDirectory)System\Random.Xoshiro256StarStarImpl.cs" />
493+
<Compile Condition="'$(Is64Bit)' != 'true'" Include="$(MSBuildThisFileDirectory)System\Random.Xoshiro128StarStarImpl.cs" />
494+
<Compile Condition="'$(Is64Bit)' == 'true'" Include="$(MSBuildThisFileDirectory)System\Random.Xoshiro256StarStarImpl.cs" />
490495
<Compile Include="$(MSBuildThisFileDirectory)System\Range.cs" />
491496
<Compile Include="$(MSBuildThisFileDirectory)System\RankException.cs" />
492497
<Compile Include="$(MSBuildThisFileDirectory)System\ReadOnlyMemory.cs" />

src/libraries/System.Private.CoreLib/src/System/Random.LegacyImpl.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ private sealed class LegacyImpl : ImplBase
2020
{
2121
/// <summary>Thread-static instance used to seed any legacy implementations created with the default ctor.</summary>
2222
[ThreadStatic]
23-
private static ImplBase? t_seedGenerator;
23+
private static XoshiroImpl? t_seedGenerator;
2424

2525
/// <summary>Reference to the <see cref="Random"/> containing this implementation instance.</summary>
2626
/// <remarks>Used to ensure that any calls to other virtual members are performed using the Random-derived instance, if one exists.</remarks>
@@ -29,7 +29,7 @@ private sealed class LegacyImpl : ImplBase
2929
private int _inext;
3030
private int _inextp;
3131

32-
public LegacyImpl(Random parent) : this(parent, (t_seedGenerator ??= CreateDefaultImpl()).Next())
32+
public LegacyImpl(Random parent) : this(parent, (t_seedGenerator ??= new()).Next())
3333
{
3434
}
3535

src/libraries/System.Private.CoreLib/src/System/Random.Xoshiro128StarStarImpl.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public partial class Random
1717
/// As such, we are free to implement however we see fit, without back compat concerns around
1818
/// the sequence of numbers generated or what methods call what other methods.
1919
/// </summary>
20-
internal sealed class Xoshiro128StarStarImpl : ImplBase
20+
internal sealed class XoshiroImpl : ImplBase
2121
{
2222
// NextUInt32 is based on the algorithm from http://prng.di.unimi.it/xoshiro128starstar.c:
2323
//
@@ -31,7 +31,7 @@ internal sealed class Xoshiro128StarStarImpl : ImplBase
3131

3232
private uint _s0, _s1, _s2, _s3;
3333

34-
public unsafe Xoshiro128StarStarImpl()
34+
public unsafe XoshiroImpl()
3535
{
3636
uint* ptr = stackalloc uint[4];
3737
do
@@ -64,7 +64,8 @@ internal uint NextUInt32()
6464
}
6565

6666
/// <summary>Produces a value in the range [0, ulong.MaxValue].</summary>
67-
private ulong NextUInt64() => (((ulong)NextUInt32()) << 32) | NextUInt32();
67+
[MethodImpl(MethodImplOptions.AggressiveInlining)] // small-ish hot path used by a handful of "next" methods
68+
internal ulong NextUInt64() => (((ulong)NextUInt32()) << 32) | NextUInt32();
6869

6970
public override int Next()
7071
{

src/libraries/System.Private.CoreLib/src/System/Random.Xoshiro256StarStarImpl.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public partial class Random
1717
/// As such, we are free to implement however we see fit, without back compat concerns around
1818
/// the sequence of numbers generated or what methods call what other methods.
1919
/// </summary>
20-
internal sealed class Xoshiro256StarStarImpl : ImplBase
20+
internal sealed class XoshiroImpl : ImplBase
2121
{
2222
// NextUInt64 is based on the algorithm from http://prng.di.unimi.it/xoshiro256starstar.c:
2323
//
@@ -31,7 +31,7 @@ internal sealed class Xoshiro256StarStarImpl : ImplBase
3131

3232
private ulong _s0, _s1, _s2, _s3;
3333

34-
public unsafe Xoshiro256StarStarImpl()
34+
public unsafe XoshiroImpl()
3535
{
3636
ulong* ptr = stackalloc ulong[4];
3737
do
@@ -45,6 +45,10 @@ public unsafe Xoshiro256StarStarImpl()
4545
while ((_s0 | _s1 | _s2 | _s3) == 0); // at least one value must be non-zero
4646
}
4747

48+
/// <summary>Produces a value in the range [0, uint.MaxValue].</summary>
49+
[MethodImpl(MethodImplOptions.AggressiveInlining)] // small-ish hot path used by very few call sites
50+
internal uint NextUInt32() => (uint)(NextUInt64() >> 32);
51+
4852
/// <summary>Produces a value in the range [0, ulong.MaxValue].</summary>
4953
[MethodImpl(MethodImplOptions.AggressiveInlining)] // small-ish hot path used by a handful of "next" methods
5054
internal ulong NextUInt64()

src/libraries/System.Private.CoreLib/src/System/Random.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,7 @@ public Random() =>
2424
// With no seed specified, if this is the base type, we can implement this however we like.
2525
// If it's a derived type, for compat we respect the previous implementation, so that overrides
2626
// are called as they were previously.
27-
_impl = GetType() == typeof(Random) ? CreateDefaultImpl() : new LegacyImpl(this);
28-
29-
/// <summary>Creates the default, optimized implementation used for `new Random()`.</summary>
30-
private static ImplBase CreateDefaultImpl() => IntPtr.Size == 8 ?
31-
new Xoshiro256StarStarImpl() : // optimized for 64-bit
32-
new Xoshiro128StarStarImpl(); // optimized for 32-bit
27+
_impl = GetType() == typeof(Random) ? new XoshiroImpl() : new LegacyImpl(this);
3328

3429
/// <summary>Initializes a new instance of the Random class, using the specified seed value.</summary>
3530
/// <param name="Seed">

src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ internal static bool LocalFindAndPop(object callback)
549549
int c = queues.Length;
550550
Debug.Assert(c > 0, "There must at least be a queue for this thread.");
551551
int maxIndex = c - 1;
552-
int i = tl.random.Next(c);
552+
uint i = tl.random.NextUInt32() % (uint)c;
553553
while (c > 0)
554554
{
555555
i = (i < maxIndex) ? i + 1 : 0;
@@ -799,31 +799,6 @@ private static void DispatchWorkItemWithWorkerTracking(object workItem, Thread c
799799
}
800800
}
801801

802-
// Simple random number generator. We don't need great randomness, we just need a little and for it to be fast.
803-
internal struct FastRandom // xorshift prng
804-
{
805-
private uint _w, _x, _y, _z;
806-
807-
public FastRandom(int seed)
808-
{
809-
_x = (uint)seed;
810-
_w = 88675123;
811-
_y = 362436069;
812-
_z = 521288629;
813-
}
814-
815-
public int Next(int maxValue)
816-
{
817-
Debug.Assert(maxValue > 0);
818-
819-
uint t = _x ^ (_x << 11);
820-
_x = _y; _y = _z; _z = _w;
821-
_w = _w ^ (_w >> 19) ^ (t ^ (t >> 8));
822-
823-
return (int)(_w % (uint)maxValue);
824-
}
825-
}
826-
827802
// Holds a WorkStealingQueue, and removes it from the list when this object is no longer referenced.
828803
internal sealed class ThreadPoolWorkQueueThreadLocals
829804
{
@@ -834,7 +809,7 @@ internal sealed class ThreadPoolWorkQueueThreadLocals
834809
public readonly ThreadPoolWorkQueue.WorkStealingQueue workStealingQueue;
835810
public readonly Thread currentThread;
836811
public readonly object? threadLocalCompletionCountObject;
837-
public FastRandom random = new FastRandom(Environment.CurrentManagedThreadId); // mutable struct, do not copy or make readonly
812+
public readonly Random.XoshiroImpl random = new Random.XoshiroImpl();
838813

839814
public ThreadPoolWorkQueueThreadLocals(ThreadPoolWorkQueue tpq)
840815
{

src/libraries/System.Runtime.Extensions/tests/System/Random.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ public void Xoshiro_AlgorithmBehavesAsExpected()
398398
Type implType = typeof(Random)
399399
.GetNestedTypes(BindingFlags.NonPublic)
400400
.Single(t => t.Name.StartsWith("Xoshiro", StringComparison.Ordinal));
401+
Assert.NotNull(implType);
401402

402403
var randOuter = new Random();
403404
object randInner = randOuter.GetType().GetField("_impl", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(randOuter);
@@ -415,8 +416,6 @@ public void Xoshiro_AlgorithmBehavesAsExpected()
415416

416417
if (IntPtr.Size == 8)
417418
{
418-
Assert.Contains("256", implType.Name);
419-
420419
// Example seeds from https://www.pcg-random.org/posts/a-quick-look-at-xoshiro256.html
421420
s0.SetValue(randInner, 0x01d353e5f3993bb0ul);
422421
s1.SetValue(randInner, 0x7b9c0df6cb193b20ul);
@@ -487,8 +486,6 @@ public void Xoshiro_AlgorithmBehavesAsExpected()
487486
}
488487
else
489488
{
490-
Assert.Contains("128", implType.Name);
491-
492489
s0.SetValue(randInner, 0x01d353e5u);
493490
s1.SetValue(randInner, 0x7b9c0df6u);
494491
s2.SetValue(randInner, 0xfdfcaa91u);

src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,30 +50,28 @@
5050
<PropertyGroup Condition="'$(Platform)' == 'x64'">
5151
<PlatformTarget>x64</PlatformTarget>
5252
<Prefer32Bit>false</Prefer32Bit>
53-
<DefineConstants>TARGET_64BIT;TARGET_AMD64;$(DefineConstants)</DefineConstants>
53+
<DefineConstants>$(DefineConstants);TARGET_AMD64</DefineConstants>
5454
</PropertyGroup>
5555
<PropertyGroup Condition="'$(Platform)' == 'x86'">
5656
<PlatformTarget>x86</PlatformTarget>
57-
<DefineConstants>TARGET_32BIT;$(DefineConstants)</DefineConstants>
5857
</PropertyGroup>
5958
<PropertyGroup Condition="'$(Platform)' == 'arm'">
6059
<PlatformTarget>arm</PlatformTarget>
61-
<DefineConstants>TARGET_32BIT;TARGET_ARM;$(DefineConstants)</DefineConstants>
60+
<DefineConstants>$(DefineConstants);TARGET_ARM</DefineConstants>
6261
</PropertyGroup>
6362
<PropertyGroup Condition="'$(Platform)' == 'arm64'">
6463
<PlatformTarget>AnyCPU</PlatformTarget>
65-
<DefineConstants>TARGET_64BIT;TARGET_ARM64;$(DefineConstants)</DefineConstants>
64+
<DefineConstants>$(DefineConstants);TARGET_ARM64</DefineConstants>
6665
</PropertyGroup>
6766
<PropertyGroup Condition="'$(Platform)' == 'wasm'">
6867
<PlatformTarget>AnyCPU</PlatformTarget>
69-
<DefineConstants>TARGET_32BIT;$(DefineConstants)</DefineConstants>
7068
</PropertyGroup>
7169

7270
<!-- Configuration specific properties -->
7371
<PropertyGroup Condition="'$(Configuration)' == 'Debug' or '$(Configuration)' == 'Checked'">
7472
<Optimize Condition="'$(Optimize)' == '' and '$(Configuration)' == 'Debug'">false</Optimize>
7573
<Optimize Condition="'$(Optimize)' == '' and '$(Configuration)' == 'Checked'">true</Optimize>
76-
<DefineConstants>_LOGGING;DEBUG;$(DefineConstants)</DefineConstants>
74+
<DefineConstants>$(DefineConstants);_LOGGING;DEBUG</DefineConstants>
7775
</PropertyGroup>
7876
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
7977
<Optimize Condition="'$(Optimize)' == ''">true</Optimize>
@@ -103,7 +101,7 @@
103101
<PropertyGroup>
104102
<NoWarn>$(NoWarn),618,67</NoWarn>
105103

106-
<DefineConstants>MONO_FEATURE_SRE;$(DefineConstants)</DefineConstants>
104+
<DefineConstants>$(DefineConstants);MONO_FEATURE_SRE</DefineConstants>
107105

108106
<FeatureManagedEtwChannels>true</FeatureManagedEtwChannels>
109107
<FeatureManagedEtw>true</FeatureManagedEtw>

0 commit comments

Comments
 (0)