aquatic http protocol: optimize request parsing

request-from-bytes:

time:   [1.5288 us 1.5362 us 1.5441 us]
change: [-38.029% -37.466% -36.890%] (p = 0.00 < 0.01)
Performance has improved.
This commit is contained in:
Joakim Frostegård 2020-07-20 22:13:42 +02:00
parent 87328f788d
commit b7d5a12046
5 changed files with 1064 additions and 1024 deletions

View file

@ -174,10 +174,12 @@ impl Request {
.with_context(|| "no query string")?;
let mut info_hashes = Vec::new();
let mut opt_peer_id = None;
let mut data = HashMap::new();
Self::parse_key_value_pairs_memchr(
&mut info_hashes,
&mut opt_peer_id,
&mut data,
query_string
)?;
@ -232,14 +234,8 @@ impl Request {
};
let request = AnnounceRequest {
info_hash: info_hashes.pop()
.with_context(|| "no info_hash")
.and_then(deserialize_20_bytes)
.map(InfoHash)?,
peer_id: data.remove("peer_id")
.with_context(|| "no peer_id")
.and_then(deserialize_20_bytes)
.map(PeerId)?,
info_hash: info_hashes.pop().with_context(|| "no info_hash")?,
peer_id: opt_peer_id.with_context(|| "no peer_id")?,
port,
bytes_left,
event,
@ -250,14 +246,8 @@ impl Request {
Ok(Request::Announce(request))
} else {
let mut parsed_info_hashes = Vec::with_capacity(info_hashes.len());
for info_hash in info_hashes {
parsed_info_hashes.push(InfoHash(deserialize_20_bytes(info_hash)?));
}
let request = ScrapeRequest {
info_hashes: parsed_info_hashes,
info_hashes,
};
Ok(Request::Scrape(request))
@ -266,7 +256,8 @@ impl Request {
/// Seems to be somewhat faster than non-memchr version
fn parse_key_value_pairs_memchr<'a>(
info_hashes: &mut Vec<SmartString<LazyCompact>>,
info_hashes: &mut Vec<InfoHash>,
opt_peer_id: &mut Option<PeerId>,
data: &mut HashMap<&'a str, SmartString<LazyCompact>>,
query_string: &'a str,
) -> anyhow::Result<()> {
@ -287,14 +278,17 @@ impl Request {
// whitelist keys to avoid having to use ddos-resistant hashmap
match key {
"info_hash" => {
let value = Self::urldecode_memchr(value)?;
let value = Self::urldecode_20_bytes(value)?;
info_hashes.push(value);
info_hashes.push(InfoHash(value));
},
"peer_id" | "port" | "left" | "event" | "compact" | "numwant" | "key" => {
let value = Self::urldecode_memchr(value)?;
"peer_id" => {
let value = Self::urldecode_20_bytes(value)?;
data.insert(key, value);
*opt_peer_id = Some(PeerId(value));
},
"port" | "left" | "event" | "compact" | "numwant" | "key" => {
data.insert(key, value.into());
},
k => {
::log::info!("ignored unrecognized key: {}", k)
@ -386,6 +380,52 @@ impl Request {
Ok(processed)
}
fn urldecode_20_bytes(value: &str) -> anyhow::Result<[u8; 20]> {
let mut out_arr = [0u8; 20];
let mut chars = value.chars();
for i in 0..20 {
let c = chars.next()
.with_context(|| "less than 20 chars")?;
if c as u32 > 255 {
return Err(anyhow::anyhow!(
"character not in single byte range: {:#?}",
c
));
}
if c == '%' {
let first = chars.next()
.with_context(|| "missing first urldecode char in pair")?;
let second = chars.next()
.with_context(|| "missing second urldecode char in pair")?;
let hex = [first as u8, second as u8];
hex::decode_to_slice(&hex, &mut out_arr[i..i+1]).map_err(|err|
anyhow::anyhow!("hex decode error: {:?}", err)
)?;
} else {
if c as u32 > 255 {
return Err(anyhow::anyhow!(
"character not in single byte range: {:#?}",
c
));
}
out_arr[i] = c as u8;
}
}
if chars.next().is_some(){
return Err(anyhow::anyhow!("more than 20 chars"));
}
Ok(out_arr)
}
pub fn as_bytes(&self) -> Vec<u8> {
match self {
Self::Announce(r) => r.as_bytes(),

View file

@ -1 +1 @@
{"mean":{"confidence_interval":{"confidence_level":0.95,"lower_bound":2460.1209520234065,"upper_bound":2496.3120750993653},"point_estimate":2477.735012452959,"standard_error":9.223219637832466},"median":{"confidence_interval":{"confidence_level":0.95,"lower_bound":2378.552109650183,"upper_bound":2392.9167858884143},"point_estimate":2386.0253054074483,"standard_error":3.7683770634335887},"median_abs_dev":{"confidence_interval":{"confidence_level":0.95,"lower_bound":99.96596815329639,"upper_bound":120.92540240951352},"point_estimate":110.60694919175964,"standard_error":5.3506476356761885},"slope":{"confidence_interval":{"confidence_level":0.95,"lower_bound":2438.3485065067393,"upper_bound":2469.501606349259},"point_estimate":2453.4596391963023,"standard_error":7.977121580504992},"std_dev":{"confidence_interval":{"confidence_level":0.95,"lower_bound":251.73297824646409,"upper_bound":332.1546916341356},"point_estimate":291.46617601681515,"standard_error":20.531098767805926}}
{"mean":{"confidence_interval":{"confidence_level":0.95,"lower_bound":1540.8390649054281,"upper_bound":1558.4887891210565},"point_estimate":1549.4245398703538,"standard_error":4.517692089474024},"median":{"confidence_interval":{"confidence_level":0.95,"lower_bound":1506.8523919746872,"upper_bound":1513.8541497552624},"point_estimate":1510.1168042545828,"standard_error":1.754123550033509},"median_abs_dev":{"confidence_interval":{"confidence_level":0.95,"lower_bound":45.152694401086514,"upper_bound":54.71728947836145},"point_estimate":49.43491905391041,"standard_error":2.470252770404891},"slope":{"confidence_interval":{"confidence_level":0.95,"lower_bound":1528.8205165274087,"upper_bound":1544.1141471464193},"point_estimate":1536.161119110567,"standard_error":3.9201641311296234},"std_dev":{"confidence_interval":{"confidence_level":0.95,"lower_bound":121.9529239151999,"upper_bound":162.2045693733035},"point_estimate":142.20072433991157,"standard_error":10.304640393049143}}

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
[1795.8747498180473,2061.4607369455734,2769.6900359523097,3035.276023079836]
[1272.4120390677972,1377.6532072370742,1658.2963223551465,1763.5374905244237]