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

MIT/Apache

72KB
1K SLoC

Scoped-Arena

crates docs actions MIT/Apache loc

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_scopetry_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&ScopeScopeProxy 将实现 core::alloc::Allocator,使它们适合用于标准 Rust 集合。

请注意,由于 Rust allocator_api 功能是不稳定的,此包的 allocator_api 功能也被视为不稳定,可能不会遵循 semver。也就是说,可以以补丁版本发布以跟踪 Rust 中 allocator_api 修改的变化,尽管它们不应破坏不使用该功能的代码。

许可

根据您的要求许可

任选其一。

贡献

除非您明确声明,否则您有意提交的任何贡献,根据 Apache-2.0 许可证定义,应按上述方式双重许可,不附加任何额外条款或条件。

无运行时依赖