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 中缺乏特殊化的情况。