3 个稳定版本
1.0.2 | 2022年6月16日 |
---|
#139 in 性能分析
19KB
104 行
在收集有关程序行为的统计数据时,我们可能会观察到发生频率很高的事件(例如,函数调用或内存分配),并且我们可能会收集一些生产成本较高的信息(例如,调用栈)。对所有事件进行采样可能会对程序的性能产生重大影响。
为什么不只采样每 N
个事件呢?这种技术被称为“系统抽样”;它简单且高效,如果我们想象一个无模式的事件流,那么它是可以接受的。但是,如果我们正在采样分配,并且程序恰好有一个循环,每次迭代都恰好进行 N
次分配呢?你将每次都会采样相同的分配;循环的其余部分将完全从你的测量中消失!更一般地说,如果每个迭代进行 M
次分配,并且 M
和 N
有任何公约数,大多数分配位置将永远不会被采样。如果它们都是偶数,比如,奇数分配将从你的结果中消失。
理想情况下,我们希望每个事件都有一些概率 P
被采样,与邻居无关,与它在序列中的位置也无关。这被称为 "伯努利抽样",并且不会受到上述任何问题的困扰。
伯努利抽样的一个缺点是,你无法确切知道你会得到多少样本:技术上,你可能会一个都不采样,或者全部都采样。但是,如果事件的数目 N
很大,这些结果不太可能;你通常可以期望大约 P * N
的事件被采样。
伯努利抽样的另一个缺点是,你必须为每个事件生成一个随机数,这可能会很慢。
<显著暂停>
但不是这个箱子的!
FastBernoulli
允许您进行真正的伯努利抽样,但只有在决定抽样事件时才会生成新的随机数,而不是在每次试验时。当决定不抽样时,对 FastBernoulli::trial
的调用只是减少计数器并比较它与零。因此,您的抽样概率越低,FastBernoulli
施加的额外开销就越少。
最后,对 0
和 1
的概率进行了高效的处理。(在两种情况下都不需要生成任何随机数。)
示例
use fast_bernoulli::FastBernoulli;
use rand::Rng;
// Get the thread-local random number generator.
let mut rng = rand::thread_rng();
// Create a `FastBernoulli` instance that samples events with probability 1/20.
let mut bernoulli = FastBernoulli::new(0.05, &mut rng);
// Each time your event occurs, perform a Bernoulli trail to determine whether
// you should sample the event or not.
let on_my_event = || {
if bernoulli.trial(&mut rng) {
// Record the sample...
}
};
灵感
这个包使用与 Jim Blandy 在 Firefox 中的 FastBernoulliTrial
类 相同的技术。这个实现并不是直接从 C++ 转录到 Rust,然而我确实复制(并略作编辑)了一些原始文档和注释(例如,本 README 和 crate 级别的文档的大部分内容)。
依赖关系
~310KB