diff --git a/.gitignore b/.gitignore index 3ecef18..9a5318b 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ cef.log /website_dev screenshots/ + +zaplib/examples/benchmark_json/data.json diff --git a/Cargo.lock b/Cargo.lock index b2b1486..e904221 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -320,6 +320,15 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "benchmark_json_zaplib" +version = "0.0.1" +dependencies = [ + "serde", + "serde_json", + "zaplib", +] + [[package]] name = "bigedit_http" version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml index 34982bd..efebdf8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ resolver = "2" members = [ "zaplib/cargo-zaplib", "zaplib/ci", + "zaplib/examples/benchmark_json/zaplib", "zaplib/examples/example_bigedit", "zaplib/examples/example_bigedit/http", "zaplib/examples/example_bigedit/hub", diff --git a/zaplib/examples/benchmark_json/generate_json.rb b/zaplib/examples/benchmark_json/generate_json.rb new file mode 100644 index 0000000..e7d90e3 --- /dev/null +++ b/zaplib/examples/benchmark_json/generate_json.rb @@ -0,0 +1,23 @@ +# https://github.com/kostya/benchmarks/blob/1dd7deb29a813d1095e6062c25ad92bd81ce0273/json/generate_json.rb + +# frozen_string_literal: true + +require 'json' + +x = [] + +524_288.times do + h = { + 'x' => rand * -10e-30, + 'y' => rand * 10e30, + 'z' => rand, + 'name' => "#{('a'..'z').to_a.sample(6).join} #{rand(10_000)}", + 'opts' => { '1' => [1, true] } + } + x << h +end + +File.write( + 'data.json', + JSON.pretty_generate('coordinates' => x, 'info' => 'some info') +) diff --git a/zaplib/examples/benchmark_json/index.html b/zaplib/examples/benchmark_json/index.html new file mode 100644 index 0000000..7163608 --- /dev/null +++ b/zaplib/examples/benchmark_json/index.html @@ -0,0 +1,82 @@ + + + + + +

Zaplib (rust/wasm) vs JS: 100mb JSON parsing

+

+ This benchmark was adapted from kostya/benchmarks to work for JS (web) vs Zaplib (rust/wasm). +

+

+ To re-generate the json file locally, run the generate_json.rb file in this directory. +

+

+ It would be interesting augment the Rust benchmark with a simd-powered parsing library, but I wasn't able to get one to compile on my M1 mac... yet! +

+

+ You may notice that the Zaplib benchmark runs about 2x slower (about the same time as the JS) if the devtools are open. We don't exactly know why this is – likely it's a debug or profiling mode that gets enabled. +

+ + + + + + + + + + + + + + + + +
LanguageParsing Time (ms)Compute Time (ms
JavaScript (JSON.parse)
Zaplib Rust Wasm (w/ Serde)
+ + + + diff --git a/zaplib/examples/benchmark_json/js/index.js b/zaplib/examples/benchmark_json/js/index.js new file mode 100644 index 0000000..279c3c0 --- /dev/null +++ b/zaplib/examples/benchmark_json/js/index.js @@ -0,0 +1,40 @@ +// Based on https://github.com/kostya/benchmarks/blob/1dd7deb29a813d1095e6062c25ad92bd81ce0273/json/test.js + +'use strict'; + +function calc(jobj) { + const coordinates = jobj['coordinates']; + const len = coordinates.length; + let x = 0; + let y = 0; + let z = 0; + + for (let i = 0; i < coordinates.length; i++) { + const coord = coordinates[i]; + x += coord['x']; + y += coord['y']; + z += coord['z']; + } + + return { + x: x / len, + y: y / len, + z: z / len + }; +} + +const textP = fetch('/zaplib/examples/benchmark_json/data.json').then(response => response.text()); + +const benchmarkJS = async function() { + const text = await textP; + + const startP = performance.now() + const jobj = JSON.parse(text); + const endP = performance.now(); + + const start = performance.now() + const results = calc(jobj); + const end = performance.now(); + + return [endP-startP, end - start]; +} diff --git a/zaplib/examples/benchmark_json/zaplib/Cargo.toml b/zaplib/examples/benchmark_json/zaplib/Cargo.toml new file mode 100644 index 0000000..8428edd --- /dev/null +++ b/zaplib/examples/benchmark_json/zaplib/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "benchmark_json_zaplib" +version = "0.0.1" +edition = "2021" +publish = false + +[dependencies] +zaplib = { path = "../../../main" } +serde_json = "1.0.79" +serde = { version = "1.0.136", features = ["derive"] } diff --git a/zaplib/examples/benchmark_json/zaplib/src/main.rs b/zaplib/examples/benchmark_json/zaplib/src/main.rs new file mode 100644 index 0000000..a76a9cf --- /dev/null +++ b/zaplib/examples/benchmark_json/zaplib/src/main.rs @@ -0,0 +1,53 @@ +// Based on https://github.com/kostya/benchmarks/blob/1dd7deb29a813d1095e6062c25ad92bd81ce0273/json/json.rs/src/json_struct.rs + +use serde::Deserialize; +use std::io::Read; +use zaplib::*; + +#[derive(Deserialize, PartialEq)] +struct Coordinate { + x: f64, + y: f64, + z: f64, +} + +#[derive(Deserialize)] +struct TestStruct { + coordinates: Vec, +} + +fn calc(jobj: TestStruct) -> Coordinate { + let len = jobj.coordinates.len() as f64; + let mut x = 0_f64; + let mut y = 0_f64; + let mut z = 0_f64; + + for coord in &jobj.coordinates { + x += coord.x; + y += coord.y; + z += coord.z; + } + + Coordinate { x: x / len, y: y / len, z: z / len } +} + +fn call_rust(_name: String, _params: Vec) -> Vec { + let mut file = UniversalFile::open("zaplib/examples/benchmark_json/data.json").unwrap(); + let mut s = String::new(); + let ret = file.read_to_string(&mut s); + if ret.is_err() { + panic!("Failed to read file"); + } + + let start_p = Instant::now(); + let jobj = serde_json::from_str::(&s).unwrap(); + let end_p: UniversalInstant = Instant::now(); + + let start = Instant::now(); + calc(jobj); + let end: UniversalInstant = Instant::now(); + + vec![vec![end_p.duration_since(start_p).as_millis() as u32, end.duration_since(start).as_millis() as u32].into_param()] +} + +register_call_rust!(call_rust);