7个版本
0.1.6 | 2021年2月2日 |
---|---|
0.1.5 | 2019年9月14日 |
0.1.2 | 2019年8月29日 |
#216 in 内存管理
137 每月下载量
在 3 crates 中使用
71KB
1K SLoC
SyncPool
一个简单且线程安全的对象池,用于在堆中重用重对象。
本crate的用途
受Go的sync.Pool
模块的启发,此crate提供了一个多线程友好的库来回收和重用重和基于堆的对象,从而减少整体分配和内存压力,从而提高性能。
本crate不适用于
在多线程项目中设计时,并没有银弹,程序员需要根据具体情况判断。
正如我们运行的一些(数百个)基准测试所示,在以下情况下,该库可以可靠地击败分配器
- 对象足够大,使其在堆中生存是有意义的。
- 在将元素放回池中之前清理写入数据所需的操作简单且运行速度快。
- 对程序运行期间同时检查出的最大元素数量的估计足够好,即并行性是确定的;否则,当池饥饿(即它没有足够的元素留下以提供)时,性能将受到影响,因为我们需要创建(并在堆中分配)新元素。
如果你的struct足够小,可以在栈上生存而不崩溃,或者它不在最热的代码路径中,你很可能不需要这个库来为你工作,现在的分配器在栈上工作得相当神奇。
示例
extern crate syncpool;
use std::collections::HashMap;
use std::sync::mpsc::{self, SyncSender};
use std::thread;
use std::time::Duration;
use syncpool::prelude::*;
/// For simplicity and illustration, here we use the most simple but unsafe way to
/// define the shared pool: make it static mut. Other safer implementation exists
/// but may require some detour depending on the business logic and project structure.
static mut POOL: Option<SyncPool<ComplexStruct>> = None;
/// Number of producers that runs in this test
const COUNT: usize = 128;
/// The complex data struct for illustration. Usually such a heavy element could also
/// contain other nested struct, and should almost always be placed in the heap. If
/// your struct is *not* heavy enough to be living in the heap, you most likely won't
/// need this library -- the allocator will work better on the stack. The only requirement
/// for the struct is that it has to implement the `Default` trait, which can be derived
/// in most cases, or implemented easily.
#[derive(Default, Debug)]
struct ComplexStruct {
id: usize,
name: String,
body: Vec<String>,
flags: Vec<usize>,
children: Vec<usize>,
index: HashMap<usize, String>,
rev_index: HashMap<String, usize>,
}
fn main() {
// Must initialize the pool first
unsafe { POOL.replace(SyncPool::with_size(COUNT / 2)); }
// use the channel that create a concurrent pipeline.
let (tx, rx) = mpsc::sync_channel(64);
// data producer loop
thread::spawn(move || {
let mut producer = unsafe { POOL.as_mut().unwrap() };
for i in 0..COUNT {
// take a pre-init element from the pool, we won't allocate in this
// call since the boxed element is already placed in the heap, and
// here we only reuse the one.
let mut content: Box<ComplexStruct> = producer.get();
content.id = i;
// simulating busy/heavy calculations we're doing in this time period,
// usually involving the `content` object.
thread::sleep(Duration::from_nanos(32));
// done with the stuff, send the result out.
tx.send(content).unwrap_or_default();
}
});
// data consumer logic
let handler = thread::spawn(move || {
let mut consumer = unsafe { POOL.as_mut().unwrap() };
// `content` has the type `Box<ComplexStruct>`
for content in rx {
println!("Receiving struct with id: {}", content.id);
consumer.put(content);
}
});
// wait for the receiver to finish and print the result.
handler.join().unwrap_or_default();
println!("All done...");
}
你可以在示例文件夹中找到更复杂(即实用)的使用案例。