private void TestXorShiftBurst() { XorshiftBurst xor = new XorshiftBurst(5132512, 3292391, 109854, 587295); for (int i = 0; i < 128; i++) { Debug.Log(xor.NextInt(0, 128)); Debug.Log(xor._seed0 + ", " + xor._seed1 + ", " + xor._seed2 + ", " + xor._seed3); } }
private void ProfileRNGs() { // Time it takes for many RNG implementations to fill a buffer with random 32 bit floats with distribution [0,1) // For each RNG I list two times measures on my machine: editor with safetychecks, build without safetychecks // If burst job is used, it is single-threaded. Obviously, using multiple threads with multiple RNGs is faster. var values = new NativeArray <float>(numVals, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); // 220ms, 55ms var rs = new System.Random(1234); var sw = System.Diagnostics.Stopwatch.StartNew(); for (int i = 0; i < values.Length; i++) { values[i] = (float)rs.NextDouble(); } sw.Stop(); Debug.Log("System.Random: " + sw.ElapsedMilliseconds); // 171ms, 40ms sw = System.Diagnostics.Stopwatch.StartNew(); for (int i = 0; i < values.Length; i++) { values[i] = UnityEngine.Random.value; } sw.Stop(); Debug.Log("Unity.Random: " + sw.ElapsedMilliseconds); // 245ms, 84ms sw = System.Diagnostics.Stopwatch.StartNew(); var uMathRng = new Unity.Mathematics.Random(1234); for (int i = 0; i < values.Length; i++) { values[i] = uMathRng.NextFloat(); } sw.Stop(); Debug.Log("Unity.Mathematics.Random: " + sw.ElapsedMilliseconds); // 248ms, 10ms sw = System.Diagnostics.Stopwatch.StartNew(); var umrj = new UMathRngJob(); umrj.Random = uMathRng; umrj.Values = values; umrj.Schedule().Complete(); sw.Stop(); Debug.Log("Unity.Mathematics.Random BurstJob: " + sw.ElapsedMilliseconds); // 226ms, 71ms var rm = new Meisui.Random.MersenneTwister(1234); sw = System.Diagnostics.Stopwatch.StartNew(); for (int i = 0; i < values.Length; i++) { values[i] = (float)rm.genrand_real2(); } sw.Stop(); Debug.Log("MersenneTwister Managed: " + sw.ElapsedMilliseconds); // 351ms, 91ms var rrm = new Ramjet.MersenneTwister(1234); sw = System.Diagnostics.Stopwatch.StartNew(); for (int i = 0; i < values.Length; i++) { values[i] = rrm.genrand_real2(); } sw.Stop(); Debug.Log("MersenneTwister Burst: " + sw.ElapsedMilliseconds); // 359ms, 15ms sw = System.Diagnostics.Stopwatch.StartNew(); var mtj = new MTJob(); mtj.Values = values; mtj.Random = rrm; mtj.Schedule().Complete(); sw.Stop(); rrm.Dispose(); Debug.Log("MersenneTwister BurstJob: " + sw.ElapsedMilliseconds); // 162ms, 76 var xor = new Xorshift(1234); sw = System.Diagnostics.Stopwatch.StartNew(); for (int i = 0; i < values.Length; i++) { values[i] = xor.NextFloat(); } sw.Stop(); Debug.Log("XORShift Managed: " + sw.ElapsedMilliseconds); // 1107, 21ms sw = System.Diagnostics.Stopwatch.StartNew(); var xorb = new XorshiftBurst(1234); var xorj = new XorShiftJob(); xorj.Values = values; xorj.Random = xorb; xorj.Schedule().Complete(); sw.Stop(); Debug.Log("XORShift BurstJob: " + sw.ElapsedMilliseconds); // Find min and max values in buffer, and turn it into a texture for visual inspection // Todo: do this for all RNGs, not just for the last one to generate values. // float min = 2f; // float max = -1f; // _tex = new Texture2D(res, res, TextureFormat.ARGB32, false, true); // var data = new Color[res * res]; // for (int i = 0; i < numVals; i++) { // float val = values[i]; // if (val > max) { // max = val; // } // if (val < min) { // min = val; // } // data[i] = new Color(val, val, val, 1f); // } // Debug.Log(min + ", " + max); // _tex.SetPixels(0, 0, res, res, data, 0); // _tex.Apply(); values.Dispose(); }