We can again (like in related question) look at source code to draw some conclusions (SecureRandom source code for reference).
All work in constructor goes for creating pseudo-random generator:
private static DigestRandomGenerator CreatePrng(string digestName, bool autoSeed)
{
IDigest digest = DigestUtilities.GetDigest(digestName);
if (digest == null)
return null;
DigestRandomGenerator prng = new DigestRandomGenerator(digest);
if (autoSeed)
{
prng.AddSeedMaterial(NextCounterValue());
prng.AddSeedMaterial(GetNextBytes(Master, digest.GetDigestSize()));
}
return prng;
}
Creating digest (hash) costs nothing (relative to other work). For example Sha256Digest
used by default (with empty constructor) just allocates small byte[]
buffer. Creating DigestRandomGenerator
itself also costs nothing (couple small buffers). Major work done is here:
prng.AddSeedMaterial(GetNextBytes(Master, digest.GetDigestSize()));
It uses "master" RNG to generate seed value. Master RNG on full .NET platform is RNGCryptoServiceProvider
(which for SecureRandom
is stored in static field and initialized only once). So all work when creating SecureRandom
goes to creating cryptographically random seed for pseudo RNG.
I'd say, it's better not create new instance every time, at least for small generation (for one-two NextInt()
calls), because if you create new instance for every single generated number - you essentially double the costs (one time to generate crypto random number for seed and one to generate your target random number). Because (as far as I know), SecureRandom
is thread safe - there is not much reason to not reuse one instance.
Side note - I don't think RNGCryptoServiceProvider
is heavy to create as your link claims. Its constructor goes like this:
public RNGCryptoServiceProvider()
: this((CspParameters) null)
{
}
[SecuritySafeCritical]
public RNGCryptoServiceProvider(CspParameters cspParams)
{
if (cspParams != null)
{
this.m_safeProvHandle = Utils.AcquireProvHandle(cspParams);
this.m_ownsHandle = true;
}
else
{
// we are interested in this path
this.m_safeProvHandle = Utils.StaticProvHandle;
this.m_ownsHandle = false;
}
}
So when you create new instance (without providing csp) - it reuses the same Utils.StaticProvHandle
, so it uses the same "unmanaged" instance of RNG provider. Which in turn means creating new instance and reusing the same instance have no difference in performance. Maybe in previous versions of .NET it was not like this, not sure.