andrewl6097 edited PR #14753.
andrewl6097 edited PR #14753:
Closes #14752
GuestProfiler::newrebuilds and re-demangles each module's symbol table on every call, which costs milliseconds for large modules.With this change, now the use of the profiler can be, optionally, split into a prepare step (where you make a
GuestProfilerModulesstruct which is an expensive but now one-time operation) separate from the profiling step - where you pass thatGuestProfilerModulesinto the profiler.The standard GuestProfiler::new just does that internally, so existing callers don't have to change. But - and
wasmtime servedoes this now - you have the option of disentangling and caching the expensive demangling effort.Added refs to this in the profiling documentation.
As it seemed practical to mirror this in the C bindings I went ahead and did that as well. I'd be happy to leave
wasmtime servealone if you prefer, but I felt that it would be good to see example code.Changes
GuestProfilerModules: a new public type for the modules a profiler attributes samples to. It is prepared once and cheap to clone, since the prepared modules sit behind anArc.
GuestProfilerModules::newandnew_componentdo the per-module work thatGuestProfiler::newused to repeat for every profile: the guest-debugging check, each module's text range, and its demangled symbol table.- The table-building code moves unchanged into
module_symbol_table.GuestProfiler::new_with_modules(module_name, interval, &modules): starts a profile from a prepared set. It needs noEngineand can't fail, because the checks already ran during preparation.
- It adds each library without its table, then attaches the shared table with
Profile::set_lib_symbol_table.Profile::add_libkeys libraries by value, so a table carried insideLibraryInfowould be hashed, every name included, on each new profile.GuestProfiler::newandnew_component: keep their signatures. They now build a set and callnew_with_modules.Moduleis unchanged.wasmtime serve --profile=guest: starts a profiler for every instance. It now prepares the component's modules once, when the server starts, and usesnew_with_modulesfor each instance.- C API: adds
wasmtime_guestprofiler_module_set_twithwasmtime_guestprofiler_module_set_{new,clone,delete}andwasmtime_guestprofiler_new_with_module_set. The array conversion shared withwasmtime_guestprofiler_newnow accepts aNULLpointer with zero length.- Docs: rustdoc for the new items, including an example that runs as a doctest, plus a short embedding note in the guest profiling page of the book.
Measurements
Release build, x86_64 Linux, median of 1000 calls, using the issue's reproduction program extended to call the new API. The 1.7 MB module is a real Rust module with about 1,500 functions.
Module Before: new()After: GuestProfilerModules::new(once)After: new_with_modules(per profile)1,500 functions 663 µs 679 µs 0.95 µs 10,000 functions 4.52 ms 4.63 ms 0.95 µs Real 1.7 MB module 2.39 ms 2.37 ms 0.95 µs The per-profile cost no longer depends on how many functions a module has. The previous revision still spent about 8 ns per function on each profile. That was the walk over every function to find the module's text range, which now runs once during preparation.
andrewl6097 commented on PR #14753:
Ok - I pushed a new version that takes a different and more explicit approach. Basically now you can build a modules cache explicitly, then optionally pass that in to the GuestProfiler as an argument, and it'll take that and use it (if you don't, it'll just do that for you internally just-in-time). I updated
wasmtime serveto demonstrate this.
github-actions[bot] added the label wasmtime:docs on PR #14753.
github-actions[bot] added the label wasmtime:c-api on PR #14753.
:thumbs_up: alexcrichton submitted PR review:
Looks great to me, thanks!
alexcrichton added PR #14753 Share GuestProfiler symbol tables through GuestProfilerModules to the merge queue.
:check: alexcrichton merged PR #14753.
alexcrichton removed PR #14753 Share GuestProfiler symbol tables through GuestProfilerModules from the merge queue.
Last updated: Oct 31 2026 at 23:03 UTC