From 19fa512f0245f96c95d0a06c6f2e7c7e83bf38e4 Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Wed, 18 Mar 2026 15:27:47 +0100 Subject: [PATCH] block: Batch drain AIO completions in next_completed_request Collect up to 32 completions per io_getevents call instead of one at a time, buffering them in the existing VecDeque. This reduces syscalls from 128 to 4 per drain cycle at the default queue depth. The stack cost is 1 KB per call. Signed-off-by: Anatol Belski --- block/src/raw_async_aio.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/block/src/raw_async_aio.rs b/block/src/raw_async_aio.rs index f59e463b4..c2e6a174e 100644 --- a/block/src/raw_async_aio.rs +++ b/block/src/raw_async_aio.rs @@ -172,18 +172,16 @@ impl AsyncIo for RawFileAsyncAio { } fn next_completed_request(&mut self) -> Option<(u64, i32)> { - // Drain synchronous completions first (from punch_hole/write_zeroes). - if let Some(completed) = self.completion_list.pop_front() { - return Some(completed); - } - - let mut events: [aio::IoEvent; 1] = [aio::IoEvent::default()]; - let rc = self.ctx.get_events(0, &mut events, None).unwrap(); - if rc == 0 { - None - } else { - Some((events[0].data, events[0].res as i32)) + if self.completion_list.is_empty() { + // Drain pending AIO completions batched into the same queue. + let mut events = [aio::IoEvent::default(); 32]; + let rc = self.ctx.get_events(0, &mut events, None).unwrap(); + for event in &events[..rc] { + self.completion_list + .push_back((event.data, event.res as i32)); + } } + self.completion_list.pop_front() } fn punch_hole(&mut self, offset: u64, length: u64, user_data: u64) -> AsyncIoResult<()> {