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

Extended RNG functionality. #1022

Merged
merged 5 commits into from
Jul 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions Src/ILGPU.Algorithms/CompatibilitySuppressions.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:ILGPU.Algorithms.Random.IRandomProvider`1.CreateProvider``1(``0@)</Target>
<Left>lib/net471/ILGPU.Algorithms.dll</Left>
<Right>lib/net471/ILGPU.Algorithms.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:ILGPU.Algorithms.Random.IRandomProvider`1.CreateProvider``1(``0@)</Target>
<Left>lib/net5.0/ILGPU.Algorithms.dll</Left>
<Right>lib/net5.0/ILGPU.Algorithms.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:ILGPU.Algorithms.Random.IRandomProvider`1.CreateProvider``1(``0@)</Target>
<Left>lib/net6.0/ILGPU.Algorithms.dll</Left>
<Right>lib/net6.0/ILGPU.Algorithms.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:ILGPU.Algorithms.Random.IRandomProvider`1.CreateProvider``1(``0@)</Target>
<Left>lib/net7.0/ILGPU.Algorithms.dll</Left>
<Right>lib/net7.0/ILGPU.Algorithms.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:ILGPU.Algorithms.Random.IRandomProvider`1.CreateProvider``1(``0@)</Target>
<Left>lib/netstandard2.1/ILGPU.Algorithms.dll</Left>
<Right>lib/netstandard2.1/ILGPU.Algorithms.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
</Suppressions>
21 changes: 15 additions & 6 deletions Src/ILGPU.Algorithms/Random/IRandomProvider.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// ---------------------------------------------------------------------------------------
// ILGPU Algorithms
// Copyright (c) 2021 ILGPU Project
// Copyright (c) 2021-2023 ILGPU Project
// www.ilgpu.net
//
// File: IRandomProvider.cs
Expand All @@ -9,6 +9,7 @@
// Source License. See LICENSE.txt for details.
// ---------------------------------------------------------------------------------------

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

Expand All @@ -19,7 +20,6 @@ namespace ILGPU.Algorithms.Random
/// </summary>
public interface IRandomProvider
{

/// <summary>
/// Generates a random int in [0..int.MaxValue].
/// </summary>
Expand Down Expand Up @@ -53,6 +53,7 @@ public interface IRandomProvider
/// An abstract RNG provider that supports period shifts.
/// </summary>
/// <typeparam name="TProvider">The implementing provider type.</typeparam>
[CLSCompliant(false)]
public interface IRandomProvider<TProvider> : IRandomProvider
where TProvider : struct, IRandomProvider<TProvider>
{
Expand All @@ -74,6 +75,14 @@ public interface IRandomProvider<TProvider> : IRandomProvider
/// <param name="random">The parent RNG instance.</param>
/// <returns>The next provider instance.</returns>
TProvider CreateProvider(System.Random random);

/// <summary>
/// Instantiates a new provider using the given random.
/// </summary>
/// <param name="random">The parent RNG instance.</param>
/// <returns>The next provider instance.</returns>
TProvider CreateProvider<TRandomProvider>(ref TRandomProvider random)
where TRandomProvider : struct, IRandomProvider<TRandomProvider>;
}

namespace Operations
Expand Down Expand Up @@ -106,7 +115,7 @@ public interface IRandomProviderOperation<T, TRandomProvider>
/// <inheritdoc cref="IRandomProviderOperation{T, TRandomProvider}.
/// Apply(ref TRandomProvider)"/>"
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly int Apply(ref TRandomProvider randomProvider) =>
public int Apply(ref TRandomProvider randomProvider) =>
randomProvider.Next();
}

Expand All @@ -121,7 +130,7 @@ public readonly int Apply(ref TRandomProvider randomProvider) =>
/// <inheritdoc cref="IRandomProviderOperation{T, TRandomProvider}.
/// Apply(ref TRandomProvider)"/>"
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly long Apply(ref TRandomProvider randomProvider) =>
public long Apply(ref TRandomProvider randomProvider) =>
randomProvider.NextLong();
}

Expand All @@ -136,7 +145,7 @@ public readonly long Apply(ref TRandomProvider randomProvider) =>
/// <inheritdoc cref="IRandomProviderOperation{T, TRandomProvider}.
/// Apply(ref TRandomProvider)"/>"
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly float Apply(ref TRandomProvider randomProvider) =>
public float Apply(ref TRandomProvider randomProvider) =>
randomProvider.NextFloat();
}

Expand All @@ -151,7 +160,7 @@ public readonly float Apply(ref TRandomProvider randomProvider) =>
/// <inheritdoc cref="IRandomProviderOperation{T, TRandomProvider}.
/// Apply(ref TRandomProvider)"/>"
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly double Apply(ref TRandomProvider randomProvider) =>
public double Apply(ref TRandomProvider randomProvider) =>
randomProvider.NextDouble();
}
}
Expand Down
6 changes: 5 additions & 1 deletion Src/ILGPU.Algorithms/Random/RNG.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// ---------------------------------------------------------------------------------------
// ILGPU Algorithms
// Copyright (c) 2021-2022 ILGPU Project
// Copyright (c) 2021-2023 ILGPU Project
// www.ilgpu.net
//
// File: RNG.cs
Expand Down Expand Up @@ -31,6 +31,7 @@ public abstract class RNG : AcceleratorObject
/// <typeparam name="TRandomProvider">The random provider type.</typeparam>
/// <param name="accelerator">The current accelerator.</param>
/// <param name="random">The parent RNG provider.</param>
[CLSCompliant(false)]
public static RNG<TRandomProvider> Create<TRandomProvider>(
Accelerator accelerator,
System.Random random)
Expand All @@ -46,6 +47,7 @@ public static RNG<TRandomProvider> Create<TRandomProvider>(
/// <param name="maxNumParallelWarps">
/// The maximum number of parallel warps.
/// </param>
[CLSCompliant(false)]
public static RNG<TRandomProvider> Create<TRandomProvider>(
Accelerator accelerator,
System.Random random,
Expand Down Expand Up @@ -146,6 +148,7 @@ public abstract void FillUniform(
/// is stored within an underlying memory buffer.
/// </summary>
/// <typeparam name="TRandomProvider">The random provider type.</typeparam>
[CLSCompliant(false)]
public readonly struct RNGView<TRandomProvider> : IRandomProvider
where TRandomProvider : unmanaged, IRandomProvider<TRandomProvider>
{
Expand Down Expand Up @@ -256,6 +259,7 @@ public readonly double NextDouble() =>
/// </summary>
/// <typeparam name="TRandomProvider">The random provider type.</typeparam>
/// <remarks>Members of this class are not thread safe.</remarks>
[CLSCompliant(false)]
public class RNG<TRandomProvider> : RNG
where TRandomProvider : unmanaged, IRandomProvider<TRandomProvider>
{
Expand Down
107 changes: 106 additions & 1 deletion Src/ILGPU.Algorithms/Random/RandomExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// ---------------------------------------------------------------------------------------
// ILGPU Algorithms
// Copyright (c) 2021 ILGPU Project
// Copyright (c) 2021-2023 ILGPU Project
// www.ilgpu.net
//
// File: RandomExtensions.cs
Expand Down Expand Up @@ -87,6 +87,48 @@ internal static ulong ShiftState(ulong state, int laneShift)
return (state + (shiftVal << 7) + (shiftVal >> 3)) | shiftVal;
}

/// <summary>
/// Generates a random int in [minValue..maxValue).
/// </summary>
/// <param name="randomProvider">The random provider.</param>
/// <param name="minValue">The minimum value (inclusive)</param>
/// <param name="maxValue">The maximum values (exclusive)</param>
/// <returns>A random int in [minValue..maxValue).</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Next<TRandomProvider>(
ref TRandomProvider randomProvider,
float minValue,
float maxValue)
where TRandomProvider : struct, IRandomProvider
{
Debug.Assert(minValue < maxValue, "Values out of range");
float dist = maxValue - minValue;
return Math.Min(
randomProvider.NextFloat() * dist + minValue,
maxValue);
}

/// <summary>
/// Generates a random int in [minValue..maxValue).
/// </summary>
/// <param name="randomProvider">The random provider.</param>
/// <param name="minValue">The minimum value (inclusive)</param>
/// <param name="maxValue">The maximum values (exclusive)</param>
/// <returns>A random int in [minValue..maxValue).</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double Next<TRandomProvider>(
ref TRandomProvider randomProvider,
double minValue,
double maxValue)
where TRandomProvider : struct, IRandomProvider
{
Debug.Assert(minValue < maxValue, "Values out of range");
double dist = maxValue - minValue;
return Math.Min(
randomProvider.NextDouble() * dist + minValue,
maxValue);
}

/// <summary>
/// Generates a random int in [minValue..maxValue).
/// </summary>
Expand Down Expand Up @@ -138,6 +180,7 @@ public static long Next<TRandomProvider>(
/// <typeparam name="TRandomProvider">The random provider type.</typeparam>
/// <param name="accelerator">The current accelerator.</param>
/// <param name="random">The parent RNG provider.</param>
[CLSCompliant(false)]
public static RNG<TRandomProvider> CreateRNG<TRandomProvider>(
this Accelerator accelerator,
System.Random random)
Expand All @@ -153,11 +196,73 @@ public static RNG<TRandomProvider> CreateRNG<TRandomProvider>(
/// <param name="maxNumParallelWarps">
/// The maximum number of parallel warps.
/// </param>
[CLSCompliant(false)]
public static RNG<TRandomProvider> CreateRNG<TRandomProvider>(
this Accelerator accelerator,
System.Random random,
int maxNumParallelWarps)
where TRandomProvider : unmanaged, IRandomProvider<TRandomProvider> =>
RNG.Create<TRandomProvider>(accelerator, random, maxNumParallelWarps);

/// <summary>
/// Initialization kernel used to initialize the actual RNG values on the device.
/// </summary>
internal static void InitializeRNGKernel<TRandomProvider>(
Index1D index,
ArrayView<XorShift128Plus> sourceProviders,
ArrayView<TRandomProvider> randomProvider)
where TRandomProvider : unmanaged, IRandomProvider<TRandomProvider>
{
var provider = sourceProviders[index];
for (LongIndex1D i = index;
i < randomProvider.Length;
i += GridExtensions.GridStrideLoopStride)
{
randomProvider[i] = default(TRandomProvider).CreateProvider(ref provider);
}

// Update provider state for future iterations
sourceProviders[index] = provider;
}

/// <summary>
/// Initializes a given view with random values.
/// </summary>
/// <typeparam name="TRandomProvider">
/// The RNG provider type to use on the device.
/// </typeparam>
/// <param name="accelerator">The current accelerator.</param>
/// <param name="stream">The current accelerator stream.</param>
/// <param name="rngView">The view to fill.</param>
/// <param name="random">The source RNG provider.</param>
/// <param name="numInitializers">The number of CPU initializers to use.</param>
[CLSCompliant(false)]
public static void InitRNGView<TRandomProvider>(
this Accelerator accelerator,
AcceleratorStream stream,
ArrayView<TRandomProvider> rngView,
System.Random random,
int numInitializers = ushort.MaxValue)
where TRandomProvider : unmanaged, IRandomProvider<TRandomProvider>
{
// Initialize a single provider per warp
numInitializers = (int)Math.Min(XMath.Clamp(
numInitializers,
accelerator.MaxNumThreadsPerGroup,
rngView.Length),
ushort.MaxValue);
var providers = new XorShift128Plus[numInitializers];
for (int i = 0; i < numInitializers; ++i)
providers[i] = default(XorShift128Plus).CreateProvider(random);

// Initialize all providers in our buffer
using var tempProviderBuffer = accelerator.Allocate1D(providers);
accelerator.LaunchAutoGrouped(
InitializeRNGKernel,
stream,
new Index1D(providers.Length),
tempProviderBuffer.View.AsContiguous(),
rngView);
}
}
}
83 changes: 83 additions & 0 deletions Src/ILGPU.Algorithms/Random/ThreadWiseRNG.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// ---------------------------------------------------------------------------------------
// ILGPU Algorithms
// Copyright (c) 2023 ILGPU Project
// www.ilgpu.net
//
// File: ThreadWiseRNG.cs
//
// This file is part of ILGPU and is distributed under the University of Illinois Open
// Source License. See LICENSE.txt for details.
// ---------------------------------------------------------------------------------------

using ILGPU.Runtime;
using ILGPU.Util;
using System;

namespace ILGPU.Algorithms.Random
{
/// <summary>
/// Represents a single RNG instance per thread stored separately in a memory buffer.
/// </summary>
/// <typeparam name="TRandomProvider">The underlying RNG provider type.</typeparam>
[CLSCompliant(false)]
public class ThreadWiseRNG<TRandomProvider> : DisposeBase
where TRandomProvider : unmanaged, IRandomProvider<TRandomProvider>
{
#region Instance

/// <summary>
/// Stores a single RNG instance per thread.
/// </summary>
private readonly MemoryBuffer1D<TRandomProvider, Stride1D.Dense> randomProviders;

/// <summary>
/// Constructs an RNG using the given provider instance.
/// </summary>
/// <param name="accelerator">The current accelerator.</param>
/// <param name="stream">The current accelerator stream.</param>
/// <param name="maxNumThreads">The maximum number of parallel threads.</param>
/// <param name="random">The parent RNG provider.</param>
/// <param name="numInitializers">
/// The maximum number of initializers used on the CPU side.
/// </param>
public ThreadWiseRNG(
Accelerator accelerator,
AcceleratorStream stream,
LongIndex1D maxNumThreads,
System.Random random,
int numInitializers = ushort.MaxValue)
{
if (maxNumThreads < 1L)
throw new ArgumentOutOfRangeException(nameof(maxNumThreads));

// Allocate all random providers
randomProviders = accelerator.Allocate1D<TRandomProvider>(maxNumThreads);
accelerator.InitRNGView(stream, RNGView, random, numInitializers);
}

#endregion

#region Methods

/// <summary>
/// Returns the underlying RNG view.
/// </summary>
public ArrayView<TRandomProvider> RNGView => randomProviders.View;

#endregion

#region IDisposable

/// <summary>
/// Frees the underlying RNG buffers.
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing)
randomProviders.Dispose();
base.Dispose(disposing);
}

#endregion
}
}
Loading
Loading