Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
## 2024-06-07 - Avoid vec![0; N] when the buffer is immediately overwritten
**Learning:** Initializing large buffers with `vec![0; N]` in `#![no_std]` allocates and zeroes out the memory. For buffers like block device reads (`raw` array in `ext4`) that are immediately overwritten by a `read_block` system call, this zeroing is unnecessary overhead. However, safe Rust requires `read_block` to write to a valid initialized buffer (or an `&mut [u8]`). Using `vec![0; N]` is often fine for small arrays, but can be a bottleneck for large ones. In Ext4 write/read block functions, there are allocations for `vec![0u8; self.sector_size]` which is 512 bytes or 4KB, where zero initialization happens every time. Given safe Rust limitations without `MaybeUninit`, keeping `vec![0; ...]` might be necessary, but we can potentially optimize buffer usage or caching. Another optimization is `String::to_string` instead of `format!`.
**Action:** Replace `format!("{}", fd)` with `fd.to_string()` in procfs iteration as it is an easy and significant performance improvement within hot loop.
## 2024-06-07 - Optimization of String Allocations via fmt::Write
**Learning:** `alloc::format!` in `no_std` allocates memory multiple times when constructing complex strings and cannot pre-allocate a known capacity. This causes performance overhead, particularly when generating dynamic procfs data for monitoring tools frequently. By using `String::with_capacity` paired with `core::fmt::Write` via the `write!` macro, we avoid intermediate string allocations.
**Action:** Replaced `format!` in `procfs` string generation (e.g., `status`, `stat`, `maps`, `fd_path`) with `String::with_capacity` + `write!`. This reduces memory churn and improves processing speed.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ axcpu = { path = "crates/axcpu" }
axplat-loongarch64-qemu-virt = { path = "crates/axplat-loongarch64-qemu-virt" }
axplat-riscv64-qemu-virt = { path = "crates/axplat-riscv64-qemu-virt" }
axio = { path = "crates/axio" }
virtio-drivers = { path = "crates/virtio-drivers" }

[patch."https://github.com/arceos-org/allocator.git"]
allocator = { path = "crates/allocator" }
Expand Down
1 change: 1 addition & 0 deletions arceos/modules/axdriver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,4 @@ axalloc = { workspace = true, optional = true }
axhal = { workspace = true, optional = true }
axconfig = { workspace = true, optional = true }
axdma = { workspace = true, optional = true }
axtask = { workspace = true }
7 changes: 7 additions & 0 deletions arceos/modules/axdriver/src/virtio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,4 +168,11 @@ unsafe impl VirtIoHal for VirtIoHalImpl {

#[inline]
unsafe fn unshare(_paddr: PhysAddr, _buffer: NonNull<[u8]>, _direction: BufferDirection) {}

#[inline]
fn busy_wait_yield() {
if axhal::asm::irqs_enabled() {
axtask::yield_now();
}
}
}
16 changes: 10 additions & 6 deletions arceos/modules/axfs/src/fs/ext4/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,11 @@ impl<D: BlockDriverOps + 'static> Ext4Disk<D> {
raw.drain(0..inner_offset);
raw.truncate(BLOCK_SIZE);

buf.copy_from_slice(&raw);
{
let mut cache = self.block_cache.lock();
cache.put(block_offset, raw.clone());
cache.put(block_offset, raw);
}
buf.copy_from_slice(&raw);
}
}

Expand All @@ -97,10 +97,14 @@ impl<D: BlockDriverOps + 'static> BlockDevice for Ext4Disk<D> {
let inner_offset = current_offset % BLOCK_SIZE;
let current_len = core::cmp::min(BLOCK_SIZE - inner_offset, buf.len() - bytes_read);

let mut block_data = [0u8; BLOCK_SIZE];
self.read_block_aligned(block_offset, &mut block_data);
buf[bytes_read..bytes_read + current_len]
.copy_from_slice(&block_data[inner_offset..inner_offset + current_len]);
if inner_offset == 0 && current_len == BLOCK_SIZE {
self.read_block_aligned(block_offset, &mut buf[bytes_read..bytes_read + current_len]);
} else {
let mut block_data = [0u8; BLOCK_SIZE];
self.read_block_aligned(block_offset, &mut block_data);
buf[bytes_read..bytes_read + current_len]
.copy_from_slice(&block_data[inner_offset..inner_offset + current_len]);
}
bytes_read += current_len;
}
}
Expand Down
95 changes: 69 additions & 26 deletions arceos/modules/axfs/src/highlevel/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,14 +340,16 @@ pub struct PageCache {
}

impl PageCache {
fn new() -> VfsResult<Self> {
fn new(skip_zero: bool) -> VfsResult<Self> {
let addr = global_allocator()
.alloc_pages(1, PAGE_SIZE)
.inspect_err(|err| {
warn!("Failed to allocate page cache: {:?}", err);
})
.map_err(|_| VfsError::StorageFull)?;
unsafe { core::ptr::write_bytes(addr as *mut u8, 0, PAGE_SIZE) };
if !skip_zero {
unsafe { core::ptr::write_bytes(addr as *mut u8, 0, PAGE_SIZE) };
}
Ok(Self {
addr: addr.into(),
dirty: false,
Expand Down Expand Up @@ -420,19 +422,71 @@ impl CachedFileShared {
}

fn flush_dirty_pages(&self, file: &FileNode) -> VfsResult<()> {
const MAX_COALESCE_PAGES: usize = 32; // Limit contiguous writes to 128KB
let file_len = file.len()?;
let mut guard = self.page_cache.lock();
for (pn, page) in guard.iter_mut() {
if !page.dirty {
continue;

let mut dirty_pns: Vec<u32> = guard
.iter()
.filter(|(_, page)| page.dirty)
.map(|(pn, _)| *pn)
.collect();
dirty_pns.sort_unstable();

if dirty_pns.is_empty() {
return Ok(());
}

let mut i = 0;
while i < dirty_pns.len() {
let mut j = i + 1;
while j < dirty_pns.len()
&& dirty_pns[j] == dirty_pns[j - 1] + 1
&& (j - i) < MAX_COALESCE_PAGES
{
j += 1;
}
let page_start = *pn as u64 * PAGE_SIZE as u64;
let len = (file_len.saturating_sub(page_start)).min(PAGE_SIZE as u64) as usize;
if len > 0 {
file.write_at(&page.data()[..len], page_start)?;

let span_pns = &dirty_pns[i..j];
let start_pn = span_pns[0];

let mut combined_data = Vec::new();
for &pn in span_pns {
if let Some(page) = guard.get_mut(&pn) {
let page_start = pn as u64 * PAGE_SIZE as u64;
let len = (file_len.saturating_sub(page_start)).min(PAGE_SIZE as u64) as usize;
if len > 0 {
combined_data.extend_from_slice(&page.data()[..len]);
}
}
}
page.dirty = false;

if !combined_data.is_empty() {
let written = file.write_at(&combined_data, start_pn as u64 * PAGE_SIZE as u64)?;
let mut bytes_marked = 0;
for &pn in span_pns {
if let Some(page) = guard.get_mut(&pn) {
let page_start = pn as u64 * PAGE_SIZE as u64;
let len = (file_len.saturating_sub(page_start)).min(PAGE_SIZE as u64) as usize;
if bytes_marked + len <= written {
bytes_marked += len;
page.dirty = false;
} else {
break;
}
}
}
} else {
for &pn in span_pns {
if let Some(page) = guard.get_mut(&pn) {
page.dirty = false;
}
}
}

i = j;
}

Ok(())
}

Expand Down Expand Up @@ -615,20 +669,7 @@ impl CachedFile {
}

fn flush_dirty_pages(&self, file: &FileNode) -> VfsResult<()> {
let file_len = file.len()?;
let mut guard = self.shared.page_cache.lock();
for (pn, page) in guard.iter_mut() {
if !page.dirty {
continue;
}
let page_start = *pn as u64 * PAGE_SIZE as u64;
let len = (file_len.saturating_sub(page_start)).min(PAGE_SIZE as u64) as usize;
if len > 0 {
file.write_at(&page.data()[..len], page_start)?;
}
page.dirty = false;
}
Ok(())
self.shared.flush_dirty_pages(file)
}

fn discard_pages(
Expand Down Expand Up @@ -683,9 +724,11 @@ impl CachedFile {
}

// Page not in cache, read it
let mut page = PageCache::new()?;
let mut page = PageCache::new(skip_read)?;
if self.in_memory {
page.data().fill(0);
if !skip_read {
page.data().fill(0);
}
} else if !skip_read {
file.read_at(page.data(), pn as u64 * PAGE_SIZE as u64)?;
}
Expand Down
3 changes: 3 additions & 0 deletions arceos/modules/axruntime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,12 +241,15 @@ fn init_allocator() {
#[cfg(feature = "irq")]
fn init_interrupt() {
// Setup timer interrupt handler
#[cfg(not(feature = "multitask"))]
const PERIODIC_INTERVAL_NANOS: u64 =
axhal::time::NANOS_PER_SEC / axconfig::TICKS_PER_SEC as u64;

#[cfg(not(feature = "multitask"))]
#[percpu::def_percpu]
static NEXT_DEADLINE: u64 = 0;

#[cfg(not(feature = "multitask"))]
fn update_timer() {
let now_ns = axhal::time::monotonic_time_nanos();
// Safety: we have disabled preemption in IRQ handler.
Expand Down
3 changes: 3 additions & 0 deletions crates/ext4_rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,6 @@ version = "2.2.1"

[dependencies.log]
version = "0.4"

[dependencies.spin]
version = "0.10.0"
1 change: 1 addition & 0 deletions crates/ext4_rs/src/ext4_defs/ext4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ pub struct Ext4 {
pub super_block: Ext4Superblock,
pub system_zone_cache: Option<Vec<SystemZone>>,
pub inode_table_cache: Vec<u32>,
pub inode_cache: spin::Mutex<[Option<InodeCacheEntry>; 16]>,
}
2 changes: 1 addition & 1 deletion crates/ext4_rs/src/ext4_defs/extents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ impl ExtentNode {
let start = size_of::<Ext4ExtentHeader>();
let indexes = &internal_data[start..];

let mut l = 0;
let mut l = 1;
let mut r = (self.header.entries_count - 1) as usize;

while l <= r {
Expand Down
6 changes: 6 additions & 0 deletions crates/ext4_rs/src/ext4_defs/inode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,12 @@ impl Ext4Inode {
}
}

#[derive(Debug, Clone, Copy)]
pub struct InodeCacheEntry {
pub inode_num: u32,
pub inode: Ext4Inode,
}

/// Reference to an inode.
#[derive(Clone)]
pub struct Ext4InodeRef {
Expand Down
1 change: 1 addition & 0 deletions crates/ext4_rs/src/ext4_impls/ext4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ impl Ext4 {
super_block,
system_zone_cache: None,
inode_table_cache,
inode_cache: spin::Mutex::new([None; 16]),
};
log::debug!("Ext4::open: initializing system zone cache");
let zones = ext4_tmp.get_system_zone();
Expand Down
Loading