Simplify setStatus implementation in run. NFC#27121
Conversation
|
Pretty much all the code size tests get worse. Are we not testing with |
A lot of the codesize tests do use For example all of the |
sbc100
left a comment
There was a problem hiding this comment.
More code simplification... way nicer now. But still sadly a codesize regression :(
sbc100
left a comment
There was a problem hiding this comment.
Simplified again.. now the regression is only 11 bytes compressed.
sbc100
left a comment
There was a problem hiding this comment.
Simplified more.. can't quite remove the regression though.
|
Maybe some valid test failures? FWIW, I'm good with the simplifying changes. I originally only had one code path for setStatus, but assumed you didn't want to see a code size regression 😄 . |
| // Yield to the enent loop to allow the browser to paint "Running..." | ||
| await new Promise((resolve) => setTimeout(resolve, 1)); | ||
| // Then clear the status text after the synchronous doRun() completes. | ||
| setTimeout(setStatus, 1, ''); |
There was a problem hiding this comment.
I can't quite figure out why the first setTimeout is awaited but not the second?
There was a problem hiding this comment.
We want to delay the doRun call until after the first setTimeout has fired, hense the await on the first one.
Then we want the second timeout to fire after the doRun (so we do not want await that one, its more fire-and-forget)
| { | ||
| "a.out.js": 18536, | ||
| "a.out.js.gz": 7636, | ||
| "a.out.js": 18572, |
There was a problem hiding this comment.
why does code size grow in these? the change looks like it should be beneficial actually
There was a problem hiding this comment.
My guess its that its because of the async + await keyword being introduced, but I will check.
There was a problem hiding this comment.
Gemini did a pretty good breakdown the size regression.. I guess we need to decide if like this simplification enough to justify it.
The code size regression in the current change (which makes run() async) is primarily due to the syntax overhead of async/await and Promise
compared to setTimeout nesting.
Here is a breakdown of why the async version is larger:
- async Keyword: Adding async to function run() adds 6 bytes (minified).
- Promise Yielding: The expression used to yield to the event loop:
await new Promise(r=>setTimeout(r,1))
is syntactically heavy and takes about 37 bytes in minified form.
In contrast, the synchronous version uses setTimeout nesting which, while visually nested, has less syntax overhead in minified JS than
constructing and awaiting a new Promise. - Local Variable Overhead: The change introduced var setStatus = Module['setStatus'] to avoid repeating Module['setStatus'] . While this saves
bytes when the variable is used many times, in this case, the overhead of defining the local variable (which includes the unminifiable string
'setStatus' ) is not fully offset by the few times it is used, resulting in a small net increase (or minimal saving) compared to letting the
minifier handle repeated property accesses (which compress well under gzip).
Overall, these factors combine to increase the generated JS size by ~50 bytes (uncompressed) per build where setStatus is enabled, which is
reflected in the codesize test regressions (e.g., -53 bytes saving when we reverted it to synchronous).
Now that we are using more async JS it's a good idea to handle these in the same way we do for `onerror`. Split out from emscripten-core#27121
Now that we are using more async JS it's a good idea to handle these in the same way we do for `onerror`. Split out from emscripten-core#27121
The codesize costs here only apply to builds that include `setStatus` in `INCOMING_MODULE_JS_API`, so don't apply to builds that really care about code size (i.e. when `-sINCOMING_MODULE_JS_API=[]` is used).
Note: The codesize costs here only apply to builds that include
setStatusinINCOMING_MODULE_JS_API, so don't apply to builds that really care about code size (i.e. when-sINCOMING_MODULE_JS_API=[]is used).