hickory-dns/bin/benches/comparison_benches.rs
2024-01-27 14:22:30 -05:00

266 lines
7.5 KiB
Rust

#![cfg(nightly)]
#![feature(test)]
extern crate test;
use std::env;
use std::fs::{DirBuilder, File};
use std::mem;
use std::net::{Ipv4Addr, SocketAddr, ToSocketAddrs};
use std::path::Path;
use std::process::{Child, Command, Stdio};
use std::str::FromStr;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use futures::Future;
use test::Bencher;
use tokio::net::TcpStream;
use tokio::net::UdpSocket;
use tokio::runtime::Runtime;
use hickory_client::client::*;
use hickory_client::op::*;
use hickory_client::rr::*;
use hickory_client::tcp::*;
use hickory_client::udp::*;
use hickory_proto::error::*;
use hickory_proto::iocompat::AsyncIoTokioAsStd;
use hickory_proto::op::NoopMessageFinalizer;
use hickory_proto::xfer::*;
fn find_test_port() -> u16 {
let server = std::net::UdpSocket::bind(("0.0.0.0", 0)).unwrap();
let server_addr = server.local_addr().unwrap();
server_addr.port()
}
struct NamedProcess {
named: Child,
}
impl Drop for NamedProcess {
fn drop(&mut self) {
self.named.kill().expect("could not kill process");
self.named.wait().expect("waiting failed");
}
}
fn wrap_process(named: Child, server_port: u16) -> NamedProcess {
let mut started = false;
for _ in 0..20 {
let io_loop = Runtime::new().unwrap();
let addr: SocketAddr = ("127.0.0.1", server_port)
.to_socket_addrs()
.unwrap()
.next()
.unwrap();
let stream = UdpClientStream::<UdpSocket>::new(addr);
let client = AsyncClient::connect(stream);
let (mut client, bg) = io_loop.block_on(client).expect("failed to create client");
io_loop.spawn(bg);
let name = domain::Name::from_str("www.example.com.").unwrap();
let response = io_loop.block_on(client.query(name.clone(), DNSClass::IN, RecordType::A));
if response.is_ok() {
started = true;
break;
} else {
// wait for the server to start
thread::sleep(Duration::from_millis(500));
}
}
assert!(started, "server did not startup...");
// return handle to child process
NamedProcess { named }
}
/// Returns a NamedProcess (cleans the process up on drop), and a socket addr for connecting
/// to the server.
fn hickory_process() -> (NamedProcess, u16) {
// find a random port to listen on
let test_port = find_test_port();
let ws_root = env::var("WORKSPACE_ROOT").unwrap_or_else(|_| "..".to_owned());
let named_path = env!("CARGO_BIN_EXE_hickory-dns");
let config_path = format!("{}/tests/test-data/test_configs/example.toml", ws_root);
let zone_dir = format!("{}/tests/test-data/test_configs", ws_root);
File::open(&named_path).expect(&named_path);
File::open(&config_path).expect(&config_path);
File::open(&zone_dir).expect(&zone_dir);
let named = Command::new(&named_path)
.stdout(Stdio::null())
.arg("-q") // TODO: need to rethink this one...
.arg(&format!("--config={}", config_path))
.arg(&format!("--zonedir={}", zone_dir))
.arg(&format!("--port={}", test_port))
.spawn()
.expect("failed to start hickory-dns");
//
let process = wrap_process(named, test_port);
// return handle to child process
(process, test_port)
}
/// Runs the bench tesk using the specified client
fn bench<F, S>(b: &mut Bencher, stream: F)
where
F: Future<Output = Result<S, ProtoError>> + 'static + Send + Unpin,
S: DnsRequestSender,
{
let io_loop = Runtime::new().unwrap();
let client = AsyncClient::connect(stream);
let (mut client, bg) = io_loop.block_on(client).expect("failed to create client");
io_loop.spawn(bg);
let name = domain::Name::from_str("www.example.com.").unwrap();
// validate the request
let query = client.query(name.clone(), DNSClass::IN, RecordType::A);
let response = io_loop.block_on(query).expect("Request failed");
assert_eq!(response.response_code(), ResponseCode::NoError);
let record = &response.answers()[0];
if let Some(RData::A(ref address)) = record.data() {
assert_eq!(address, &Ipv4Addr::new(127, 0, 0, 1));
} else {
unreachable!();
}
b.iter(|| {
let response = io_loop.block_on(client.query(name.clone(), DNSClass::IN, RecordType::A));
response.unwrap();
});
}
#[bench]
fn hickory_udp_bench(b: &mut Bencher) {
let (named, server_port) = hickory_process();
let addr: SocketAddr = ("127.0.0.1", server_port)
.to_socket_addrs()
.unwrap()
.next()
.unwrap();
let stream = UdpClientStream::<UdpSocket>::new(addr);
bench(b, stream);
// cleaning up the named process
drop(named);
}
#[bench]
#[ignore]
fn hickory_udp_bench_prof(b: &mut Bencher) {
let server_port = 6363;
let addr: SocketAddr = ("127.0.0.1", server_port)
.to_socket_addrs()
.unwrap()
.next()
.unwrap();
let stream = UdpClientStream::<UdpSocket>::new(addr);
bench(b, stream);
}
#[bench]
fn hickory_tcp_bench(b: &mut Bencher) {
let (named, server_port) = hickory_process();
let addr: SocketAddr = ("127.0.0.1", server_port)
.to_socket_addrs()
.unwrap()
.next()
.unwrap();
let (stream, sender) = TcpClientStream::<AsyncIoTokioAsStd<TcpStream>>::new(addr);
let mp = DnsMultiplexer::new(stream, sender, None::<Arc<NoopMessageFinalizer>>);
bench(b, mp);
// cleaning up the named process
drop(named);
}
// downloaded from https://www.isc.org/downloads/file/bind-9-11-0-p1/
// cd bind-9-11-0-p1
// .configure
// make
// export TDNS_BIND_PATH=${PWD}/bin/named/named
fn bind_process() -> (NamedProcess, u16) {
let test_port = find_test_port();
let bind_path = env::var("TDNS_BIND_PATH").unwrap_or_else(|_| "".to_owned());
let server_path = env::var("TDNS_WORKSPACE_ROOT").unwrap_or_else(|_| "..".to_owned());
let bind_path = format!("{}/sbin/named", bind_path);
// create the work directory
let working_dir = format!("{}/../target/bind_pwd", server_path);
if !Path::new(&working_dir).exists() {
DirBuilder::new()
.create(&working_dir)
.expect("failed to create dir");
}
let mut named = Command::new(bind_path)
.current_dir(&working_dir)
.stderr(Stdio::piped())
.arg("-c")
.arg("../../server/benches/bind_conf/example.conf")
//.arg("-d").arg("0")
.arg("-D")
.arg("Hickory DNS cmp bench")
.arg("-g")
.arg("-p")
.arg(&format!("{}", test_port))
.spawn()
.expect("failed to start named");
mem::replace(&mut named.stderr, None).unwrap();
let process = wrap_process(named, test_port);
(process, test_port)
}
#[bench]
#[ignore]
fn bind_udp_bench(b: &mut Bencher) {
let (named, server_port) = bind_process();
let addr: SocketAddr = ("127.0.0.1", server_port)
.to_socket_addrs()
.unwrap()
.next()
.unwrap();
let stream = UdpClientStream::<UdpSocket>::new(addr);
bench(b, stream);
// cleaning up the named process
drop(named);
}
#[bench]
#[ignore]
fn bind_tcp_bench(b: &mut Bencher) {
let (named, server_port) = bind_process();
let addr: SocketAddr = ("127.0.0.1", server_port)
.to_socket_addrs()
.unwrap()
.next()
.unwrap();
let (stream, sender) = TcpClientStream::<AsyncIoTokioAsStd<TcpStream>>::new(addr);
let mp = DnsMultiplexer::new(stream, sender, None::<Arc<NoopMessageFinalizer>>);
bench(b, mp);
// cleaning up the named process
drop(named);
}