Skip to content

refactor(fd): replace dyn ObjectInterface with enum Fd#2096

Merged
mkroening merged 2 commits intomainfrom
fd-enum
Mar 4, 2026
Merged

refactor(fd): replace dyn ObjectInterface with enum Fd#2096
mkroening merged 2 commits intomainfrom
fd-enum

Conversation

@mkroening
Copy link
Copy Markdown
Member

@mkroening mkroening commented Nov 29, 2025

We currently define ObjectInterface::read and ObjectInterface::write ourselves:

kernel/src/fd/mod.rs

Lines 205 to 215 in 4712cd2

/// `async_read` attempts to read `len` bytes from the object references
/// by the descriptor
async fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
Err(Errno::Nosys)
}
/// `async_write` attempts to write `len` bytes to the object references
/// by the descriptor
async fn write(&self, _buf: &[u8]) -> io::Result<usize> {
Err(Errno::Nosys)
}

It would be useful to migrate to embedded_io_async::Read and embedded_io_async::Write instead: ObjectInterface: Read + Write.

#1891 has already migrated non-async I/O to embedded-io.

#1900 has prepared migrating to ObjectInterface to Read and Write traits by moving socket-internal synchronization to the outside.
This was required since Read and Write take self mutably, while our methods take self sharedly.
Additionally, moving the synchronization to the outside allowed us to get rid of a lot of boilerplate code and enabled performing multiple operations while avoiding frequent relocking.

This PR further prepares migrating ObjectInterface to Read and Write.
This is done by replacing dyn ObjectInterface via async-trait with an enum Fd, enumerating all possible file descriptors.

Continuing to use dyn ObjectInterface would require the respective macros (dynosaur or async-trait) to also be applied to the respective traits, since dyn async traits in Rust are not there yet (Dyn Async Traits · baby steps, In-place initialization - Rust Project Goals).
I am not sure if embedded-io-async would be open to this optional additional dependency.
This would also require us to do the same thing with every new async trait that we would want to depend on in the future.

Using enum_dispatch or static-dispatch to avoid writing macros ourselves for forwarding is also not possible, because the respective trait needs to be registered with the macro too.

While using enum_delegate would be possible, I chose delegate to implement this.

I did a rough benchmark of this with AArch64 on AArch64 with TCG:

use std::fs::File;
use std::io::{Read, Write};
use std::time::Instant;

#[cfg(target_os = "hermit")]
use hermit as _;

const BUF_SIZE: usize = 8 * 1024;

fn main() {
	let mut file = File::create("/tmp/hello.txt").unwrap();
	let mut buf = vec![0; BUF_SIZE];

	let now = Instant::now();
	for _ in 0..100_000 {
		file.write(b"Hello, world!\n").unwrap();
		file.read(&mut buf).unwrap();
	}
	println!("{:?}", now.elapsed());
}
  • Before this PR: 750ms
  • After this PR: 635ms

@mkroening mkroening self-assigned this Nov 29, 2025
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark Results

Details
Benchmark Current: 4f67f20 Previous: e5acf06 Performance Ratio
startup_benchmark Build Time 91.58 s 89.85 s 1.02
startup_benchmark File Size 0.79 MB 0.86 MB 0.93
Startup Time - 1 core 0.96 s (±0.04 s) 0.96 s (±0.04 s) 1.00
Startup Time - 2 cores 0.93 s (±0.03 s) 0.97 s (±0.03 s) 0.96
Startup Time - 4 cores 0.97 s (±0.03 s) 0.98 s (±0.03 s) 0.98
multithreaded_benchmark Build Time 89.98 s 92.13 s 0.98
multithreaded_benchmark File Size 0.89 MB 0.96 MB 0.93
Multithreaded Pi Efficiency - 2 Threads 88.71 % (±8.11 %) 88.10 % (±7.90 %) 1.01
Multithreaded Pi Efficiency - 4 Threads 43.88 % (±3.68 %) 43.66 % (±3.27 %) 1.00
Multithreaded Pi Efficiency - 8 Threads 25.55 % (±2.13 %) 25.68 % (±1.88 %) 0.99
micro_benchmarks Build Time 93.19 s 100.52 s 0.93
micro_benchmarks File Size 0.90 MB 0.97 MB 0.93
Scheduling time - 1 thread 67.96 ticks (±2.82 ticks) 72.33 ticks (±4.30 ticks) 0.94
Scheduling time - 2 threads 37.88 ticks (±4.93 ticks) 41.00 ticks (±4.56 ticks) 0.92
Micro - Time for syscall (getpid) 2.99 ticks (±0.27 ticks) 3.02 ticks (±0.26 ticks) 0.99
Memcpy speed - (built_in) block size 4096 63496.08 MByte/s (±45084.52 MByte/s) 63974.55 MByte/s (±45765.78 MByte/s) 0.99
Memcpy speed - (built_in) block size 1048576 29615.50 MByte/s (±24438.56 MByte/s) 29717.30 MByte/s (±24731.72 MByte/s) 1.00
Memcpy speed - (built_in) block size 16777216 28136.17 MByte/s (±23426.38 MByte/s) 25086.84 MByte/s (±21148.06 MByte/s) 1.12
Memset speed - (built_in) block size 4096 63515.45 MByte/s (±45096.35 MByte/s) 64834.94 MByte/s (±46392.11 MByte/s) 0.98
Memset speed - (built_in) block size 1048576 30361.47 MByte/s (±24879.74 MByte/s) 30521.69 MByte/s (±25181.58 MByte/s) 0.99
Memset speed - (built_in) block size 16777216 28835.06 MByte/s (±23804.76 MByte/s) 25857.70 MByte/s (±21637.02 MByte/s) 1.12
Memcpy speed - (rust) block size 4096 57751.71 MByte/s (±42006.49 MByte/s) 56270.99 MByte/s (±41319.92 MByte/s) 1.03
Memcpy speed - (rust) block size 1048576 29539.29 MByte/s (±24528.15 MByte/s) 29679.01 MByte/s (±24834.72 MByte/s) 1.00
Memcpy speed - (rust) block size 16777216 28266.30 MByte/s (±23560.36 MByte/s) 24657.77 MByte/s (±20777.47 MByte/s) 1.15
Memset speed - (rust) block size 4096 58724.45 MByte/s (±42749.87 MByte/s) 57165.11 MByte/s (±41890.59 MByte/s) 1.03
Memset speed - (rust) block size 1048576 30346.51 MByte/s (±24984.26 MByte/s) 30496.56 MByte/s (±25300.52 MByte/s) 1.00
Memset speed - (rust) block size 16777216 29034.83 MByte/s (±23998.05 MByte/s) 25438.12 MByte/s (±21286.00 MByte/s) 1.14
alloc_benchmarks Build Time 91.55 s 93.58 s 0.98
alloc_benchmarks File Size 0.86 MB 0.93 MB 0.93
Allocations - Allocation success 100.00 % 100.00 % 1
Allocations - Deallocation success 100.00 % 100.00 % 1
Allocations - Pre-fail Allocations 100.00 % 100.00 % 1
Allocations - Average Allocation time 10663.61 Ticks (±138.47 Ticks) 10901.86 Ticks (±116.46 Ticks) 0.98
Allocations - Average Allocation time (no fail) 10663.61 Ticks (±138.47 Ticks) 10901.86 Ticks (±116.46 Ticks) 0.98
Allocations - Average Deallocation time 1184.82 Ticks (±770.79 Ticks) 4769.36 Ticks (±477.10 Ticks) 0.25
mutex_benchmark Build Time 96.90 s 92.64 s 1.05
mutex_benchmark File Size 0.90 MB 0.96 MB 0.93
Mutex Stress Test Average Time per Iteration - 1 Threads 13.32 ns (±0.61 ns) 13.24 ns (±0.74 ns) 1.01
Mutex Stress Test Average Time per Iteration - 2 Threads 23.78 ns (±17.02 ns) 22.36 ns (±13.66 ns) 1.06

This comment was automatically generated by workflow using github-action-benchmark.

@mkroening mkroening added this pull request to the merge queue Mar 4, 2026
Merged via the queue into main with commit 7c500d8 Mar 4, 2026
19 checks passed
@mkroening mkroening deleted the fd-enum branch March 13, 2026 12:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant