Some benchmarks and low-hanging speed improvements

This commit is contained in:
Dennis Schwerdel 2015-11-25 21:05:11 +01:00
parent f6b6faf50a
commit 946e384660
10 changed files with 166 additions and 54 deletions

View File

@ -20,3 +20,4 @@ gcc = "0.3"
[features]
default = []
crypto = ["libsodium-sys"] #not default as it requires external library
bench = []

86
src/benches.rs Normal file
View File

@ -0,0 +1,86 @@
use test::Bencher;
use time::{SteadyTime, Duration};
use std::str::FromStr;
use std::net::ToSocketAddrs;
use super::udpmessage::{Options, Message, encode, decode};
use super::crypto::Crypto;
use super::ethernet::{Frame, SwitchTable};
use super::types::{Address, Table, Protocol};
use super::ip::Packet;
#[bench]
fn message_encode(b: &mut Bencher) {
let mut options = Options::default();
let mut crypto = Crypto::None;
let payload = [0; 1400];
let msg = Message::Data(&payload);
let mut buf = [0; 1500];
b.iter(|| {
encode(&mut options, &msg, &mut buf[..], &mut crypto);
});
}
#[bench]
fn message_decode(b: &mut Bencher) {
let mut options = Options::default();
let mut crypto = Crypto::None;
let payload = [0; 1400];
let msg = Message::Data(&payload);
let mut buf = [0; 1500];
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
b.iter(|| {
decode(&mut buf[..size], &mut crypto).unwrap();
});
}
#[bench]
fn switch_learn(b: &mut Bencher) {
let mut table = SwitchTable::new(Duration::seconds(10));
let addr = Address::from_str("12:34:56:78:90:ab").unwrap();
let peer = "1.2.3.4:5678".to_socket_addrs().unwrap().next().unwrap();
b.iter(|| {
table.learn(addr.clone(), None, peer);
})
}
#[bench]
fn switch_lookup(b: &mut Bencher) {
let mut table = SwitchTable::new(Duration::seconds(10));
let addr = Address::from_str("12:34:56:78:90:ab").unwrap();
let peer = "1.2.3.4:5678".to_socket_addrs().unwrap().next().unwrap();
table.learn(addr.clone(), None, peer);
b.iter(|| {
table.lookup(&addr);
})
}
#[bench]
fn ethernet_parse(b: &mut Bencher) {
let data = [0; 1500];
b.iter(|| {
Frame::parse(&data).unwrap()
})
}
#[bench]
fn ipv4_parse(b: &mut Bencher) {
let mut data = [0; 1500];
data[0] = 4*16;
b.iter(|| {
Packet::parse(&data).unwrap()
})
}
#[bench]
fn ipv6_parse(b: &mut Bencher) {
let mut data = [0; 1500];
data[0] = 6*16;
b.iter(|| {
Packet::parse(&data).unwrap()
})
}

View File

@ -225,7 +225,7 @@ impl<P: Protocol> GenericCloud<P> {
return Err(Error::WrongNetwork(options.network_id));
}
}
debug!("Recieved {:?} from {}", msg, peer);
debug!("Received {:?} from {}", msg, peer);
match msg {
Message::Data(payload) => {
let (src, _dst) = try!(P::parse(payload));
@ -237,7 +237,7 @@ impl<P: Protocol> GenericCloud<P> {
return Err(Error::TunTapDevError("Failed to write to device"));
}
}
self.peers.add(&peer);
// not adding peer to increase performance
if self.learning {
//learn single address
self.table.learn(src, None, peer);

View File

@ -38,10 +38,12 @@ impl Device {
&self.ifname
}
#[inline]
pub fn read(&mut self, mut buffer: &mut [u8]) -> Result<usize, Error> {
self.fd.read(&mut buffer).map_err(|_| Error::TunTapDevError("Read error"))
}
#[inline]
pub fn write(&mut self, data: &[u8]) -> Result<(), Error> {
match self.fd.write_all(&data) {
Ok(_) => Ok(()),
@ -51,6 +53,7 @@ impl Device {
}
impl AsRawFd for Device {
#[inline(always)]
fn as_raw_fd(&self) -> RawFd {
self.fd.as_raw_fd()
}

View File

@ -25,27 +25,23 @@ impl Protocol for Frame {
if data.len() < pos + 2 {
return Err(Error::ParseError("Vlan frame is too short"));
}
let mut src = Vec::with_capacity(8);
let mut dst = Vec::with_capacity(8);
let mut src = [0; 16];
let mut dst = [0; 16];
unsafe {
src.set_len(8);
ptr::copy_nonoverlapping(data[pos..].as_ptr(), src.as_mut_ptr(), 2);
ptr::copy_nonoverlapping(src_data.as_ptr(), src[2..].as_mut_ptr(), 6);
dst.set_len(8);
ptr::copy_nonoverlapping(data[pos..].as_ptr(), dst.as_mut_ptr(), 2);
ptr::copy_nonoverlapping(dst_data.as_ptr(), dst[2..].as_mut_ptr(), 6);
}
Ok((Address(src), Address(dst)))
Ok((Address(src, 8), Address(dst, 8)))
} else {
let mut src = Vec::with_capacity(6);
let mut dst = Vec::with_capacity(6);
let mut src = [0; 16];
let mut dst = [0; 16];
unsafe {
ptr::copy_nonoverlapping(src_data.as_ptr(), src.as_mut_ptr(), 6);
src.set_len(6);
ptr::copy_nonoverlapping(dst_data.as_ptr(), dst.as_mut_ptr(), 6);
dst.set_len(6);
}
Ok((Address(src), Address(dst)))
Ok((Address(src, 6), Address(dst, 6)))
}
}
}
@ -84,6 +80,7 @@ impl Table for SwitchTable {
self.cache = None;
}
#[inline]
fn learn(&mut self, key: Address, _prefix_len: Option<u8>, addr: SocketAddr) {
let value = SwitchTableValue{address: addr, timeout: SteadyTime::now()+self.timeout};
if self.table.insert(key.clone(), value).is_none() {
@ -91,6 +88,7 @@ impl Table for SwitchTable {
}
}
#[inline]
fn lookup(&mut self, key: &Address) -> Option<SocketAddr> {
match self.table.get(key) {
Some(value) => Some(value.address),
@ -108,14 +106,14 @@ impl Table for SwitchTable {
fn without_vlan() {
let data = [6,5,4,3,2,1,1,2,3,4,5,6,1,2,3,4,5,6,7,8];
let (src, dst) = Frame::parse(&data).unwrap();
assert_eq!(src, Address(vec![1,2,3,4,5,6]));
assert_eq!(dst, Address(vec![6,5,4,3,2,1]));
assert_eq!(src, Address([1,2,3,4,5,6,0,0,0,0,0,0,0,0,0,0], 6));
assert_eq!(dst, Address([6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0], 6));
}
#[test]
fn with_vlan() {
let data = [6,5,4,3,2,1,1,2,3,4,5,6,0x81,0,4,210,1,2,3,4,5,6,7,8];
let (src, dst) = Frame::parse(&data).unwrap();
assert_eq!(src, Address(vec![4,210,1,2,3,4,5,6]));
assert_eq!(dst, Address(vec![4,210,6,5,4,3,2,1]));
assert_eq!(src, Address([4,210,1,2,3,4,5,6,0,0,0,0,0,0,0,0], 8));
assert_eq!(dst, Address([4,210,6,5,4,3,2,1,0,0,0,0,0,0,0,0], 8));
}

View File

@ -1,9 +1,10 @@
use std::net::SocketAddr;
use std::collections::{hash_map, HashMap};
use std::io::Read;
use std::ptr;
use super::types::{Protocol, Error, Table, Address};
use super::util::to_vec;
#[allow(dead_code)]
pub struct Packet;
@ -14,18 +15,28 @@ impl Protocol for Packet {
return Err(Error::ParseError("Empty header"));
}
let version = data[0] >> 4;
let mut src = [0; 16];
let mut dst = [0; 16];
match version {
4 => {
if data.len() < 20 {
return Err(Error::ParseError("Truncated header"));
}
Ok((Address(to_vec(&data[12..16])), Address(to_vec(&data[16..20]))))
unsafe {
ptr::copy_nonoverlapping(data[12..].as_ptr(), src.as_mut_ptr(), 4);
ptr::copy_nonoverlapping(data[16..].as_ptr(), dst.as_mut_ptr(), 4);
}
Ok((Address(src, 4), Address(dst, 4)))
},
6 => {
if data.len() < 40 {
return Err(Error::ParseError("Truncated header"));
}
Ok((Address(to_vec(&data[8..24])), Address(to_vec(&data[24..40]))))
unsafe {
ptr::copy_nonoverlapping(data[8..].as_ptr(), src.as_mut_ptr(), 16);
ptr::copy_nonoverlapping(data[24..].as_ptr(), dst.as_mut_ptr(), 16);
}
Ok((Address(src, 16), Address(dst, 16)))
},
_ => Err(Error::ParseError("Invalid version"))
}
@ -35,11 +46,11 @@ impl Protocol for Packet {
struct RoutingEntry {
address: SocketAddr,
bytes: Vec<u8>,
bytes: [u8; 16],
prefix_len: u8
}
pub struct RoutingTable(HashMap<Vec<u8>, Vec<RoutingEntry>>);
pub struct RoutingTable(HashMap<[u8; 16], Vec<RoutingEntry>>);
impl RoutingTable {
pub fn new() -> Self {
@ -55,7 +66,10 @@ impl Table for RoutingTable {
};
info!("New routing entry: {:?}/{} => {}", addr, prefix_len, address);
let group_len = (prefix_len as usize / 16) * 2;
let group_bytes: Vec<u8> = addr.0[..group_len].iter().map(|b| *b).collect();
assert!(group_len <= 16);
let mut group_bytes = [0; 16];
unsafe { ptr::copy_nonoverlapping(addr.0.as_ptr(), group_bytes.as_mut_ptr(), group_len) };
let routing_entry = RoutingEntry{address: address, bytes: addr.0, prefix_len: prefix_len};
match self.0.entry(group_bytes) {
hash_map::Entry::Occupied(mut entry) => entry.get_mut().push(routing_entry),

View File

@ -1,3 +1,4 @@
#![cfg_attr(feature = "bench", feature(test))]
#[macro_use] extern crate log;
extern crate time;
extern crate docopt;
@ -6,6 +7,7 @@ extern crate epoll;
extern crate signal;
extern crate nix;
#[cfg(feature = "crypto")] extern crate libsodium_sys;
#[cfg(feature = "bench")] extern crate test;
#[macro_use] mod util;
mod types;
@ -15,6 +17,7 @@ mod ethernet;
mod ip;
mod cloud;
mod device;
#[cfg(feature = "bench")] mod benches;
use time::Duration;
use docopt::Docopt;
@ -35,10 +38,12 @@ use crypto::Crypto;
struct SimpleLogger;
impl log::Log for SimpleLogger {
#[inline(always)]
fn enabled(&self, _metadata: &log::LogMetadata) -> bool {
true
}
#[inline(always)]
fn log(&self, record: &log::LogRecord) {
if self.enabled(record.metadata()) {
println!("{} - {}", record.level(), record.args());

View File

@ -7,12 +7,27 @@ use super::util::{as_bytes, as_obj};
pub type NetworkId = u64;
#[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone)]
pub struct Address(pub Vec<u8>);
#[derive(PartialOrd, Eq, Ord, Clone, Hash)]
pub struct Address(pub [u8; 16], pub u8);
impl PartialEq for Address {
fn eq(&self, rhs: &Self) -> bool {
if self.1 != rhs.1 {
return false;
}
for i in 0..self.1 as usize {
if self.0[i] != rhs.0[i] {
return false;
}
}
true
}
}
impl fmt::Debug for Address {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self.0.len() {
match self.1 {
4 => write!(formatter, "{}.{}.{}.{}", self.0[0], self.0[1], self.0[2], self.0[3]),
6 => write!(formatter, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]),
@ -36,12 +51,11 @@ impl FromStr for Address {
fn from_str(text: &str) -> Result<Self, Self::Err> {
if let Ok(addr) = Ipv4Addr::from_str(text) {
let ip = addr.octets();
let mut res = Vec::with_capacity(4);
let mut res = [0; 16];
unsafe {
res.set_len(4);
ptr::copy_nonoverlapping(ip.as_ptr(), res.as_mut_ptr(), ip.len());
}
return Ok(Address(res));
return Ok(Address(res, 4));
}
if let Ok(addr) = Ipv6Addr::from_str(text) {
let mut segments = addr.segments();
@ -49,20 +63,19 @@ impl FromStr for Address {
segments[i] = segments[i].to_be();
}
let bytes = unsafe { as_bytes(&segments) };
let mut res = Vec::with_capacity(16);
let mut res = [0; 16];
unsafe {
res.set_len(16);
ptr::copy_nonoverlapping(bytes.as_ptr(), res.as_mut_ptr(), bytes.len());
}
return Ok(Address(res));
return Ok(Address(res, 16));
}
let parts: Vec<&str> = text.split(':').collect();
if parts.len() == 6 {
let mut bytes = Vec::with_capacity(6);
let mut bytes = [0; 16];
for i in 0..6 {
bytes.push(try!(u8::from_str_radix(&parts[i], 16).map_err(|_| Error::ParseError("Failed to parse mac"))));
bytes[i] = try!(u8::from_str_radix(&parts[i], 16).map_err(|_| Error::ParseError("Failed to parse mac")));
}
return Ok(Address(bytes));
return Ok(Address(bytes, 6));
}
return Err(Error::ParseError("Failed to parse address"))
}
@ -154,8 +167,8 @@ impl fmt::Display for Error {
#[test]
fn address_fmt() {
assert_eq!(format!("{:?}", Address(vec![120,45,22,5])), "120.45.22.5");
assert_eq!(format!("{:?}", Address(vec![120,45,22,5,1,2])), "78:2d:16:05:01:02");
assert_eq!(format!("{:?}", Address(vec![3,56,120,45,22,5,1,2])), "vlan824/78:2d:16:05:01:02");
assert_eq!(format!("{:?}", Address(vec![0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15])), "0001:0203:0405:0607:0809:0a0b:0c0d:0e0f");
assert_eq!(format!("{:?}", Address([120,45,22,5,0,0,0,0,0,0,0,0,0,0,0,0], 4)), "120.45.22.5");
assert_eq!(format!("{:?}", Address([120,45,22,5,1,2,0,0,0,0,0,0,0,0,0,0], 6)), "78:2d:16:05:01:02");
assert_eq!(format!("{:?}", Address([3,56,120,45,22,5,1,2,0,0,0,0,0,0,0,0], 8)), "vlan824/78:2d:16:05:01:02");
assert_eq!(format!("{:?}", Address([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], 16)), "0001:0203:0405:0607:0809:0a0b:0c0d:0e0f");
}

View File

@ -3,7 +3,7 @@ use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr, SocketAddrV6, Ipv6Addr};
use std::u16;
use super::types::{Error, NetworkId, Range, Address};
use super::util::{as_obj, as_bytes, to_vec};
use super::util::{as_obj, as_bytes};
use super::crypto::Crypto;
const MAGIC: [u8; 3] = [0x76, 0x70, 0x6e];
@ -166,7 +166,9 @@ pub fn decode<'a>(data: &'a mut [u8], crypto: &mut Crypto) -> Result<(Options, M
if end < pos + len + 1 {
return Err(Error::ParseError("Init data too short"));
}
let base = Address(to_vec(&data[pos..pos+len]));
let mut addr = [0; 16];
unsafe { ptr::copy_nonoverlapping(data[pos..].as_ptr(), addr.as_mut_ptr(), len) };
let base = Address(addr, len as u8);
let prefix_len = data[pos+len];
pos += len + 1;
addrs.push(Range{base: base, prefix_len: prefix_len});
@ -260,8 +262,7 @@ pub fn encode(options: &Options, msg: &Message, buf: &mut [u8], crypto: &mut Cry
pos += 1;
for range in ranges {
let base = &range.base;
let len = base.0.len();
assert!(len <= 255);
let len = base.1 as usize;
assert!(buf.len() >= pos + 1 + len + 1);
buf[pos] = len as u8;
pos += 1;
@ -350,8 +351,8 @@ fn encode_option_network_id() {
fn encode_message_init() {
let mut options = Options::default();
let mut crypto = Crypto::None;
let addrs = vec![Range{base: Address(vec![0,1,2,3]), prefix_len: 24},
Range{base: Address(vec![0,1,2,3,4,5]), prefix_len: 16}];
let addrs = vec![Range{base: Address([0,1,2,3,0,0,0,0,0,0,0,0,0,0,0,0], 4), prefix_len: 24},
Range{base: Address([0,1,2,3,4,5,0,0,0,0,0,0,0,0,0,0], 6), prefix_len: 16}];
let msg = Message::Init(addrs);
let mut buf = [0; 1024];
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);

View File

@ -1,4 +1,4 @@
use std::{mem, slice, ptr};
use std::{mem, slice};
#[inline(always)]
pub unsafe fn as_bytes<T>(obj: &T) -> &[u8] {
@ -11,15 +11,6 @@ pub unsafe fn as_obj<T>(data: &[u8]) -> &T {
mem::transmute(data.as_ptr())
}
pub fn to_vec(data: &[u8]) -> Vec<u8> {
let mut v = Vec::with_capacity(data.len());
unsafe {
ptr::copy_nonoverlapping(data.as_ptr(), v.as_mut_ptr(), data.len());
v.set_len(data.len());
}
v
}
macro_rules! fail {
($format:expr) => ( {
use std::process;