Math.random()运行所需的时间与简单算术运算相比如何?

时间:2016-07-23 21:20:07

标签: java performance random

在Java中,相对于简单的算术运算多长时间,Math.random()会生成一个数字?我试图在ArrayList中随机分配对象,这些对象已经包含了一些有点均匀但不完全均匀的分布,并且我不确定是否通过使用Math.random()为每个插入点选择一个随机索引最好的方法。

澄清:插入对象的分布应该足够均匀,以至于值不是全部集中在一个区域,而是也不均匀,分布不可预测(如果有人要去通过逐个值,他们将无法通过检测常量模式来确定下一个值是否将成为新插入的值。)

3 个答案:

答案 0 :(得分:4)

不要使用Math.random。它依赖于java.util.Random的全局实例,它使用了AtomicLong。虽然java.util.Random中使用的PRNG算法是pretty simple,但性能主要受原子CAS和相关缓存一致性流量的影响。

对于多线程应用(如this example),这可能特别糟糕,但即使在单线程情况下也会受到惩罚。

ThreadLocalRandom始终优于Math.random。它不依赖于原子操作,也不会受到争用。它只会更新thread-local state并使用几个arithmetic and bitwise operations

这是一个JMH基准,用于将Math.random()ThreadLocalRandom.current().nextDouble()的性能与简单的算术运算进行比较。

package bench;

import org.openjdk.jmh.annotations.*;
import java.util.concurrent.ThreadLocalRandom;

@State(Scope.Thread)
public class RandomBench {
    double x = 1;

    @Benchmark
    public double multiply() {
        return x * Math.PI;
    }

    @Benchmark
    public double mathRandom() {
        return Math.random();
    }

    @Benchmark
    public double threadLocalRandom() {
        return ThreadLocalRandom.current().nextDouble();
    }
}

结果表明,ThreadLocalRandom仅在几纳秒内工作,其性能可与简单的算术运算相媲美,并且在多线程环境中完美地扩展,与Math.random不同。

Benchmark                      Threads     Score     Error  Units
RandomBench.mathRandom            1       34.265 ±   1.709  ns/op
RandomBench.multiply              1        4.531 ±   0.108  ns/op
RandomBench.threadLocalRandom     1        8.322 ±   0.047  ns/op

RandomBench.mathRandom            2      366.589 ±  63.899  ns/op
RandomBench.multiply              2        4.627 ±   0.118  ns/op
RandomBench.threadLocalRandom     2        8.342 ±   0.079  ns/op

RandomBench.mathRandom            4     1328.472 ± 177.216  ns/op
RandomBench.multiply              4        4.592 ±   0.091  ns/op
RandomBench.threadLocalRandom     4        8.474 ±   0.157  ns/op

答案 1 :(得分:2)

Java文档将此报告为Random.nextDouble()的实现,这是Math.random()最终调用的内容。

 public double nextDouble() {
   return (((long)next(26) << 27) + next(27))
     / (double)(1L << 53);
 }

下一步将种子更新为(seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)并返回(int)(seed >>> (48 - bits))

正如您所看到的,它使用一种简单的算法来生成伪随机值。它只需要一些便宜的操作,因此我不担心使用它。

答案 2 :(得分:1)

创建一个随机数是一个简单的操作,你不应该担心它。 但你应该记住几件事 最好重用Random实例,每次需要随机值时创建新的Random()实例通常是一个错误的决定。 但是,不要同时在多个线程中使用相同的Random实例来避免争用,您可以使用ThreadLocalRandom.current()代替。 如果你做圆顶密码术,请改用SecureRandom。