7 个不稳定版本 (3 个重大更改)
0.4.1 | 2022年2月21日 |
---|---|
0.4.0 | 2022年2月2日 |
0.3.0 | 2021年8月24日 |
0.2.1 | 2021年8月22日 |
0.1.1 | 2021年8月18日 |
#332 in 内存管理
用于 4 crate
72KB
1K SLoC
Scoped-Arena
Scoped-Arena提供具有显式作用域的区域分配器。
区域分配
区域分配器简单且提供极快的分配速度。
基本上,分配只需要在内存块内部指针上进行增量,以达到分配对象的对齐,然后到分配对象的大小,就完成了。
当内存块耗尽时,区域将分配新的更大的内存块。
然后,区域可以在所有分配的对象不再使用后重置,只保留最后一个内存块并重新使用它。
经过几次预热迭代后,唯一的内存块足够大,可以处理所有的分配,直到下一次重置。
示例
use scoped_arena::Scope;
struct Cat {
name: String,
hungry: bool,
}
/// Create new arena with `Global` allocator.
let mut scope = Scope::new();
/// Construct a cat and move it to the scope.
let cat: &mut Cat = scope.to_scope(Cat {
name: "Fluffy".to_owned(),
hungry: true,
});
// Now `cat` is a mutable reference bound to scope borrow lifetime.
assert_eq!(&cat.name, "Fluffy");
assert!(cat.hungry);
cat.hungry = false;
// This cat instance on scope will be automatically dropped when `scope` is dropped or reset.
// It is impossible to reset before last usage of `cat`.
// Next line will drop cat value and free memory occupied by it.
scope.reset();
// If there were more cats or any other objects put on scope they all would be dropped and memory freed.
作用域
为了更早地重用内存,这个crate提供了带有创建子-Scope
方法的Scope
。
当子-Scope
重置或丢弃时,它将丢弃所有存储的值,释放由作用域分配的内存,并将最后的新分配内存块刷新到父作用域。
而父-Scope
中分配的对象保持不变且仍然有效。
合理放置的作用域可以显着减少内存消耗。
例如,如果几个函数调用使用了大量动态内存,但不需要在调用者那里可用
它们可以提供子作用域。
同时,父作用域中分配的任何内存仍然保留分配状态。
创建子作用域成本低,在子作用域中分配的速度与在父作用域中分配一样快。
示例
use scoped_arena::{Scope, ScopeProxy};
fn heavy_on_memory(mut scope: Scope<'_>, foobar: &String) {
for _ in 0 .. 42 {
let foobar: &mut String = scope.to_scope(foobar.clone());
}
// new `scope` is dropped here and drops all allocated strings and frees memory.
}
let mut scope = Scope::new();
// Proxy is required to be friends with borrow checker.
// Creating sub-scope must lock parent `Scope` from being used, which requires mutable borrow, but any allocation borrows `Scope`.
// `Proxy` relaxes this a bit. `Proxy` borrows `Scope` mutably and tie allocated objects lifetime to scopes' borrow lifetime.
// So sub-scope can borrow proxy mutably while there are objects allocated from it.
let mut proxy = scope.proxy();
let foobar: &mut String = proxy.to_scope("foobar".to_owned());
// Make sub-scope for the call.
heavy_on_memory(proxy.scope(), &*foobar);
// If `heavy_on_memory` didn't trigger new memory object allocation in the scope,
// sub-scope drop would rewind scope's internals to exactly the same state.
// Otherwise last of new blocks will become current block in parent scope.
//
// Note that `foobar` is still alive.
heavy_on_memory(proxy.scope(), &*foobar);
heavy_on_memory(proxy.scope(), &*foobar);
heavy_on_memory(proxy.scope(), &*foobar);
heavy_on_memory(proxy.scope(), &*foobar);
// Once peak memory consumption is reached, any number of `heavy_on_memory` calls would not require new memory blocks to be allocated.
// Even `loop { heavy_on_memory(proxy.scope(), &*foobar) }` will settle on some big enough block.
丢弃
to_scope
和 try_to_scope
方法为需要丢弃的值存储丢弃粘合剂。在重置或丢弃作用域时,它将迭代并正确丢弃所有值。不需要丢弃粘合剂的类型不会添加丢弃粘合剂。 Scope
分配足够的内存并将值写入其中,没有账簿开销。
迭代收集
to_scope_from_iter
方法类似于 to_scope
,但它在迭代器上工作并返回切片。局限性在于 to_scope_from_iter
需要分配足够内存来容纳迭代器可以生成的上限。如果上限太大或者迭代器是无界的,它将始终失败。可以使用 try_to_scope_from_iter
,这样失败将是 Err
而不是 panic。迭代器可以产生比其报告的上限更多的项目是安全的,to_scope_from_iter
不会超出上限迭代。成功时,它返回对包含迭代器中项目的切片的可变引用,顺序排列。所有值将在作用域重置或丢弃时删除,与 to_scope
相同。
此方法特别适用于处理需要切片的 API(例如,对 FFI 的凝视),将数据收集到临时 Vec
会花费更多。
#![no_std] 支持
Scoped-Arena 是一个 no_std 包。它只依赖于核心包,可选地依赖于 alloc
包。
夜间 Rust 功能(allocator_api) 支持
Scoped-Arena 使用底层分配器的 allocator_api
特性和类型。
在夜间频道中,可以为此包启用 allocator_api
功能。然后将使用实际的 allocator_api
特性和类型,允许使用任何兼容的分配器。此外,&Arena
、&Scope
和 ScopeProxy
将实现 core::alloc::Allocator
,使它们适合用于标准 Rust 集合。
请注意,由于 Rust allocator_api
功能是不稳定的,此包的 allocator_api
功能也被视为不稳定,可能不会遵循 semver。也就是说,可以以补丁版本发布以跟踪 Rust 中 allocator_api
修改的变化,尽管它们不应破坏不使用该功能的代码。
许可
根据您的要求许可
- Apache License,版本 2.0,(license/APACHE 或 https://apache.ac.cn/licenses/LICENSE-2.0)
- MIT 许可证 (license/MIT 或 http://opensource.org/licenses/MIT)
任选其一。
贡献
除非您明确声明,否则您有意提交的任何贡献,根据 Apache-2.0 许可证定义,应按上述方式双重许可,不附加任何额外条款或条件。