Wasm Serialization Test

Results

Tests

Shared Code

///The primary rust serialization/deserialization library
extern crate serde;
#[macro_use]
extern crate serde_derive;
///The Rust implementation of Message Pack binary
///format using serde
extern crate rmp_serde;
///The Rust memory model based binary format
///using serde
extern crate bincode;

use std::ops::Sub;
use std::fmt::Debug;
///import the functions from the ser/de libs that we will actually use
use rmp_serde::{to_vec as rmp_ser, from_slice as rmp_de};
use bincode::{serialize as bin_ser, deserialize as bin_de};

///The primary data type we will be serializing and deserializing
///represents a webchat message
#[derive(Serialize, Deserialize,Debug,PartialEq, Clone)]
enum Message {
    Ping, Pong, Chat(String), Nick(String), Me(String)
}
///The test results object we will pass back to js
///the enaric T here is for the numeric type of the
///measurement, all js numbers are `f64s` but the 
///native Duration will be a `u32`
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TestResult<T> {
    rmp: Test<T>,
    bin: Test<T>,
}
///An individual test for the TestResult again T is 
///the numeric type
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Test<T> {
    start_timestamp: T,
    end_timestamp: T,
    time_unit: String,
    largest: usize,
    total_size: usize,
}
///The primary entry point for getting the test results object
pub fn get_res<T:Sub<T>>(now: Box<Fn() -> T>, time_unit: &str) -> TestResult<T>
where <T as Sub>::Output: Debug
{
    let rmp_msgs = msgs();
    let rmp_start = (now)();
    let rmp_res = rmp_test(rmp_msgs);
    let rmp_end = (now)();
    let rmp_test = Test {
        start_timestamp: rmp_start,
        end_timestamp: rmp_end,
        time_unit: String::from(time_unit),
        largest: rmp_res.0,
        total_size: rmp_res.1,
    };
    let bin_msgs = msgs();
    let bin_start = (now)();
    let bin_res = bin_test(bin_msgs);
    let bin_end = (now)();
    let bin_test = Test {
        start_timestamp: bin_start,
        end_timestamp: bin_end,
        time_unit: String::from(time_unit),
        largest: bin_res.0,
        total_size: bin_res.1
    };
    TestResult {
        rmp: rmp_test,
        bin: bin_test,
    }
}
///Run the test on a list of messages using 
///the rmp ser/deser functions
fn rmp_test(x: Vec<Message>) -> (usize, usize) {
    let mut total_size = 0;
    let mut max = 0;
    for msg in x {
        let serialized = rmp_ser(&msg).expect("Unable to serialize");
        let size = serialized.len();
        total_size += size;
        if max < size {
            max = size;
        }
        let _: Message = rmp_de(&serialized).expect("Unable to deserialize");
    }
    (max, total_size)
}
///Run the test on a list of messages using 
///the bincode ser/deser functions
fn bin_test(x: Vec<Message>) -> (usize, usize) {
    let mut total_size = 0;
    let mut max = 0;
    for msg in x {
        let serialized = bin_ser(&msg).expect("Unable to serialize");
        let size = serialized.len();
        total_size += size;
        if max < size {
            max = size;
        }
        let _: Message = bin_de(&serialized).expect("Unable to deserialize");
    }
    (max, total_size)
}
///generate 5000 messages (1000 of each type)
fn msgs() -> Vec<Message> {
    let mut ret = vec!();
    let one_of_each = vec![
        Message::Ping,
        Message::Pong,
        Message::Chat(String::from("Hello World!")),
        Message::Nick(String::from("CoolDood2001")),
        Message::Me(String::from("Me!"))
    ];
    for _ in 0..1000 {
        ret.extend(one_of_each.clone());
    }
    ret
}

Native Code

///This is a Hyper.rs endpoint for getting the results running on a native 
///machine. The imports and r500 are missing here becuase it is a partial snippet
///of a larger file.
pub fn get_wasm_results(_req: Request) -> Box<Future<Item = Response, Error = Error>> {
    ///Run the test, it will be measured in nano seconds
    let resluts = get_res(Box::new(now), "ns");
    let body = serde_json::to_vec(&resluts);
    match body {
        Ok(b) => Box::new(
            ok(
                Response::new()
                    .with_header(ContentLength(b.len() as u64))
                    .with_body(b)
            )
        ),
        Err(_) => Box::new(ok(r500()))
    }
}
///Abstract the calculation of now using the Unix Epoch as a base line
///This will be passed to get_res
fn now() -> u32 {
    let start = ::std::time::SystemTime::now().duration_since(::std::time::UNIX_EPOCH);
    start.unwrap().subsec_nanos()
}

WASM Code

///enable the wasm-bindgen macros
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
extern crate ser_test;
extern crate serde_json;
use wasm_bindgen::prelude::*;
use ser_test::get_res;
use serde_json::to_string;

///import the window.performance object's now method
///from the JS runtime.
///Note: this is the most accurate we can get but it will not have SystemTime levels
///of accuracy due to side channel attack mitigation
#[wasm_bindgen]
extern {
    type Performance;
    static performance: Performance;
    #[wasm_bindgen(method)]
    fn now(this: &Performance) -> f64;
}
///export this function to the js runtime
///it will run the tests and return the JSON results
#[wasm_bindgen]
pub fn run_test() -> JsValue {
    let data = get_res(Box::new(now), "ms");
    match to_string(&data) {
        Ok(data) => JsValue::from_str(&data),
        Err(_) => JsValue::from_str("Unable to serialize tests")
    }
}
///Abstract the window.performance.now() call
///this will be passed to get_res
fn now() -> f64 {
    performance.now()
}