asyncify: increasing performance of JS-DOS on 25%

60 views
Skip to first unread message

Александр Гурьянов

unread,
Dec 18, 2019, 11:10:06 PM12/18/19
to emscripte...@googlegroups.com
Hi. I used asyncifiy in default version of JS-DOS. However I found way
how to increase performance of it on 25%. While dosbox is doing
emulation it uses one loop that works forever:

while (1) {
static lastSleep = GetTicks();
if (GetTicks() - lastSleep > 16) {
emscripten_sleep(0);
lastSleep = GetTicks();
}
PIC_RunQueue();
}

16 is a period of continuous emulation (== 60 FPS).

BUT, this implementation is not so effective as it can be.
Implementation of emscripten_sleep is based on setTimeout function. As
known minimum timeout delay is near ~4ms. So real execution flow is
follow:

| 16ms emulation | 4 ms idle | 16ms emulation | 4ms idle | ....

4ms is 25%. I checked it in profilers of Chrome and Firefox, and as
expected I see idle self time ~ 20-25%.

This 4m ms idle can be removed if we switch implementation from
setTimeout to postMessage. I tried it, and results are very good I see
improvement of Dhrystone 2 test on 60% (sic!), and also profilers
reports about 1-2% idle time.

This is very good, but have some questions:

1. Is any chance to change implementation of emscripten_sleep to
post/recive message (in case when sleep time <4ms)?
2. Even though it works, I don't like fact that I am checking
emulation period in c++, I want to do in this way:

while (1) {
syncSleep();
PIC_RunQueue();
}
//...
EM_JS(void, syncSleep, (), {
if (Date.now() - Module.last_sync_time <= 16) {
return;
}

return Asyncify.handleSleep(function(wakeUp) {
// post/recive impl
});
});

BUT, looks like it's not possible to not call Asyncify.handleSleep in
ASYNCIFY_IMPORT function. If I return 0/undfeinied from it execution
always terminate with message "Unreachable executed...". Is there are
any workaround for this?

3. Can you explain how asyncify handle recursion. Looks it not valid
case for asyncify. DOSBOX can run emulation loop inside emulation
loop, like this:

void loop() {
while(1) {
syncSleep();
// <...>
printf("int21 handler\n");
loop();
}
}

For this case browser hangs. I added console.log into syncSleep
function, and typical log is:
syncSleep (1593)
int21 handler called
// no any syncSleep anymore

I think maybe in case of recursion call stack is overwritten or
something similar, what do you think?
Cause I don't understand why log stops.

Alon Zakai

unread,
Dec 20, 2019, 12:28:57 PM12/20/19
to emscripte...@googlegroups.com
Interesting, I thought browsers throttled postMessage like they do timers,
for the same reasons. I'm surprised one is much more accurate than the
other, and in both chrome and firefox...

I'd be ok with changing to this, given those facts. But we should
double-check them. Perhaps you can make a simple benchmark
that shows this?
 
2. Even though it works, I don't like fact that I am checking
emulation period in c++, I want to do in this way:

while (1) {
  syncSleep();
  PIC_RunQueue();
}
//...
EM_JS(void, syncSleep, (), {
  if (Date.now() - Module.last_sync_time <= 16) {
      return;
  }

  return Asyncify.handleSleep(function(wakeUp) {
      // post/recive impl
  });
});

BUT, looks like it's not possible to not call Asyncify.handleSleep in
ASYNCIFY_IMPORT function. If I return 0/undfeinied from it execution
always terminate with message "Unreachable executed...". Is there are
any workaround for this?

A function in the asyncify imports is where it is ok to call
handleSleep, but I don't think we have support for doing that
*conditionally*. The assumption is that it always will. Maybe that's
the problem here?

More specifically, imagine that when rewinding the call stack
the condition checked at the top has changed - then we may
not reach handleSleep the second time. The assumption in
asyncify is that when we rewind we will get back to where we
left from, *and not run any new code on the way*.


3. Can you explain how asyncify handle recursion. Looks it not valid
case for asyncify. DOSBOX can run emulation loop inside emulation
loop, like this:

void loop() {
  while(1) {
    syncSleep();
    // <...>
    printf("int21 handler\n");
    loop();
  }
}

For this case browser hangs. I added console.log into syncSleep
function, and typical log is:
syncSleep (1593)
int21 handler called
// no any syncSleep anymore

I think maybe in case of recursion call stack is overwritten or
something similar, what do you think?
Cause I don't understand why log stops.

Hmm, good question about recursion. That function will eventually run
out of stack space, though - it never executes the loop, it just keeps
recursing? In this case I'd expect it to fail because of that. I'm not
sure if in other cases recursion might work or not - it wasn't something
I thought about when writing asyncify, but without investigation I don't
know if it just works or not.

--
You received this message because you are subscribed to the Google Groups "emscripten-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to emscripten-disc...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/emscripten-discuss/CAKOm%3DVFaCX1Y--QjVP1Rxqs3TTf7ETZ-%2B%3DmHqZ_UyRDVbkqJ6g%40mail.gmail.com.
Reply all
Reply to author
Forward
0 new messages