diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 00000000..74e7ec91 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,6 @@ +## 2024-06-07 - Avoid format! for single integer to string conversions +**Learning:** `format!("{var}")` invokes `alloc::format!` which requires parsing the format string and comes with formatting overhead. For simply converting an integer to a string, calling `var.to_string()` (from `alloc::string::ToString` trait) is significantly faster in `#![no_std]` as it relies on optimized itoa under the hood and exactly allocates the capacity. +**Action:** Replace `format!("{}", fd)` with `alloc::string::ToString::to_string(&fd)` (or just import the trait) to avoid runtime format string parsing and potential over-allocations when converting single integers to strings in `procfs.rs`. +## 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. diff --git a/arceos/modules/axfs/src/fs/procfs.rs b/arceos/modules/axfs/src/fs/procfs.rs index bc0e5aac..af33d67f 100644 --- a/arceos/modules/axfs/src/fs/procfs.rs +++ b/arceos/modules/axfs/src/fs/procfs.rs @@ -1,4 +1,4 @@ -use alloc::{borrow::ToOwned, collections::BTreeMap, format, string::String, sync::Arc, vec::Vec}; +use alloc::{borrow::ToOwned, collections::BTreeMap, format, string::{String, ToString}, sync::Arc, vec::Vec}; use core::{ any::Any, borrow::Borrow, @@ -531,7 +531,7 @@ impl ProcFilesystem { if let Some(fds) = provider.process_fds(pid) { for fd in fds { - let name = format!("{}", fd); + let name = fd.to_string(); // Bolt: Use to_string() instead of format! for single integer conversion to avoid allocation overhead let child_ino = PID_INODE_START + (pid << PID_INODE_SHIFT) + SUB_INO_FD_BASE + fd as u64; entries.insert(name.into(), InodeRef::new(child_ino)); } @@ -706,7 +706,7 @@ fn render_proc_file(fs: &ProcFilesystem, kind: ProcLiveFileKind) -> String { ProcLiveFileKind::SelfSymlink => { if let Some(provider) = PROCESS_PROVIDER.get() { if let Some(pid) = provider.current_pid() { - return format!("{}", pid); + return pid.to_string(); // Bolt: Use to_string() instead of format! for single integer conversion } } "1".to_owned() @@ -715,7 +715,7 @@ fn render_proc_file(fs: &ProcFilesystem, kind: ProcLiveFileKind) -> String { if let Some(provider) = PROCESS_PROVIDER.get() { let pids = provider.process_pids(); if let Some(&min_pid) = pids.iter().min() { - return format!("{}", min_pid); + return min_pid.to_string(); // Bolt: Use to_string() instead of format! for single integer conversion } } "1".to_owned() @@ -1016,7 +1016,7 @@ impl DirNodeOps for ProcNode { if let Some(fds) = provider.process_fds(pid) { for fd in fds { - let name = format!("{}", fd); + let name = fd.to_string(); // Bolt: Use to_string() instead of format! for single integer conversion to avoid allocation overhead let child_ino = PID_INODE_START + (pid << PID_INODE_SHIFT) + SUB_INO_FD_BASE + fd as u64; all_entries.push((name, child_ino)); } @@ -1032,7 +1032,7 @@ impl DirNodeOps for ProcNode { if self.ino == ROOT_INO { if let Some(provider) = PROCESS_PROVIDER.get() { for pid in provider.process_pids() { - let name = format!("{}", pid); + let name = pid.to_string(); // Bolt: Use to_string() instead of format! for single integer conversion to avoid allocation overhead let child_ino = PID_INODE_START + (pid << PID_INODE_SHIFT) + SUB_INO_DIR; all_entries.push((name, child_ino)); }