aquatic_http: serialize responses to a buffer ref instead of new vec

This improves benchmark performance somewhat and performance
during load testing a bit too.
This commit is contained in:
Joakim Frostegård 2020-07-24 21:58:41 +02:00
parent 228511b3aa
commit fd68a5f603
7 changed files with 1101 additions and 1089 deletions

View file

@ -26,9 +26,14 @@ pub fn bench(c: &mut Criterion) {
let response = Response::Announce(announce_response);
c.bench_function("announce-response-to-bytes", |b| b.iter(||
Response::to_bytes(black_box(&response))
));
let mut buffer = [0u8; 4096];
let mut buffer = ::std::io::Cursor::new(&mut buffer[..]);
c.bench_function("announce-response-to-bytes", |b| b.iter(|| {
buffer.set_position(0);
Response::write(black_box(&response), black_box(&mut buffer)).unwrap();
}));
}
criterion_group!{

View file

@ -1,4 +1,5 @@
use std::net::{Ipv4Addr, Ipv6Addr};
use std::io::Write;
use std::collections::BTreeMap;
use serde::Serialize;
@ -50,51 +51,50 @@ pub struct AnnounceResponse {
impl AnnounceResponse {
fn to_bytes(&self) -> Vec<u8> {
let peers_bytes_len = self.peers.0.len() * 6;
let peers6_bytes_len = self.peers6.0.len() * 18;
fn write<W: Write>(&self, output: &mut W) -> ::std::io::Result<usize> {
let mut bytes_written = 0usize;
let mut bytes = Vec::with_capacity(
12 +
5 + // Upper estimate
15 +
5 + // Upper estimate
12 +
5 + // Upper estimate
8 +
peers_bytes_len +
8 +
peers6_bytes_len +
1
);
bytes_written += output.write(b"d8:completei")?;
bytes_written += output.write(
itoa::Buffer::new().format(self.complete).as_bytes()
)?;
bytes.extend_from_slice(b"d8:completei");
let _ = itoa::write(&mut bytes, self.complete);
bytes_written += output.write(b"e10:incompletei")?;
bytes_written += output.write(
itoa::Buffer::new().format(self.incomplete).as_bytes()
)?;
bytes.extend_from_slice(b"e10:incompletei");
let _ = itoa::write(&mut bytes, self.incomplete);
bytes_written += output.write(b"e8:intervali")?;
bytes_written += output.write(
itoa::Buffer::new().format(self.announce_interval).as_bytes()
)?;
bytes.extend_from_slice(b"e8:intervali");
let _ = itoa::write(&mut bytes, self.announce_interval);
bytes.extend_from_slice(b"e5:peers");
let _ = itoa::write(&mut bytes, peers_bytes_len);
bytes.push(b':');
bytes_written += output.write(b"e5:peers")?;
bytes_written += output.write(
itoa::Buffer::new().format(self.peers.0.len() * 6).as_bytes()
)?;
bytes_written += output.write(b":")?;
for peer in self.peers.0.iter() {
bytes.extend_from_slice(&u32::from(peer.ip_address).to_be_bytes());
bytes.extend_from_slice(&peer.port.to_be_bytes())
bytes_written += output.write(
&u32::from(peer.ip_address).to_be_bytes()
)?;
bytes_written += output.write(&peer.port.to_be_bytes())?;
}
bytes.extend_from_slice(b"6:peers6");
let _ = itoa::write(&mut bytes, peers6_bytes_len);
bytes.push(b':');
bytes_written += output.write(b"6:peers6")?;
bytes_written += output.write(
itoa::Buffer::new().format(self.peers6.0.len() * 18).as_bytes()
)?;
bytes_written += output.write(b":")?;
for peer in self.peers6.0.iter() {
bytes.extend_from_slice(&u128::from(peer.ip_address).to_be_bytes());
bytes.extend_from_slice(&peer.port.to_be_bytes())
bytes_written += output.write(
&u128::from(peer.ip_address).to_be_bytes()
)?;
bytes_written += output.write(&peer.port.to_be_bytes())?;
}
bytes.push(b'e');
bytes_written += output.write(b"e")?;
bytes
Ok(bytes_written)
}
}
@ -107,36 +107,28 @@ pub struct ScrapeResponse {
impl ScrapeResponse {
fn to_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::with_capacity(
9 +
self.files.len() * (
3 +
20 +
12 +
5 + // Upper estimate
31 +
5 + // Upper estimate
2
) +
2
);
fn write<W: Write>(&self, output: &mut W) -> ::std::io::Result<usize> {
let mut bytes_written = 0usize;
bytes.extend_from_slice(b"d5:filesd");
bytes_written += output.write(b"d5:filesd")?;
for (info_hash, statistics) in self.files.iter(){
bytes.extend_from_slice(b"20:");
bytes.extend_from_slice(&info_hash.0);
bytes.extend_from_slice(b"d8:completei");
let _ = itoa::write(&mut bytes, statistics.complete);
bytes.extend_from_slice(b"e10:downloadedi0e10:incompletei");
let _ = itoa::write(&mut bytes, statistics.incomplete);
bytes.extend_from_slice(b"ee");
bytes_written += output.write(b"20:")?;
bytes_written += output.write(&info_hash.0)?;
bytes_written += output.write(b"d8:completei")?;
bytes_written += output.write(
itoa::Buffer::new().format(statistics.complete).as_bytes()
)?;
bytes_written += output.write(b"e10:downloadedi0e10:incompletei")?;
bytes_written += output.write(
itoa::Buffer::new().format(statistics.incomplete).as_bytes()
)?;
bytes_written += output.write(b"ee")?;
}
bytes.extend_from_slice(b"ee");
bytes_written += output.write(b"ee")?;
bytes
Ok(bytes_written)
}
}
@ -148,24 +140,20 @@ pub struct FailureResponse {
impl FailureResponse {
fn to_bytes(&self) -> Vec<u8> {
fn write<W: Write>(&self, output: &mut W) -> ::std::io::Result<usize> {
let mut bytes_written = 0usize;
let reason_bytes = self.failure_reason.as_bytes();
let mut bytes = Vec::with_capacity(
18 +
3 + // Upper estimate
1 +
reason_bytes.len() +
1
);
bytes_written += output.write(b"d14:failure_reason")?;
bytes_written += output.write(
itoa::Buffer::new().format(reason_bytes.len()).as_bytes()
)?;
bytes_written += output.write(b":")?;
bytes_written += output.write(reason_bytes)?;
bytes_written += output.write(b"e")?;
bytes.extend_from_slice(b"d14:failure_reason");
let _ = itoa::write(&mut bytes, reason_bytes.len());
bytes.push(b':');
bytes.extend_from_slice(reason_bytes);
bytes.push(b'e');
bytes
Ok(bytes_written)
}
}
@ -180,11 +168,11 @@ pub enum Response {
impl Response {
pub fn to_bytes(&self) -> Vec<u8> {
pub fn write<W: Write>(&self, output: &mut W) -> ::std::io::Result<usize> {
match self {
Response::Announce(r) => r.to_bytes(),
Response::Failure(r) => r.to_bytes(),
Response::Scrape(r) => r.to_bytes(),
Response::Announce(r) => r.write(output),
Response::Failure(r) => r.write(output),
Response::Scrape(r) => r.write(output),
}
}
}
@ -286,7 +274,11 @@ mod tests {
&Response::Announce(response.clone())
).unwrap();
response.to_bytes() == reference
let mut output = Vec::new();
response.write(&mut output).unwrap();
output == reference
}
#[quickcheck]
@ -294,7 +286,10 @@ mod tests {
let reference = bendy::serde::to_bytes(
&Response::Scrape(response.clone())
).unwrap();
let hand_written = response.to_bytes();
let mut hand_written = Vec::new();
response.write(&mut hand_written).unwrap();
let success = hand_written == reference;
@ -312,6 +307,10 @@ mod tests {
&Response::Failure(response.clone())
).unwrap();
response.to_bytes() == reference
let mut output = Vec::new();
response.write(&mut output).unwrap();
output == reference
}
}

View file

@ -1 +1 @@
{"mean":{"confidence_interval":{"confidence_level":0.95,"lower_bound":679.6199835027244,"upper_bound":683.1765435179024},"point_estimate":681.3262892361256,"standard_error":0.9087088681117722},"median":{"confidence_interval":{"confidence_level":0.95,"lower_bound":675.1429773039165,"upper_bound":678.0182057889292},"point_estimate":676.3105408394639,"standard_error":0.6854649236080195},"median_abs_dev":{"confidence_interval":{"confidence_level":0.95,"lower_bound":22.428283556649202,"upper_bound":25.203260654300284},"point_estimate":24.35095483756527,"standard_error":0.69150074330993},"slope":{"confidence_interval":{"confidence_level":0.95,"lower_bound":672.118472824725,"upper_bound":677.6479220619163},"point_estimate":674.6741040360789,"standard_error":1.4165525766541145},"std_dev":{"confidence_interval":{"confidence_level":0.95,"lower_bound":23.51653893971022,"upper_bound":33.64225930231783},"point_estimate":28.565599471881452,"standard_error":2.588927502189203}}
{"mean":{"confidence_interval":{"confidence_level":0.95,"lower_bound":658.5830856810967,"upper_bound":662.6140140431078},"point_estimate":660.5315794423136,"standard_error":1.0267544650468448},"median":{"confidence_interval":{"confidence_level":0.95,"lower_bound":645.305767448625,"upper_bound":648.8067825908649},"point_estimate":646.7621214660466,"standard_error":0.965885560495337},"median_abs_dev":{"confidence_interval":{"confidence_level":0.95,"lower_bound":8.471955633936192,"upper_bound":13.107607852360173},"point_estimate":10.494262498769816,"standard_error":1.2403366025964986},"slope":{"confidence_interval":{"confidence_level":0.95,"lower_bound":657.1689080616346,"upper_bound":661.4062067913236},"point_estimate":659.2109887653004,"standard_error":1.0812556505845492},"std_dev":{"confidence_interval":{"confidence_level":0.95,"lower_bound":26.296406154649638,"upper_bound":39.21798204128364},"point_estimate":32.5892125764255,"standard_error":3.3046616744256077}}

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
[560.1173877793271,609.6904444532156,741.8852622502513,791.4583189241397]
[555.2496610557599,598.3305180332976,713.2128033067314,756.2936602842691]