Get-Random vs System.Random

In the process of learning PowerShell it is customary to hear people say that you shouldn’t re-invent the wheel; if there is a native command for it, you should use that. And I agree. But there is a case to be made for taking the time to properly explore all the different options available to you. For instance when coding for a specific version of PowerShell, or if speed is important. This post will discuss the latter; specifically a comparison of the native command for generating random numbers (Get-Random) and using the .NET class System.Random.

UPDATE! Dave Wyatt commented, correctly, that I was being unfair against Get-Random by using the pipeline. And he is absolutely right! But don’t just take his word for it, check out the updated table – it makes all the difference in the world. This just shows that using the pipeline incorrectly can seriously slow down your script.

This is the script I wrote for the comparisons:

$iterations = 1
$numbers = 10
$method = 'Get-Random'
#$method = 'System.Random'
$random = New-Object -TypeName System.Random

Write-Host "Generating $numbers random number(s) in $iterations iteration(s)."

for ($i = 1; $i -le $iterations; $i++) {
    Measure-Command {
        for ($n = 1; $n -le $numbers; $n++) {
            if ($method -eq 'Get-Random') {
                1..10000 | Get-Random
            }
            elseif ($method -eq 'System.Random') {
                $random.Next(10000)
            }
            else {
                Get-Random -Maximum 10000
            }
        }
    }
}

I ran the code generating 1, 10, 100 and 1000 random numbers for each of the methods (Get-Random and System.Random). I initially wanted to do several iterations and averaging out the results; hence the outer for-loop, but later decided against it, since the numbers where quite clear without needing to take more readings.

Results

Numbers Method Time
1 Get-Random 98 milliseconds
1 System.Random 0.4 milliseconds
1 Get-Random (Optimized) 1 milliseconds
10 Get-Random 950 milliseconds
10 System.Random 0.5 milliseconds
10 Get-Random (Optimized) 1.1 milliseconds
100 Get-Random 9.4 seconds
100 System.Random 0.9 milliseconds
100 Get-Random (Optimized) 4.7 milliseconds
1000 Get-Random 1.3 minutes
1000 System.Random 10.7 milliseconds
1000 Get-Random (Optimized) 55 milliseconds

Conclusion

As you see, when the numbers go up, the differences between the two methods differ dramatically in favour of using .NET directly. So what can we learn from this? Optimization is important, and taking the extra time to investigate other ways of doing things will usually pay off. If nothing else, you have learned something new. If you just want to generate 1 random number, I personally wouldn’t bother using anything besides the built in Get-Random, but dealing with a great deal of numbers, in this case, using .NET is preferable – at least if you care about speed.

Note that neither of the methods shown here will give you cryptographically secure random numbers. If you need that, check out the RNGCryptoServiceProvider class.

4 comments

  1. While a .NET object will tend to be faster than a PowerShell cmdlet, you’re being a bit unfair to Get-Random here by using the pipeline approach of “1..10000 | Get-Random”. The cmdlet would be much faster (though probably still not quite as fast as dealing directly with the object) if you just used “Get-Random -Maximum 10000”.

    Like

    1. Hey Dave. Thanks for you comment. And it’s of course a fair point. I will do some additional testing and add it in. Using the pipeline correctly is in itself a good optimization tip 🙂 So thank you!

      Like

  2. Dear friends, I do not know everything but I am in the dark testing doing rather than making things abstract in my pretty little head.
    Although system.random is going in to .net i prefer get-random to be quiet honest with you. I find system.random very un-wcreative. Check it out for yourselves because I don’t know how to upload a print screen on this fancy blog:
    Compare the results from
    $a=1 ; do { $a++ ; Get-Random -Minimum 1 -Maximum 4 } while ($a -le 60)
    to this
    $a= 1 ; do { $a++ ; (New-Object System.Random).Next(1,4)} while ($a -le 60)
    The difference is staggering … you want randomness ? get-random is random … system.random repeats itself quiet often.

    I bow to thee

    Like

    1. Hey Victor
      I know this is 5 years later but I came across this thread only today.
      In your example you’re calling System.Random in-line which behaves differently from establishing an object outside the loop.
      I’m not 100% how this works because I’m just diving into it myself; however, I’m pretty sure the behavior you’re seeing is due to the seed generation that occurs.
      You’re spawning a new random object in every iteration which (I believe) use time stamps to generate their random returns, and since the loop is going so fast, that timestamp doesn’t change which is why you’re seeing the same numbers multiple times in a row.
      If you generate one object and call it inside your loop you see the expected results like so:
      $random = New-Object System.Random
      $a= 1 ; do { $a++ ; $random.Next(1,4)} while ($a -le 60)

      This is also the recommended method as shown here in the Microsoft docs:https://docs.microsoft.com/en-us/dotnet/api/system.random?view=net-6.0#avoiding-multiple-instantiations

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s