wasm2c: Optimize call indirect code footprint for better performance - #2807
Conversation
zherczeg
left a comment
There was a problem hiding this comment.
I know very little about wasm2c, so I am not the best reviewer for this part. I have experience with code optimizations though.
|
If Garbage Collection will ever be part of wabt, do you plan to support it in wasm2c? Type checking will be affected by GC. |
This is not on my todo list for the forseeable future and I haven't heard anything from this other contributors about this. Maybe @keithw or @sbc100 know more. After the performance improvements, my next two features are probably uvwasi support and threads support, although I don't really have a timeline on these as this will depend on when I am not too busy with other responsibilities. |
|
(1) I think it's awesome that you're diving into these performance corners! I really liked your talk yesterday. (2) I am a little surprised by these results given that (at the time I did this call_indirect optimization) I had it benchmarked down to individual instruction counts (#2120) and the penalty from cross-module call_indirect was basically zero (when the linker is able to merge the string constants, so it's just a pointer equality test) or 1-1.5 cycles (when the CPU has to do the actual 256-bit equality test). See discussion on #2120 for more (I certainly had to refresh my memory). Do you think what you're seeing now is consistent with that or has this bitrotted somehow? And, wrt Firefox, is it possible to see if the linker is merging the string constants (as I would have expected)? Or is the idea that it is merging the string constants, so in practice it is just a 64-bit equality test, but just "having the AVX2 256-bit equality test" inline is what's blowing out the icache in production? |
ce9f6a4 to
7b13ce3
Compare
@keithw Yup, it's the icache blowout. I don't think my tests exercise the |
@keithw Caveat to the above. I suppose the slowdown could be a mix of other micro-architectural effect that I haven't checked as well beyond just the icache (e.g., speculative execution prefers the non branching path, runs AVX instructions followed by a rollback which could be extra slow 🤷 etc.). But, either way, the fix is the same --- move the AVX instructions more clearly into a slow/cold code path |
|
There is something I don't understand. |
@zherczeg I believe some of the logic was documented in #2120 The main issue is how you can assign a unique number for all the function types you will encounter, and there are a few options that almost work, but most of these end up having issues of performance or compatibility
I think all this let @keithw converge on the design he implemented in wasm2c
|
|
The problem with collision is that if it ever happens, you cannot fix it. What about option 1 with a header file, where all types are in a generated header file? The header is parsed/extended as necessary. |
7b13ce3 to
60bf19c
Compare
This would require compilation of all modules to occur
The current design has the advantage that any Wasm module can be compiled independently and will continue to work. Also, I do want to be cautious that we don't detour this PR into a full redesign of call-indirect. If anyone is interested in doing the work for that, I am happy to discuss designs for that in a proposed PR/bug for that. Right now, I just want to keep the changes minimal so we improve performance with the design we have in place Note Cleaned up the design a bit more. |
60bf19c to
7f08d6d
Compare
zherczeg
left a comment
There was a problem hiding this comment.
The code looks reasonable. LGTM
7f08d6d to
02a3135
Compare
Currently call indirects in Wasm2c add a lot of inline checks. This bloats code size impacting icache and has a visible impact on Wasm-sandboxed code. For example, in one of the Firefox code paths that is indirect call-heavy on a Wasm-sandboxed libexpat (for XML parsing), this code change improved code performance by 4.33% (i.e. eliminated 13.5% of wasm2c's overhead over native code in this benchmark)
The changes in this PR are
CHECK_CALL_INDIRECTand null check on the local function type. This will generally be populated by Wasm2c runtime code, and will never populated to be nullThis PR does not change any functionality; it only optimizes performance.
(Note this PR can be landed independently of other perf PRs I submit. I will rebase PRs depending on which land first)