Chrome 为什么Math.random被crypto.getRandomValues超越?

ufj5ltwl  于 2023-06-19  发布在  Go
关注(0)|答案(1)|浏览(121)

我使用crypto.getRandomValues重新实现了Math.random(),并采用了“熵池”方案来减少调用crypto.getRandomValues的开销。在我的电脑(x86_64 Windows Chrome)上,这似乎比Math.random()快260%。我觉得这很令人惊讶,因为我的印象是Chrome使用非常快的xorshift128+ PRNG实现了后者,这应该比前者使用的CSPRNG快几个数量级。

class EntropyPool {
    #entropy; #index
    constructor(poolSize = 1024) {
        this.#entropy = new Uint32Array(poolSize)
        this.#index = 0
        crypto.getRandomValues(this.#entropy)
    }
    next() {
        const value = this.#entropy[this.#index++]
        if (this.#index === this.#entropy.length) {
            crypto.getRandomValues(this.#entropy)
            this.#index = 0
        }
        return value
    }
}

const pool = new EntropyPool()

function random() {
    return pool.next() / 4294967296
}
i2byvkas

i2byvkas1#

(V8开发者在这里)。

    • 微基准测试的第一条规则是它们会误导你。**

最常见的陷阱之一是不使用测试操作的结果,这允许优化编译器优化死代码,并且您将留下测量(几乎)空循环。有时这只适用于您正在比较的变量之一,这将产生严重的偏差结果。
如果通过将测试更改为强制使用结果:

let result = 0;
for (let i = 0; i < 100; i++) {
    result += Math.random();
}
if (result > 100) throw "bad";

然后你会看到Math.random实际上比你的定制random快(我看到的是642K vs 507K ops/sec)。
换句话说:这个问题的前提是不正确的,Math.random * 不 * 被crypto.getRandomValues超越。只是微基准测试使用后者的特定方式在结果不被使用时更适合于死代码消除。

    • 为了避免被微基准误导,你真的需要检查生成的代码和/或跟踪其他引擎内部。**如果你没有时间或专业知识来做这件事,那就不要使用微基准。相反,在你的真实应用中实现A/B比较,看看你是否可以测量那里的差异。如果可以,那么您也可以立即知道它们与您的应用程序相关。如果你不能(例如因为与应用程序的其他部分相比,它们太小了),那么你就知道它们并不重要。分析是一种很好的方法,可以找出影响力最大的潜力在哪里。

相关问题