2016-07-23 88 views
0

在Java中,相对于简单的算术运算需要多长时间才能用Math.random()生成一个数字?我试图随机分配一个ArrayList中的对象,这个对象已经包含了一些值,这样就可以创建一个平均的,但不是完全均匀的分布,我不确定是否使用Math.random()为每个插入点选择一个随机索引是最好的方法。Math.random()运行的时间与简单算术运算的时间相比如何?

澄清:插入对象的分配,就是要甚至没有足够的值不都集中在一个区域,也够不均衡的分布是不可预测的(如果有人要经过值一个由一个,他们将无法通过检测一个常量模式来确定下一个值是否将成为新插入的值)。

+0

的Math.random()给你一个随机位置。所以“有点”,甚至是不会发生的每一次......如果你想有一个一致的输出(均匀分布)\你应该创建自己的操作... – DarkV1

+2

只需基准!它的速度比添加的要慢,但速度足够快,可以多次采样。如果您需要一个以上的随机样本,则一次调用这些样本会更快(例如,创建一个随机池)。我不知道你在做什么,但我怀疑这里有更多的算法问题。例如:很难实现正确的随机播放。 – sascha

+2

生成一个随机值(通过Math.random)是一个相当常见的操作,如果没有很好的理由,也不应该担心。它看起来像不成熟的优化。你有什么其他的方法? – zoom

回答

4

不要使用Math.random。它依赖于使用AtomicLong的全局实例java.util.Random。尽管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 
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))

正如您所看到的,它使用简单的算法来生成伪随机值。它只需要几个便宜的操作,所以我不会担心使用它。

+1

尽管该算法非常简单,但其性能受[原子CAS]影响(http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/a11ab21bb799/src/share/classes/java/util /Random.java#l200),即使在单线程应用程序中也会有性能损失,更不用说多线程情况下可能出现的争用。 – apangin

1

创建一个随机数是一个简单的操作,你不应该担心它。

但是你应该记住几件事情

  • 这是更好地重用随机实例,创建每次你需要一个随机值,通常是一个错误的决定时间new Random()实例。

  • 但是不要在同一个线程中同时使用同一个随机实例来避免争用,您可以使用ThreadLocalRandom.current()代替。

  • 如果您使用圆顶密码术,请改用SecureRandom