Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

misc/wasm: long tasks with Go WebAssembly #39620

Open
jalextowle opened this issue Jun 16, 2020 · 11 comments
Open

misc/wasm: long tasks with Go WebAssembly #39620

jalextowle opened this issue Jun 16, 2020 · 11 comments

Comments

@jalextowle
Copy link

@jalextowle jalextowle commented Jun 16, 2020

What version of Go are you using (go version)?

go v1.14.3

$ go version

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

GOOS=js and GOARCH=wasm

go env Output
$ go env

What did you do?

Compiled 0x-mesh to WebAssembly and ran in the browser. I put a hot function inside of a goroutine and used a channel for synchronization to try to get the go runtime to release the main thread enough to prevent the UI from stuttering.

What did you expect to see?

I expected the go runtime to not block the event loop for long periods of time when a goroutine was invoked.

What did you see instead?

These are two flame graphs of different functions that block for a long period of time (up to 1 second). As you'll notice, the sub-calls within these graphs run in significantly less time, so I would think that it would be possible to pick execution back up after clearing the event loop.

Screen Shot 2020-06-16 at 4 50 44 PM

Screen Shot 2020-06-16 at 4 56 03 PM

Notes

If there is an easy way to break these tasks up (like putting something inside of a go-routine), that would fix my problem.

The tasks in the pictures provided correspond to two different scenarios involving the same function, HandleOrderSyncResponse. This can be found in our codebase here: https://github.com/0xProject/0x-mesh/blob/master/core/ordersync/ordersync.go#L471.

We do a few things that are somewhat non-standard for optimization purposes within this callstack including calling out to functions registered on the Javascript window object. This code can be found here: https://github.com/0xProject/0x-mesh/blob/master/orderfilter/validate_js.go#L36. I recently created a new PR that makes the Javascript function that is being called asynchronous, which significantly reduced the time that the individual calls would hold onto the event loop; however, the underlying task held onto the event loop for a much longer period of time. The second image is the flame graph contains calls to the updated MatchOrder function. This PR can be found here.

@andybons andybons changed the title Long tasks with Go WebAssembly misc/wasm: long tasks with Go WebAssembly Jun 17, 2020
@andybons
Copy link
Member

@andybons andybons commented Jun 17, 2020

@neelance
Copy link
Member

@neelance neelance commented Jun 17, 2020

Preemption of goroutines to let the event loop continue is not yet implemented. I think it only makes sense to tackle this after we have WebAssembly threads. You can try time.Sleep to actively pause your goroutine after certain intervals.

@jalextowle
Copy link
Author

@jalextowle jalextowle commented Jun 17, 2020

Thanks for the quick response @neelance. I'll try this out.

I'm still somewhat confused about something. Why do I see the event loop being able to continue for some function calls and not others? There are other functions that we call that do not seem to cause these problems, but I haven't been able to determine what contributes to this difference yet.

@neelance
Copy link
Member

@neelance neelance commented Jun 17, 2020

I don't know. In general the event loop can continue as soon as all goroutines are asleep/blocked.

@jalextowle
Copy link
Author

@jalextowle jalextowle commented Jun 17, 2020

Gotcha. Okay, well I will try to see if sleeping solves the problem. Thanks again!

@cherrymui
Copy link
Contributor

@cherrymui cherrymui commented Jun 17, 2020

I wonder if it would be possible/helpful to let the scheduler do a very short sleep (to give the event loop a chance to kick in) once a while, instead of only yielding when we run out of works?

@jalextowle
Copy link
Author

@jalextowle jalextowle commented Jun 17, 2020

@cherrymui That seems super valuable if it would actually solve the problem.

@neelance
Copy link
Member

@neelance neelance commented Jun 17, 2020

@cherrymui Maybe possible, but not trivial. An event/callback handler needs to be able to run synchronous. Yielding would make the handler return early.

@agnivade
Copy link
Contributor

@agnivade agnivade commented Aug 16, 2020

Related: #32840.

@neelance
Copy link
Member

@neelance neelance commented Aug 16, 2020

#32840 is related, but not the same. The issue here is about yielding to the browser's event loop, #32840 is about yielding to a different goroutine.

@agnivade
Copy link
Contributor

@agnivade agnivade commented Aug 16, 2020

Ah yes, now that makes sense. I was wondering why would yielding make the handler return early. Thanks for that correction @neelance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
5 participants
You can’t perform that action at this time.