Skip to content

Simplify setStatus implementation in run. NFC#27121

Open
sbc100 wants to merge 1 commit into
emscripten-core:mainfrom
sbc100:setStatus
Open

Simplify setStatus implementation in run. NFC#27121
sbc100 wants to merge 1 commit into
emscripten-core:mainfrom
sbc100:setStatus

Conversation

@sbc100

@sbc100 sbc100 commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

Note: 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).

@brendandahl

Copy link
Copy Markdown
Collaborator

Pretty much all the code size tests get worse. Are we not testing with sINCOMING_MODULE_JS_API=[] ?

@sbc100

sbc100 commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator Author

Pretty much all the code size tests get worse. Are we not testing with sINCOMING_MODULE_JS_API=[] ?

A lot of the codesize tests do use -sINCOMING_MODULE_JS_API=[] yes.

For example all of the test_minimal_runtime_code_size tests (because they use -sSTRICT). This is why you only see some of the codesize tests regress here.

@sbc100 sbc100 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More code simplification... way nicer now. But still sadly a codesize regression :(

@sbc100 sbc100 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplified again.. now the regression is only 11 bytes compressed.

@sbc100 sbc100 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplified more.. can't quite remove the regression though.

@brendandahl

Copy link
Copy Markdown
Collaborator

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 😄 .

Comment thread src/postamble.js Outdated
Comment thread src/postamble.js
// 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, '');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't quite figure out why the first setTimeout is awaited but not the second?

@sbc100 sbc100 Jun 16, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does code size grow in these? the change looks like it should be beneficial actually

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My guess its that its because of the async + await keyword being introduced, but I will check.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. async Keyword: Adding async to function run() adds 6 bytes (minified).
  2. 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.
  3. 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).

sbc100 added a commit to sbc100/emscripten that referenced this pull request Jun 17, 2026
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
sbc100 added a commit to sbc100/emscripten that referenced this pull request Jun 17, 2026
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).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants