Instance per Invocation

Published: | Updated:

This is a series on common ways that System.Random is misused, based on my experience reviewing code test submissions from job candidates.

Another basic error in using System.Random is to use a new instance of the class for each call to .Next.

Seven of the submissions did something equivalent to:

var value = new Random().Next(0, 10);

This avoids any issues with thread-safety, or lack thereof (more on that later), in System.Random, but there are a couple of reasons why this is bad.

First of all, System.Random, is a Psuedo-Random Number Generator, or PRNG. It generates “random” numbers using a deterministic algorithm. The number sequence appears to be random, following a uniform distribution, but is actually generated from a kind of algorithmic state machine. The constructor does a bunch of work to set up the state machine, and then each call to Next() gets a value and increments the state of the machine.

Unnecessary Expense

So we have two basic problems. One is that constructing a new instance of System.Random is relatively expensive. The following code is a basic benchmark comparing the cost of generating a million random numbers from a single instance vs. creating a new instance for each call to Next().

var sw = new Stopwatch();
var iterations = 1_000_000;

var rnd1 = new Random();
sw.Start();
for (int i = 0; i < iterations; i++)
{
	rnd1.Next(0, 10);
}
sw.Stop();
var singleInstanceDuration = sw.ElapsedMilliseconds;

sw.Restart();
for (int i = 0; i < iterations; i++)
{
	var rnd2 = new Random();
	rnd2.Next(0, 10);
}
sw.Stop();
var instancePerNextDuration = sw.ElapsedMilliseconds;

Console.WriteLine(instancePerNextDuration / singleInstanceDuration);

On my machine, running this code in LINQPad, the code consistently outputs a number between 20 and 30. That is, it’s 20-30 times more expensive to create a new instance of System.Random for each call to Next(), than to use a single instance for all calls. And that’s not even considering the GC pressure caused by all those extra objects.

Pseudo-Non-Random Numbers

But that’s not the only problem. Creating a new instance of System.Random creates a new state machine for generating pseudo-random numbers. Initialising a new state machine for each number generated compromises the randomness of the values.

And from our previous discussion of the way that seeding works, it should be clear that, if you’re requesting multiple random numbers in a short period of time, say rolling 5 dice for a game of Yahtzee, then there’s a good change that you’ll end up with a run of the same number.

Conclusion

So, don’t create an instance of System.Random for each call to Next(). Create a single instance once, then use it to generate all the random numbers you need. Unless you’re generating random numbers in multiple threads. But that’s the topic for next time.