str::splitn(N, ' ') is slower than it ought to be #82471
Open
Description
I wrote a small benchmark and was surprised at how much slower it was using str::splitn rather than using bytes directly.
splitn
Time elapsed in parse_response() is: 2.290853462s
as bytes
Time elapsed in parse_response() is: 661.947666ms
Is it possible to do something similar to #46693 where if splitting on a single byte.
https://users.rust-lang.org/t/performance-comparison/56041/2
use std::time::{Duration, Instant};
fn main() {
let response = String::from("HTTP/1.1 418 I'm a teapot\r\n");
let mut res: (&str, &str, &str) = ("", "", "");
let start = Instant::now();
for _ in 0..100_000_000 {
res = match parse_http(&response) {
Ok(data) => data,
Err(_) => {
continue;
}
};
}
let duration = start.elapsed();
println!("version:{}\ncode:{}\ndescription:{}\n", res.0, res.1, res.2);
println!("Time elapsed in parse_response() is: {:?}", duration);
let start2 = Instant::now();
for _ in 0..100_000_000 {
res = match parse_http2(&response) {
Ok(data) => data,
Err(_) => {
continue;
}
};
}
let duration2 = start2.elapsed();
println!("version:{}\ncode:{}\ndescription:{}\n", res.0, res.1, res.2);
println!("Time elapsed in parse_response() is: {:?}", duration2);
}
fn parse_http(s: &str) -> Result<(&str, &str, &str), &str> {
let mut parts = s.splitn(3, ' ');
let version = parts.next().ok_or("No Version")?;
let code = parts.next().ok_or("No status code")?;
let description = parts.next().ok_or("No description")?;
Ok((version, code, description))
}
fn parse_http2(s: &str) -> Result<(&str, &str, &str), &str> {
let mut bytes = s.as_bytes().iter();
let i = bytes.position(|b| *b == b' ').ok_or("No Version")?;
let j = bytes.position(|b| *b == b' ').ok_or("No status code")? + i + 1;
let version = &s[..i];
let code = &s[(i + 1)..j];
let description = &s[(j + 1)..];
Ok((version, code, description))
}