I've been looking through some WASM code generated by compiling a Rust program using wasm32-wasip1
target and I've noticed that it contains a lot of blocks that are used as ifs. As far as I understand instead of an if like:
(if (i32.eqz (local.get $foo))
(then
(call $bar)))
Some of the generated code used br_if
:
(block $b1
;; if $foo is *not* equal zero, we skip to the end of the block,
;; otherwise we handle the if case
(br_if $b1 (i32.ne (i32.const 0) (local.get $foo)))
(call $bar)
)
I've create a simple benchmark doing the following for ifs:
(if (i32.eqz (i32.const 0)) (then
(if (i32.eqz (i32.const 0)) (then
;; 248 more ifs here
))))
```
and the following benchmark for blocks with `if_br`:
```wat
(block $foo1
(block $foo2
;; 248 more blocks
(br_if $foo2 (i32.ne (i32.const 1) (i32.const 0))))
(br_if $foo1 (i32.ne (i32.const 1) (i32.const 0))))
Then I looped both pieces of code and I measured the runtime. When executing the files in Wasmtime I consistently see the block version being faster then the if version. full runtime is ~300ms for blocks vs ~540ms for ifs. I've also checked on node.js and it's ~1s vs ~1.5s, so it seems like it's not only a detail of Wasmtime implementation.
Is it something that's expected? Anyone more familiar with the VM implementation knows why it would be the case?
Definitely not expected. One thing I notice is that the conditions aren't the same -- though both should be constant-folded away. Could you dump the CLIF and/or machine code from this? (For the latter, wasmtime compile
then objdump
)
Yeah, in order to go through all of the br_if
statements the statement has to be reversed.
I added the source and binaries here: https://gist.github.com/drogus/019496b172bd33c8b936cbbd168254c2
oh sorry, I meant an objdump of the .cwasm file, showing native assembly
How do I do that? I tried wasm-objdump
, but it works only with .wasm files. Wasmtime doesn't seem to have objdump
as a subcommand and when I try wasm-tools objdump
it says:
error: failed to parse `blocks.cwasm`: input bytes aren't valid utf-8
I think he means the objdump
from e.g. GNU Binutils or similar, i.e. one meant for native binaries, not Wasm.
Oh, ok, I thought it needs to be a WASM specific tool :sweat_smile: I updated the gist
woah, fascinating -- in the first (blocks
) case all the branches are elided, while in the second case they're still there. We don't do any branch-folding in the mid-end, so probably something is different in the Wasm translation itself
I don't have a ton of time to dig into this right now but if you could file an issue so we can track this, that'd be very useful!
Last updated: Dec 23 2024 at 12:05 UTC