From 116a4a8bcc018242a92a9c7abd55501cefecc0ec Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Fri, 26 Feb 2021 17:07:51 -0500 Subject: [PATCH 1/4] Add a non-artificial Message parsing benchmark --- crates/proto/benches/lib.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/crates/proto/benches/lib.rs b/crates/proto/benches/lib.rs index 7e16eebb80..726cef9750 100644 --- a/crates/proto/benches/lib.rs +++ b/crates/proto/benches/lib.rs @@ -121,3 +121,24 @@ fn bench_parse_message(b: &mut Bencher) { Message::read(&mut decoder) }) } + +#[bench] +fn bench_parse_real_message(b: &mut Bencher) { + let bytes = [ + 145, 188, 129, 128, 0, 1, 0, 6, 0, 0, 0, 0, 5, 118, 105, 100, 101, 111, 5, 116, 119, 105, + 109, 103, 3, 99, 111, 109, 0, 0, 1, 0, 1, 192, 12, 0, 5, 0, 1, 0, 0, 0, 245, 0, 11, 8, 118, + 105, 100, 101, 111, 45, 97, 107, 192, 18, 192, 45, 0, 5, 0, 1, 0, 0, 12, 213, 0, 24, 5, + 118, 105, 100, 101, 111, 5, 116, 119, 105, 109, 103, 6, 97, 107, 97, 100, 110, 115, 3, 110, + 101, 116, 0, 192, 68, 0, 5, 0, 1, 0, 0, 0, 57, 0, 28, 5, 118, 105, 100, 101, 111, 5, 116, + 119, 105, 109, 103, 3, 99, 111, 109, 9, 97, 107, 97, 109, 97, 105, 122, 101, 100, 192, 87, + 192, 104, 0, 5, 0, 1, 0, 0, 2, 194, 0, 22, 5, 118, 105, 100, 101, 111, 5, 116, 119, 105, + 109, 103, 3, 99, 111, 109, 3, 101, 105, 112, 192, 80, 192, 144, 0, 5, 0, 1, 0, 0, 0, 43, 0, + 35, 8, 101, 105, 112, 45, 116, 97, 116, 97, 5, 118, 105, 100, 101, 111, 5, 116, 119, 105, + 109, 103, 3, 99, 111, 109, 7, 97, 107, 97, 104, 111, 115, 116, 192, 87, 192, 178, 0, 1, 0, + 1, 0, 0, 0, 23, 0, 4, 184, 31, 3, 236, + ]; + b.iter(|| { + let mut decoder = BinDecoder::new(&bytes[..]); + Message::read(&mut decoder); + }) +} From 7b6b79a960aaf573967daa9ddde62197400d9615 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Thu, 18 Feb 2021 17:06:48 -0500 Subject: [PATCH 2/4] Optimize Name parsing by using a pair of TinyVec The previous implementation used Rc to represent Label, then composed those in an array to represent Name. That produced a large number of small allocations in the parsing code path. This new implementation avoids allocations entirely for small names, and unless the name has a very large number of labels, it is stored entirely in one allocation. This also removes the Index impl for Name. Since we no longer contain any Labels, we cannot implement that (a common problem with Index leaking implementation details). --- Cargo.lock | 1 + crates/client/src/rr/lower_name.rs | 11 +- crates/proto/Cargo.toml | 1 + crates/proto/src/rr/domain/label.rs | 12 +- crates/proto/src/rr/domain/name.rs | 245 +++++++++++++++++---------- crates/resolver/src/error.rs | 1 + crates/server/src/authority/error.rs | 1 + 7 files changed, 163 insertions(+), 109 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7ddb52531a..3a518d767b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1792,6 +1792,7 @@ dependencies = [ "smallvec", "socket2", "thiserror", + "tinyvec", "tokio", "url", "wasm-bindgen", diff --git a/crates/client/src/rr/lower_name.rs b/crates/client/src/rr/lower_name.rs index b0b98b3e59..a7bea3ccf1 100644 --- a/crates/client/src/rr/lower_name.rs +++ b/crates/client/src/rr/lower_name.rs @@ -11,14 +11,13 @@ use std::borrow::Borrow; use std::cmp::{Ordering, PartialEq}; use std::fmt; use std::hash::{Hash, Hasher}; -use std::ops::Index; use std::str::FromStr; use crate::proto::error::*; #[cfg(feature = "serde-config")] use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; -use crate::rr::{Label, Name}; +use crate::rr::Name; use crate::serialize::binary::*; /// them should be through references. As a workaround the Strings are all Rc as well as the array @@ -191,14 +190,6 @@ impl fmt::Display for LowerName { } } -impl Index for LowerName { - type Output = Label; - - fn index(&self, _index: usize) -> &Label { - &(self.0[_index]) - } -} - impl PartialOrd for LowerName { fn partial_cmp(&self, other: &LowerName) -> Option { Some(self.cmp(other)) diff --git a/crates/proto/Cargo.toml b/crates/proto/Cargo.toml index 30471554e9..8a7cc7f5d4 100644 --- a/crates/proto/Cargo.toml +++ b/crates/proto/Cargo.toml @@ -78,6 +78,7 @@ serde = { version = "1.0", features = ["derive"], optional = true } smallvec = "1.6" socket2 = { version = "0.3.16", optional = true } thiserror = "1.0.20" +tinyvec = { version = "1.1.1", features = ["alloc"] } tokio = { version = "1.0", optional = true } url = "2.1.0" wasm-bindgen-crate = { version = "0.2.58", optional = true, package = "wasm-bindgen" } diff --git a/crates/proto/src/rr/domain/label.rs b/crates/proto/src/rr/domain/label.rs index f9eee48ab7..3e40401a03 100644 --- a/crates/proto/src/rr/domain/label.rs +++ b/crates/proto/src/rr/domain/label.rs @@ -17,7 +17,7 @@ use std::borrow::Borrow; use std::cmp::{Ordering, PartialEq}; use std::fmt::{self, Debug, Display, Formatter, Write}; use std::hash::{Hash, Hasher}; -use std::sync::Arc as Rc; +use tinyvec::TinyVec; use idna; use log::debug; @@ -29,7 +29,7 @@ const IDNA_PREFIX: &[u8] = b"xn--"; /// Labels are always stored as ASCII, unicode characters must be encoded with punycode #[derive(Clone, Eq)] -pub struct Label(Rc<[u8]>); +pub struct Label(TinyVec<[u8; 24]>); impl Label { /// These must only be ASCII, with unicode encoded to PunyCode, or other such transformation. @@ -40,7 +40,7 @@ impl Label { if bytes.len() > 63 { return Err(format!("Label exceeds maximum length 63: {}", bytes.len()).into()); }; - Ok(Label(Rc::from(bytes))) + Ok(Label(TinyVec::from(bytes))) } /// Translates this string into IDNA safe name, encoding to punycode as necessary. @@ -86,7 +86,7 @@ impl Label { /// Returns a new Label of the Wildcard, i.e. "*" pub fn wildcard() -> Self { - Label(Rc::from(WILDCARD.to_vec())) + Label(TinyVec::from(WILDCARD)) } /// Converts this label to lowercase @@ -100,7 +100,7 @@ impl Label { { let mut lower_label: Vec = self.0.to_vec(); lower_label[idx..].make_ascii_lowercase(); - Label(Rc::from(lower_label)) + Label(TinyVec::from(lower_label.as_slice())) } else { self.clone() } @@ -201,7 +201,7 @@ impl Label { impl AsRef<[u8]> for Label { fn as_ref(&self) -> &[u8] { - &self.0 + self.as_bytes() } } diff --git a/crates/proto/src/rr/domain/name.rs b/crates/proto/src/rr/domain/name.rs index 5ebb0e5fba..7c285cb661 100644 --- a/crates/proto/src/rr/domain/name.rs +++ b/crates/proto/src/rr/domain/name.rs @@ -7,14 +7,11 @@ //! domain name, aka labels, implementation -use std::borrow::Borrow; use std::char; use std::cmp::{Ordering, PartialEq}; use std::fmt::{self, Write}; use std::hash::{Hash, Hasher}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use std::ops::Index; -use std::slice::Iter; use std::str::FromStr; use crate::error::*; @@ -24,12 +21,16 @@ use crate::serialize::binary::*; use ipnet::{IpNet, Ipv4Net, Ipv6Net}; #[cfg(feature = "serde-config")] use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; +use tinyvec::TinyVec; -/// Them should be through references. As a workaround the Strings are all Rc as well as the array +/// A domain name #[derive(Clone, Default, Debug, Eq)] pub struct Name { is_fqdn: bool, - labels: Vec