NukkitRandom vs ThreadLocalRandom, which one is faster and safer?

It’s a known fact that java.util.Random is slower than java.util.ThreadLocalRandom because the first is a synchronized and thread-safe class while the other takes advantage of java.lang.ThreadLocal to cache an instance for each thread that needs it and generate random data without the need to be synchronized. But what about NukkitRandom?

NukkitRandom already existed when I started working with bedrock edition but I had no clue of its benefits and disadvantages. But now I finally did a proper comparison between them by putting them under stress using JMH and JUnit.

Are them really random?

Both implementations are seed-based but ThreadLocalRandom makes sure that two random generators will never get the same seed in the same JVM instance, the downside is that we can’t set a custom seed, if we need that feature then we would have to use the regular java.util.Random which is slower.

NukkitRandom in the other hands, allows us to set a custom seed but does not make any seed protection, the default constructor creates a new instance based on the clock seconds (not even milliseconds), so if you create 2000 instances in the same seconds, they all will have the same seed and will generate the same numbers.

This makes NukkitRandom a bad choice for things that needs something to be random and will be called frequently where the seed isn’t important and makes ThreadLocalRandom inviable when we need to use a set seed.

Which one are faster?

That is the main question in this thread. To measure the speed, I used JMH to test the true performance of their operation. I picked two common used cases when we need to generate drops:

  1. Reusing the random generator to generate a lot of ints in a range from 2 to 8
  2. Acquiring a random generator to do the same as above

The cases may look similar but the cost of the generator acquisition is relevant when we don’t need a seed, like when we are randomizing drops or particle movements.

Reusing the object

In this case, we create the generator one time and reuse it multiple times to generate random data.

To make sure NukkitRandom is generating real random data, I set the seed during the setup phase with the help of ThreadLocalRandom, but the performance was compared between:

nukkitRandom.nextRange(2, 8)

and:

threadLocalRandom.nextInt(2, 9)

Those methods are semantically equivalent with the only difference that the second argument in NukkitRandom is inclusive and in ThreadLocalRandom is exclusive, that’s why you see an 8 and 9 there to generate numbers from 2 (inclusive) to 8 (inclusive)

See the test source code
@State(Scope.Thread)
public class NukkitRandomVsThreadLocalRandom {
    private NukkitRandom nukkitRandom;
    private ThreadLocalRandom threadLocalRandom;
    
    @Setup
    public void setup() {
        nukkitRandom = new NukkitRandom(ThreadLocalRandom.current().nextLong());
        threadLocalRandom = ThreadLocalRandom.current();
    }
    
    @Benchmark
    public void range2to8NukkitRandom() {
        nukkitRandom.nextRange(2, 8);
    }

    @Benchmark
    public void range2to8ThreadLocalRandom() {
        threadLocalRandom.nextInt(2, 9);
    }
}

And the results are:

Benchmark                                                    Mode  Cnt          Score         Error  Units
NukkitRandomVsThreadLocalRandom.range2to8NukkitRandom       thrpt   25  296408341,250 ± 3800873,315  ops/s
NukkitRandomVsThreadLocalRandom.range2to8ThreadLocalRandom  thrpt   25  276838017,832 ± 8827005,730  ops/s
See the full test output
"C:\Program Files\Java\jdk1.8.0_241\bin\java.exe" -Dfile.encoding=UTF-8 -classpath C:\Users\joserobjr\Projects\PowerNukkit-Benchmark\powernukkit-benchmark\target\classes;C:\Users\joserobjr\Projects\PowerNukkit\target\classes;C:\Users\joserobjr\.m2\repository\org\powernukkit\bedrock\network\raknet\1.6.15-PN2\raknet-1.6.15-PN2.jar;C:\Users\joserobjr\.m2\repository\org\powernukkit\bedrock\network\bedrock-network-common\1.6.15-PN\bedrock-network-common-1.6.15-PN.jar;C:\Users\joserobjr\.m2\repository\io\netty\netty-buffer\4.1.38.Final\netty-buffer-4.1.38.Final.jar;C:\Users\joserobjr\.m2\repository\io\netty\netty-transport-native-epoll\4.1.38.Final\netty-transport-native-epoll-4.1.38.Final-linux-x86_64.jar;C:\Users\joserobjr\.m2\repository\io\netty\netty-transport-native-unix-common\4.1.38.Final\netty-transport-native-unix-common-4.1.38.Final.jar;C:\Users\joserobjr\.m2\repository\io\netty\netty-transport-native-kqueue\4.1.38.Final\netty-transport-native-kqueue-4.1.38.Final-osx-x86_64.jar;C:\Users\joserobjr\.m2\repository\io\netty\netty-handler\4.1.38.Final\netty-handler-4.1.38.Final.jar;C:\Users\joserobjr\.m2\repository\io\netty\netty-common\4.1.38.Final\netty-common-4.1.38.Final.jar;C:\Users\joserobjr\.m2\repository\io\netty\netty-transport\4.1.38.Final\netty-transport-4.1.38.Final.jar;C:\Users\joserobjr\.m2\repository\io\netty\netty-resolver\4.1.38.Final\netty-resolver-4.1.38.Final.jar;C:\Users\joserobjr\.m2\repository\io\netty\netty-codec\4.1.38.Final\netty-codec-4.1.38.Final.jar;C:\Users\joserobjr\.m2\repository\net\sf\trove4j\experimental\3.1.0\experimental-3.1.0.jar;C:\Users\joserobjr\.m2\repository\net\sf\trove4j\core\3.1.0\core-3.1.0.jar;C:\Users\joserobjr\.m2\repository\org\powernukkit\fastutil\fastutil-lite\8.1.1\fastutil-lite-8.1.1.jar;C:\Users\joserobjr\.m2\repository\com\google\guava\guava\24.1.1-jre\guava-24.1.1-jre.jar;C:\Users\joserobjr\.m2\repository\org\checkerframework\checker-compat-qual\2.0.0\checker-compat-qual-2.0.0.jar;C:\Users\joserobjr\.m2\repository\com\google\errorprone\error_prone_annotations\2.1.3\error_prone_annotations-2.1.3.jar;C:\Users\joserobjr\.m2\repository\com\google\j2objc\j2objc-annotations\1.1\j2objc-annotations-1.1.jar;C:\Users\joserobjr\.m2\repository\org\codehaus\mojo\animal-sniffer-annotations\1.14\animal-sniffer-annotations-1.14.jar;C:\Users\joserobjr\.m2\repository\com\google\code\gson\gson\2.4\gson-2.4.jar;C:\Users\joserobjr\.m2\repository\org\yaml\snakeyaml\1.16\snakeyaml-1.16.jar;C:\Users\joserobjr\.m2\repository\org\powernukkit\bedrock\leveldb\bedrock-leveldb\0.11.0-PN\bedrock-leveldb-0.11.0-PN.jar;C:\Users\joserobjr\.m2\repository\org\powernukkit\bedrock\leveldb\bedrock-leveldb-api\0.11.0-PN\bedrock-leveldb-api-0.11.0-PN.jar;C:\Users\joserobjr\.m2\repository\com\nimbusds\nimbus-jose-jwt\7.9\nimbus-jose-jwt-7.9.jar;C:\Users\joserobjr\.m2\repository\com\github\stephenc\jcip\jcip-annotations\1.0-1\jcip-annotations-1.0-1.jar;C:\Users\joserobjr\.m2\repository\net\minidev\json-smart\2.3\json-smart-2.3.jar;C:\Users\joserobjr\.m2\repository\net\minidev\accessors-smart\1.2\accessors-smart-1.2.jar;C:\Users\joserobjr\.m2\repository\org\ow2\asm\asm\5.0.4\asm-5.0.4.jar;C:\Users\joserobjr\.m2\repository\org\apache\logging\log4j\log4j-api\2.13.3\log4j-api-2.13.3.jar;C:\Users\joserobjr\.m2\repository\org\apache\logging\log4j\log4j-core\2.13.3\log4j-core-2.13.3.jar;C:\Users\joserobjr\.m2\repository\net\sf\jopt-simple\jopt-simple\5.0.4\jopt-simple-5.0.4.jar;C:\Users\joserobjr\.m2\repository\net\minecrell\terminalconsoleappender\1.1.1\terminalconsoleappender-1.1.1.jar;C:\Users\joserobjr\.m2\repository\org\jline\jline-terminal\3.9.0\jline-terminal-3.9.0.jar;C:\Users\joserobjr\.m2\repository\org\jline\jline-terminal-jna\3.9.0\jline-terminal-jna-3.9.0.jar;C:\Users\joserobjr\.m2\repository\net\java\dev\jna\jna\4.2.2\jna-4.2.2.jar;C:\Users\joserobjr\.m2\repository\org\jline\jline-reader\3.9.0\jline-reader-3.9.0.jar;C:\Users\joserobjr\.m2\repository\com\google\code\findbugs\jsr305\3.0.2\jsr305-3.0.2.jar;C:\Users\joserobjr\.m2\repository\org\openjdk\jmh\jmh-core\1.25.1\jmh-core-1.25.1.jar;C:\Users\joserobjr\.m2\repository\org\apache\commons\commons-math3\3.2\commons-math3-3.2.jar org.openjdk.jmh.Main org.powernukkit.benchmark.math.NukkitRandomVsThreadLocalRandom.*
# JMH version: 1.25.1
# VM version: JDK 1.8.0_241, Java HotSpot(TM) 64-Bit Server VM, 25.241-b07
# VM invoker: C:\Program Files\Java\jdk1.8.0_241\jre\bin\java.exe
# VM options: -Dfile.encoding=UTF-8
# Warmup: 5 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.powernukkit.benchmark.math.NukkitRandomVsThreadLocalRandom.range2to8NukkitRandom

# Run progress: 0,00% complete, ETA 00:16:40
# Fork: 1 of 5
# Warmup Iteration   1: 289500415,551 ops/s
# Warmup Iteration   2: 291828484,741 ops/s
# Warmup Iteration   3: 289838362,583 ops/s
# Warmup Iteration   4: 292057271,654 ops/s
# Warmup Iteration   5: 299795786,796 ops/s
Iteration   1: 294283861,422 ops/s
Iteration   2: 295551424,266 ops/s
Iteration   3: 299226484,078 ops/s
Iteration   4: 297834191,269 ops/s
Iteration   5: 292892531,854 ops/s

# Run progress: 10,00% complete, ETA 00:15:15
# Fork: 2 of 5
# Warmup Iteration   1: 298604197,260 ops/s
# Warmup Iteration   2: 300040047,426 ops/s
# Warmup Iteration   3: 300768155,387 ops/s
# Warmup Iteration   4: 298479384,523 ops/s
# Warmup Iteration   5: 299820548,328 ops/s
Iteration   1: 298708505,290 ops/s
Iteration   2: 295239968,563 ops/s
Iteration   3: 301580252,465 ops/s
Iteration   4: 292815608,417 ops/s
Iteration   5: 282473565,629 ops/s

# Run progress: 20,00% complete, ETA 00:13:31
# Fork: 3 of 5
# Warmup Iteration   1: 277636388,871 ops/s
# Warmup Iteration   2: 282469759,202 ops/s
# Warmup Iteration   3: 292958151,158 ops/s
# Warmup Iteration   4: 291862364,720 ops/s
# Warmup Iteration   5: 297655987,108 ops/s
Iteration   1: 298213139,889 ops/s
Iteration   2: 300670742,803 ops/s
Iteration   3: 295853845,595 ops/s
Iteration   4: 301049300,267 ops/s
Iteration   5: 299771887,732 ops/s

# Run progress: 30,00% complete, ETA 00:11:49
# Fork: 4 of 5
# Warmup Iteration   1: 299536804,877 ops/s
# Warmup Iteration   2: 283388678,084 ops/s
# Warmup Iteration   3: 291190217,226 ops/s
# Warmup Iteration   4: 297150003,994 ops/s
# Warmup Iteration   5: 299205416,561 ops/s
Iteration   1: 302035080,231 ops/s
Iteration   2: 301565669,728 ops/s
Iteration   3: 296373691,406 ops/s
Iteration   4: 291155700,330 ops/s
Iteration   5: 299428172,725 ops/s

# Run progress: 40,00% complete, ETA 00:10:07
# Fork: 5 of 5
# Warmup Iteration   1: 299164529,190 ops/s
# Warmup Iteration   2: 301887052,177 ops/s
# Warmup Iteration   3: 297760708,030 ops/s
# Warmup Iteration   4: 299178908,380 ops/s
# Warmup Iteration   5: 297359192,612 ops/s
Iteration   1: 296202438,291 ops/s
Iteration   2: 298589881,995 ops/s
Iteration   3: 285957650,923 ops/s
Iteration   4: 289800280,705 ops/s
Iteration   5: 302934655,367 ops/s


Result "org.powernukkit.benchmark.math.NukkitRandomVsThreadLocalRandom.range2to8NukkitRandom":
  296408341,250 ±(99.9%) 3800873,315 ops/s [Average]
  (min, avg, max) = (282473565,629, 296408341,250, 302934655,367), stdev = 5074057,131
  CI (99.9%): [292607467,935, 300209214,565] (assumes normal distribution)


# JMH version: 1.25.1
# VM version: JDK 1.8.0_241, Java HotSpot(TM) 64-Bit Server VM, 25.241-b07
# VM invoker: C:\Program Files\Java\jdk1.8.0_241\jre\bin\java.exe
# VM options: -Dfile.encoding=UTF-8
# Warmup: 5 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.powernukkit.benchmark.math.NukkitRandomVsThreadLocalRandom.range2to8ThreadLocalRandom

# Run progress: 50,00% complete, ETA 00:08:26
# Fork: 1 of 5
# Warmup Iteration   1: 306709570,741 ops/s
# Warmup Iteration   2: 321789448,964 ops/s
# Warmup Iteration   3: 284062998,785 ops/s
# Warmup Iteration   4: 279697507,348 ops/s
# Warmup Iteration   5: 285231305,783 ops/s
Iteration   1: 279413295,423 ops/s
Iteration   2: 286626160,709 ops/s
Iteration   3: 275469319,132 ops/s
Iteration   4: 272361371,943 ops/s
Iteration   5: 266454009,022 ops/s

# Run progress: 60,00% complete, ETA 00:06:45
# Fork: 2 of 5
# Warmup Iteration   1: 270110978,037 ops/s
# Warmup Iteration   2: 321894534,149 ops/s
# Warmup Iteration   3: 291051674,340 ops/s
# Warmup Iteration   4: 266676339,122 ops/s
# Warmup Iteration   5: 293441635,488 ops/s
Iteration   1: 287457532,873 ops/s
Iteration   2: 281545357,842 ops/s
Iteration   3: 296719702,090 ops/s
Iteration   4: 291921310,631 ops/s
Iteration   5: 286700801,995 ops/s

# Run progress: 70,00% complete, ETA 00:05:03
# Fork: 3 of 5
# Warmup Iteration   1: 276520610,324 ops/s
# Warmup Iteration   2: 329727471,644 ops/s
# Warmup Iteration   3: 285626731,959 ops/s
# Warmup Iteration   4: 261786230,628 ops/s
# Warmup Iteration   5: 267302212,506 ops/s
Iteration   1: 252492407,674 ops/s
Iteration   2: 260983039,836 ops/s
Iteration   3: 267096646,462 ops/s
Iteration   4: 267809607,360 ops/s
Iteration   5: 279958132,901 ops/s

# Run progress: 80,00% complete, ETA 00:03:22
# Fork: 4 of 5
# Warmup Iteration   1: 290778690,201 ops/s
# Warmup Iteration   2: 314662112,455 ops/s
# Warmup Iteration   3: 287832331,067 ops/s
# Warmup Iteration   4: 260810676,048 ops/s
# Warmup Iteration   5: 279443293,435 ops/s
Iteration   1: 277888605,939 ops/s
Iteration   2: 292051036,290 ops/s
Iteration   3: 276070917,424 ops/s
Iteration   4: 296736984,734 ops/s
Iteration   5: 264012586,401 ops/s

# Run progress: 90,00% complete, ETA 00:01:41
# Fork: 5 of 5
# Warmup Iteration   1: 304094025,898 ops/s
# Warmup Iteration   2: 325600222,904 ops/s
# Warmup Iteration   3: 284703627,129 ops/s
# Warmup Iteration   4: 282491485,533 ops/s
# Warmup Iteration   5: 272098243,558 ops/s
Iteration   1: 260623156,074 ops/s
Iteration   2: 266563506,972 ops/s
Iteration   3: 283426476,104 ops/s
Iteration   4: 276844988,882 ops/s
Iteration   5: 273723491,091 ops/s


Result "org.powernukkit.benchmark.math.NukkitRandomVsThreadLocalRandom.range2to8ThreadLocalRandom":
  276838017,832 ±(99.9%) 8827005,730 ops/s [Average]
  (min, avg, max) = (252492407,674, 276838017,832, 296736984,734), stdev = 11783800,107
  CI (99.9%): [268011012,102, 285665023,562] (assumes normal distribution)


# Run complete. Total time: 00:16:52

REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.

Benchmark                                                    Mode  Cnt          Score         Error  Units
NukkitRandomVsThreadLocalRandom.range2to8NukkitRandom       thrpt   25  296408341,250 ± 3800873,315  ops/s
NukkitRandomVsThreadLocalRandom.range2to8ThreadLocalRandom  thrpt   25  276838017,832 ± 8827005,730  ops/s

Process finished with exit code 0

That means that NukkitRandom version could be executed 296,408,341.250 times in one second and ThreadLocalRandom could 276,838,017.832 times

At the first look, NukkitRandom is winning on performance here for a little bit. Now, let’s add the acquisition cost to the equation.

Acquiring and generating

In this use case, we need a random generator that can be used safely in our thread but we don’t care about the seed, we just need it to be fast and random. Let’s ignore the fact that NukkitRandom generates the same numbers for all calls to new NukkitRandom() in the same second for now since JMH is taking around 15 minutes to complete the test for each method annotated with @Benchmark.

We will compare the performance of:

new NukkitRandom().nextRange(2, 8);

And:

ThreadLocalRandom.current().nextInt(2, 9);
See the test source code
package org.powernukkit.benchmark.math;

import cn.nukkit.math.NukkitRandom;
import org.openjdk.jmh.annotations.*;

import java.util.concurrent.ThreadLocalRandom;

@State(Scope.Benchmark)
public class NukkitRandomVsThreadLocalRandomStateless {
    @Benchmark
    public void range2to8NukkitRandomStateless() {
        new NukkitRandom().nextRange(2, 8);
    }

    @Benchmark
    public void range2to8ThreadLocalRandomStateless() {
        ThreadLocalRandom.current().nextInt(2, 9);
    }
}

And the results:

Benchmark                                                                      Mode  Cnt          Score         Error  Units
NukkitRandomVsThreadLocalRandomStateless.range2to8NukkitRandomStateless       thrpt   25   77928996,242 ± 1453433,269  ops/s
NukkitRandomVsThreadLocalRandomStateless.range2to8ThreadLocalRandomStateless  thrpt   25  311815656,371 ± 9664664,343  ops/s
See the full test output
"C:\Program Files\Java\jdk1.8.0_241\bin\java.exe" -Dfile.encoding=UTF-8 -classpath C:\Users\joserobjr\Projects\PowerNukkit-Benchmark\powernukkit-benchmark\target\classes;C:\Users\joserobjr\Projects\PowerNukkit\target\classes;C:\Users\joserobjr\.m2\repository\org\powernukkit\bedrock\network\raknet\1.6.15-PN2\raknet-1.6.15-PN2.jar;C:\Users\joserobjr\.m2\repository\org\powernukkit\bedrock\network\bedrock-network-common\1.6.15-PN\bedrock-network-common-1.6.15-PN.jar;C:\Users\joserobjr\.m2\repository\io\netty\netty-buffer\4.1.38.Final\netty-buffer-4.1.38.Final.jar;C:\Users\joserobjr\.m2\repository\io\netty\netty-transport-native-epoll\4.1.38.Final\netty-transport-native-epoll-4.1.38.Final-linux-x86_64.jar;C:\Users\joserobjr\.m2\repository\io\netty\netty-transport-native-unix-common\4.1.38.Final\netty-transport-native-unix-common-4.1.38.Final.jar;C:\Users\joserobjr\.m2\repository\io\netty\netty-transport-native-kqueue\4.1.38.Final\netty-transport-native-kqueue-4.1.38.Final-osx-x86_64.jar;C:\Users\joserobjr\.m2\repository\io\netty\netty-handler\4.1.38.Final\netty-handler-4.1.38.Final.jar;C:\Users\joserobjr\.m2\repository\io\netty\netty-common\4.1.38.Final\netty-common-4.1.38.Final.jar;C:\Users\joserobjr\.m2\repository\io\netty\netty-transport\4.1.38.Final\netty-transport-4.1.38.Final.jar;C:\Users\joserobjr\.m2\repository\io\netty\netty-resolver\4.1.38.Final\netty-resolver-4.1.38.Final.jar;C:\Users\joserobjr\.m2\repository\io\netty\netty-codec\4.1.38.Final\netty-codec-4.1.38.Final.jar;C:\Users\joserobjr\.m2\repository\net\sf\trove4j\experimental\3.1.0\experimental-3.1.0.jar;C:\Users\joserobjr\.m2\repository\net\sf\trove4j\core\3.1.0\core-3.1.0.jar;C:\Users\joserobjr\.m2\repository\org\powernukkit\fastutil\fastutil-lite\8.1.1\fastutil-lite-8.1.1.jar;C:\Users\joserobjr\.m2\repository\com\google\guava\guava\24.1.1-jre\guava-24.1.1-jre.jar;C:\Users\joserobjr\.m2\repository\org\checkerframework\checker-compat-qual\2.0.0\checker-compat-qual-2.0.0.jar;C:\Users\joserobjr\.m2\repository\com\google\errorprone\error_prone_annotations\2.1.3\error_prone_annotations-2.1.3.jar;C:\Users\joserobjr\.m2\repository\com\google\j2objc\j2objc-annotations\1.1\j2objc-annotations-1.1.jar;C:\Users\joserobjr\.m2\repository\org\codehaus\mojo\animal-sniffer-annotations\1.14\animal-sniffer-annotations-1.14.jar;C:\Users\joserobjr\.m2\repository\com\google\code\gson\gson\2.4\gson-2.4.jar;C:\Users\joserobjr\.m2\repository\org\yaml\snakeyaml\1.16\snakeyaml-1.16.jar;C:\Users\joserobjr\.m2\repository\org\powernukkit\bedrock\leveldb\bedrock-leveldb\0.11.0-PN\bedrock-leveldb-0.11.0-PN.jar;C:\Users\joserobjr\.m2\repository\org\powernukkit\bedrock\leveldb\bedrock-leveldb-api\0.11.0-PN\bedrock-leveldb-api-0.11.0-PN.jar;C:\Users\joserobjr\.m2\repository\com\nimbusds\nimbus-jose-jwt\7.9\nimbus-jose-jwt-7.9.jar;C:\Users\joserobjr\.m2\repository\com\github\stephenc\jcip\jcip-annotations\1.0-1\jcip-annotations-1.0-1.jar;C:\Users\joserobjr\.m2\repository\net\minidev\json-smart\2.3\json-smart-2.3.jar;C:\Users\joserobjr\.m2\repository\net\minidev\accessors-smart\1.2\accessors-smart-1.2.jar;C:\Users\joserobjr\.m2\repository\org\ow2\asm\asm\5.0.4\asm-5.0.4.jar;C:\Users\joserobjr\.m2\repository\org\apache\logging\log4j\log4j-api\2.13.3\log4j-api-2.13.3.jar;C:\Users\joserobjr\.m2\repository\org\apache\logging\log4j\log4j-core\2.13.3\log4j-core-2.13.3.jar;C:\Users\joserobjr\.m2\repository\net\sf\jopt-simple\jopt-simple\5.0.4\jopt-simple-5.0.4.jar;C:\Users\joserobjr\.m2\repository\net\minecrell\terminalconsoleappender\1.1.1\terminalconsoleappender-1.1.1.jar;C:\Users\joserobjr\.m2\repository\org\jline\jline-terminal\3.9.0\jline-terminal-3.9.0.jar;C:\Users\joserobjr\.m2\repository\org\jline\jline-terminal-jna\3.9.0\jline-terminal-jna-3.9.0.jar;C:\Users\joserobjr\.m2\repository\net\java\dev\jna\jna\4.2.2\jna-4.2.2.jar;C:\Users\joserobjr\.m2\repository\org\jline\jline-reader\3.9.0\jline-reader-3.9.0.jar;C:\Users\joserobjr\.m2\repository\com\google\code\findbugs\jsr305\3.0.2\jsr305-3.0.2.jar;C:\Users\joserobjr\.m2\repository\org\openjdk\jmh\jmh-core\1.25.1\jmh-core-1.25.1.jar;C:\Users\joserobjr\.m2\repository\org\apache\commons\commons-math3\3.2\commons-math3-3.2.jar org.openjdk.jmh.Main org.powernukkit.benchmark.math.NukkitRandomVsThreadLocalRandomStateless.*
# JMH version: 1.25.1
# VM version: JDK 1.8.0_241, Java HotSpot(TM) 64-Bit Server VM, 25.241-b07
# VM invoker: C:\Program Files\Java\jdk1.8.0_241\jre\bin\java.exe
# VM options: -Dfile.encoding=UTF-8
# Warmup: 5 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.powernukkit.benchmark.math.NukkitRandomVsThreadLocalRandomStateless.range2to8NukkitRandomStateless

# Run progress: 0,00% complete, ETA 00:16:40
# Fork: 1 of 5
# Warmup Iteration   1: 82731671,715 ops/s
# Warmup Iteration   2: 90571912,800 ops/s
# Warmup Iteration   3: 85066964,728 ops/s
# Warmup Iteration   4: 81739356,711 ops/s
# Warmup Iteration   5: 80746101,154 ops/s
Iteration   1: 78203793,856 ops/s
Iteration   2: 74528004,730 ops/s
Iteration   3: 76015443,519 ops/s
Iteration   4: 78894352,719 ops/s
Iteration   5: 77286814,555 ops/s

# Run progress: 10,00% complete, ETA 00:15:15
# Fork: 2 of 5
# Warmup Iteration   1: 87840053,702 ops/s
# Warmup Iteration   2: 85121377,248 ops/s
# Warmup Iteration   3: 77390282,702 ops/s
# Warmup Iteration   4: 76477261,449 ops/s
# Warmup Iteration   5: 77823485,134 ops/s
Iteration   1: 79065896,366 ops/s
Iteration   2: 78571746,357 ops/s
Iteration   3: 79261246,060 ops/s
Iteration   4: 76141887,193 ops/s
Iteration   5: 75949599,856 ops/s

# Run progress: 20,00% complete, ETA 00:13:31
# Fork: 3 of 5
# Warmup Iteration   1: 79530180,707 ops/s
# Warmup Iteration   2: 80315889,622 ops/s
# Warmup Iteration   3: 78726536,493 ops/s
# Warmup Iteration   4: 79853409,471 ops/s
# Warmup Iteration   5: 71786696,798 ops/s
Iteration   1: 76796331,040 ops/s
Iteration   2: 79182286,468 ops/s
Iteration   3: 78261908,880 ops/s
Iteration   4: 78915114,086 ops/s
Iteration   5: 75573097,683 ops/s

# Run progress: 30,00% complete, ETA 00:11:49
# Fork: 4 of 5
# Warmup Iteration   1: 81176742,887 ops/s
# Warmup Iteration   2: 83994523,730 ops/s
# Warmup Iteration   3: 77991999,374 ops/s
# Warmup Iteration   4: 76434753,990 ops/s
# Warmup Iteration   5: 77231914,265 ops/s
Iteration   1: 78224093,410 ops/s
Iteration   2: 78971310,495 ops/s
Iteration   3: 82018434,842 ops/s
Iteration   4: 81912681,846 ops/s
Iteration   5: 77216853,608 ops/s

# Run progress: 40,00% complete, ETA 00:10:07
# Fork: 5 of 5
# Warmup Iteration   1: 85797749,565 ops/s
# Warmup Iteration   2: 86278518,060 ops/s
# Warmup Iteration   3: 72337795,626 ops/s
# Warmup Iteration   4: 78176113,596 ops/s
# Warmup Iteration   5: 79166389,652 ops/s
Iteration   1: 74323981,760 ops/s
Iteration   2: 79244437,757 ops/s
Iteration   3: 79639484,809 ops/s
Iteration   4: 77117244,146 ops/s
Iteration   5: 76908860,006 ops/s


Result "org.powernukkit.benchmark.math.NukkitRandomVsThreadLocalRandomStateless.range2to8NukkitRandomStateless":
  77928996,242 ±(99.9%) 1453433,269 ops/s [Average]
  (min, avg, max) = (74323981,760, 77928996,242, 82018434,842), stdev = 1940291,832
  CI (99.9%): [76475562,972, 79382429,511] (assumes normal distribution)


# JMH version: 1.25.1
# VM version: JDK 1.8.0_241, Java HotSpot(TM) 64-Bit Server VM, 25.241-b07
# VM invoker: C:\Program Files\Java\jdk1.8.0_241\jre\bin\java.exe
# VM options: -Dfile.encoding=UTF-8
# Warmup: 5 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.powernukkit.benchmark.math.NukkitRandomVsThreadLocalRandomStateless.range2to8ThreadLocalRandomStateless

# Run progress: 50,00% complete, ETA 00:08:26
# Fork: 1 of 5
# Warmup Iteration   1: 272307704,767 ops/s
# Warmup Iteration   2: 304475336,972 ops/s
# Warmup Iteration   3: 308116737,787 ops/s
# Warmup Iteration   4: 309487651,418 ops/s
# Warmup Iteration   5: 286302533,754 ops/s
Iteration   1: 295241529,635 ops/s
Iteration   2: 302019641,350 ops/s
Iteration   3: 297340387,267 ops/s
Iteration   4: 271894636,290 ops/s
Iteration   5: 314595780,471 ops/s

# Run progress: 60,00% complete, ETA 00:06:44
# Fork: 2 of 5
# Warmup Iteration   1: 248556544,036 ops/s
# Warmup Iteration   2: 282746506,519 ops/s
# Warmup Iteration   3: 312387106,741 ops/s
# Warmup Iteration   4: 314075338,676 ops/s
# Warmup Iteration   5: 316520845,405 ops/s
Iteration   1: 315884376,523 ops/s
Iteration   2: 289172159,965 ops/s
Iteration   3: 310351054,890 ops/s
Iteration   4: 311216744,949 ops/s
Iteration   5: 318388428,801 ops/s

# Run progress: 70,00% complete, ETA 00:05:03
# Fork: 3 of 5
# Warmup Iteration   1: 292276139,403 ops/s
# Warmup Iteration   2: 275686728,479 ops/s
# Warmup Iteration   3: 299176237,001 ops/s
# Warmup Iteration   4: 306217643,210 ops/s
# Warmup Iteration   5: 307445558,947 ops/s
Iteration   1: 307503002,140 ops/s
Iteration   2: 323000057,364 ops/s
Iteration   3: 320483872,705 ops/s
Iteration   4: 310408390,513 ops/s
Iteration   5: 309727220,165 ops/s

# Run progress: 80,00% complete, ETA 00:03:22
# Fork: 4 of 5
# Warmup Iteration   1: 277995678,285 ops/s
# Warmup Iteration   2: 276806536,362 ops/s
# Warmup Iteration   3: 290949052,009 ops/s
# Warmup Iteration   4: 298567819,263 ops/s
# Warmup Iteration   5: 299324334,318 ops/s
Iteration   1: 329916773,659 ops/s
Iteration   2: 323421386,509 ops/s
Iteration   3: 318175127,556 ops/s
Iteration   4: 307652285,998 ops/s
Iteration   5: 315314329,965 ops/s

# Run progress: 90,00% complete, ETA 00:01:41
# Fork: 5 of 5
# Warmup Iteration   1: 274383238,791 ops/s
# Warmup Iteration   2: 301535062,606 ops/s
# Warmup Iteration   3: 294940264,522 ops/s
# Warmup Iteration   4: 316359610,978 ops/s
# Warmup Iteration   5: 308666230,260 ops/s
Iteration   1: 316195671,772 ops/s
Iteration   2: 322448146,992 ops/s
Iteration   3: 327679674,220 ops/s
Iteration   4: 317491257,503 ops/s
Iteration   5: 319869472,085 ops/s


Result "org.powernukkit.benchmark.math.NukkitRandomVsThreadLocalRandomStateless.range2to8ThreadLocalRandomStateless":
  311815656,371 ±(99.9%) 9664664,343 ops/s [Average]
  (min, avg, max) = (271894636,290, 311815656,371, 329916773,659), stdev = 12902050,390
  CI (99.9%): [302150992,028, 321480320,715] (assumes normal distribution)


# Run complete. Total time: 00:16:51

REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.

Benchmark                                                                      Mode  Cnt          Score         Error  Units
NukkitRandomVsThreadLocalRandomStateless.range2to8NukkitRandomStateless       thrpt   25   77928996,242 ± 1453433,269  ops/s
NukkitRandomVsThreadLocalRandomStateless.range2to8ThreadLocalRandomStateless  thrpt   25  311815656,371 ± 9664664,343  ops/s

Process finished with exit code 0

Oh boy, the score is missing a digit there! We can clearly see that the ThreadLocalRandom performed a lot better. NukkitRandom version could be executed 77,928,996.242 times per second while ThreadLocalRandom version could 311,815,656.371 times.

Conclusion

So, with all this data, we can clearly say that :

NukkitRandom is the best choice for world generation because we know the seed, it is faster than ThreadLocalRandom when we reuse the instance and ThreadLocalRandom is faster than Random as indicated by JavaDoc itself.

And ThreadLocalRandom is the best when we are calculating drops or doing stuff where the seed is irrelevant and just need to be fast and random.

Use case NukkitRandom ThreadLocalRandom
World Gen Fast Impossible
Calculating drops Not 100% random, slower 100% random, faster
Reusing the random generator 296,408,341.250 op/s, faster 276,838,017.832 op/s, slower
Acquiring and generating 77,928,996.242 op/s, slower 311,815,656.371, faster