在Web服务器上,许多线程正在向客户机提供内容。A/B测试是在网站上执行的,因此我们需要一个prng来为每个会话和测试选择一个变量。显然,当使用一个prng实例时,它是并发访问的,因此可能需要适当的锁定或其他机制。
最初我们使用了java.util.Random(jur),但由于它存在上述缺陷,例如java.util.random有多好,所以我们尝试使用mersennetwester。然而,由于Mersenne Twister依赖于内部状态,因此性能下降,因此需要同步访问nextInt()。另一种选择可能是xor-shift prng,但它与mersenne twister有相同的问题。您可以在以下位置找到解释:http://xorshift.di.unimi.it/
Random使用了compareAndset操作,该操作似乎更快,因为它不需要锁定,但根据javadoc类,它仍然不是线程安全的。相反,建议使用ThreadLocalRandom,这基本上会导致一个prng池。根据请求,一个随机可用线程处理https请求,因此从一组可用prng中选择一个随机prng。显然这非常快。
从这样一个池中生成的随机数是否与从单个prng实例中生成的随机数一样好?
另一种方法是使用单个prng实例从中预生成值流,例如使用ArrayBlockingQueue。
哪种解决方案在性能方面更有效?
对于这个应用程序(选择是否向用户显示a或b),只要随机数是均匀分布的,那么它们的质量实际上并不重要。
您正在努力进行预优化。Random被记录为线程安全的,与Web应用程序所需的所有IO(数据库查询、HTTP请求等)相比,每次启动会话时调用NextBoolean()的性能完全可以忽略。
在随机数质量方面,这个想法试图想出比java.util.random更好的方法。我们实际上有两个以上的变量,所以我们画整数。@亨利,就我所读到的,随机比特的概率分布不均匀。
即便如此,随机性还是足够好的。但如果你想要一个完美的分布,你甚至不需要随机分布。只需使用一个atomicinteger,并在每个会话开始时递增它。然后计算变量数的模。你只需要一个好的分布,而不是一个好的随机性。
但我的问题仍未得到解答。一个prng的池和一个prng一样好吗?
@我喜欢你简单的想法。但是,这只在进行1/N拆分时有效。如果我想对两个变体进行60/40%的分割,我想这是行不通的。
如果模5是0,1或2,选择版本A。如果是3或4,选择版本B。
让我们在聊天中继续讨论。
通过将结果传递给BlockingQueue,可以使任意随机数生成器线程安全。
class SafeRandom implements Runnable {
Random r = new Random();
BlockingQueue q = new ArrayBlockingQueue<>(10);
double get() throws InterruptedException {
return q.take();
}
@Override
public void run() {
try {
while (true) {
q.put(r.nextDouble());
}
} catch (InterruptedException ie) {
}
}
}
为了避免同步问题,每个线程都有一个RNG。为了避免特定于线程的RNG给出相同的输出,让主RNG为特定于线程的RNG生成一系列初始种子。这可能需要向代码中传递一个额外的种子参数来生成一个新线程。
您将需要测试自己在套件上运行RNG的不同选项的速度。如果需要,可以为主RNG和特定于线程的RNG使用不同的RNG引擎。一般来说,为特定于线程的RNG选择具有快速设置时间的RNG。这对主RNG来说并不重要,因为它只设置了一次。