From 2b545ba948e72c26d669541b797ed10076c91545 Mon Sep 17 00:00:00 2001 From: Yuekai Jia Date: Sat, 29 Jul 2023 00:51:58 +0800 Subject: [PATCH 1/3] alloc: minor changes of allocator API --- Cargo.lock | 17 ++++++----- README.md | 6 ++-- crates/allocator/Cargo.toml | 3 ++ crates/allocator/src/buddy.rs | 13 ++++---- crates/allocator/src/lib.rs | 57 +++++++++++++++++++++++++++++++++-- crates/allocator/src/slab.rs | 13 ++++---- modules/axalloc/Cargo.toml | 3 +- modules/axalloc/src/lib.rs | 29 +++++++++++++----- modules/axdriver/src/ixgbe.rs | 10 +++--- modules/axdriver/src/lib.rs | 1 + modules/axruntime/src/lib.rs | 8 ++--- ulib/axlibc/c/stdlib.c | 6 ++-- ulib/axlibc/src/malloc.rs | 24 +++++++++------ 13 files changed, 136 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index be9201cf39..1a54d6e5dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -195,6 +195,7 @@ version = "0.1.0" dependencies = [ "allocator", "axerrno", + "cfg-if", "log", "memory_addr", "spinlock", @@ -1409,18 +1410,18 @@ checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.175" +version = "1.0.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d25439cd7397d044e2748a6fe2432b5e85db703d6d097bd014b3c0ad1ebff0b" +checksum = "63ba2516aa6bf82e0b19ca8b50019d52df58455d3cf9bdaf6315225fdd0c560a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.175" +version = "1.0.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b23f7ade6f110613c0d63858ddb8b94c1041f550eab58a16b371bdf2c9c80ab4" +checksum = "401797fe7833d72109fedec6bfcbe67c0eed9b99772f26eb8afd261f0abc6fd3" dependencies = [ "proc-macro2", "quote", @@ -1429,9 +1430,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -1857,9 +1858,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7" +checksum = "25b5872fa2e10bd067ae946f927e726d7d603eaeb6e02fa6a350e0722d2b8c11" dependencies = [ "memchr", ] diff --git a/README.md b/README.md index 116f76b4f9..21fa3e615b 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ export PATH=`pwd`/x86_64-linux-musl-cross/bin:`pwd`/aarch64-linux-musl-cross/bin ```bash # in arceos directory -make A=path/to/app ARCH= LOG= NET=[y|n] BLK=[y|n] +make A=path/to/app ARCH= LOG= ``` Where `` should be one of `riscv64`, `aarch64`,`x86_64`. @@ -106,9 +106,11 @@ More arguments and targets can be found in [Makefile](Makefile). For example, to run the [httpserver](apps/net/httpserver/) on `qemu-system-aarch64` with 4 cores: ```bash -make A=apps/net/httpserver ARCH=aarch64 LOG=info NET=y SMP=4 run +make A=apps/net/httpserver ARCH=aarch64 LOG=info SMP=4 run NET=y ``` +Note that the `NET=y` argument is required to enable the network device in QEMU. These arguments (`BLK`, `GRAPHIC`, etc.) only take effect at runtime not build time. + ### Your custom apps #### Rust diff --git a/crates/allocator/Cargo.toml b/crates/allocator/Cargo.toml index b3e727f5b4..e2ce793b34 100644 --- a/crates/allocator/Cargo.toml +++ b/crates/allocator/Cargo.toml @@ -9,6 +9,9 @@ homepage = "https://github.com/rcore-os/arceos" repository = "https://github.com/rcore-os/arceos/tree/main/crates/allocator" documentation = "https://rcore-os.github.io/arceos/allocator/index.html" +[features] +allocator_api = [] + [dependencies] buddy_system_allocator = { version = "0.9", default-features = false } bitmap-allocator = { git = "https://github.com/rcore-os/bitmap-allocator.git", rev = "88e871a" } diff --git a/crates/allocator/src/buddy.rs b/crates/allocator/src/buddy.rs index b5e647b9f4..7b33b1d99d 100644 --- a/crates/allocator/src/buddy.rs +++ b/crates/allocator/src/buddy.rs @@ -4,6 +4,7 @@ use buddy_system_allocator::Heap; use core::alloc::Layout; +use core::num::NonZeroUsize; use crate::{AllocError, AllocResult, BaseAllocator, ByteAllocator}; @@ -35,17 +36,17 @@ impl BaseAllocator for BuddyByteAllocator { } impl ByteAllocator for BuddyByteAllocator { - fn alloc(&mut self, size: usize, align_pow2: usize) -> AllocResult { + fn alloc(&mut self, layout: Layout) -> AllocResult { self.inner - .alloc(Layout::from_size_align(size, align_pow2).unwrap()) - .map(|ptr| ptr.as_ptr() as usize) + .alloc(layout) + .map(|ptr| ptr.addr()) .map_err(|_| AllocError::NoMemory) } - fn dealloc(&mut self, pos: usize, size: usize, align_pow2: usize) { + fn dealloc(&mut self, pos: NonZeroUsize, layout: Layout) { self.inner.dealloc( - unsafe { core::ptr::NonNull::new_unchecked(pos as *mut u8) }, - Layout::from_size_align(size, align_pow2).unwrap(), + unsafe { core::ptr::NonNull::new_unchecked(pos.get() as _) }, + layout, ) } diff --git a/crates/allocator/src/lib.rs b/crates/allocator/src/lib.rs index b584a65097..7e30fe0cf6 100644 --- a/crates/allocator/src/lib.rs +++ b/crates/allocator/src/lib.rs @@ -9,7 +9,9 @@ //! - [`IdAllocator`]: Used to allocate unique IDs. #![no_std] +#![feature(strict_provenance)] #![feature(result_option_inspect)] +#![cfg_attr(feature = "allocator_api", feature(allocator_api))] mod bitmap; mod buddy; @@ -19,6 +21,9 @@ pub use bitmap::BitmapPageAllocator; pub use buddy::BuddyByteAllocator; pub use slab::SlabByteAllocator; +use core::alloc::Layout; +use core::num::NonZeroUsize; + /// The error type used for allocation. #[derive(Debug)] pub enum AllocError { @@ -47,10 +52,10 @@ pub trait BaseAllocator { /// Byte-granularity allocator. pub trait ByteAllocator: BaseAllocator { /// Allocate memory with the given size (in bytes) and alignment. - fn alloc(&mut self, size: usize, align_pow2: usize) -> AllocResult; + fn alloc(&mut self, layout: Layout) -> AllocResult; /// Deallocate memory at the given position, size, and alignment. - fn dealloc(&mut self, pos: usize, size: usize, align_pow2: usize); + fn dealloc(&mut self, pos: NonZeroUsize, layout: Layout); /// Returns total memory size in bytes. fn total_bytes(&self) -> usize; @@ -116,3 +121,51 @@ const fn align_down(pos: usize, align: usize) -> usize { const fn align_up(pos: usize, align: usize) -> usize { (pos + align - 1) & !(align - 1) } + +#[cfg(feature = "allocator_api")] +mod allocator_api { + extern crate alloc; + + use super::ByteAllocator; + use alloc::rc::Rc; + use core::alloc::{AllocError, Allocator, Layout}; + use core::cell::RefCell; + use core::ptr::NonNull; + + /// A byte-allocator wrapped in [`Rc`] that implements [`core::alloc::Allocator`]. + pub struct AllocatorRc(Rc>); + + impl AllocatorRc { + /// Creates a new allocator with the given memory pool. + pub fn new(mut inner: A, pool: &mut [u8]) -> Self { + inner.init(pool.as_mut_ptr() as usize, pool.len()); + Self(Rc::new(RefCell::new(inner))) + } + } + + unsafe impl Allocator for AllocatorRc { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + match layout.size() { + 0 => Ok(NonNull::slice_from_raw_parts(NonNull::dangling(), 0)), + size => { + let raw_addr = self.0.borrow_mut().alloc(layout).map_err(|_| AllocError)?; + let ptr = unsafe { NonNull::new_unchecked(raw_addr.get() as _) }; + Ok(NonNull::slice_from_raw_parts(ptr, size)) + } + } + } + + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + self.0.borrow_mut().dealloc(ptr.addr(), layout) + } + } + + impl Clone for AllocatorRc { + fn clone(&self) -> Self { + Self(self.0.clone()) + } + } +} + +#[cfg(feature = "allocator_api")] +pub use allocator_api::AllocatorRc; diff --git a/crates/allocator/src/slab.rs b/crates/allocator/src/slab.rs index 426e09daed..ba643c9542 100644 --- a/crates/allocator/src/slab.rs +++ b/crates/allocator/src/slab.rs @@ -4,6 +4,7 @@ use super::{AllocError, AllocResult, BaseAllocator, ByteAllocator}; use core::alloc::Layout; +use core::num::NonZeroUsize; use slab_allocator::Heap; /// A byte-granularity memory allocator based on the [slab allocator]. @@ -42,17 +43,15 @@ impl BaseAllocator for SlabByteAllocator { } impl ByteAllocator for SlabByteAllocator { - fn alloc(&mut self, size: usize, align_pow2: usize) -> AllocResult { + fn alloc(&mut self, layout: Layout) -> AllocResult { self.inner_mut() - .allocate(Layout::from_size_align(size, align_pow2).unwrap()) + .allocate(layout) + .map(|addr| NonZeroUsize::new(addr).unwrap()) .map_err(|_| AllocError::NoMemory) } - fn dealloc(&mut self, pos: usize, size: usize, align_pow2: usize) { - unsafe { - self.inner_mut() - .deallocate(pos, Layout::from_size_align(size, align_pow2).unwrap()) - } + fn dealloc(&mut self, pos: NonZeroUsize, layout: Layout) { + unsafe { self.inner_mut().deallocate(pos.get(), layout) } } fn total_bytes(&self) -> usize { diff --git a/modules/axalloc/Cargo.toml b/modules/axalloc/Cargo.toml index 79edbaf966..83ef1527ff 100644 --- a/modules/axalloc/Cargo.toml +++ b/modules/axalloc/Cargo.toml @@ -11,7 +11,8 @@ documentation = "https://rcore-os.github.io/arceos/axalloc/index.html" [dependencies] log = "0.4" +cfg-if = "1.0" spinlock = { path = "../../crates/spinlock" } memory_addr = { path = "../../crates/memory_addr" } -allocator = { path = "../../crates/allocator" } +allocator = { path = "../../crates/allocator"} axerrno = { path = "../../crates/axerrno" } diff --git a/modules/axalloc/src/lib.rs b/modules/axalloc/src/lib.rs index 5a29fb8d79..9ddcb818fb 100644 --- a/modules/axalloc/src/lib.rs +++ b/modules/axalloc/src/lib.rs @@ -16,6 +16,7 @@ mod page; use allocator::{AllocResult, BaseAllocator, ByteAllocator, PageAllocator}; use allocator::{BitmapPageAllocator, SlabByteAllocator}; use core::alloc::{GlobalAlloc, Layout}; +use core::num::NonZeroUsize; use spinlock::SpinNoIrq; const PAGE_SIZE: usize = 0x1000; @@ -46,6 +47,11 @@ impl GlobalAllocator { } } + /// Returns the name of the allocator. + pub const fn name(&self) -> &'static str { + "slab" + } + /// Initializes the allocator with the given region. /// /// It firstly adds the whole region to the page allocator, then allocates @@ -77,15 +83,18 @@ impl GlobalAllocator { /// /// `align_pow2` must be a power of 2, and the returned region bound will be /// aligned to it. - pub fn alloc(&self, size: usize, align_pow2: usize) -> AllocResult { + pub fn alloc(&self, layout: Layout) -> AllocResult { // simple two-level allocator: if no heap memory, allocate from the page allocator. let mut balloc = self.balloc.lock(); loop { - if let Ok(ptr) = balloc.alloc(size, align_pow2) { + if let Ok(ptr) = balloc.alloc(layout) { return Ok(ptr); } else { let old_size = balloc.total_bytes(); - let expand_size = old_size.max(size).next_power_of_two().max(PAGE_SIZE); + let expand_size = old_size + .max(layout.size()) + .next_power_of_two() + .max(PAGE_SIZE); let heap_ptr = self.alloc_pages(expand_size / PAGE_SIZE, PAGE_SIZE)?; debug!( "expand heap memory: [{:#x}, {:#x})", @@ -104,8 +113,8 @@ impl GlobalAllocator { /// undefined. /// /// [`alloc`]: GlobalAllocator::alloc - pub fn dealloc(&self, pos: usize, size: usize, align_pow2: usize) { - self.balloc.lock().dealloc(pos, size, align_pow2) + pub fn dealloc(&self, pos: NonZeroUsize, layout: Layout) { + self.balloc.lock().dealloc(pos, layout) } /// Allocates contiguous pages. @@ -152,15 +161,19 @@ impl GlobalAllocator { unsafe impl GlobalAlloc for GlobalAllocator { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - if let Ok(ptr) = GlobalAllocator::alloc(self, layout.size(), layout.align()) { - ptr as _ + if let Ok(ptr) = GlobalAllocator::alloc(self, layout) { + ptr.get() as _ } else { alloc::alloc::handle_alloc_error(layout) } } unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - GlobalAllocator::dealloc(self, ptr as _, layout.size(), layout.align()) + GlobalAllocator::dealloc( + self, + NonZeroUsize::new(ptr as _).expect("dealloc null ptr"), + layout, + ) } } diff --git a/modules/axdriver/src/ixgbe.rs b/modules/axdriver/src/ixgbe.rs index b8f6655f06..acd3441280 100644 --- a/modules/axdriver/src/ixgbe.rs +++ b/modules/axdriver/src/ixgbe.rs @@ -1,14 +1,15 @@ use axalloc::global_allocator; use axhal::mem::{phys_to_virt, virt_to_phys}; -use core::ptr::NonNull; +use core::{alloc::Layout, ptr::NonNull}; use driver_net::ixgbe::{IxgbeHal, PhysAddr as IxgbePhysAddr}; pub struct IxgbeHalImpl; unsafe impl IxgbeHal for IxgbeHalImpl { fn dma_alloc(size: usize) -> (IxgbePhysAddr, NonNull) { - let vaddr = if let Ok(vaddr) = global_allocator().alloc(size, 2) { - vaddr + let layout = Layout::from_size_align(size, 8).unwrap(); + let vaddr = if let Ok(vaddr) = global_allocator().alloc(layout) { + vaddr.get() } else { return (0, NonNull::dangling()); }; @@ -18,7 +19,8 @@ unsafe impl IxgbeHal for IxgbeHalImpl { } unsafe fn dma_dealloc(_paddr: IxgbePhysAddr, vaddr: NonNull, size: usize) -> i32 { - global_allocator().dealloc(vaddr.as_ptr() as usize, size, 2); + let layout = Layout::from_size_align(size, 8).unwrap(); + global_allocator().dealloc(vaddr.addr(), layout); 0 } diff --git a/modules/axdriver/src/lib.rs b/modules/axdriver/src/lib.rs index 05b5d98524..43db35283b 100644 --- a/modules/axdriver/src/lib.rs +++ b/modules/axdriver/src/lib.rs @@ -57,6 +57,7 @@ #![no_std] #![feature(doc_auto_cfg)] #![feature(associated_type_defaults)] +#![feature(strict_provenance)] #[macro_use] extern crate log; diff --git a/modules/axruntime/src/lib.rs b/modules/axruntime/src/lib.rs index 481ccb2788..3801c9d172 100644 --- a/modules/axruntime/src/lib.rs +++ b/modules/axruntime/src/lib.rs @@ -138,10 +138,7 @@ pub extern "C" fn rust_main(cpu_id: usize, dtb: usize) -> ! { } #[cfg(feature = "alloc")] - { - info!("Initialize global memory allocator..."); - init_allocator(); - } + init_allocator(); #[cfg(feature = "paging")] { @@ -201,6 +198,9 @@ pub extern "C" fn rust_main(cpu_id: usize, dtb: usize) -> ! { fn init_allocator() { use axhal::mem::{memory_regions, phys_to_virt, MemRegionFlags}; + info!("Initialize global memory allocator..."); + info!(" use {} allocator.", axalloc::global_allocator().name()); + let mut max_region_size = 0; let mut max_region_paddr = 0.into(); for r in memory_regions() { diff --git a/ulib/axlibc/c/stdlib.c b/ulib/axlibc/c/stdlib.c index b622273571..91c742dd31 100644 --- a/ulib/axlibc/c/stdlib.c +++ b/ulib/axlibc/c/stdlib.c @@ -404,8 +404,10 @@ long double strtold(const char *restrict s, char **restrict p) #endif // AX_CONFIG_FP_SIMD +typedef int (*cmpfun)(const void *, const void *); + // TODO -void qsort(void *, size_t, size_t, int (*)(const void *, const void *)) +void qsort(void *base, size_t nel, size_t width, cmpfun cmp) { unimplemented(); return; @@ -419,7 +421,7 @@ int mkostemp(char *__template, int __flags) } // TODO -int system(const char *) +int system(const char *cmd) { unimplemented(); return 0; diff --git a/ulib/axlibc/src/malloc.rs b/ulib/axlibc/src/malloc.rs index b36dac076f..d67597d53d 100644 --- a/ulib/axlibc/src/malloc.rs +++ b/ulib/axlibc/src/malloc.rs @@ -6,6 +6,7 @@ //! order to maintain consistency, C user programs also choose to share the kernel heap, //! skipping the sys_brk step. +use core::alloc::Layout; use core::result::Result::{Err, Ok}; use core::{ffi::c_void, mem::size_of}; @@ -23,9 +24,11 @@ pub unsafe extern "C" fn ax_malloc(size: usize) -> *mut c_void { // Allocate `(actual length) + 8`. The lowest 8 Bytes are stored in the actual allocated space size. // This is because free(uintptr_t) has only one parameter representing the address, // So we need to save in advance to know the size of the memory space that needs to be released - match axalloc::global_allocator().alloc(size + size_of::(), BYTES_OF_USIZE) - { + let layout = + Layout::from_size_align(size + size_of::(), BYTES_OF_USIZE).unwrap(); + match axalloc::global_allocator().alloc(layout) { Ok(addr) => { + let addr = addr.get(); let control_block = unsafe { &mut *(addr as *mut MemoryControlBlock) }; control_block.size = size; (addr + 8) as *mut c_void @@ -41,15 +44,16 @@ pub unsafe extern "C" fn ax_malloc(size: usize) -> *mut c_void { /// (currently used) does not check the validity of address to be released. #[no_mangle] pub unsafe extern "C" fn ax_free(addr: *mut c_void) { + let addr = (addr as usize) + .checked_sub(size_of::()) + .filter(|&a| a > 0) + .expect("free a null pointer"); let size = { - let control_block = unsafe { - &mut *((addr as usize - size_of::()) as *mut MemoryControlBlock) - }; + let control_block = unsafe { &mut *(addr as *mut MemoryControlBlock) }; control_block.size }; - axalloc::global_allocator().dealloc( - addr as usize - size_of::(), - size + size_of::(), - BYTES_OF_USIZE, - ) + let ptr = unsafe { core::num::NonZeroUsize::new_unchecked(addr) }; + let layout = + Layout::from_size_align(size + size_of::(), BYTES_OF_USIZE).unwrap(); + axalloc::global_allocator().dealloc(ptr, layout) } From 7d5b91033bbfc6d8eea54e2fc1347a191257040c Mon Sep 17 00:00:00 2001 From: Yuekai Jia Date: Fri, 28 Jul 2023 21:34:33 +0800 Subject: [PATCH 2/3] alloc: add benchmark for all allocators --- Cargo.lock | 304 +++++++++++++++++++++++- crates/allocator/Cargo.toml | 9 + crates/allocator/benches/collections.rs | 96 ++++++++ crates/allocator/benches/utils/mod.rs | 25 ++ crates/allocator/tests/allocator.rs | 133 +++++++++++ crates/slab_allocator/src/slab.rs | 6 - 6 files changed, 564 insertions(+), 9 deletions(-) create mode 100644 crates/allocator/benches/collections.rs create mode 100644 crates/allocator/benches/utils/mod.rs create mode 100644 crates/allocator/tests/allocator.rs diff --git a/Cargo.lock b/Cargo.lock index 1a54d6e5dc..a9b4471b1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,8 +24,11 @@ dependencies = [ name = "allocator" version = "0.1.0" dependencies = [ + "allocator", "bitmap-allocator", "buddy_system_allocator", + "criterion", + "rand", "slab_allocator", ] @@ -44,6 +47,18 @@ dependencies = [ "libc", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + [[package]] name = "arceos-bwbench" version = "0.1.0" @@ -178,7 +193,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -546,13 +561,19 @@ dependencies = [ "bitflags 2.3.3", ] +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cbindgen" version = "0.24.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b922faaf31122819ec80c4047cc684c6979a087366c069611e33649bf98e18d" dependencies = [ - "clap", + "clap 3.2.25", "heck", "indexmap 1.9.3", "log", @@ -601,6 +622,33 @@ dependencies = [ "winapi", ] +[[package]] +name = "ciborium" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" + +[[package]] +name = "ciborium-ll" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +dependencies = [ + "ciborium-io", + "half", +] + [[package]] name = "clang-sys" version = "1.6.1" @@ -620,13 +668,32 @@ checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", "bitflags 1.3.2", - "clap_lex", + "clap_lex 0.2.4", "indexmap 1.9.3", "strsim", "termcolor", "textwrap", ] +[[package]] +name = "clap" +version = "4.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" +dependencies = [ + "anstyle", + "clap_lex 0.5.0", +] + [[package]] name = "clap_lex" version = "0.2.4" @@ -636,6 +703,12 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + [[package]] name = "core-foundation-sys" version = "0.8.4" @@ -657,12 +730,91 @@ dependencies = [ "syn 2.0.27", ] +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap 4.3.19", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + [[package]] name = "critical-section" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52" +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + [[package]] name = "defmt" version = "0.3.5" @@ -855,6 +1007,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + [[package]] name = "handler_table" version = "0.1.0" @@ -908,6 +1066,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + [[package]] name = "iana-time-zone" version = "0.1.57" @@ -951,6 +1115,26 @@ dependencies = [ "hashbrown 0.14.0", ] +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi 0.3.2", + "rustix", + "windows-sys", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.9" @@ -1059,6 +1243,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + [[package]] name = "memory_addr" version = "0.1.0" @@ -1109,12 +1302,28 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.2", + "libc", +] + [[package]] name = "once_cell" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + [[package]] name = "os_str_bytes" version = "6.5.1" @@ -1172,6 +1381,34 @@ dependencies = [ "syn 2.0.27", ] +[[package]] +name = "plotters" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" + +[[package]] +name = "plotters-svg" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +dependencies = [ + "plotters-backend", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1282,6 +1519,28 @@ dependencies = [ "bitflags 2.3.3", ] +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + [[package]] name = "redox_syscall" version = "0.3.5" @@ -1371,6 +1630,15 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "sbi-rt" version = "0.0.2" @@ -1592,6 +1860,16 @@ dependencies = [ name = "timer_list" version = "0.1.0" +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tock-registers" version = "0.8.1" @@ -1673,6 +1951,16 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793" +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" @@ -1739,6 +2027,16 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "which" version = "4.4.0" diff --git a/crates/allocator/Cargo.toml b/crates/allocator/Cargo.toml index e2ce793b34..1eb6b17990 100644 --- a/crates/allocator/Cargo.toml +++ b/crates/allocator/Cargo.toml @@ -16,3 +16,12 @@ allocator_api = [] buddy_system_allocator = { version = "0.9", default-features = false } bitmap-allocator = { git = "https://github.com/rcore-os/bitmap-allocator.git", rev = "88e871a" } slab_allocator = { path = "../slab_allocator" } + +[dev-dependencies] +allocator = { path = ".", features = ["allocator_api"] } +rand = { version = "0.8", features = ["small_rng"] } +criterion = { version = "0.5", features = ["html_reports"] } + +[[bench]] +name = "collections" +harness = false diff --git a/crates/allocator/benches/collections.rs b/crates/allocator/benches/collections.rs new file mode 100644 index 0000000000..1c3c77a003 --- /dev/null +++ b/crates/allocator/benches/collections.rs @@ -0,0 +1,96 @@ +#![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}; +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 = 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::::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, + "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); diff --git a/crates/allocator/benches/utils/mod.rs b/crates/allocator/benches/utils/mod.rs new file mode 100644 index 0000000000..995e1535c9 --- /dev/null +++ b/crates/allocator/benches/utils/mod.rs @@ -0,0 +1,25 @@ +use std::alloc::Layout; +use std::ptr::NonNull; + +pub struct MemoryPool { + ptr: NonNull, + 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) }; + } +} diff --git a/crates/allocator/tests/allocator.rs b/crates/allocator/tests/allocator.rs new file mode 100644 index 0000000000..f689f3dba4 --- /dev/null +++ b/crates/allocator/tests/allocator.rs @@ -0,0 +1,133 @@ +#![feature(btreemap_alloc)] +#![feature(allocator_api)] +#![feature(strict_provenance)] + +use std::alloc::{Allocator, Layout}; +use std::collections::BTreeMap; +use std::io::Write; + +use allocator::{AllocatorRc, BuddyByteAllocator, SlabByteAllocator}; +use rand::{prelude::SliceRandom, Rng}; + +const POOL_SIZE: usize = 1024 * 1024 * 128; + +fn test_vec(n: usize, alloc: &(impl Allocator + Clone)) { + let mut v = Vec::with_capacity_in(n, alloc.clone()); + for _ in 0..n { + v.push(rand::random::()); + } + v.sort(); + for i in 0..n - 1 { + assert!(v[i] <= v[i + 1]); + } +} + +fn test_vec2(n: usize, blk_size: usize, alloc: &(impl Allocator + Clone)) { + let mut v = Vec::new_in(alloc.clone()); + for _ in 0..n { + let block = Vec::::with_capacity_in(blk_size, alloc.clone()); + v.push(block); + } + + let mut index = Vec::with_capacity_in(n, alloc.clone()); + for i in 0..n { + index.push(i); + } + index.shuffle(&mut rand::thread_rng()); + + for i in index { + v[i] = Vec::new_in(alloc.clone()) + } +} + +fn test_btree_map(n: usize, alloc: &(impl Allocator + Clone)) { + let mut m = BTreeMap::new_in(alloc.clone()); + for _ in 0..n { + if rand::random::() % 5 == 0 && !m.is_empty() { + m.pop_first(); + } else { + let value = rand::random::(); + let mut key = Vec::new_in(alloc.clone()); + write!(&mut key, "key_{value}").unwrap(); + m.insert(key, value); + } + } + for (k, v) in m.iter() { + let key = std::str::from_utf8(k) + .unwrap() + .strip_prefix("key_") + .unwrap(); + assert_eq!(key.parse::().unwrap(), *v); + } +} + +pub fn test_alignment(n: usize, alloc: &(impl Allocator + Clone)) { + let mut rng = rand::thread_rng(); + let mut blocks = vec![]; + for _ in 0..n { + if rng.gen_ratio(2, 3) || blocks.len() == 0 { + // insert a block + let size = + ((1 << rng.gen_range(0..16)) as f32 * rng.gen_range(1.0..2.0)).round() as usize; + let align = 1 << rng.gen_range(0..8); + let layout = Layout::from_size_align(size, align).unwrap(); + let ptr = alloc.allocate(layout).unwrap(); + // println!("{:?} {:#x}", layout, ptr.addr()); + blocks.push((ptr, layout)); + } else { + // delete a block + let idx = rng.gen_range(0..blocks.len()); + let blk = blocks.swap_remove(idx); + unsafe { alloc.deallocate(blk.0.cast(), blk.1) }; + } + } + for blk in blocks { + unsafe { alloc.deallocate(blk.0.cast(), blk.1) }; + } +} + +fn run_test(f: impl FnOnce(&mut [u8])) { + let layout = Layout::from_size_align(POOL_SIZE, 4096).unwrap(); + let ptr = unsafe { std::alloc::alloc_zeroed(layout) }; + let pool = unsafe { core::slice::from_raw_parts_mut(ptr, POOL_SIZE) }; + + f(pool); + + unsafe { std::alloc::dealloc(ptr, layout) }; +} + +#[test] +fn system_alloc() { + run_test(|_pool| { + let alloc = std::alloc::System; + test_alignment(50, &alloc); + test_vec(3_000_000, &alloc); + test_vec2(30_000, 64, &alloc); + test_vec2(7_500, 520, &alloc); + test_btree_map(50_000, &alloc); + }) +} + +#[test] +fn buddy_alloc() { + run_test(|pool| { + let alloc = AllocatorRc::new(BuddyByteAllocator::new(), pool); + test_alignment(50, &alloc); + test_vec(3_000_000, &alloc); + test_vec2(30_000, 64, &alloc); + test_vec2(7_500, 520, &alloc); + test_btree_map(50_000, &alloc); + }) +} + +#[test] +fn slab_alloc() { + run_test(|pool| { + let alloc = AllocatorRc::new(SlabByteAllocator::new(), pool); + test_alignment(50, &alloc); + test_vec(3_000_000, &alloc); + test_vec2(30_000, 64, &alloc); + test_vec2(7_500, 520, &alloc); + test_btree_map(50_000, &alloc); + }) +} diff --git a/crates/slab_allocator/src/slab.rs b/crates/slab_allocator/src/slab.rs index ee43226636..0060c8c731 100644 --- a/crates/slab_allocator/src/slab.rs +++ b/crates/slab_allocator/src/slab.rs @@ -109,12 +109,6 @@ impl FreeBlockList { } } -impl Drop for FreeBlockList { - fn drop(&mut self) { - while self.pop().is_some() {} - } -} - struct FreeBlock { next: Option<&'static mut FreeBlock>, } From 12cd9dc23420e80526d5f298f96421fe2db0e064 Mon Sep 17 00:00:00 2001 From: Yuekai Jia Date: Sat, 29 Jul 2023 00:51:58 +0800 Subject: [PATCH 3/3] alloc: add TLSF allocator as the default --- Cargo.lock | 44 ++++++++++++++ apps/c/memtest/expect_trace.out | 1 + apps/memtest/expect_trace.out | 1 + apps/memtest/src/main.rs | 4 +- crates/allocator/Cargo.toml | 18 ++++-- crates/allocator/benches/collections.rs | 7 ++- crates/allocator/src/lib.rs | 18 +++++- crates/allocator/src/tlsf.rs | 81 +++++++++++++++++++++++++ crates/allocator/tests/allocator.rs | 15 ++++- modules/axalloc/Cargo.toml | 8 ++- modules/axalloc/src/lib.rs | 31 ++++++++-- ulib/axlibc/Cargo.toml | 3 + ulib/axstd/Cargo.toml | 3 + 13 files changed, 215 insertions(+), 19 deletions(-) create mode 100644 crates/allocator/src/tlsf.rs diff --git a/Cargo.lock b/Cargo.lock index a9b4471b1c..dfec50b078 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,6 +29,7 @@ dependencies = [ "buddy_system_allocator", "criterion", "rand", + "rlsf", "slab_allocator", ] @@ -474,6 +475,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "bindgen" version = "0.66.1" @@ -709,6 +716,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +[[package]] +name = "const-default" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa" + [[package]] name = "core-foundation-sys" version = "0.8.4" @@ -1590,6 +1603,18 @@ dependencies = [ "embedded-hal", ] +[[package]] +name = "rlsf" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222fb240c3286247ecdee6fa5341e7cdad0ffdf8e7e401d9937f2d58482a20bf" +dependencies = [ + "cfg-if", + "const-default", + "libc", + "svgbobdoc", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -1775,6 +1800,19 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "svgbobdoc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2c04b93fc15d79b39c63218f15e3fdffaa4c227830686e3b7c5f41244eb3e50" +dependencies = [ + "base64", + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-width", +] + [[package]] name = "syn" version = "1.0.109" @@ -1917,6 +1955,12 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + [[package]] name = "version_check" version = "0.9.4" diff --git a/apps/c/memtest/expect_trace.out b/apps/c/memtest/expect_trace.out index aeebb3fae9..cdea6b34db 100644 --- a/apps/c/memtest/expect_trace.out +++ b/apps/c/memtest/expect_trace.out @@ -12,6 +12,7 @@ Found physcial memory regions: .bss (READ | WRITE | RESERVED) free memory (READ | WRITE | FREE) Initialize global memory allocator... + use TLSF allocator. initialize global allocator at: \[0x[0-9a-f]\+, 0x[0-9a-f]\+) Initialize kernel page table... Initialize platform devices... diff --git a/apps/memtest/expect_trace.out b/apps/memtest/expect_trace.out index f70c388dde..57fe228a34 100644 --- a/apps/memtest/expect_trace.out +++ b/apps/memtest/expect_trace.out @@ -12,6 +12,7 @@ Found physcial memory regions: .bss (READ | WRITE | RESERVED) free memory (READ | WRITE | FREE) Initialize global memory allocator... + use TLSF allocator. initialize global allocator at: \[0x[0-9a-f]\+, 0x[0-9a-f]\+) Initialize kernel page table... Initialize platform devices... diff --git a/apps/memtest/src/main.rs b/apps/memtest/src/main.rs index 1d3a081c25..e23e95e723 100644 --- a/apps/memtest/src/main.rs +++ b/apps/memtest/src/main.rs @@ -10,7 +10,7 @@ use std::collections::BTreeMap; use std::vec::Vec; fn test_vec(rng: &mut impl RngCore) { - const N: usize = 1_000_000; + const N: usize = 3_000_000; let mut v = Vec::with_capacity(N); for _ in 0..N { v.push(rng.next_u32()); @@ -23,7 +23,7 @@ fn test_vec(rng: &mut impl RngCore) { } fn test_btree_map(rng: &mut impl RngCore) { - const N: usize = 10_000; + const N: usize = 50_000; let mut m = BTreeMap::new(); for _ in 0..N { let value = rng.next_u32(); diff --git a/crates/allocator/Cargo.toml b/crates/allocator/Cargo.toml index 1eb6b17990..b8770f124d 100644 --- a/crates/allocator/Cargo.toml +++ b/crates/allocator/Cargo.toml @@ -10,15 +10,25 @@ repository = "https://github.com/rcore-os/arceos/tree/main/crates/allocator" documentation = "https://rcore-os.github.io/arceos/allocator/index.html" [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] -buddy_system_allocator = { version = "0.9", default-features = false } -bitmap-allocator = { git = "https://github.com/rcore-os/bitmap-allocator.git", rev = "88e871a" } -slab_allocator = { path = "../slab_allocator" } +buddy_system_allocator = { version = "0.9", default-features = false, optional = true } +slab_allocator = { path = "../slab_allocator", optional = true } +rlsf = { version = "0.2", optional = true } +bitmap-allocator = { git = "https://github.com/rcore-os/bitmap-allocator.git", rev = "88e871a", optional = true } [dev-dependencies] -allocator = { path = ".", features = ["allocator_api"] } +allocator = { path = ".", features = ["full"] } rand = { version = "0.8", features = ["small_rng"] } criterion = { version = "0.5", features = ["html_reports"] } diff --git a/crates/allocator/benches/collections.rs b/crates/allocator/benches/collections.rs index 1c3c77a003..c31fd84b44 100644 --- a/crates/allocator/benches/collections.rs +++ b/crates/allocator/benches/collections.rs @@ -7,7 +7,7 @@ use std::alloc::Allocator; use std::collections::BTreeMap; use std::io::Write; -use allocator::{AllocatorRc, BuddyByteAllocator, SlabByteAllocator}; +use allocator::{AllocatorRc, BuddyByteAllocator, SlabByteAllocator, TlsfByteAllocator}; use criterion::{black_box, criterion_group, criterion_main, Criterion}; use rand::{rngs::SmallRng, seq::SliceRandom, RngCore, SeedableRng}; @@ -80,6 +80,11 @@ fn bench(c: &mut Criterion, alloc_name: &str, alloc: impl Allocator + Clone) { 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", diff --git a/crates/allocator/src/lib.rs b/crates/allocator/src/lib.rs index 7e30fe0cf6..f8a62e6b02 100644 --- a/crates/allocator/src/lib.rs +++ b/crates/allocator/src/lib.rs @@ -13,14 +13,26 @@ #![feature(result_option_inspect)] #![cfg_attr(feature = "allocator_api", feature(allocator_api))] +#[cfg(feature = "bitmap")] mod bitmap; -mod buddy; -mod slab; - +#[cfg(feature = "bitmap")] pub use bitmap::BitmapPageAllocator; + +#[cfg(feature = "buddy")] +mod buddy; +#[cfg(feature = "buddy")] pub use buddy::BuddyByteAllocator; + +#[cfg(feature = "slab")] +mod slab; +#[cfg(feature = "slab")] pub use slab::SlabByteAllocator; +#[cfg(feature = "tlsf")] +mod tlsf; +#[cfg(feature = "tlsf")] +pub use tlsf::TlsfByteAllocator; + use core::alloc::Layout; use core::num::NonZeroUsize; diff --git a/crates/allocator/src/tlsf.rs b/crates/allocator/src/tlsf.rs new file mode 100644 index 0000000000..72448a113d --- /dev/null +++ b/crates/allocator/src/tlsf.rs @@ -0,0 +1,81 @@ +//! The TLSF (Two-Level Segregated Fit) dynamic memory allocation algorithm. +//! +//! This module wraps the implementation provided by the [rlsf] crate. + +use super::{AllocError, AllocResult, BaseAllocator, ByteAllocator}; +use core::alloc::Layout; +use core::num::NonZeroUsize; +use core::ptr::NonNull; +use rlsf::Tlsf; + +/// A TLSF (Two-Level Segregated Fit) memory allocator. +/// +/// It's just a wrapper structure of [`rlsf::Tlsf`], with `FLLEN` and `SLLEN` +/// fixed to 28 and 32. +pub struct TlsfByteAllocator { + inner: Tlsf<'static, u32, u32, 28, 32>, // max pool size: 32 * 2^28 = 8G + total_bytes: usize, + used_bytes: usize, +} + +impl TlsfByteAllocator { + /// Creates a new empty [`TlsfByteAllocator`]. + pub const fn new() -> Self { + Self { + inner: Tlsf::new(), + total_bytes: 0, + used_bytes: 0, + } + } +} + +impl BaseAllocator for TlsfByteAllocator { + fn init(&mut self, start: usize, size: usize) { + unsafe { + let pool = core::slice::from_raw_parts_mut(start as *mut u8, size); + self.inner + .insert_free_block_ptr(NonNull::new(pool).unwrap()) + .unwrap(); + } + self.total_bytes = size; + } + + fn add_memory(&mut self, start: usize, size: usize) -> AllocResult { + unsafe { + let pool = core::slice::from_raw_parts_mut(start as *mut u8, size); + self.inner + .insert_free_block_ptr(NonNull::new(pool).unwrap()) + .ok_or(AllocError::InvalidParam)?; + } + self.total_bytes += size; + Ok(()) + } +} + +impl ByteAllocator for TlsfByteAllocator { + fn alloc(&mut self, layout: Layout) -> AllocResult { + let ptr = self.inner.allocate(layout).ok_or(AllocError::NoMemory)?; + self.used_bytes += layout.size(); + Ok(ptr.addr()) + } + + fn dealloc(&mut self, pos: NonZeroUsize, layout: Layout) { + unsafe { + self.inner + .deallocate(NonNull::new_unchecked(pos.get() as _), layout.align()) + } + self.used_bytes -= layout.size(); + } + + fn total_bytes(&self) -> usize { + self.total_bytes + } + + fn used_bytes(&self) -> usize { + self.used_bytes + } + + fn available_bytes(&self) -> usize { + self.total_bytes - self.used_bytes + } +} diff --git a/crates/allocator/tests/allocator.rs b/crates/allocator/tests/allocator.rs index f689f3dba4..d1f8ce66b4 100644 --- a/crates/allocator/tests/allocator.rs +++ b/crates/allocator/tests/allocator.rs @@ -6,7 +6,7 @@ use std::alloc::{Allocator, Layout}; use std::collections::BTreeMap; use std::io::Write; -use allocator::{AllocatorRc, BuddyByteAllocator, SlabByteAllocator}; +use allocator::{AllocatorRc, BuddyByteAllocator, SlabByteAllocator, TlsfByteAllocator}; use rand::{prelude::SliceRandom, Rng}; const POOL_SIZE: usize = 1024 * 1024 * 128; @@ -72,7 +72,6 @@ pub fn test_alignment(n: usize, alloc: &(impl Allocator + Clone)) { let align = 1 << rng.gen_range(0..8); let layout = Layout::from_size_align(size, align).unwrap(); let ptr = alloc.allocate(layout).unwrap(); - // println!("{:?} {:#x}", layout, ptr.addr()); blocks.push((ptr, layout)); } else { // delete a block @@ -131,3 +130,15 @@ fn slab_alloc() { test_btree_map(50_000, &alloc); }) } + +#[test] +fn tlsf_alloc() { + run_test(|pool| { + let alloc = AllocatorRc::new(TlsfByteAllocator::new(), pool); + test_alignment(50, &alloc); + test_vec(3_000_000, &alloc); + test_vec2(30_000, 64, &alloc); + test_vec2(7_500, 520, &alloc); + test_btree_map(50_000, &alloc); + }) +} diff --git a/modules/axalloc/Cargo.toml b/modules/axalloc/Cargo.toml index 83ef1527ff..40eedf404f 100644 --- a/modules/axalloc/Cargo.toml +++ b/modules/axalloc/Cargo.toml @@ -9,10 +9,16 @@ homepage = "https://github.com/rcore-os/arceos" repository = "https://github.com/rcore-os/arceos/tree/main/modules/axalloc" documentation = "https://rcore-os.github.io/arceos/axalloc/index.html" +[features] +default = ["tlsf"] +tlsf = ["allocator/tlsf"] +slab = ["allocator/slab"] +buddy = ["allocator/buddy"] + [dependencies] log = "0.4" cfg-if = "1.0" spinlock = { path = "../../crates/spinlock" } memory_addr = { path = "../../crates/memory_addr" } -allocator = { path = "../../crates/allocator"} +allocator = { path = "../../crates/allocator", features = ["bitmap"] } axerrno = { path = "../../crates/axerrno" } diff --git a/modules/axalloc/src/lib.rs b/modules/axalloc/src/lib.rs index 9ddcb818fb..64ea1de243 100644 --- a/modules/axalloc/src/lib.rs +++ b/modules/axalloc/src/lib.rs @@ -13,8 +13,7 @@ extern crate alloc; mod page; -use allocator::{AllocResult, BaseAllocator, ByteAllocator, PageAllocator}; -use allocator::{BitmapPageAllocator, SlabByteAllocator}; +use allocator::{AllocResult, BaseAllocator, BitmapPageAllocator, ByteAllocator, PageAllocator}; use core::alloc::{GlobalAlloc, Layout}; use core::num::NonZeroUsize; use spinlock::SpinNoIrq; @@ -24,6 +23,16 @@ const MIN_HEAP_SIZE: usize = 0x8000; // 32 K pub use page::GlobalPage; +cfg_if::cfg_if! { + if #[cfg(feature = "slab")] { + use allocator::SlabByteAllocator as DefaultByteAllocator; + } else if #[cfg(feature = "buddy")] { + use allocator::BuddyByteAllocator as DefaultByteAllocator; + } else if #[cfg(feature = "tlsf")] { + use allocator::TlsfByteAllocator as DefaultByteAllocator; + } +} + /// The global allocator used by ArceOS. /// /// It combines a [`ByteAllocator`] and a [`PageAllocator`] into a simple @@ -31,10 +40,12 @@ pub use page::GlobalPage; /// there is no memory, asks the page allocator for more memory and adds it to /// the byte allocator. /// -/// Currently, [`SlabByteAllocator`] is used as the byte allocator, while +/// Currently, [`TlsfByteAllocator`] is used as the byte allocator, while /// [`BitmapPageAllocator`] is used as the page allocator. +/// +/// [`TlsfByteAllocator`]: allocator::TlsfByteAllocator pub struct GlobalAllocator { - balloc: SpinNoIrq, + balloc: SpinNoIrq, palloc: SpinNoIrq>, } @@ -42,14 +53,22 @@ impl GlobalAllocator { /// Creates an empty [`GlobalAllocator`]. pub const fn new() -> Self { Self { - balloc: SpinNoIrq::new(SlabByteAllocator::new()), + balloc: SpinNoIrq::new(DefaultByteAllocator::new()), palloc: SpinNoIrq::new(BitmapPageAllocator::new()), } } /// Returns the name of the allocator. pub const fn name(&self) -> &'static str { - "slab" + cfg_if::cfg_if! { + if #[cfg(feature = "slab")] { + "slab" + } else if #[cfg(feature = "buddy")] { + "buddy" + } else if #[cfg(feature = "tlsf")] { + "TLSF" + } + } } /// Initializes the allocator with the given region. diff --git a/ulib/axlibc/Cargo.toml b/ulib/axlibc/Cargo.toml index bad87e7dab..4cb5d00a70 100644 --- a/ulib/axlibc/Cargo.toml +++ b/ulib/axlibc/Cargo.toml @@ -27,6 +27,9 @@ fp_simd = ["axstd/fp_simd"] # Memory alloc = ["axstd/alloc", "dep:axalloc"] +alloc_tlsf = ["axalloc/tlsf"] +alloc_slab = ["axalloc/slab"] +alloc_buddy = ["axalloc/buddy"] paging = ["axstd/paging"] # Interrupts diff --git a/ulib/axstd/Cargo.toml b/ulib/axstd/Cargo.toml index 08945a31ad..1c6b0371ee 100644 --- a/ulib/axstd/Cargo.toml +++ b/ulib/axstd/Cargo.toml @@ -27,6 +27,9 @@ fp_simd = ["axhal/fp_simd"] # Memory alloc = ["dep:axalloc", "axruntime/alloc", "axio/alloc"] +alloc_tlsf = ["axalloc/tlsf"] +alloc_slab = ["axalloc/slab"] +alloc_buddy = ["axalloc/buddy"] paging = ["axruntime/paging"] # Interrupts