rs-soroban-env
rs-soroban-env copied to clipboard
Multi wasmi
This is a preliminary PR that brings wasmi 0.34 into soroban for protocol 22 and also enables "multi-wasmi" execution -- running older contracts on 0.31 and newer ones on 0.34. There are two reasons for this:
- The new wasmi has a completely different execution engine, so even the exact same wasm code run on 0.34 with the exact same cost model as we ran a contract on 0.31 will not always cost the same, and in particular will not run out of gas at the exact same points. So we have to run old contracts on old wasmi and new ones on new wasmi. Hopefully in the future this won't happen a lot more times -- it will depend to some extent on how often wasmi changes their internal representation. I've talked to @robbepop about trying to work on a stable gas-consumption interface at least, but wasmi's not there yet.
- The type structure / API surface of wasmi changed a bit, enough that we need a bunch of switching shims in between the two even if they had exactly the same execution engine. But they don't anyways.
The good news is that this enables two significant sources of performance improvement:
- The new register machine runs straight-line wasm code something like 2x faster than the old stack machine on computationally intensive benchmarks (eg. coremark)
- The new engine supports lazy compilation which means VM instantiation only does a quick pre-parse of the module and then parses/validates/translates code on demand as functions are called. This is ideal for smart contracts since usually each transaction only calls a fraction of the code in a module, so we should see fairly dramatic improvements in contract startup overhead from this.
I don't have concrete numbers on realistic contracts yet unfortunately, and there's probably lots of tidying to do here, I haven't even reviewed the unified diff yet to look for FIXMEs, but I at least have the testsuite passing now locally (it'll probably break somehow in CI) so I figured I'd post this as progress.
I have not (yet) looked into the changes in detail though I looked into the changes done to Wasmi v0.34.0: https://github.com/stellar/wasmi/commit/c5b7c4315db549b01755a387fb142a12fcbe1148
Particularly there are changes to the Wasmi fuel API to provide total fuel which probably was done to mirror the behavior of the old Wasmi. I think it would be better to do it conversely and instead host the total amount on the host side if possible. The reason is that the Wasmi v0.34.0 fuel API likely won't change in the future and it is way simpler than the old fuel API. Hosting the total fuel on the host side would maybe imply minor changes to the old Wasmi version interopt but would get rid of this particular change to all future Wasmi versions.
Furthermore there might be room for even more optimizations:
More Optimizations
Lazy + Unchecked Compilation
At Parity where Wasmi was originally used the smart contracts life cycle is divided into three stages.
upload_code: Uploads the smart contract code onto the chain. Here validation and compilation is performed fully and eagerly.instantiate: Here a previously uploaded code is going to be instantiated to make it possible to interopt with the resulting smart contracts. This sets up initial state by calling thedeployfunction of a smart contract.execute: This executes a previously instantiated smart contract with some parameters and returns the result of the execution.
For stages 2. and 3. it is possible to rely on the fact that stage 1. already fully validated and compiled the smart contract and thus can use lazy compilation combined with unchecked compilation thus skipping validation entirely and only lazily compile the contract. Lazy unchecked compilation is roughly twice as fast as lazy checked compilation.
Linker Builder
As demonstrated in the blog post linked by @graydon the LinkerBuilder was introduced to improve setup time of host functions by a huge amount. With 50 host functions we saw 120x improvement. If it is possible to setup a LinkerBuilder once and re-use it for stage 2. and 3. above it will get rid of most of the overhead associated to those stages for setup. Maybe soroban already solves this problem in another way though of which I am unaware.
Version 0.35.0
This update further optimizes Wasmi module creation and in particular for lazy compilation scenarios. The speed-up is due to decreased locking calls (Mutex/RwLock) for the Wasm function section and improved data structures, e.g. getting rid of a whole BTreeMap during module creation. Also v0.35.0 fixes the long-standing dead-lock with module creation in host functions. In local benchmarks I saw improvements of up to 23% in lazy+checked benchmarks.
Stable Fuel Metering
I have not (yet) started working on stable fuel metering for Wasmi though I have had the idea that it should be possible to have a (somewhat) stable fuel metering if fuel metering was based on Wasm instructions instead of Wasmi instructions. While this very likely yields higher fuel costs because we must no longer depend on optimizations performed by Wasmi it would be independent of Wasmi internals. At least that's the idea. The even better idea though is to make this a Wasm standard across Wasm engines. There seems to be quite some people who are very interested in getting this into multiple Wasm engines so that fuel metering could even be stable across Wasm engines in an ideal world. The amount of work required to support this stable fuel metering in Wasmi is likely not small though.
FYI: In case the Linker is too inefficient for the purposes of Soroban and the LinkerBuilder cannot be used, I just released Wasmi v0.36.0 with the new Instance::new API similar to the one found in Wasmtime.
This is rather low-level compared to using the Wasmi Linker, but also has the potential advantage of not having to use the Linker and instead take driving the import resolution process into the users hands if needed.
we're not going to pursue this direction. went with https://github.com/stellar/rs-soroban-env/pull/1442 instead. closing.