【问题标题】:Why is Random.NextBytes() "surprisingly slow"?为什么 Random.NextBytes() “出奇的慢”?
【发布时间】:2017-05-28 15:22:24
【问题描述】:

来自Fastest way to generate a random boolean,CodesInChaos在评论中说:

MS 搞砸了NextBytes 的实现,所以速度出奇的慢。

[...] 性能与为每个字节调用 Next 一样糟糕,而不是利用所有 31 位。但由于System.Random 几乎在每个级别都有糟糕的设计和实现,这是我较小的抱怨之一。

他为什么说微软几乎在每个层面都进行了设计和实现?

Random 类是如何错误实现的?

【问题讨论】:

  • 标题中的问题由您自己的报价回答。你身体里的问题是一个单独的问题。
  • @hvd 看了.NET参考源后,不知道为什么他说慢:for (int i=0; i<buffer.Length; i++) buffer[i]=(byte)(InternalSample()%(Byte.MaxValue+1)); }?
  • 算法不是微软设计的,他们是从 Donald Knuth 的 The Art of Programming 中得到的。程序员抱怨他们的随机数生成器就像农民抱怨天气一样。

标签: c# .net performance random


【解决方案1】:

当然我不能看他的内心,但System.Random很奇怪。

InternalSample() 返回一个非负的int,它不能是int.MaxValue。一开始这听起来并没有那么糟糕,但这意味着它具有几乎(但不完全)31 个可用位的随机性。这使事情变得复杂,例如有效地实现NextBytes(byte[] buffer).. 它甚至没有尝试过!它这样做:

for (int index = 0; index < buffer.Length; ++index)
    buffer[index] = (byte) (this.InternalSample() % 256);

InternalSample 的调用次数大约是必要次数的 4 倍。 % 256 也没有用,无论如何转换为字节都会被截断。这也是有偏差的,255 的可能性比任何其他结果都小,因为内部样本不可能是int.MaxValue

但情况会变得更糟。例如,NextDouble 使用 this.InternalSample() * 4.6566128752458E-10。这可能不是很明显,但4.6566128752458E-101.0 / int.MaxValue。令人讨厌的是,它不是 2 的幂,所以它是一个“混乱”的数字,导致相邻可能结果之间的差距不均匀。

更糟糕的是,Next(int)Next(int, int) 的算法本质上是有偏差的,因为它们只是简单地缩放一个随机双精度数并且什么都不拒绝。它也不是特别快,这通常是避免拒绝抽样的原因。

总的来说,它也相当慢。它是一个减法生成器,一个相对不为人知的 PRNG,显然还不错,但它有一个很大的状态(播种速度很慢,缓存占用空间很大)和采样算法中的一堆烦人的操作。当然,它的质量比基本 LCG 更好,但它受到有偏差的缩放方法和糟糕的性能的严重影响。

界面设计也很烦人。由于任何地方的上限都是互斥的,因此没有简单的方法可以在 [0 .. int.MaxValue] 或 [int.MinValue .. int.MaxValue] 中生成样本,这两者都相当普遍。独占上限通常可以很好地避免奇怪的 -1,提供无法获得全范围样本的方法只是烦人。当然可以通过NextDouble完成,但是由于NextDouble的输入已经不是全范围的样本,结果必然有偏差。

我可能遗漏了一些不足之处。

【讨论】:

  • "例如,NextDouble 使用this.InternalSample() * 4.6566128752458E-10。可能不是很明显,但4.6566128752458E-101.0 / int.MaxValue。" -- 代码有return (InternalSample()*(1.0/MBIG));private const int MBIG = Int32.MaxValue;。如果您正在查看反编译器输出,我推荐参考源。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-16
  • 2012-09-23
  • 1970-01-01
  • 2021-09-03
  • 2022-01-09
相关资源
最近更新 更多