随机数性能对比

主题

随机数性能对比

对比方案

三种使用随机数方式,通过 JMH 进行对比:

  • 使用 Random
  • 使用 ThreadLocalRandom,但是缓存第一次获得的值(注意,这是错误的使用方法)
  • 使用 ThreadLocalRandom,每次读取(注意,这是正确的使用方法)

JMH 测试代码

package random;

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;

import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;

// 表明测试目标是吞吐量
@BenchmarkMode(Mode.Throughput)
// JVM热化配置,迭代3次,每次1s
@Warmup(iterations = 3, time = 1)
// 测量配置,迭代5次,每次3s
@Measurement(iterations = 5, time = 3)
// 执行测试时线程配置,4线程运行
@Threads(4)
// 执行测试时执行次数配置,执行3次
@Fork(3)
// 测试数据作用域,基于整个测试
@State(Scope.Benchmark)
public class RandomVsThreadLocalRandomBenchmark {
public Random random = new Random();
/*
* 错误实现,这么做固定了此变量,值为创建该对象的线程对应的 ThreadLocalRandom
*/
public ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();


@Benchmark
public void random(Blackhole blackhole) {
blackhole.consume(random.nextInt());
}

/**
* 错误实现,这么做实际上是在同一个对象存在竞态
* @param blackhole 黑洞对象
*/
@Benchmark
public void localVal(Blackhole blackhole) {
blackhole.consume(threadLocalRandom.nextInt());
}

/**
* 正确实现,各个线程调用自己的ThreadLocalRandom,无竞态
* @param blackhole 黑洞对象
*/
@Benchmark
public void threadLocal(Blackhole blackhole) {
blackhole.consume(ThreadLocalRandom.current().nextInt());
}
}

测试结果

# JMH version: 1.21
# VM version: JDK 1.8.0_181, Java HotSpot(TM) 64-Bit Server VM, 25.181-b13
# VM invoker: /usr/java/jdk1.8.0_181-amd64/jre/bin/java
# VM options: -Dfile.encoding=UTF-8
# Warmup: 3 iterations, 1 s each
# Measurement: 5 iterations, 3 s each
# Timeout: 10 min per iteration
# Threads: 4 threads, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: random.RandomVsThreadLocalRandomBenchmark.measureRandom
...
Benchmark Mode Cnt Score Error Units
random thrpt 15 10843296.059 ± 977932.491 ops/s
localVal thrpt 15 315967812.728 ± 16518868.974 ops/s
threadLocal thrpt 15 470922078.096 ± 5079007.485 ops/s

解读

  1. java.util.Random 很慢,毫无疑问
  2. 就算不正确使用的情况下,ThreadLocalRandom 仍然比 Random
  3. 明显可以看到,不正确使用下,ThreadLocalRandom 的波动比正确使用时高许多。(数据中 ± 后面部分即波动范围。)
  4. 正确使用 ThreadLocalRandom 情况下,是使用 Random50倍
  5. 在正式生产环境下,由于线程更多,RandomThreadLocalRandom 获得的收益可能更大。因为潜在的争抢点消失了一个,阻塞的线程会减少许多。(测试中只有四个线程,不能和生产比。)
  6. 这里只讨论了 RandomThreadLocalRandom,不涉及 SecureRandom