-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 0dc4c1d
Showing
11 changed files
with
843 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
name: CI | ||
|
||
on: [push, pull_request] | ||
|
||
jobs: | ||
ci: | ||
runs-on: ubuntu-latest | ||
strategy: | ||
fail-fast: false | ||
matrix: | ||
rust-toolchain: [nightly] | ||
targets: [x86_64-unknown-linux-gnu, x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none-softfloat] | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: dtolnay/rust-toolchain@nightly | ||
with: | ||
toolchain: ${{ matrix.rust-toolchain }} | ||
components: rust-src, clippy, rustfmt | ||
targets: ${{ matrix.targets }} | ||
- name: Check rust version | ||
run: rustc --version --verbose | ||
- name: Check code format | ||
run: cargo fmt --all -- --check | ||
- name: Clippy | ||
run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default | ||
- name: Build | ||
run: cargo build --target ${{ matrix.targets }} --all-features | ||
- name: Unit test | ||
if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }} | ||
run: cargo test --target ${{ matrix.targets }} -- --nocapture | ||
|
||
doc: | ||
runs-on: ubuntu-latest | ||
strategy: | ||
fail-fast: false | ||
permissions: | ||
contents: write | ||
env: | ||
default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }} | ||
RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: dtolnay/rust-toolchain@nightly | ||
- name: Build docs | ||
continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }} | ||
run: | | ||
cargo doc --no-deps --all-features | ||
printf '<meta http-equiv="refresh" content="0;url=%s/index.html">' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html | ||
- name: Deploy to Github Pages | ||
if: ${{ github.ref == env.default-branch }} | ||
uses: JamesIves/github-pages-deploy-action@v4 | ||
with: | ||
single-commit: true | ||
branch: gh-pages | ||
folder: target/doc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/target | ||
/.vscode | ||
.DS_Store | ||
Cargo.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
[package] | ||
name = "allocator" | ||
version = "0.1.0" | ||
edition = "2021" | ||
authors = ["Yuekai Jia <[email protected]>"] | ||
description = "Various allocator algorithms in a unified interface" | ||
license = "GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0" | ||
homepage = "https://github.com/arceos-org/arceos" | ||
repository = "https://github.com/arceos-org/allocator" | ||
documentation = "https://arceos-org.github.io/allocator" | ||
|
||
[features] | ||
default = [] | ||
full = ["bitmap", "tlsf", "slab", "buddy", "allocator_api"] | ||
|
||
bitmap = ["dep:bitmap-allocator"] | ||
|
||
tlsf = ["dep:rlsf"] | ||
slab = ["dep:slab_allocator"] | ||
buddy = ["dep:buddy_system_allocator"] | ||
|
||
allocator_api = [] | ||
|
||
[dependencies] | ||
rlsf = { version = "0.2", optional = true } | ||
buddy_system_allocator = { version = "0.10", default-features = false, optional = true } | ||
slab_allocator = { git = "https://github.com/arceos-org/slab_allocator.git", tag = "v0.3.1", optional = true } | ||
bitmap-allocator = { git = "https://github.com/rcore-os/bitmap-allocator.git", rev = "88e871a", optional = true } | ||
|
||
[dev-dependencies] | ||
allocator = { path = ".", features = ["full"] } | ||
rand = { version = "0.8", features = ["small_rng"] } | ||
criterion = { version = "0.5", features = ["html_reports"] } | ||
|
||
[[bench]] | ||
name = "collections" | ||
harness = false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
#![feature(allocator_api)] | ||
#![feature(btreemap_alloc)] | ||
|
||
mod utils; | ||
|
||
use std::alloc::Allocator; | ||
use std::collections::BTreeMap; | ||
use std::io::Write; | ||
|
||
use allocator::{AllocatorRc, BuddyByteAllocator, SlabByteAllocator, TlsfByteAllocator}; | ||
use criterion::{black_box, criterion_group, criterion_main, Criterion}; | ||
use rand::{rngs::SmallRng, seq::SliceRandom, RngCore, SeedableRng}; | ||
|
||
use self::utils::MemoryPool; | ||
|
||
const POOL_SIZE: usize = 1024 * 1024 * 128; | ||
|
||
fn vec_push(n: usize, alloc: &(impl Allocator + Clone)) { | ||
let mut v: Vec<u32, _> = Vec::new_in(alloc.clone()); | ||
for _ in 0..n { | ||
v.push(0xdead_beef); | ||
} | ||
drop(v); | ||
} | ||
|
||
fn vec_rand_free(n: usize, blk_size: usize, alloc: &(impl Allocator + Clone)) { | ||
let mut v = Vec::new_in(alloc.clone()); | ||
for _ in 0..n { | ||
let block = Vec::<u64, _>::with_capacity_in(blk_size, alloc.clone()); | ||
v.push(block); | ||
} | ||
|
||
let mut rng = SmallRng::seed_from_u64(0xdead_beef); | ||
let mut index = Vec::with_capacity_in(n, alloc.clone()); | ||
for i in 0..n { | ||
index.push(i); | ||
} | ||
index.shuffle(&mut rng); | ||
|
||
for i in index { | ||
v[i] = Vec::new_in(alloc.clone()); | ||
} | ||
drop(v); | ||
} | ||
|
||
fn btree_map(n: usize, alloc: &(impl Allocator + Clone)) { | ||
let mut rng = SmallRng::seed_from_u64(0xdead_beef); | ||
let mut m = BTreeMap::new_in(alloc.clone()); | ||
for _ in 0..n { | ||
if rng.next_u32() % 5 == 0 && !m.is_empty() { | ||
m.pop_first(); | ||
} else { | ||
let value = rng.next_u32(); | ||
let mut key = Vec::new_in(alloc.clone()); | ||
write!(&mut key, "key_{value}").unwrap(); | ||
m.insert(key, value); | ||
} | ||
} | ||
m.clear(); | ||
drop(m); | ||
} | ||
|
||
fn bench(c: &mut Criterion, alloc_name: &str, alloc: impl Allocator + Clone) { | ||
let mut g = c.benchmark_group(alloc_name); | ||
g.bench_function("vec_push_3M", |b| { | ||
b.iter(|| vec_push(black_box(3_000_000), &alloc)); | ||
}); | ||
g.sample_size(10); | ||
g.bench_function("vec_rand_free_25K_64", |b| { | ||
b.iter(|| vec_rand_free(black_box(25_000), black_box(64), &alloc)); | ||
}); | ||
g.bench_function("vec_rand_free_7500_520", |b| { | ||
b.iter(|| vec_rand_free(black_box(7_500), black_box(520), &alloc)); | ||
}); | ||
g.bench_function("btree_map_50K", |b| { | ||
b.iter(|| btree_map(black_box(50_000), &alloc)); | ||
}); | ||
} | ||
|
||
fn criterion_benchmark(c: &mut Criterion) { | ||
let mut pool = MemoryPool::new(POOL_SIZE); | ||
bench(c, "system", std::alloc::System); | ||
bench( | ||
c, | ||
"tlsf", | ||
AllocatorRc::new(TlsfByteAllocator::new(), pool.as_slice()), | ||
); | ||
bench( | ||
c, | ||
"slab", | ||
AllocatorRc::new(SlabByteAllocator::new(), pool.as_slice()), | ||
); | ||
bench( | ||
c, | ||
"buddy", | ||
AllocatorRc::new(BuddyByteAllocator::new(), pool.as_slice()), | ||
); | ||
} | ||
|
||
criterion_group!(benches, criterion_benchmark); | ||
criterion_main!(benches); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
use std::alloc::Layout; | ||
use std::ptr::NonNull; | ||
|
||
pub struct MemoryPool { | ||
ptr: NonNull<u8>, | ||
layout: Layout, | ||
} | ||
|
||
impl MemoryPool { | ||
pub fn new(size: usize) -> Self { | ||
let layout = Layout::from_size_align(size, 4096).unwrap(); | ||
let ptr = NonNull::new(unsafe { std::alloc::alloc_zeroed(layout) }).unwrap(); | ||
Self { ptr, layout } | ||
} | ||
|
||
pub fn as_slice(&mut self) -> &mut [u8] { | ||
unsafe { core::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.layout.size()) } | ||
} | ||
} | ||
|
||
impl Drop for MemoryPool { | ||
fn drop(&mut self) { | ||
unsafe { std::alloc::dealloc(self.ptr.as_ptr(), self.layout) }; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
//! Bitmap allocation in page-granularity. | ||
//! | ||
//! TODO: adaptive size | ||
use bitmap_allocator::BitAlloc; | ||
|
||
use crate::{AllocError, AllocResult, BaseAllocator, PageAllocator}; | ||
|
||
// Support max 1M * 4096 = 4GB memory. | ||
type BitAllocUsed = bitmap_allocator::BitAlloc1M; | ||
|
||
/// A page-granularity memory allocator based on the [bitmap_allocator]. | ||
/// | ||
/// It internally uses a bitmap, each bit indicates whether a page has been | ||
/// allocated. | ||
/// | ||
/// The `PAGE_SIZE` must be a power of two. | ||
/// | ||
/// [bitmap_allocator]: https://github.com/rcore-os/bitmap-allocator | ||
pub struct BitmapPageAllocator<const PAGE_SIZE: usize> { | ||
base: usize, | ||
total_pages: usize, | ||
used_pages: usize, | ||
inner: BitAllocUsed, | ||
} | ||
|
||
impl<const PAGE_SIZE: usize> BitmapPageAllocator<PAGE_SIZE> { | ||
/// Creates a new empty `BitmapPageAllocator`. | ||
pub const fn new() -> Self { | ||
Self { | ||
base: 0, | ||
total_pages: 0, | ||
used_pages: 0, | ||
inner: BitAllocUsed::DEFAULT, | ||
} | ||
} | ||
} | ||
|
||
impl<const PAGE_SIZE: usize> BaseAllocator for BitmapPageAllocator<PAGE_SIZE> { | ||
fn init(&mut self, start: usize, size: usize) { | ||
assert!(PAGE_SIZE.is_power_of_two()); | ||
let end = super::align_down(start + size, PAGE_SIZE); | ||
let start = super::align_up(start, PAGE_SIZE); | ||
self.base = start; | ||
self.total_pages = (end - start) / PAGE_SIZE; | ||
self.inner.insert(0..self.total_pages); | ||
} | ||
|
||
fn add_memory(&mut self, _start: usize, _size: usize) -> AllocResult { | ||
Err(AllocError::NoMemory) // unsupported | ||
} | ||
} | ||
|
||
impl<const PAGE_SIZE: usize> PageAllocator for BitmapPageAllocator<PAGE_SIZE> { | ||
const PAGE_SIZE: usize = PAGE_SIZE; | ||
|
||
fn alloc_pages(&mut self, num_pages: usize, align_pow2: usize) -> AllocResult<usize> { | ||
if align_pow2 % PAGE_SIZE != 0 { | ||
return Err(AllocError::InvalidParam); | ||
} | ||
let align_pow2 = align_pow2 / PAGE_SIZE; | ||
if !align_pow2.is_power_of_two() { | ||
return Err(AllocError::InvalidParam); | ||
} | ||
let align_log2 = align_pow2.trailing_zeros() as usize; | ||
match num_pages.cmp(&1) { | ||
core::cmp::Ordering::Equal => self.inner.alloc().map(|idx| idx * PAGE_SIZE + self.base), | ||
core::cmp::Ordering::Greater => self | ||
.inner | ||
.alloc_contiguous(num_pages, align_log2) | ||
.map(|idx| idx * PAGE_SIZE + self.base), | ||
_ => return Err(AllocError::InvalidParam), | ||
} | ||
.ok_or(AllocError::NoMemory) | ||
.inspect(|_| self.used_pages += num_pages) | ||
} | ||
|
||
fn dealloc_pages(&mut self, pos: usize, num_pages: usize) { | ||
// TODO: not decrease `used_pages` if deallocation failed | ||
self.used_pages -= num_pages; | ||
self.inner.dealloc((pos - self.base) / PAGE_SIZE) | ||
} | ||
|
||
fn total_pages(&self) -> usize { | ||
self.total_pages | ||
} | ||
|
||
fn used_pages(&self) -> usize { | ||
self.used_pages | ||
} | ||
|
||
fn available_pages(&self) -> usize { | ||
self.total_pages - self.used_pages | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
//! Buddy memory allocation. | ||
//! | ||
//! TODO: more efficient | ||
use buddy_system_allocator::Heap; | ||
use core::alloc::Layout; | ||
use core::ptr::NonNull; | ||
|
||
use crate::{AllocError, AllocResult, BaseAllocator, ByteAllocator}; | ||
|
||
/// A byte-granularity memory allocator based on the [buddy_system_allocator]. | ||
/// | ||
/// [buddy_system_allocator]: https://docs.rs/buddy_system_allocator/latest/buddy_system_allocator/ | ||
pub struct BuddyByteAllocator { | ||
inner: Heap<32>, | ||
} | ||
|
||
impl BuddyByteAllocator { | ||
/// Creates a new empty `BuddyByteAllocator`. | ||
pub const fn new() -> Self { | ||
Self { | ||
inner: Heap::<32>::new(), | ||
} | ||
} | ||
} | ||
|
||
impl BaseAllocator for BuddyByteAllocator { | ||
fn init(&mut self, start: usize, size: usize) { | ||
unsafe { self.inner.init(start, size) }; | ||
} | ||
|
||
fn add_memory(&mut self, start: usize, size: usize) -> AllocResult { | ||
unsafe { self.inner.add_to_heap(start, start + size) }; | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl ByteAllocator for BuddyByteAllocator { | ||
fn alloc(&mut self, layout: Layout) -> AllocResult<NonNull<u8>> { | ||
self.inner.alloc(layout).map_err(|_| AllocError::NoMemory) | ||
} | ||
|
||
fn dealloc(&mut self, pos: NonNull<u8>, layout: Layout) { | ||
self.inner.dealloc(pos, layout) | ||
} | ||
|
||
fn total_bytes(&self) -> usize { | ||
self.inner.stats_total_bytes() | ||
} | ||
|
||
fn used_bytes(&self) -> usize { | ||
self.inner.stats_alloc_actual() | ||
} | ||
|
||
fn available_bytes(&self) -> usize { | ||
self.inner.stats_total_bytes() - self.inner.stats_alloc_actual() | ||
} | ||
} |
Oops, something went wrong.