11个不稳定版本 (3个重大更改)
| 0.4.3 | 2020年8月9日 |
|---|---|
| 0.4.2 | 2020年4月24日 |
| 0.3.1 | 2020年4月23日 |
| 0.3.0 | 2020年3月10日 |
| 0.1.0 | 2019年11月26日 |
#262 in 内存管理
每月下载量34,853
在 9 个库中使用 (8 个直接使用)
69KB
1.5K SLoC
refpool
refpool是对Rust的std::boxed::Box和std::rc::Rc的重实现,它使用可重用内存池来加速重新分配。
速度快吗?
当内存池非空时,它在Linux系统上大约比系统分配器快两倍,在Windows系统上快六倍。对于某些数据类型,收益甚至更高。
文档
许可证
版权所有2019年Bodil Stokke
本软件受Mozilla公共许可证第2.0版条款约束。如果此文件未附带MPL副本,您可以从http://mozilla.org/MPL/2.0/获取。
行为准则
请注意,该项目附带贡献者行为准则。通过参与此项目,您同意遵守其条款。
lib.rs:
使用可重用内存池加速重新分配的std::boxed::Box和std::rc::Rc的重实现。
先决条件
要从内存池中初始化一个类型到其默认值,请使用 PoolBox::default() 或 PoolRef::default() 方法,需要实现 PoolDefault。
如果想要使用 PoolRef::make_mut() 方法,还需要实现 PoolClone。
使用 PoolRef::new() 方法构建值时,没有要求。
对于大多数原始类型和 std 的数据类型,都实现了 PoolDefault 和 PoolClone,您可以轻松地为自定义类型提供默认实现,通过实现标记特质 PoolDefaultImpl。如果您有不需要在构造时完全初始化内存的数据结构,也可以自行实现,这可以带来轻微的性能提升。(这种优化是为什么 PoolDefault 和 PoolClone 作为独立的特质存在,否则 Default 和 Clone 就足够了。)
使用方法
您可以通过调用 PoolRef::default(pool) 或 PoolRef::new(pool, value) 来创建新值。如果可用,这将使用池中的内存,如果池为空,则回退到正常的堆分配。当引用值的最后一个 PoolRef 被丢弃时,其分配的内存将返回到池中。
与 Box 和 Rc 的区别
PoolBox 与 Box 和 PoolRef 与 Rc 兼容 API,有以下例外
- 池中处理的类型必须是
Sized。这意味着池不会接受特质对象,即不允许有Pool<dyn A>。 - 构造函数需要一个
Pool参数,因此它们必然是不同的:而不是Rc::new(value),你有PoolRef::default(pool)来构造默认值,以及PoolRef::new(pool, value)作为Rc::new(value)的等效。同样适用于PoolBox。 PoolBox和PoolRef没有实现Default,因为构造实例需要一个Pool参数。使用PoolRef::default(pool)。- 目前没有
Weak对应的PoolRef。 - 实验性 API 未实现。
线程安全
Pool 是严格线程本地的,即它不实现 Sync,如果你仍然设法从两个不同的线程访问它,它将以令人震惊的方式失败。没有 Arc 的等效,因为向池中添加线程安全会降低性能,以至于池不再提供显著的性能优势,即使是在野外的最慢的系统分配器(我指的是 Windows)也不例外。
性能
你可以期望 Pool 总是优于系统分配器,尽管性能提升在不同平台上会有所不同。初步基准测试显示,它在 Linux 上的速度大约快两倍,在 Windows 上快 5-6 倍。像 jemalloc 这样的自定义分配器可能会产生更小的效益,但你几乎不可能找到一个能超越池的分配器。
对于具有有益的 PoolDefault 和 PoolClone 实现的数据类型,你可以期望获得更大的性能提升,“有益”的意思是指可以不初始化分配的内存的大部分情况。例如,sized_chunks::Chunk,它在 64 位平台上分配 528 字节,但只需要初始化其中的 16 个用于 PoolDefault。
示例
// Create a pool of `usize` with a max size of 1 (for argument's sake).
let mut pool: Pool<usize> = Pool::new(1);
{
// Create a reference handle to a usize initialised to 0.
// The pool starts out empty, so this triggers a normal heap alloc.
let value_ref = PoolRef::default(&mut pool);
assert_eq!(0, *value_ref); // You can deref it just like `Rc`.
} // `value_ref` is dropped here, and its heap memory goes on the pool.
// Check that we do indeed have one allocation in the pool now.
assert_eq!(1, pool.get_pool_size());
// Create another reference and initialise it to 31337, a good round number.
// This will reuse `value_ref`'s memory.
let another_value_ref = PoolRef::new(&mut pool, 31337);
assert_eq!(31337, *another_value_ref);
// Check that the pool is again empty after we reused the previous memory.
assert_eq!(0, pool.get_pool_size());
功能标志
目前有一个功能标志可用,即 default_impl,它需要一个夜间编译的 rustc,因为它依赖于 min_specialization 语言特性,该特性删除了 PoolDefaultImpl 特性,并为任何实现了 Clone 和 Default 的类型的 PoolClone 和 PoolDefault 提供了一个可覆盖的默认实现。 PoolDefaultImpl 是一个不幸的漏洞,用于绕过当前稳定 rustc 中缺乏特殊化的情况。