From 94d547f44387ffa710f5debc9a520ea07a6d05a1 Mon Sep 17 00:00:00 2001 From: Shark Date: Mon, 29 Apr 2024 00:29:59 +0200 Subject: [PATCH] reimplement wasm timings --- Cargo.lock | 33 +++++- crates/gosub_shared/Cargo.toml | 9 ++ crates/gosub_shared/src/timing.rs | 172 +++++++++++++++++++++++------- 3 files changed, 174 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 960b7af34..0b362163e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1179,9 +1179,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "js-sys", @@ -1402,11 +1402,15 @@ version = "0.1.0" dependencies = [ "anyhow", "chardet", + "getrandom", + "js-sys", "lazy_static", "rand 0.9.0-alpha.1", "thiserror", "url", "uuid", + "wasm-bindgen-test", + "web-sys", ] [[package]] @@ -3549,6 +3553,31 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "wasm-bindgen-test" +version = "0.3.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "wayland-backend" version = "0.3.3" diff --git a/crates/gosub_shared/Cargo.toml b/crates/gosub_shared/Cargo.toml index c13841e40..9096033d7 100644 --- a/crates/gosub_shared/Cargo.toml +++ b/crates/gosub_shared/Cargo.toml @@ -13,3 +13,12 @@ anyhow = "1.0.82" lazy_static = "1.4.0" uuid = { version = "1.8.0", features = ["v4"] } rand = "0.9.0-alpha.1" + + +[target.'cfg(target_arch = "wasm32")'.dependencies] +js-sys = "0.3.69" +getrandom = { version = "0.2.14", features = ["js"] } +web-sys = { version = "0.3.69", features = ["Performance", "Window"] } + +[dev-dependencies] +wasm-bindgen-test = "0.3.20" \ No newline at end of file diff --git a/crates/gosub_shared/src/timing.rs b/crates/gosub_shared/src/timing.rs index ce7e14289..21a4b10a0 100644 --- a/crates/gosub_shared/src/timing.rs +++ b/crates/gosub_shared/src/timing.rs @@ -1,9 +1,12 @@ use std::collections::HashMap; use std::fmt::{Display, Formatter}; use std::sync::Mutex; +#[cfg(not(target_arch = "wasm32"))] use std::time::Instant; use lazy_static::lazy_static; +#[cfg(target_arch = "wasm32")] +use web_sys::window; type TimerId = uuid::Uuid; @@ -181,30 +184,17 @@ lazy_static! { #[macro_export] macro_rules! timing_start { ($namespace:expr, $context:expr) => {{ - #[cfg(not(target_arch = "wasm32"))] - { - $crate::timing::TIMING_TABLE - .lock() - .unwrap() - .start_timer($namespace, Some($context.to_string())) - } - #[cfg(target_arch = "wasm32")] - { - let _ = $context; - let _ = $namespace; - } + $crate::timing::TIMING_TABLE + .lock() + .unwrap() + .start_timer($namespace, Some($context.to_string())) }}; ($namespace:expr) => {{ - #[cfg(not(target_arch = "wasm32"))] - { - $crate::timing::TIMING_TABLE - .lock() - .unwrap() - .start_timer($namespace, None) - } - #[cfg(target_arch = "wasm32")] - let _ = $namespace; + $crate::timing::TIMING_TABLE + .lock() + .unwrap() + .start_timer($namespace, None) }}; } @@ -212,15 +202,10 @@ macro_rules! timing_start { #[macro_export] macro_rules! timing_stop { ($timer_id:expr) => {{ - #[cfg(not(target_arch = "wasm32"))] - { - $crate::timing::TIMING_TABLE - .lock() - .unwrap() - .stop_timer($timer_id); - } - #[cfg(target_arch = "wasm32")] - let _ = $timer_id; + $crate::timing::TIMING_TABLE + .lock() + .unwrap() + .stop_timer($timer_id); }}; } @@ -253,31 +238,67 @@ macro_rules! timing_display { pub struct Timer { id: TimerId, context: Option, + #[cfg(not(target_arch = "wasm32"))] start: Instant, + #[cfg(target_arch = "wasm32")] + start: f64, + #[cfg(not(target_arch = "wasm32"))] end: Option, + #[cfg(target_arch = "wasm32")] + end: Option, duration_us: u64, } impl Timer { pub fn new(context: Option) -> Timer { + #[cfg(not(target_arch = "wasm32"))] + let start = { Instant::now() }; + + #[cfg(target_arch = "wasm32")] + let start = { + window() + .and_then(|w| w.performance()) + .map(|p| p.now()) + .unwrap_or(f64::NAN) + }; + Timer { id: new_timer_id(), context, - start: Instant::now(), + start, end: None, duration_us: 0, } } + #[cfg(not(target_arch = "wasm32"))] pub fn start(&mut self) { self.start = Instant::now(); } + #[cfg(target_arch = "wasm32")] + pub fn start(&mut self) { + self.start = window() + .and_then(|w| w.performance()) + .map(|p| p.now()) + .unwrap_or(f64::NAN); + } + + #[cfg(not(target_arch = "wasm32"))] pub fn end(&mut self) { self.end = Some(Instant::now()); self.duration_us = self.end.expect("").duration_since(self.start).as_micros() as u64; } + #[cfg(target_arch = "wasm32")] + pub fn end(&mut self) { + self.end = window().and_then(|w| w.performance()).map(|p| p.now()); + self.duration_us = self + .end + .map(|e| (e - self.start) * 1000.0) + .unwrap_or(f64::NAN) as u64; + } + pub(crate) fn has_finished(&self) -> bool { self.end.is_some() } @@ -293,38 +314,50 @@ impl Timer { #[cfg(test)] mod tests { + use rand::random; + #[cfg(not(target_arch = "wasm32"))] use std::thread::sleep; + use std::time::Duration; - use rand::random; + #[cfg(target_arch = "wasm32")] + use { + js_sys::wasm_bindgen::closure::Closure, std::sync::atomic::AtomicBool, std::sync::Arc, + wasm_bindgen_test::wasm_bindgen_test_configure, wasm_bindgen_test::*, + web_sys::wasm_bindgen::JsCast, + }; use super::*; + #[cfg(target_arch = "wasm32")] + wasm_bindgen_test_configure!(run_in_browser); + #[test] + #[cfg(not(target_arch = "wasm32"))] fn test_timing_defaults() { let t = timing_start!("dns.lookup", "www.foo.bar"); - sleep(std::time::Duration::from_millis(10)); + sleep(Duration::from_millis(10)); timing_stop!(t); for _i in 0..10 { let t = timing_start!("html5.parse", "index.html"); - sleep(std::time::Duration::from_millis(random::() % 50)); + sleep(Duration::from_millis(random::() % 50)); timing_stop!(t); } let t = timing_start!("html5.parse", "index.html"); - sleep(std::time::Duration::from_millis(20)); + sleep(Duration::from_millis(20)); timing_stop!(t); let t = timing_start!("html5.parse", "page2.html"); - sleep(std::time::Duration::from_millis(20)); + sleep(Duration::from_millis(20)); timing_stop!(t); let t = timing_start!("html5.parse", "page3.html"); - sleep(std::time::Duration::from_millis(20)); + sleep(Duration::from_millis(20)); timing_stop!(t); let t = timing_start!("css.parse"); - sleep(std::time::Duration::from_millis(20)); + sleep(Duration::from_millis(20)); timing_stop!(t); TIMING_TABLE @@ -332,4 +365,67 @@ mod tests { .unwrap() .print_timings(true, Scale::Auto); } + + #[wasm_bindgen_test] + #[cfg(target_arch = "wasm32")] + fn test_timing_defaults_wasm() { + let window = &window().expect("no global `window` exists"); + + let t = timing_start!("dns.lookup", "www.foo.bar"); + sleep(window, Duration::from_millis(10)); + timing_stop!(t); + + for _i in 0..10 { + let t = timing_start!("html5.parse", "index.html"); + sleep(window, Duration::from_millis(random::() % 50)); + timing_stop!(t); + } + + let t = timing_start!("html5.parse", "index.html"); + sleep(window, Duration::from_millis(20)); + timing_stop!(t); + + let t = timing_start!("html5.parse", "page2.html"); + sleep(window, Duration::from_millis(20)); + timing_stop!(t); + + let t = timing_start!("html5.parse", "page3.html"); + sleep(window, Duration::from_millis(20)); + timing_stop!(t); + + let t = timing_start!("css.parse"); + sleep(window, Duration::from_millis(20)); + timing_stop!(t); + + TIMING_TABLE + .lock() + .unwrap() + .print_timings(true, Scale::Auto); + } + + //This should only be used for testing purposes + #[cfg(target_arch = "wasm32")] + fn sleep(window: &web_sys::Window, duration: Duration) { + let finished = Arc::new(AtomicBool::new(false)); + let mut remaining_loops = 50_000 * duration.as_millis(); //just meant as a backup to avoid infinite loops + + let barrier = Arc::clone(&finished); + let closure: Box ()> = Box::new(move || { + barrier.store(true, std::sync::atomic::Ordering::SeqCst); + }); + window + .set_timeout_with_callback_and_timeout_and_arguments_0( + Closure::wrap(closure).as_ref().unchecked_ref(), + duration.as_millis() as i32, + ) + .unwrap(); + + while !finished.load(std::sync::atomic::Ordering::SeqCst) { + std::hint::spin_loop(); + if remaining_loops == 0 { + break; + } + remaining_loops -= 1; + } + } }