Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/yvt/rlsf
Constant-time dynamic memory allocator in Rust
https://github.com/yvt/rlsf
bare-metal embedded-systems library memory-allocator real-time-systems rust
Last synced: about 7 hours ago
JSON representation
Constant-time dynamic memory allocator in Rust
- Host: GitHub
- URL: https://github.com/yvt/rlsf
- Owner: yvt
- License: apache-2.0
- Created: 2021-04-04T12:54:40.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2023-02-17T01:04:43.000Z (over 1 year ago)
- Last Synced: 2024-05-02T13:09:52.018Z (6 months ago)
- Topics: bare-metal, embedded-systems, library, memory-allocator, real-time-systems, rust
- Language: Rust
- Homepage:
- Size: 221 KB
- Stars: 73
- Watchers: 3
- Forks: 4
- Open Issues: 7
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE-APACHE
Awesome Lists containing this project
README
# rlsf
This crate implements the TLSF (Two-Level Segregated Fit) dynamic memory
allocation algorithm¹. Requires Rust 1.61.0 or later.- **Allocation and deallocation operations are guaranteed to complete in
constant time.** TLSF is suitable for real-time applications.- **Fast and small.** You can have both. It was found to be smaller and
faster² than most `no_std`-compatible allocator crates.- **Accepts any kinds of memory pools.** The low-level type
[`Tlsf`](#tlsf-core-api) just divides any memory pools you provide
(e.g., a `static` array) to serve allocation requests.
The high-level type [`GlobalTlsf`](#globaltlsf-global-allocator)
automatically acquires memory pages using standard methods on supported
systems.- **This crate supports `#![no_std]`.** It can be used in bare-metal and
RTOS-based applications.¹ M. Masmano, I. Ripoll, A. Crespo and J. Real, "TLSF: a new dynamic
memory allocator for real-time systems," *Proceedings. 16th Euromicro
Conference on Real-Time Systems*, 2004. ECRTS 2004., Catania, Italy, 2004,
pp. 79-88, doi: 10.1109/EMRTS.2004.1311009.² Compiled for and measured on a STM32F401 microcontroller using
FarCri.rs.## Measured Performance
![The result of latency measurement on STM32F401 is shown here. rlsf:
260–320 cycles. buddy-alloc: 340–440 cycles. umm_malloc: 300–700 cycles.
dlmalloc: 450–750 cycles.
](https://yvt.jp/files/programs/rlsf/time-cm4f-xf-3.svg)![The result of code size measurement on WebAssembly is shown here. rlsf:
1267 bytes, rlsf + pool coalescing: 1584 bytes, wee_alloc: 1910 bytes,
dlmalloc: 9613 bytes.
](https://yvt.jp/files/programs/rlsf/size-wasm-xf.svg)## Drawbacks
- **It does not support concurrent access.** A whole pool must be locked
for allocation and deallocation. If you use a FIFO lock to protect the
pool, the worst-case execution time will be `O(num_contending_threads)`.
You should consider using a thread-caching memory allocator (e.g.,
TCMalloc, jemalloc) if achieving a maximal throughput in a highly
concurrent environment is desired.- **Segregated freelists with constant-time lookup cause internal
fragmentation proportional to free block sizes.** The `SLLEN` paramter
allows for adjusting the trade-off between fewer freelists and lower
fragmentation.- **No special handling for small allocations (one algorithm for all
sizes).** This may lead to inefficiencies in allocation-heavy
applications compared to modern scalable memory allocators, such as
glibc and jemalloc.## Examples
### `Tlsf`: Core API
```rust
use rlsf::Tlsf;
use std::{mem::MaybeUninit, alloc::Layout};let mut pool = [MaybeUninit::uninit(); 65536];
// On 32-bit systems, the maximum block size is 16 << FLLEN = 65536 bytes.
// The worst-case internal fragmentation is (16 << FLLEN) / SLLEN - 2 = 4094 bytes.
// `'pool` represents the memory pool's lifetime (`pool` in this case).
let mut tlsf: Tlsf<'_, u16, u16, 12, 16> = Tlsf::new();
// ^^ ^^ ^^
// | | |
// 'pool | SLLEN
// FLLEN
tlsf.insert_free_block(&mut pool);unsafe {
let mut ptr1 = tlsf.allocate(Layout::new::()).unwrap().cast::();
let mut ptr2 = tlsf.allocate(Layout::new::()).unwrap().cast::();
*ptr1.as_mut() = 42;
*ptr2.as_mut() = 56;
assert_eq!(*ptr1.as_ref(), 42);
assert_eq!(*ptr2.as_ref(), 56);
tlsf.deallocate(ptr1.cast(), Layout::new::().align());
tlsf.deallocate(ptr2.cast(), Layout::new::().align());
}
```### `GlobalTlsf`: Global Allocator
`GlobalTlsf` automatically acquires memory pages through platform-specific
mechanisms. It doesn't support returning memory pages to the system even if
the system supports it.```rust
#[cfg(all(target_arch = "wasm32", not(target_feature = "atomics")))]
#[global_allocator]
static A: rlsf::SmallGlobalTlsf = rlsf::SmallGlobalTlsf::new();let mut m = std::collections::HashMap::new();
m.insert(1, 2);
m.insert(5, 3);
drop(m);
```## Details
### Changes from the Original Algorithm
- The end of each memory pool is capped by a sentinel block
(a permanently occupied block) instead of a normal block with a
last-block-in-pool flag. This simplifies the code a bit and improves
its worst-case performance and code size.
## Cargo Features
- `unstable`: Enables experimental features that are exempt from the API
stability guarantees.## License
MIT/Apache-2.0