Skip to content

Commit a6413a4

Browse files
author
DioCrafts
committed
perf: make BufList::remaining() O(1) by caching total bytes
Add a field to BufList that tracks the total number of bytes across all buffers. This avoids iterating the entire VecDeque on every call to remaining(), which is invoked from hot paths like poll_flush, can_buffer, and advance. Previously, remaining() was O(n) where n is the number of buffers in the queue (up to 16 in Queue write strategy). Now it is O(1) — a simple field read.
1 parent 8ba9008 commit a6413a4

File tree

1 file changed

+14
-3
lines changed

1 file changed

+14
-3
lines changed

src/common/buf.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,21 @@ use bytes::{Buf, BufMut, Bytes, BytesMut};
55

66
pub(crate) struct BufList<T> {
77
bufs: VecDeque<T>,
8+
remaining: usize,
89
}
910

1011
impl<T: Buf> BufList<T> {
1112
pub(crate) fn new() -> BufList<T> {
1213
BufList {
1314
bufs: VecDeque::new(),
15+
remaining: 0,
1416
}
1517
}
1618

1719
#[inline]
1820
pub(crate) fn push(&mut self, buf: T) {
1921
debug_assert!(buf.has_remaining());
22+
self.remaining += buf.remaining();
2023
self.bufs.push_back(buf);
2124
}
2225

@@ -29,7 +32,7 @@ impl<T: Buf> BufList<T> {
2932
impl<T: Buf> Buf for BufList<T> {
3033
#[inline]
3134
fn remaining(&self) -> usize {
32-
self.bufs.iter().map(|buf| buf.remaining()).sum()
35+
self.remaining
3336
}
3437

3538
#[inline]
@@ -39,6 +42,7 @@ impl<T: Buf> Buf for BufList<T> {
3942

4043
#[inline]
4144
fn advance(&mut self, mut cnt: usize) {
45+
self.remaining -= cnt;
4246
while cnt > 0 {
4347
{
4448
let front = &mut self.bufs[0];
@@ -78,12 +82,18 @@ impl<T: Buf> Buf for BufList<T> {
7882
Some(front) if front.remaining() == len => {
7983
let b = front.copy_to_bytes(len);
8084
self.bufs.pop_front();
85+
self.remaining -= len;
8186
b
8287
}
83-
Some(front) if front.remaining() > len => front.copy_to_bytes(len),
88+
Some(front) if front.remaining() > len => {
89+
self.remaining -= len;
90+
front.copy_to_bytes(len)
91+
}
8492
_ => {
85-
assert!(len <= self.remaining(), "`len` greater than remaining");
93+
assert!(len <= self.remaining, "`len` greater than remaining");
8694
let mut bm = BytesMut::with_capacity(len);
95+
// Note: `self.take(len)` calls `self.advance()` internally,
96+
// which already decrements `self.remaining`.
8797
bm.put(self.take(len));
8898
bm.freeze()
8999
}
@@ -100,6 +110,7 @@ mod tests {
100110
fn hello_world_buf() -> BufList<Bytes> {
101111
BufList {
102112
bufs: vec![Bytes::from("Hello"), Bytes::from(" "), Bytes::from("World")].into(),
113+
remaining: 11,
103114
}
104115
}
105116

0 commit comments

Comments
 (0)