Skip to content

Instantly share code, notes, and snippets.

@hsqStephenZhang
Last active February 11, 2026 22:09
Show Gist options
  • Select an option

  • Save hsqStephenZhang/17f1c950e9be20d838a468d955fd0f0e to your computer and use it in GitHub Desktop.

Select an option

Save hsqStephenZhang/17f1c950e9be20d838a468d955fd0f0e to your computer and use it in GitHub Desktop.
wasm simd benchmark for sonic-rs
//! A simple benchmark for comparing wasm fallback vs simd128 performance.
//!
//! Build & run:
//! # fallback (no simd128)
//! cargo build --example wasm_bench --target wasm32-wasip1 --release
//! wasmtime run target/wasm32-wasip1/release/examples/wasm_bench.wasm
//!
//! # simd128
//! RUSTFLAGS="-C target-feature=+simd128" \
//! cargo build --example wasm_bench --target wasm32-wasip1 --release
//! wasmtime run target/wasm32-wasip1/release/examples/wasm_bench.wasm
use std::time::Instant;
use sonic_rs::pointer;
/// A medium-sized JSON payload (~4KB) that exercises various JSON features:
/// strings, numbers, booleans, nulls, nested objects, arrays.
const SAMPLE_JSON: &str = r#"
{
"statuses": [
{
"id": 505874924095815700,
"id_str": "505874924095815681",
"text": "RT @yaborutweet: Oteki tweet",
"source": "<a href=\"http://twitter.com/download/android\">Twitter for Android</a>",
"truncated": false,
"user": {
"id": 2696111005,
"name": "MDistribution",
"screen_name": "MaboroshiDist",
"location": "Japan",
"description": "This is a test description that spans multiple words and has some unicode: café résumé naïve",
"followers_count": 21,
"friends_count": 5,
"listed_count": 0,
"favourites_count": 0,
"statuses_count": 1,
"created_at": "Mon Jul 28 12:38:12 +0000 2014",
"verified": false,
"lang": "ja",
"profile_image_url": "http://pbs.twimg.com/profile_images/12345/image_normal.jpeg"
},
"retweet_count": 4,
"favorite_count": 0,
"entities": {
"hashtags": [],
"symbols": [],
"urls": [],
"user_mentions": [
{
"screen_name": "yaborutweet",
"name": "test user",
"id": 461104190,
"indices": [3, 15]
}
]
},
"lang": "ja"
},
{
"id": 505874922023837700,
"id_str": "505874922023837696",
"text": "Some example text with special chars: \"quotes\" and \\backslash\\ and /slashes/ and tab:\there",
"source": "web",
"truncated": false,
"user": {
"id": 1234567890,
"name": "Test User 2",
"screen_name": "testuser2",
"location": null,
"description": null,
"followers_count": 100,
"friends_count": 200,
"listed_count": 5,
"favourites_count": 50,
"statuses_count": 300,
"created_at": "Thu Jan 01 00:00:00 +0000 2015",
"verified": true,
"lang": "en"
},
"coordinates": {
"type": "Point",
"coordinates": [-73.99, 40.74]
},
"retweet_count": 0,
"favorite_count": 10,
"entities": {
"hashtags": [
{"text": "test", "indices": [0, 5]},
{"text": "benchmark", "indices": [6, 16]}
],
"symbols": [],
"urls": [
{
"url": "http://t.co/example",
"expanded_url": "http://example.com/very/long/path/to/resource",
"display_url": "example.com/very/long/pa...",
"indices": [17, 37]
}
],
"user_mentions": []
},
"lang": "en"
}
],
"search_metadata": {
"completed_in": 0.087,
"max_id": 505874924095815700,
"max_id_str": "505874924095815681",
"next_results": "?max_id=505874847260864511&q=%E4%B8%80&count=100&include_entities=1",
"query": "%E4%B8%80",
"refresh_url": "?since_id=505874924095815681&q=%E4%B8%80&include_entities=1",
"count": 100,
"since_id": 0,
"since_id_str": "0"
}
}
"#;
fn bench_parse_to_value(json: &[u8], iterations: usize) -> std::time::Duration {
let start = Instant::now();
for _ in 0..iterations {
let v: sonic_rs::Value = sonic_rs::from_slice(json).unwrap();
std::hint::black_box(&v);
}
start.elapsed()
}
fn bench_get_from(json: &[u8], iterations: usize) -> std::time::Duration {
let start = Instant::now();
for _ in 0..iterations {
let v = sonic_rs::get(json, pointer!["statuses", 0, "user", "name"]);
assert!(v.is_ok());
std::hint::black_box(&v);
let v = sonic_rs::get(json, &["search_metadata", "query"]);
assert!(v.is_ok());
std::hint::black_box(&v);
let v = sonic_rs::get(
json,
pointer!["statuses", 1, "entities", "urls", 0, "expanded_url"],
);
assert!(v.is_ok());
std::hint::black_box(&v);
}
start.elapsed()
}
fn bench_serialize(json: &[u8], iterations: usize) -> std::time::Duration {
let v: sonic_rs::Value = sonic_rs::from_slice(json).unwrap();
let start = Instant::now();
for _ in 0..iterations {
let s = sonic_rs::to_string(&v).unwrap();
std::hint::black_box(&s);
}
start.elapsed()
}
fn main() {
let json = SAMPLE_JSON.trim().as_bytes();
let json_len = json.len();
// Detect SIMD mode
let simd_mode = if cfg!(all(target_arch = "wasm32", target_feature = "simd128")) {
"wasm-simd128"
} else if cfg!(target_feature = "sse2") {
"sse2"
} else if cfg!(all(target_feature = "neon", target_arch = "aarch64")) {
"neon"
} else {
"fallback (scalar)"
};
println!("=== sonic-rs wasm benchmark ===");
println!("SIMD mode: {simd_mode}");
println!("JSON payload size: {json_len} bytes");
println!();
// Warmup
let _ = bench_parse_to_value(json, 100);
let iterations = 10_000;
// 1. Parse to Value
let dur = bench_parse_to_value(json, iterations);
let ns_per_op = dur.as_nanos() as f64 / iterations as f64;
let throughput = json_len as f64 / (ns_per_op / 1e9) / 1024.0 / 1024.0;
println!("[parse_to_value] {iterations} iters, {ns_per_op:.0} ns/op, {throughput:.1} MB/s");
// 2. get_from (3 lookups per iteration)
let dur = bench_get_from(json, iterations);
let ns_per_op = dur.as_nanos() as f64 / iterations as f64;
println!("[get_from x3] {iterations} iters, {ns_per_op:.0} ns/op");
// 3. Serialize
let dur = bench_serialize(json, iterations);
let ns_per_op = dur.as_nanos() as f64 / iterations as f64;
let throughput = json_len as f64 / (ns_per_op / 1e9) / 1024.0 / 1024.0;
println!("[serialize] {iterations} iters, {ns_per_op:.0} ns/op, {throughput:.1} MB/s");
println!();
println!("Done.");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment