Closed gahaas closed 2 years ago
The original intention of the de/serialize functions was to provide the equivalent of what's available on the JS side for sharing modules, e.g., between workers. How exactly it's implemented would be up to the engine.
So, technically, the above choices are an implementation detail. When compilation actually takes place is not semantically observable, beyond validation and checking for static limits, which obviously has to happen beforehand. In practice, though, there probably is some AOT expectation (and I hope we never get ourselves into a situation where Wasm depends on type feedback). So the third option would be a weird choice. Beyond that it's really a spectrum that is rather specific to an engine's implementation choices. Not sure what could be said there in a general fashion.
In all three items it would be good to have the original wasm module available in deserialize. Having the original wasm module as a second parameter to deserialize would be nice, to avoid writing it into the serialized wasm module.
If an implementation needs the original wasm code, then it should include it in the serialised module – or the parts of it that are relevant to deserialisation. Semantically, it does not make sense to make it a separate parameter, because it's not actually independent. And the engine would need to be able to verify that it actually is equal to the original.
Thanks for your answer.
For the record, collecting type feedback is a thing now. Inlining call_ref
calls at all is impossible without it, and even for engine-side inlining of call_direct
, feedback makes the inlining decisions much better, yielding tangible performance benefits.
Dynamic tiering (i.e. optimizing functions that get "hot") is also a thing now, we're shipping it by default. That means when serialize
is called, depending on prior execution, any number of functions (including "none" and "all of them") may have been optimized. Our plan is to serialize whatever optimized code exists (i.e. neither tossing it out, nor explicitly creating/awaiting more), but we realize that this may not be satisfactory for all users, and would be happy to reconsider that choice.
Leaving the choice here entirely up to the engine seems counter to Wasm's general desire to offer a predictable cost model to users. I'd expect that users of this API would like some control over what it does under the hood. Maybe it would make sense to have an additional serializeOptimized
function, or some other way to distinguish between e.g. "I just want to ship this module to another process, I don't particularly care about specific performance characteristics" and "I specifically use serialize/deserialize as a means to store a warmed-up snapshot of my module that offers super-fast startup upon deserialization". Or maybe this just-do-whatever solution is good enough... I encourage users of this API to speak up to express their needs. (Should we reopen this issue as further encouragement?)
Leaving the choice here entirely up to the engine seems counter to Wasm's general desire to offer a predictable cost model to users.
I don't disagree, but of course dynamic optimisations and tiering already make that a true statement regardless of serialisation. Serialisation does not even make it worse.
In V8 we are refactoring our tiering strategy for WebAssembly. This refactoring also influences our implementation of
serialize
anddeserialize
in the C-API. However, the expectations on the implementation ofserialize
anddeserialize
are unclear to me. Possible options are:serialize
first optimizes the compiled as much as possible, and then returns the optimized code. Afterdeserialize
the wasm module can start with optimal performance. The downside is thatserialize
then may require additional time. Also, some optimizations may be based on feedback, which would make the code hard to serialize, and maybe also impossible to optimize without the feedback.serialize
takes a snapshot of the current state of the compiled module. For example,serialize
could be called after startup to make sure that all startup code exists. Ifserialize
is called too early, and some kind of lazy compilation is used, this could mean that an empty module gets serialize. The serialized module would also have to contain the original wasm module to be able to do lazy compilation.serialize
produces something thatdeserialize
can turn into a compile wasm module. A trivial implementation would be forserialize
to just return the original wasm module.deserialize
would compile the module bytes.In all three items it would be good to have the original wasm module available in
deserialize
. Having the original wasm module as a second parameter todeserialize
would be nice, to avoid writing it into the serialized wasm module.