I decided to ask the Java compiler. Short answer is that, yes, re-using the SecureRandom
object has some performance benefits but is no bettor or worse wrt actual randomness. This is purely a tuning issue. Not a security issue.
Note, however, it takes a little while for the JIT to kick in so you see the benefits. The take-away is that for heavy/frequent use, definitely re-use the object. For infrequent use, you might be better off use a new object every time.
Results
warm up
-----------------------------
default seed - re-use - 1807 ms
explicit seed - re-use - 835 ms
constant seed - new every time - 1044 ms
default seed - new every time - 1621 ms
-----------------------------
interation 0
-----------------------------
default seed - re-use - 412 ms
explicit seed - re-use - 418 ms
constant seed - new every time - 955 ms
default seed - new every time - 1676 ms
-----------------------------
interation 1
-----------------------------
default seed - re-use - 389 ms
explicit seed - re-use - 369 ms
constant seed - new every time - 893 ms
default seed - new every time - 1498 ms
Source
package foo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.security.SecureRandom;
import java.util.HashSet;
import java.util.Set;
import org.junit.BeforeClass;
import org.junit.Test;
public class SecureRandomTest {
static long elapsedMillis( long startNs ) {
long now = System.nanoTime();
return (now - startNs) / 1_000_000;
}
final static long seed = 123456789123456L;
final static int nIter = 1000000;
public static void main(String[] args) {
warmup();
SecureRandomTest test = new SecureRandomTest();
for ( int ix = 0; ix < 5; ++ix ) {
test.run(ix);
}
}
void run(int ix) {
System.out.printf( "interation %d\n-----------------------------\n", ix);
secure_random_default_seed_reuse();
secure_random_constant_seed_reuse();
secure_random_constant_seed();
secure_random_default_seed();
System.out.println("-----------------------------");
}
/* Warm up JVM/JIT */
@BeforeClass
public static void warmup() {
new SecureRandomTest().run(-1);
}
@Test
public void secure_random_constant_seed() {
long started = System.nanoTime();
int nDupes = 0, ix = 0;
Set<Long> generated = new HashSet<>(nIter);
for ( /**/; ix < nIter; ++ix) {
SecureRandom rand = new SecureRandom();
rand.setSeed(seed);
long xRand = rand.nextLong();
if ( !generated.add(xRand) ) {
++nDupes;
}
}
assertEquals( "Unexpected # of dupes " + nDupes + ", ix == " + ix, nIter-1, nDupes );
System.out.printf( "constant seed - new every time - %d ms\n", elapsedMillis(started) );
}
@Test
public void secure_random_constant_seed_reuse() {
long started = System.nanoTime();
int nDupes = 0, ix = 0;
SecureRandom rand = new SecureRandom();
rand.setSeed(seed);
Set<Long> generated = new HashSet<>(nIter);
for ( /**/; ix < nIter; ++ix) {
long xRand = rand.nextLong();
if ( !generated.add(xRand) ) {
++nDupes;
}
}
assertTrue( "Unexpected # of dupes " + nDupes + ", ix == " + ix, 0 == nDupes );
System.out.printf( "explicit seed - re-use - %d ms\n", elapsedMillis(started) );
}
@Test
public void secure_random_default_seed() {
long started = System.nanoTime();
int nDupes = 0, ix = 0;
Set<Long> generated = new HashSet<>(nIter);
for ( /**/; ix < nIter; ++ix) {
long xRand = new SecureRandom().nextLong();
if ( !generated.add(xRand) ) {
++nDupes;
}
}
assertTrue( "Unexpected # of dupes " + nDupes + ", ix == " + ix, 0 == nDupes );
System.out.printf( "default seed - new every time - %d ms\n", elapsedMillis(started) );
}
@Test
public void secure_random_default_seed_reuse() {
long started = System.nanoTime();
int nDupes = 0, ix = 0;
SecureRandom rand = new SecureRandom();
Set<Long> generated = new HashSet<>(nIter);
for ( /**/; ix < nIter; ++ix) {
long xRand = rand.nextLong();
if ( !generated.add(xRand) ) {
++nDupes;
}
}
assertTrue( "Unexpected # of dupes " + nDupes + ", ix == " + ix, 0 == nDupes );
System.out.printf( "default seed - re-use - %d ms\n", elapsedMillis(started) );
}
}