#[derive(Clone, Copy)]
pub struct Mark {
#[cfg(feature = "ssr-timings")]
at: std::time::Instant,
}
impl Mark {
#[cfg(feature = "ssr-timings")]
fn now() -> Self {
Self {
at: std::time::Instant::now(),
}
}
#[cfg(not(feature = "ssr-timings"))]
#[inline(always)]
fn now() -> Self {
Self {}
}
#[cfg(feature = "ssr-timings")]
fn elapsed(self) -> std::time::Duration {
self.at.elapsed()
}
}
#[cfg(feature = "ssr-timings")]
mod on {
use parking_lot::Mutex;
use std::{sync::Arc, time::Duration};
use super::Mark;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct SsrTimings {
pub total: Duration,
pub instantiate: Duration,
pub wasm_wall: Duration,
pub wasm_mount: Duration,
pub wasm_handle_url: Duration,
pub wasm_reentry: Duration,
pub host_in_wasm: Duration,
pub decode_dom: Duration,
pub dom_apply: Duration,
pub build_response: Duration,
pub html_tree: Duration,
pub html_inject: Duration,
pub html_string: Duration,
pub fetch_wait: Duration,
pub dom_batches: u32,
pub dom_commands: u32,
pub dom_blob_bytes: u64,
pub host_calls: u32,
pub wasm_calls: u32,
pub reentry_calls: u32,
pub fetches: u32,
pub html_bytes: u64,
}
impl SsrTimings {
pub fn wasm_self(&self) -> Duration {
self.wasm_wall.saturating_sub(self.host_in_wasm)
}
pub fn dom_build(&self) -> Duration {
self.decode_dom + self.dom_apply
}
pub fn html_total(&self) -> Duration {
self.html_tree + self.html_inject + self.html_string
}
pub fn unaccounted(&self) -> Duration {
self.total
.saturating_sub(self.instantiate)
.saturating_sub(self.wasm_wall)
.saturating_sub(self.dom_apply)
.saturating_sub(self.build_response)
.saturating_sub(self.fetch_wait)
}
}
#[derive(Clone, Default)]
pub struct SsrProbe(Arc<Mutex<SsrTimings>>);
impl SsrProbe {
pub fn new() -> Self {
Self::default()
}
pub fn start(&self) -> Mark {
Mark::now()
}
fn add(&self, mark: Mark, pick: impl FnOnce(&mut SsrTimings) -> &mut Duration) {
let elapsed = mark.elapsed();
let mut guard = self.0.lock();
*pick(&mut guard) += elapsed;
}
pub fn instantiate(&self, mark: Mark) {
let elapsed = mark.elapsed();
let mut guard = self.0.lock();
guard.instantiate += elapsed;
}
pub fn wasm_call(&self, name: &'static str, mark: Mark) {
let elapsed = mark.elapsed();
let mut guard = self.0.lock();
guard.wasm_wall += elapsed;
guard.wasm_calls += 1;
match name {
super::ENTRY_FUNCTION => guard.wasm_mount += elapsed,
super::HANDLE_URL_FUNCTION => guard.wasm_handle_url += elapsed,
_ => {
guard.wasm_reentry += elapsed;
guard.reentry_calls += 1;
}
}
}
pub fn host_call(&self, mark: Mark) {
let elapsed = mark.elapsed();
let mut guard = self.0.lock();
guard.host_in_wasm += elapsed;
guard.host_calls += 1;
}
pub fn decoded(&self, mark: Mark, blob_bytes: usize, commands: usize) {
let elapsed = mark.elapsed();
let mut guard = self.0.lock();
guard.decode_dom += elapsed;
guard.dom_batches += 1;
guard.dom_commands += commands as u32;
guard.dom_blob_bytes += blob_bytes as u64;
}
pub fn dom_apply(&self, mark: Mark) {
self.add(mark, |timings| &mut timings.dom_apply);
}
pub fn build_response(&self, mark: Mark) {
self.add(mark, |timings| &mut timings.build_response);
}
pub fn html_tree(&self, mark: Mark) {
self.add(mark, |timings| &mut timings.html_tree);
}
pub fn html_inject(&self, mark: Mark) {
self.add(mark, |timings| &mut timings.html_inject);
}
pub fn html_string(&self, mark: Mark) {
self.add(mark, |timings| &mut timings.html_string);
}
pub fn fetch_wait(&self, mark: Mark) {
self.add(mark, |timings| &mut timings.fetch_wait);
}
pub fn fetch_started(&self) {
self.0.lock().fetches += 1;
}
pub fn finish(&self, mark: Mark, body_len: usize) -> SsrTimings {
let elapsed = mark.elapsed();
let mut guard = self.0.lock();
guard.total = elapsed;
guard.html_bytes = body_len as u64;
guard.clone()
}
}
}
#[cfg(not(feature = "ssr-timings"))]
mod off {
use super::Mark;
#[derive(Clone, Default)]
pub struct SsrProbe;
impl SsrProbe {
#[inline(always)]
pub fn new() -> Self {
Self
}
#[inline(always)]
pub fn start(&self) -> Mark {
Mark::now()
}
#[inline(always)]
pub fn instantiate(&self, _mark: Mark) {}
#[inline(always)]
pub fn wasm_call(&self, _name: &'static str, _mark: Mark) {}
#[inline(always)]
pub fn host_call(&self, _mark: Mark) {}
#[inline(always)]
pub fn decoded(&self, _mark: Mark, _blob_bytes: usize, _commands: usize) {}
#[inline(always)]
pub fn dom_apply(&self, _mark: Mark) {}
#[inline(always)]
pub fn build_response(&self, _mark: Mark) {}
#[inline(always)]
pub fn html_tree(&self, _mark: Mark) {}
#[inline(always)]
pub fn html_inject(&self, _mark: Mark) {}
#[inline(always)]
pub fn html_string(&self, _mark: Mark) {}
#[inline(always)]
pub fn fetch_wait(&self, _mark: Mark) {}
#[inline(always)]
pub fn fetch_started(&self) {}
}
}
pub const ENTRY_FUNCTION: &str = "vertigo_entry_function";
pub const HANDLE_URL_FUNCTION: &str = "vertigo_export_handle_url";
pub const WASM_COMMAND_FUNCTION: &str = "vertigo_export_wasm_command";
#[cfg(feature = "ssr-timings")]
pub use on::{SsrProbe, SsrTimings};
#[cfg(not(feature = "ssr-timings"))]
pub use off::SsrProbe;