1 个不稳定版本
0.1.0 | 2023 年 8 月 26 日 |
---|
#710 在 并发
26KB
430 行
一个测试支持库,用于加剧测试代码中的竞争。
目标
- 通过加剧竞争来帮助模糊测试多线程代码。
- 帮助在高竞争下基准测试多线程代码。
动机
在开发多线程代码时,通常会提出两个问题:
- 它是否正确?
- 它是否足够快?
遗憾的是,虽然可能可以彻底测试串行代码的所有代码路径,但没有被测试代码的广泛合作,测试并发代码的所有潜在交织是不可能的。同样,基准测试并发代码可能相当困难,特别是测量延迟。
这就是 Bursty
的目的:帮助构建测试和基准测试,作为管道中的一系列步骤,其中所有并发线程都同步执行每个步骤,从而在每个步骤中最大化竞争。
[^1] 有关有趣的方法,请参阅 loom
。
它是如何工作的?
Bursty
是一个框架,用于在用户配置的线程数上同步执行一系列步骤,同时访问全局共享和线程局部状态。
快速演示 —— 摘自 BurstyBuilder
文档
use std::sync::atomic::{AtomicI32, Ordering};
use bursty::BurstyBuilder;
// Construct a builder with a globally-shared `AtomicI32` and two thread-local "states" (1 and 10).
let mut builder = BurstyBuilder::new(AtomicI32::new(0), vec!(1, 10));
// Add a step in the pipeline to execute on each thread, which adds the local integer to the global one.
builder.add_simple_step(|| |global: &AtomicI32, local: &mut i32| { global.fetch_add(*local, Ordering::Relaxed); });
// Launch the execution, with 4 iterations of the pipeline.
let mut bursty = builder.launch(4);
assert!(44 >= bursty.global().load(Ordering::Relaxed));
// Join all threads, blocking until they are done, or one panicks.
bursty.join();
// Check the global and local states.
assert_eq!(44, bursty.global().load(Ordering::Relaxed));
assert_eq!(vec!(1, 10), bursty.into_locals());
重要的是要注意,用户定义的步骤不必在所有线程上具有相同的行为。例如,步骤可以完美地添加奇数并减去偶数。这允许测试混合的工作负载。
如何实现同步执行?
通过自旋。
在执行每个步骤之间,Bursty
注入一个会合点,一个初始化为线程数的简单原子整数。当达到这个原子时,每个线程都会递减它,然后自旋,直到原子为 0,然后继续。
这是确保所有线程几乎同时开始执行下一个步骤的最准确方式,但这也意味着极端的 CPU 密集型。
通常不建议在机器上运行比物理核心更多的线程。
如何进行模糊测试?
使用Bursty进行模糊测试时,只需创建测试流水线,并根据执行时间和你愿意等待的时间调整迭代次数。每次迭代可能与其他迭代有略微不同的间隔,随着时间的推移,将测试越来越多不同的间隔...但并没有一个保证所有间隔都会被测试的神奇数字。
如何进行基准测试?
选择一个允许用户报告测量的基准测试框架,然后创建流水线。为了提高效率,建议创建一个可以多次连续执行的自动重置流水线,并一次性安排多个迭代,这样就不需要每次都创建N个线程。
类似项目
我不知道有任何项目允许在高度竞争下进行基准测试延迟。
对于测试正确性,正如所述,loom
可能会很有兴趣。特别是,loom
更有可能提供确定的答案,而模糊测试方法只能提高信心。