From 19bd782bdf7ab45389463c2da83a094b3ed4d357 Mon Sep 17 00:00:00 2001 From: maxisoft Date: Sun, 19 Nov 2023 11:46:10 +0100 Subject: [PATCH] Optimize RandomUtils to use Math and MemoryMarshal.Cast This commit modifies the GaussianRandom class to use Math instead of MathF and MemoryMarshal.Cast instead of BitConverter. This allows the plugin to be compatible with trimmed ASF binaries that do not include those methods. This also improves the performance by reducing the number of calls to Fill(bytes) from 2 to 1. This fixes the issue #46 (https://github.com/maxisoft/ASFFreeGames/issues/46) that was reported. --- ASFFreeGames/RandomUtils.cs | 52 ++++++++----------------------------- 1 file changed, 11 insertions(+), 41 deletions(-) diff --git a/ASFFreeGames/RandomUtils.cs b/ASFFreeGames/RandomUtils.cs index e9ef9ae..de69199 100644 --- a/ASFFreeGames/RandomUtils.cs +++ b/ASFFreeGames/RandomUtils.cs @@ -1,6 +1,8 @@ using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Security.Cryptography; namespace Maxisoft.ASF; @@ -8,35 +10,6 @@ namespace Maxisoft.ASF; #nullable enable public static class RandomUtils { - /// - /// Generates a random number from a normal distribution with the specified mean and standard deviation. - /// - /// The random number generator to use. - /// The mean of the normal distribution. - /// The standard deviation of the normal distribution. - /// A random number from the normal distribution. - /// - /// This method uses the Box-Muller transform to convert two uniformly distributed random numbers into two normally distributed random numbers. - /// - public static double NextGaussian([NotNull] this RandomNumberGenerator random, double mean, double standardDeviation) { - Debug.Assert(random != null, nameof(random) + " != null"); - - // Generate two uniform random numbers - Span bytes = stackalloc byte[8]; - random.GetBytes(bytes); - double u1 = BitConverter.ToUInt32(bytes) / (double) uint.MaxValue; - random.GetBytes(bytes); - double u2 = BitConverter.ToUInt32(bytes) / (double) uint.MaxValue; - - // Apply the Box-Muller formula - double randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); - - // Scale and shift to get a random number with the desired mean and standard deviation - double randNormal = mean + (standardDeviation * randStdNormal); - - return randNormal; - } - internal sealed class GaussianRandom : RandomNumberGenerator { // A flag to indicate if there is a stored value for the next Gaussian number private bool HasNextGaussian; @@ -55,23 +28,20 @@ private double NextDouble() { return NextGaussianValue; } - // Generate two uniform random numbers - Span bytes = stackalloc byte[8]; - GetBytes(bytes); - float u1 = BitConverter.ToUInt32(bytes) / (float) uint.MaxValue; - GetBytes(bytes); - float u2 = BitConverter.ToUInt32(bytes) / (float) uint.MaxValue; + Span bytes = stackalloc byte[16]; + Fill(bytes); + Span ulongs = MemoryMarshal.Cast(bytes); + double u1 = ulongs[0] / (double) ulong.MaxValue; + double u2 = ulongs[1] / (double) ulong.MaxValue; // Apply the Box-Muller formula - float r = MathF.Sqrt(-2.0f * MathF.Log(u1)); - float theta = 2.0f * MathF.PI * u2; + double r = Math.Sqrt(-2.0f * Math.Log(u1)); + double theta = 2.0 * Math.PI * u2; - // Store one of the values for next time - NextGaussianValue = r * MathF.Sin(theta); + NextGaussianValue = r * Math.Sin(theta); HasNextGaussian = true; - // Return the other value - return r * MathF.Cos(theta); + return r * Math.Cos(theta); } ///