Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazily initialize base state for Random-derived type #81627

Merged
merged 1 commit into from
Feb 14, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Runtime.CompilerServices;

Expand All @@ -18,7 +19,7 @@ private sealed class Net5CompatSeedImpl : ImplBase
private CompatPrng _prng; // mutable struct; do not make this readonly

public Net5CompatSeedImpl(int seed) =>
_prng = new CompatPrng(seed);
_prng.EnsureInitialized(seed);

public override double Sample() => _prng.Sample();

Expand Down Expand Up @@ -98,25 +99,40 @@ private sealed class Net5CompatDerivedImpl : ImplBase
/// <summary>Reference to the <see cref="Random"/> containing this implementation instance.</summary>
/// <remarks>Used to ensure that any calls to other virtual members are performed using the Random-derived instance, if one exists.</remarks>
private readonly Random _parent;
/// <summary>Potentially lazily-initialized algorithm backing this instance.</summary>
/// <summary>Seed specified at construction time used to lazily initialize <see cref="_prng"/>.</summary>
private readonly int _seed;
/// <summary>Lazily-initialized algorithm backing this instance.</summary>
private CompatPrng _prng; // mutable struct; do not make this readonly

public Net5CompatDerivedImpl(Random parent) : this(parent, Shared.Next()) { }

public Net5CompatDerivedImpl(Random parent, int seed)
{
_parent = parent;
_prng = new CompatPrng(seed);
_seed = seed;
}

public override double Sample() => _prng.Sample();
public override double Sample()
{
_prng.EnsureInitialized(_seed);
return _prng.Sample();
}

public override int Next() => _prng.InternalSample();
public override int Next()
{
_prng.EnsureInitialized(_seed);
return _prng.InternalSample();
}

public override int Next(int maxValue) => (int)(_parent.Sample() * maxValue);
public override int Next(int maxValue)
{
_prng.EnsureInitialized(_seed);
return (int)(_parent.Sample() * maxValue);
}

public override int Next(int minValue, int maxValue)
{
_prng.EnsureInitialized(_seed);
long range = (long)maxValue - minValue;
return range <= int.MaxValue ?
(int)(_parent.Sample() * range) + minValue :
Expand All @@ -125,6 +141,7 @@ public override int Next(int minValue, int maxValue)

public override long NextInt64()
{
_prng.EnsureInitialized(_seed);
while (true)
{
// Get top 63 bits to get a value in the range [0, long.MaxValue], but try again
Expand All @@ -146,6 +163,8 @@ public override long NextInt64(long minValue, long maxValue)

if (exclusiveRange > 1)
{
_prng.EnsureInitialized(_seed);

// Narrow down to the smallest range [0, 2^bits] that contains maxValue - minValue
// Then repeatedly generate a value in that outer range until we get one within the inner range.
int bits = BitOperations.Log2Ceiling(exclusiveRange);
Expand All @@ -169,14 +188,27 @@ public override long NextInt64(long minValue, long maxValue)
(((ulong)(uint)_parent.Next(1 << 22)) << 22) |
(((ulong)(uint)_parent.Next(1 << 20)) << 44);

public override double NextDouble() => _parent.Sample();
public override double NextDouble()
{
_prng.EnsureInitialized(_seed);
return _parent.Sample();
}

public override float NextSingle() => (float)_parent.Sample();
public override float NextSingle()
{
_prng.EnsureInitialized(_seed);
return (float)_parent.Sample();
}

public override void NextBytes(byte[] buffer) => _prng.NextBytes(buffer);
public override void NextBytes(byte[] buffer)
{
_prng.EnsureInitialized(_seed);
_prng.NextBytes(buffer);
}

public override void NextBytes(Span<byte> buffer)
{
_prng.EnsureInitialized(_seed);
for (int i = 0; i < buffer.Length; i++)
{
buffer[i] = (byte)_parent.Next();
Expand All @@ -191,13 +223,25 @@ public override void NextBytes(Span<byte> buffer)
/// </summary>
private struct CompatPrng
{
private int[] _seedArray;
private int[]? _seedArray;
private int _inext;
private int _inextp;

public CompatPrng(int seed)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MemberNotNull(nameof(_seedArray))]
internal void EnsureInitialized(int seed)
{
// Initialize seed array.
if (_seedArray is null)
{
Initialize(seed);
}
}

[MemberNotNull(nameof(_seedArray))]
private void Initialize(int seed)
{
Debug.Assert(_seedArray is null);

int[] seedArray = new int[56];

int subtraction = (seed == int.MinValue) ? int.MaxValue : Math.Abs(seed);
Expand Down Expand Up @@ -261,6 +305,8 @@ internal void NextBytes(Span<byte> buffer)

internal int InternalSample()
{
Debug.Assert(_seedArray is not null);

int locINext = _inext;
if (++locINext >= 56)
{
Expand Down