From 25968c7c996aaca86c1c83afffc011b55fa52ab4 Mon Sep 17 00:00:00 2001 From: Marcel Koester Date: Tue, 29 Aug 2023 11:33:14 +0100 Subject: [PATCH] Random ranges. (#1038) * Added new random ranges to create random numbers in certain intervals. * Added vector-specific random extensions. --- .gitignore | 1 + Src/ILGPU.Algorithms/ILGPU.Algorithms.csproj | 9 + .../Random/RandomExtensions.cs | 86 +++- Src/ILGPU.Algorithms/Random/RandomRanges.tt | 393 ++++++++++++++++++ Src/ILGPU.Algorithms/Random/XorShift128.cs | 2 +- Src/ILGPU.Algorithms/Random/XorShift32.cs | 2 +- 6 files changed, 482 insertions(+), 11 deletions(-) create mode 100644 Src/ILGPU.Algorithms/Random/RandomRanges.tt diff --git a/.gitignore b/.gitignore index 8967e0621..c5680028b 100644 --- a/.gitignore +++ b/.gitignore @@ -291,6 +291,7 @@ Src/ILGPU.Algorithms/IL/ILContext.Generated.cs Src/ILGPU.Algorithms/PTX/PTXContext.Generated.cs Src/ILGPU.Algorithms/RadixSortOperations.cs Src/ILGPU.Algorithms/Vectors/VectorTypes.cs +Src/ILGPU.Algorithms/Random/RandomRanges.cs Src/ILGPU.Algorithms/Runtime/Cuda/API/CuBlasNativeMethods.cs Src/ILGPU.Algorithms/Runtime/Cuda/API/CuFFTAPI.Generated.cs Src/ILGPU.Algorithms/Runtime/Cuda/API/CuFFTNativeMethods.cs diff --git a/Src/ILGPU.Algorithms/ILGPU.Algorithms.csproj b/Src/ILGPU.Algorithms/ILGPU.Algorithms.csproj index 8fece5da9..b89300f85 100644 --- a/Src/ILGPU.Algorithms/ILGPU.Algorithms.csproj +++ b/Src/ILGPU.Algorithms/ILGPU.Algorithms.csproj @@ -114,6 +114,10 @@ TextTemplatingFileGenerator RadixSortOperations.cs + + TextTemplatingFileGenerator + RandomRanges.cs + TextTemplatingFileGenerator CuFFTAPI.Generated.cs @@ -252,6 +256,11 @@ True RadixSortOperations.tt + + True + True + RandomRanges.tt + True True diff --git a/Src/ILGPU.Algorithms/Random/RandomExtensions.cs b/Src/ILGPU.Algorithms/Random/RandomExtensions.cs index 282f73647..9c60c05f4 100644 --- a/Src/ILGPU.Algorithms/Random/RandomExtensions.cs +++ b/Src/ILGPU.Algorithms/Random/RandomExtensions.cs @@ -10,8 +10,10 @@ // --------------------------------------------------------------------------------------- using ILGPU.Runtime; +using ILGPU.Util; using System; using System.Diagnostics; +using System.Numerics; using System.Runtime.CompilerServices; namespace ILGPU.Algorithms.Random @@ -49,7 +51,7 @@ internal static uint MergeULong(ulong nextULong) => /// /// Separates the given unsigned int into an unsigned long. /// - internal static ulong SeperateUInt(uint nextUInt) => + internal static ulong SeparateUInt(uint nextUInt) => ((ulong)nextUInt << 32) | nextUInt; /// @@ -91,8 +93,8 @@ internal static ulong ShiftState(ulong state, int laneShift) /// Generates a random int in [minValue..maxValue). /// /// The random provider. - /// The minimum value (inclusive) - /// The maximum values (exclusive) + /// The minimum value (inclusive). + /// The maximum values (exclusive). /// A random int in [minValue..maxValue). [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Next( @@ -112,8 +114,8 @@ public static float Next( /// Generates a random int in [minValue..maxValue). /// /// The random provider. - /// The minimum value (inclusive) - /// The maximum values (exclusive) + /// The minimum value (inclusive). + /// The maximum values (exclusive). /// A random int in [minValue..maxValue). [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Next( @@ -133,8 +135,8 @@ public static double Next( /// Generates a random int in [minValue..maxValue). /// /// The random provider. - /// The minimum value (inclusive) - /// The maximum values (exclusive) + /// The minimum value (inclusive). + /// The maximum values (exclusive). /// A random int in [minValue..maxValue). [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Next( @@ -154,8 +156,8 @@ public static int Next( /// Generates a random long in [minValue..maxValue). /// /// The random provider. - /// The minimum value (inclusive) - /// The maximum values (exclusive) + /// The minimum value (inclusive). + /// The maximum values (exclusive). /// A random long in [minValue..maxValue). [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long Next( @@ -174,6 +176,72 @@ public static long Next( return Math.Min(intermediate + minValue, maxValue - 1); } +#if NET7_0_OR_GREATER + /// + /// Generates a new random vector containing provided RNG-based values. + /// + /// The vector element type. + /// The RNG provider type. + /// The generic RNG value range. + /// The random provider instance to use. + /// The generic range instance to use. + /// The created random vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe Vector NextVector( + ref TRandomProvider randomProvider, + TRange range) + where T : unmanaged + where TRandomProvider : struct, IRandomProvider + where TRange : struct, IRandomRange + { + int vectorLength = Vector.Count; + int length = Interop.SizeOf() * vectorLength; + + // Allocate temporary buffers + var source = stackalloc byte[length + vectorLength]; + var span = new Span( + (void*)Interop.Align((long)source, length, vectorLength), + vectorLength); + + // Generated random numbers + for (int i = 0; i < vectorLength; ++i) + span[i] = range.Next(ref randomProvider); + + // Load aligned vector + return span.LoadAlignedVectorUnsafe(); + } + + /// + /// Generates a new random vector containing provided RNG-based values. + /// + /// The vector element type. + /// The RNG range provider. + /// The range provider instance to use. + /// The created random vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe Vector NextVector( + ref TRangeProvider rangeProvider) + where T : unmanaged + where TRangeProvider : struct, IRandomRangeProvider + { + int vectorLength = Vector.Count; + int length = Interop.SizeOf() * vectorLength; + + // Allocate temporary buffers + var source = stackalloc byte[length + vectorLength]; + var span = new Span( + (void*)Interop.Align((long)source, length, vectorLength), + vectorLength); + + // Generated random numbers + for (int i = 0; i < vectorLength; ++i) + span[i] = rangeProvider.Next(); + + // Load aligned vector + return span.LoadAlignedVectorUnsafe(); + } +#endif + /// /// Constructs an RNG using the given provider instance. /// diff --git a/Src/ILGPU.Algorithms/Random/RandomRanges.tt b/Src/ILGPU.Algorithms/Random/RandomRanges.tt new file mode 100644 index 000000000..1282190e4 --- /dev/null +++ b/Src/ILGPU.Algorithms/Random/RandomRanges.tt @@ -0,0 +1,393 @@ +// --------------------------------------------------------------------------------------- +// ILGPU Algorithms +// Copyright (c) 2023 ILGPU Project +// www.ilgpu.net +// +// File: RandomRanges.tt/RandomRanges.cs +// +// This file is part of ILGPU and is distributed under the University of Illinois Open +// Source License. See LICENSE.txt for details. +// --------------------------------------------------------------------------------------- + +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ include file="../TypeInformation.ttinclude"#> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ output extension=".cs" #> +<# +var rngTypes = SignedIntTypes.Concat(FloatTypes); +var functionMapping = new Dictionary() + { + { "Int8", "(byte)randomProvider.Next(0, byte.MaxValue)" }, + { "Int16", "(short)randomProvider.Next(0, short.MaxValue)" }, + { "Int32", "randomProvider.Next()" }, + { "Int64", "randomProvider.NextLong()" }, + + { "Half", "(Half)randomProvider.NextFloat()" }, + { "Float", "randomProvider.NextFloat()" }, + { "Double", "randomProvider.NextDouble()" }, + }; +#> +using System; +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using System.Runtime.CompilerServices; + +#pragma warning disable CA1000 // No static members on generic types +#pragma warning disable IDE0004 // Cast is redundant + +#if NET7_0_OR_GREATER + +namespace ILGPU.Algorithms.Random +{ + /// + /// A generic random number range operating on a generic type + /// . + /// + /// The element type to operate on. + public interface IBasicRandomRange + where T : struct + { + /// + /// Returns the min value of this range (inclusive). + /// + T MinValue { get; } + + /// + /// Returns the max value of this range (exclusive). + /// + T MaxValue { get; } + } + + /// + /// A generic random number range operating on a generic type + /// . + /// + /// The element type to operate on. + public interface IRandomRange : IBasicRandomRange + where T : struct + { + /// + /// Generates a new random value by taking min and max value ranges into account. + /// + /// The random provider type. + /// The random provider instance. + /// The retrieved random value. + /// + /// CAUTION: This function implementation is meant to be thread safe in general to + /// support massively parallel evaluations on CPU and GPU. + /// + [SuppressMessage( + "Naming", + "CA1716:Identifiers should not match keywords", + Justification = "Like the method System.Random.Next()")] + T Next(ref TRandomProvider randomProvider) + where TRandomProvider : struct, IRandomProvider; + } + + /// + /// A generic random number range provider operating on a generic type + /// . + /// + /// The element type to operate on. + /// + /// CAUTION: A type implementing this interface is meant to be thread safe in general + /// to support massively parallel evaluations on CPU and GPU. + /// + public interface IRandomRangeProvider + where T : struct + { + /// + /// Generates a new random value by taking min and max value ranges into account. + /// + /// The retrieved random value. + [SuppressMessage( + "Naming", + "CA1716:Identifiers should not match keywords", + Justification = "Like the method System.Random.Next()")] + T Next(); + } + + /// + /// A generic random number range provider operating on a generic type + /// . + /// + /// The type implementing this interface. + /// The element type to operate on. + /// + /// CAUTION: A type implementing this interface is meant to be thread safe in general + /// to support massively parallel evaluations on CPU and GPU. + /// + public interface IRandomRangeProvider : + IRandomRangeProvider, IBasicRandomRange + where TSelf : struct, IRandomRangeProvider + where T : unmanaged + { + /// + /// Instantiates a new random range using the given random provider. + /// + /// The parent RNG instance. + /// The minimum value (inclusive). + /// The maximum value (exclusive). + static abstract TSelf Create(System.Random random, T minValue, T maxValue); + + /// + /// Instantiates a new random range using the given random provider. + /// + /// The parent RNG instance. + /// The minimum value (inclusive). + /// The maximum value (exclusive). + static abstract TSelf Create( + ref TOtherProvider random, + T minValue, + T maxValue) + where TOtherProvider : struct, IRandomProvider; + + /// + /// Creates a new random range vector provider compatible with this provider. + /// + RandomRangeVectorProvider CreateVectorProvider(); + } + + /// + /// Represents a default RNG range for vectors types returning specified value + /// intervals for type Vector. + /// + /// The vector element type. + /// The underlying range provider. + public struct RandomRangeVectorProvider : + IRandomRangeProvider>, + IRandomRangeProvider, + IBasicRandomRange + where T : unmanaged + where TRangeProvider : struct, IRandomRangeProvider + { + private TRangeProvider rangeProvider; + + /// + /// Instantiates a new random range provider using the given random provider. + /// + /// The RNG provider to use. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public RandomRangeVectorProvider(TRangeProvider provider) + { + rangeProvider = provider; + } + + /// + /// Returns the min value of this range (inclusive). + /// + public readonly T MinValue => rangeProvider.MinValue; + + /// + /// Returns the max value of this range (exclusive). + /// + public readonly T MaxValue => rangeProvider.MaxValue; + + /// + /// Generates a new random value using the given min and max values. + /// + [SuppressMessage( + "Naming", + "CA1716:Identifiers should not match keywords", + Justification = "Like the method System.Random.Next()")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector Next() => + RandomExtensions.NextVector(ref rangeProvider); + + /// + /// Generates a new random value using the given min and max values. + /// + [SuppressMessage( + "Naming", + "CA1716:Identifiers should not match keywords", + Justification = "Like the method System.Random.Next()")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + T IRandomRangeProvider.Next() => rangeProvider.Next(); + } + + /// + /// A container class holding specialized random range instances while providing + /// specialized extension methods for different RNG providers. + /// + public static class RandomRanges + { +<# foreach (var type in rngTypes) { #> +<# var providerName = $"RandomRange{type.Name}Provider"; #> + /// + /// Represents a default RNG range for type <#= type.Name #> returning + /// specified value intervals for type <#= type.Name #> (in analogy to calling + /// the appropriate NextXYZ method on the random provider given using min and + /// max values). + /// + /// The minimum value (inclusive). + /// The maximum values (exclusive). + public readonly record struct RandomRange<#= type.Name #>( + <#= type.Type #> MinValue, + <#= type.Type #> MaxValue) : + IRandomRange<<#= type.Type #>> + { + /// + /// Instantiates a new random range provider using the given random provider. + /// + /// The parent RNG instance. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public <#= providerName #> + CreateProvider(System.Random random) + where TRandomProvider : struct, IRandomProvider => + <#= providerName #>.Create( + random, + MinValue, + MaxValue); + + /// + /// Instantiates a new random range provider using the given random provider. + /// + /// The parent RNG instance. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public <#= providerName #> + CreateProvider(ref TRandomProvider random) + where TRandomProvider : struct, IRandomProvider => + <#= providerName #>.Create( + ref random, + MinValue, + MaxValue); + + /// + /// Instantiates a new random range provider using the given random provider. + /// + /// The parent RNG instance. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public <#= providerName #> + CreateProvider( + ref TOtherRandomProvider random) + where TRandomProvider : struct, IRandomProvider + where TOtherRandomProvider : + struct, IRandomProvider => + <#= providerName #>.Create( + ref random, + MinValue, + MaxValue); + + /// + /// Generates a new random value using the given min and max values. + /// + [SuppressMessage( + "Naming", + "CA1716:Identifiers should not match keywords", + Justification = "Like the method System.Random.Next()")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public <#= type.Type #> Next( + ref TRandomProvider randomProvider) + where TRandomProvider : struct, IRandomProvider => + (<#= type.Type #>)RandomExtensions.Next( + ref randomProvider, + MinValue, + MaxValue); + } + + /// + /// Represents a default RNG range for type <#= type.Name #> returning + /// specified value intervals for type <#= type.Name #> (in analogy to calling + /// the appropriate NextXYZ method on the random provider given using min and + /// max values). + /// + /// The underlying random provider. + public struct <#= providerName #> : + IRandomRangeProvider< + <#= providerName #>, + <#= type.Type #>> + where TRandomProvider : struct, IRandomProvider + { + private TRandomProvider randomProvider; + + /// + /// Instantiates a new random range provider using the given random provider. + /// + /// The RNG instance to use. + /// The minimum value (inclusive). + /// The maximum value (exclusive). + public <#= providerName #>( + TRandomProvider random, + <#= type.Type #> minValue, + <#= type.Type #> maxValue) + { + randomProvider = random; + MinValue = minValue; + MaxValue = maxValue; + } + + /// + /// Returns the min value of this range (inclusive). + /// + public <#= type.Type #> MinValue { get; } + + /// + /// Returns the max value of this range (exclusive). + /// + public <#= type.Type #> MaxValue { get; } + + /// + /// Instantiates a new random range provider using the given random provider. + /// + /// The parent RNG instance. + /// The minimum value (inclusive). + /// The maximum value (exclusive). + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static <#= providerName #> + Create( + System.Random random, + <#= type.Type #> minValue, + <#= type.Type #> maxValue) => + new(default(TRandomProvider).CreateProvider(random), minValue, maxValue); + + /// + /// Instantiates a new random range provider using the given random provider. + /// + /// The parent RNG instance. + /// The minimum value (inclusive). + /// The maximum value (exclusive). + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static <#= providerName #> + Create( + ref TOtherProvider random, + <#= type.Type #> minValue, + <#= type.Type #> maxValue) + where TOtherProvider : struct, IRandomProvider => + new( + default(TRandomProvider).CreateProvider(ref random), + minValue, + maxValue); + + /// + /// Creates a new random range vector provider compatible with this provider. + /// + public readonly RandomRangeVectorProvider< + <#= type.Type #>, + <#= providerName #>> CreateVectorProvider() => + new(this); + + /// + /// Generates a new random value using the given min and max values. + /// + [SuppressMessage( + "Naming", + "CA1716:Identifiers should not match keywords", + Justification = "Like the method System.Random.Next()")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public <#= type.Type #> Next() => + (<#= type.Type #>)RandomExtensions.Next( + ref randomProvider, + MinValue, + MaxValue); + } + +<# } #> + } +} + +#endif + +#pragma warning restore IDE0004 +#pragma warning restore CA1000 \ No newline at end of file diff --git a/Src/ILGPU.Algorithms/Random/XorShift128.cs b/Src/ILGPU.Algorithms/Random/XorShift128.cs index b942f603d..ccde7beb3 100644 --- a/Src/ILGPU.Algorithms/Random/XorShift128.cs +++ b/Src/ILGPU.Algorithms/Random/XorShift128.cs @@ -112,7 +112,7 @@ public uint NextUInt() /// Generates a random ulong in [0..ulong.MaxValue]. /// /// A random ulong in [0..ulong.MaxValue]. - public ulong NextULong() => SeperateUInt(NextUInt()); + public ulong NextULong() => SeparateUInt(NextUInt()); /// public int Next() => ToInt(NextUInt()); diff --git a/Src/ILGPU.Algorithms/Random/XorShift32.cs b/Src/ILGPU.Algorithms/Random/XorShift32.cs index adce2c54c..725ea5dd6 100644 --- a/Src/ILGPU.Algorithms/Random/XorShift32.cs +++ b/Src/ILGPU.Algorithms/Random/XorShift32.cs @@ -83,7 +83,7 @@ public uint NextUInt() /// Generates a random ulong in [0..ulong.MaxValue]. /// /// A random ulong in [0..ulong.MaxValue]. - public ulong NextULong() => SeperateUInt(NextUInt()); + public ulong NextULong() => SeparateUInt(NextUInt()); /// public int Next() => ToInt(NextUInt());