Skip to content

Commit f67ea17

Browse files
perf: skip PADDING 16 bytes at a time in Decoder::skip_while (#4085)
* perf: scan padding in 16-byte chunks * bench: decode PADDING at every 16-byte alignment * codec: clarify skip_while chunk comment * perf: align skip_while chunk loads to 16 bytes * codec: name the skip_while chunk width * Shorten skip_while and padding bench comments --------- Co-authored-by: CodSpeed Bot <no-reply@codspeed.io>
1 parent f819183 commit f67ea17

2 files changed

Lines changed: 37 additions & 3 deletions

File tree

‎neqo-common/src/codec.rs‎

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,27 @@ impl<'a> Decoder<'a> {
7474
/// Skip while the current byte is `predicate`. Returns the number of bytes
7575
/// skipped.
7676
pub fn skip_while(&mut self, predicate: u8) -> usize {
77-
let until = self
78-
.as_ref() // remaining bytes
77+
/// One SIMD register (SSE2/NEON), so each chunk is a single wide load and compare.
78+
const CHUNK: usize = 16;
79+
80+
let buf = self.as_ref(); // remaining bytes
81+
// Go bytewise until `CHUNK`-aligned, so chunk loads never straddle cache lines.
82+
let head = buf.as_ptr().align_offset(CHUNK).min(buf.len());
83+
if let Some(i) = buf[..head].iter().position(|v| *v != predicate) {
84+
self.skip(i);
85+
return i;
86+
}
87+
let (chunks, _) = buf[head..].as_chunks::<CHUNK>();
88+
let start = head
89+
+ chunks
90+
.iter()
91+
.position(|c| *c != [predicate; CHUNK])
92+
.unwrap_or(chunks.len())
93+
* CHUNK;
94+
let until = buf[start..]
7995
.iter()
8096
.position(|v| *v != predicate)
81-
.unwrap_or_else(|| self.remaining());
97+
.map_or(buf.len(), |i| start + i);
8298
self.skip(until);
8399
until
84100
}

‎neqo-transport/benches/frame_decode.rs‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,28 @@ fn frame_decode(c: &mut Criterion, n: usize) {
4545
});
4646
}
4747

48+
/// Number of distinct start offsets for a PADDING frame within a 16-byte chunk.
49+
const PADDING_ALIGNMENTS: usize = 16;
50+
51+
/// Decode an `n`-byte PADDING frame at every alignment, as real PADDING can start anywhere.
52+
fn padding_decode(c: &mut Criterion, n: usize) {
53+
let buf = vec![0; n + PADDING_ALIGNMENTS - 1];
54+
c.bench_function(&format!("Frame::decode PADDING {n} bytes"), |b| {
55+
b.iter(|| {
56+
for offset in 0..PADDING_ALIGNMENTS {
57+
let mut dec = Decoder::new(&buf[offset..offset + n]);
58+
black_box(Frame::decode(&mut dec).expect("decode frame"));
59+
}
60+
});
61+
});
62+
}
63+
4864
fn benchmark(c: &mut Criterion) {
4965
frame_decode(c, 10);
5066
frame_decode(c, 100);
5167
frame_decode(c, 1_000);
68+
padding_decode(c, 1_500);
69+
padding_decode(c, 16_383);
5270
}
5371

5472
criterion_group! {

0 commit comments

Comments
 (0)