Time Profiling

This section describes how to profile Web pages using Rust and WebAssembly where the goal is improving throughput or latency.

⚡ Always make sure you are using an optimized build when profiling! wasm-pack build will build with optimizations by default.

Available Tools

The window.performance.now() Timer

The performance.now() function returns a monotonic timestamp measured in milliseconds since the Web page was loaded.

Calling performance.now has little overhead, so we can create simple, granular measurements from it without distorting the performance of the rest of the system and inflicting bias upon our measurements.

We can use it to time various operations, and we can access window.performance.now() via the web-sys crate:


# #![allow(unused_variables)]
#fn main() {
extern crate web_sys;

fn now() -> f64 {
    web_sys::window()
        .expect("should have a Window")
        .performance()
        .expect("should have a Performance")
        .now()
}
#}

Developer Tools Profilers

All Web browsers' built-in developer tools include a profiler. These profilers display which functions are taking the most time with the usual kinds of visualizations like call trees and flame graphs.

If you build with debug symbols so that the "name" custom section is included in the wasm binary, then these profilers should display the Rust function names instead of something opaque like wasm-function[123].

Note that these profilers won't show inlined functions, and since Rust and LLVM rely on inlining so heavily, the results might still end up a bit perplexing.

Screenshot of profiler with Rust symbols

Resources

The console.time and console.timeEnd Functions

The console.time and console.timeEnd functions allow you to log the timing of named operations to the browser's developer tools console. You call console.time("some operation") when the operation begins, and call console.timeEnd("some operation") when it finishes. The string label naming the operation is optional.

You can use these functions directly via the web-sys crate:

Here is a screenshot of console.time logs in the browser's console:

Screenshot of console.time logs

Additionally, console.time and console.timeEnd logs will show up in your browser's profiler's "timeline" or "waterfall" view:

Screenshot of console.time logs

Using #[bench] with Native Code

The same way we can often leverage our operating system's native code debugging tools by writing #[test]s rather than debugging on the Web, we can leverage our operating system's native code profiling tools by writing #[bench] functions.

Write your benchmarks in the benches subdirectory of your crate. Make sure that your crate-type includes "rlib" or else the bench binaries won't be able to link your main lib.

However! Make sure that you know the bottleneck is in the WebAssembly before investing much energy in native code profiling! Use your browser's profiler to confirm this, or else you risk wasting your time optimizing code that isn't hot.

Resources