mirror of
https://github.com/YGGverse/aquatic.git
synced 2026-03-31 17:55:36 +00:00
aquatic http protocol: request parsing: remove hashmap for performance
request-from-bytes: time: [720.22 ns 723.34 ns 726.73 ns] change: [-53.265% -52.884% -52.477%] (p = 0.00 < 0.01) Performance has improved.
This commit is contained in:
parent
289cc4fcb5
commit
8fea96bcd2
6 changed files with 1057 additions and 1095 deletions
1
TODO.md
1
TODO.md
|
|
@ -22,7 +22,6 @@
|
||||||
## aquatic_http
|
## aquatic_http
|
||||||
* upper limit on request read buffer
|
* upper limit on request read buffer
|
||||||
* request parsing:
|
* request parsing:
|
||||||
* just skip the hashmap and save data directly into struct?
|
|
||||||
* smartstring: maybe use for keys? maybe use less? needs benchmarking
|
* smartstring: maybe use for keys? maybe use less? needs benchmarking
|
||||||
* test multiple scrape hashes
|
* test multiple scrape hashes
|
||||||
* test with strange/bad inputs, with and without quickcheck
|
* test with strange/bad inputs, with and without quickcheck
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use hashbrown::HashMap;
|
|
||||||
use smartstring::{SmartString, LazyCompact};
|
use smartstring::{SmartString, LazyCompact};
|
||||||
|
|
||||||
use super::common::*;
|
use super::common::*;
|
||||||
|
|
@ -169,94 +168,16 @@ impl Request {
|
||||||
let query_string = split_parts.next()
|
let query_string = split_parts.next()
|
||||||
.with_context(|| "no query string")?;
|
.with_context(|| "no query string")?;
|
||||||
|
|
||||||
|
// -- Parse key-value pairs
|
||||||
|
|
||||||
let mut info_hashes = Vec::new();
|
let mut info_hashes = Vec::new();
|
||||||
let mut opt_peer_id = None;
|
let mut opt_peer_id = None;
|
||||||
let mut data = HashMap::new();
|
let mut opt_port = None;
|
||||||
|
let mut opt_bytes_left = None;
|
||||||
|
let mut event = AnnounceEvent::default();
|
||||||
|
let mut opt_numwant = None;
|
||||||
|
let mut opt_key = None;
|
||||||
|
|
||||||
Self::parse_key_value_pairs_memchr(
|
|
||||||
&mut info_hashes,
|
|
||||||
&mut opt_peer_id,
|
|
||||||
&mut data,
|
|
||||||
query_string
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if location == "/announce" {
|
|
||||||
let numwant = if let Some(s) = data.remove("numwant"){
|
|
||||||
let numwant = s.parse::<usize>()
|
|
||||||
.map_err(|err|
|
|
||||||
anyhow::anyhow!("parse 'numwant': {}", err)
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Some(numwant)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let key = if let Some(s) = data.remove("key"){
|
|
||||||
if s.len() > 100 {
|
|
||||||
return Err(anyhow::anyhow!("'key' is too long"))
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(s)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let port = if let Some(port) = data.remove("port"){
|
|
||||||
port.parse().with_context(|| "parse port")?
|
|
||||||
} else {
|
|
||||||
return Err(anyhow::anyhow!("no port"));
|
|
||||||
};
|
|
||||||
let bytes_left = if let Some(left) = data.remove("left"){
|
|
||||||
left.parse().with_context(|| "parse bytes left")?
|
|
||||||
} else {
|
|
||||||
return Err(anyhow::anyhow!("no left"));
|
|
||||||
};
|
|
||||||
let event = if let Some(event) = data.remove("event"){
|
|
||||||
if let Ok(event) = event.parse(){
|
|
||||||
event
|
|
||||||
} else {
|
|
||||||
return Err(anyhow::anyhow!("invalid event: {}", event));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
AnnounceEvent::default()
|
|
||||||
};
|
|
||||||
let compact = if let Some(compact) = data.remove("compact"){
|
|
||||||
if compact.as_str() == "1" {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
return Err(anyhow::anyhow!("compact set, but not to 1"));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
};
|
|
||||||
|
|
||||||
let request = AnnounceRequest {
|
|
||||||
info_hash: info_hashes.pop().with_context(|| "no info_hash")?,
|
|
||||||
peer_id: opt_peer_id.with_context(|| "no peer_id")?,
|
|
||||||
port,
|
|
||||||
bytes_left,
|
|
||||||
event,
|
|
||||||
compact,
|
|
||||||
numwant,
|
|
||||||
key,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Request::Announce(request))
|
|
||||||
} else {
|
|
||||||
let request = ScrapeRequest {
|
|
||||||
info_hashes,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Request::Scrape(request))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Seems to be somewhat faster than non-memchr version
|
|
||||||
fn parse_key_value_pairs_memchr<'a>(
|
|
||||||
info_hashes: &mut Vec<InfoHash>,
|
|
||||||
opt_peer_id: &mut Option<PeerId>,
|
|
||||||
data: &mut HashMap<&'a str, SmartString<LazyCompact>>,
|
|
||||||
query_string: &'a str,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let query_string_bytes = query_string.as_bytes();
|
let query_string_bytes = query_string.as_bytes();
|
||||||
|
|
||||||
let mut ampersand_iter = ::memchr::memchr_iter(b'&', query_string_bytes);
|
let mut ampersand_iter = ::memchr::memchr_iter(b'&', query_string_bytes);
|
||||||
|
|
@ -271,7 +192,6 @@ impl Request {
|
||||||
let value = query_string.get(equal_sign_index + 1..segment_end)
|
let value = query_string.get(equal_sign_index + 1..segment_end)
|
||||||
.with_context(|| format!("no value at {}..{}", equal_sign_index + 1, segment_end))?;
|
.with_context(|| format!("no value at {}..{}", equal_sign_index + 1, segment_end))?;
|
||||||
|
|
||||||
// whitelist keys to avoid having to use ddos-resistant hashmap
|
|
||||||
match key {
|
match key {
|
||||||
"info_hash" => {
|
"info_hash" => {
|
||||||
let value = Self::urldecode_20_bytes(value)?;
|
let value = Self::urldecode_20_bytes(value)?;
|
||||||
|
|
@ -281,10 +201,32 @@ impl Request {
|
||||||
"peer_id" => {
|
"peer_id" => {
|
||||||
let value = Self::urldecode_20_bytes(value)?;
|
let value = Self::urldecode_20_bytes(value)?;
|
||||||
|
|
||||||
*opt_peer_id = Some(PeerId(value));
|
opt_peer_id = Some(PeerId(value));
|
||||||
},
|
},
|
||||||
"port" | "left" | "event" | "compact" | "numwant" | "key" => {
|
"port" => {
|
||||||
data.insert(key, value.into());
|
opt_port = Some(value.parse::<u16>().with_context(|| "parse port")?);
|
||||||
|
},
|
||||||
|
"left" => {
|
||||||
|
opt_bytes_left = Some(value.parse::<usize>().with_context(|| "parse left")?);
|
||||||
|
},
|
||||||
|
"event" => {
|
||||||
|
event = value.parse::<AnnounceEvent>().map_err(|err|
|
||||||
|
anyhow::anyhow!("invalid event: {}", err)
|
||||||
|
)?;
|
||||||
|
},
|
||||||
|
"compact" => {
|
||||||
|
if value != "1" {
|
||||||
|
return Err(anyhow::anyhow!("compact set, but not to 1"));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"numwant" => {
|
||||||
|
opt_numwant = Some(value.parse::<usize>().with_context(|| "parse numwant")?);
|
||||||
|
},
|
||||||
|
"key" => {
|
||||||
|
if value.len() > 100 {
|
||||||
|
return Err(anyhow::anyhow!("'key' is too long"))
|
||||||
|
}
|
||||||
|
opt_key = Some(value.into());
|
||||||
},
|
},
|
||||||
k => {
|
k => {
|
||||||
::log::info!("ignored unrecognized key: {}", k)
|
::log::info!("ignored unrecognized key: {}", k)
|
||||||
|
|
@ -298,7 +240,28 @@ impl Request {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
// -- Put together request
|
||||||
|
|
||||||
|
if location == "/announce" {
|
||||||
|
let request = AnnounceRequest {
|
||||||
|
info_hash: info_hashes.pop().with_context(|| "no info_hash")?,
|
||||||
|
peer_id: opt_peer_id.with_context(|| "no peer_id")?,
|
||||||
|
port: opt_port.with_context(|| "no port")?,
|
||||||
|
bytes_left: opt_bytes_left.with_context(|| "no left")?,
|
||||||
|
event,
|
||||||
|
compact: true,
|
||||||
|
numwant: opt_numwant,
|
||||||
|
key: opt_key,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Request::Announce(request))
|
||||||
|
} else {
|
||||||
|
let request = ScrapeRequest {
|
||||||
|
info_hashes,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Request::Scrape(request))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The info hashes and peer id's that are received are url-encoded byte
|
/// The info hashes and peer id's that are received are url-encoded byte
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
{"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}}
|
{"mean":{"confidence_interval":{"confidence_level":0.95,"lower_bound":725.4551837276395,"upper_bound":735.0471255185286},"point_estimate":730.0304941357815,"standard_error":2.44315882364003},"median":{"confidence_interval":{"confidence_level":0.95,"lower_bound":710.2692059508443,"upper_bound":713.9114247675105},"point_estimate":711.6918749031568,"standard_error":0.939200067715216},"median_abs_dev":{"confidence_interval":{"confidence_level":0.95,"lower_bound":21.74599816051584,"upper_bound":25.76476135212916},"point_estimate":23.51684998144059,"standard_error":1.0048983533788292},"slope":{"confidence_interval":{"confidence_level":0.95,"lower_bound":720.2235427692906,"upper_bound":726.7290589621539},"point_estimate":723.3388090543597,"standard_error":1.655406933329102},"std_dev":{"confidence_interval":{"confidence_level":0.95,"lower_bound":60.65633946284278,"upper_bound":94.33752282501924},"point_estimate":77.10614491661927,"standard_error":8.653988561946822}}
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
||||||
[1272.4120390677972,1377.6532072370742,1658.2963223551465,1763.5374905244237]
|
[598.8933477988796,648.0355807205904,779.0815351784856,828.2237681001964]
|
||||||
Loading…
Add table
Add a link
Reference in a new issue