Random.cs 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. // Copyright (c) .NET Foundation. All rights reserved.
  2. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  3. using System;
  4. using System.Runtime.CompilerServices;
  5. using System.Runtime.InteropServices;
  6. using System.Threading;
  7. namespace PlatformBenchmarks
  8. {
  9. public class ConcurrentRandom
  10. {
  11. private static int nextSeed = 0;
  12. // Random isn't thread safe
  13. [ThreadStatic]
  14. private static Random _random;
  15. private static Random Random => _random ?? CreateRandom();
  16. [MethodImpl(MethodImplOptions.NoInlining)]
  17. private static Random CreateRandom()
  18. {
  19. _random = new Random(Interlocked.Increment(ref nextSeed));
  20. return _random;
  21. }
  22. public int Next(int fromInclusive, int toExclusive)
  23. {
  24. // The total possible range is [0, 4,294,967,295).
  25. // Subtract one to account for zero being an actual possibility.
  26. uint range = (uint)toExclusive - (uint)fromInclusive - 1;
  27. // If there is only one possible choice, nothing random will actually happen, so return
  28. // the only possibility.
  29. if (range == 0)
  30. {
  31. return fromInclusive;
  32. }
  33. // Create a mask for the bits that we care about for the range. The other bits will be
  34. // masked away.
  35. uint mask = range;
  36. mask |= mask >> 1;
  37. mask |= mask >> 2;
  38. mask |= mask >> 4;
  39. mask |= mask >> 8;
  40. mask |= mask >> 16;
  41. Span<uint> resultSpan = stackalloc uint[1];
  42. uint result;
  43. do
  44. {
  45. Random.NextBytes(MemoryMarshal.AsBytes(resultSpan));
  46. result = mask & resultSpan[0];
  47. }
  48. while (result > range);
  49. return (int)result + fromInclusive;
  50. }
  51. }
  52. }