Breakout session on Threads

This breakout session was part of the Workshop on Web games. Minutes taken by Dominique Hazaël-Massieux.

David: got some ideas shared by email after our discussion yesterday
... initially, I thought that Web workers are painful to start
... the serialization/deserialization needed to communicate among workers is also costly when you need to constantly share info across threads
... for instance, synchronizing scene graphs across workers is costly
... SharedArrayBuffer is useful but not enough
... someone shared the idea of a Task concept which captures and freezes a context
... the task starts on a new thread and has read-only access to the starting context
... this would work very well for my scene graph case, physics, or collision detection
... more difficult for particles, but still probably usable
... this would solve quite a few problems

@@@: with a readonly context, you probably need to have very light threads because you would likely need to start many of them rapidly for a short time

David: there is a similar approach in C# with a pool of threads that get recycled
... very cheap to start

Myles: the worker in this model would see a read-only view of the model
... what about the main thread then?

Rafael: it would have to be read-only

David: I think the main thread should be kept out of the equation to simplify the model

Myles: what is the status of rAF in workers?

David: it works
... I have rAF in a worker, OffscreenCanvas in a worker, soon user input
... the last thing I need is to be able to separate the work in that thread

Myles: OffscreenCanvas still needs to hands off its results to the main thread
... this could be done here as well

@@@: you will still need some form of blocking for read-only state management, so we need to keep this thread-spawning off the main thread

david: that's what the proposal is indeed

Navid: I believe there is work under way to use OffscreenCanvas without interfering with the main thread at all
... workers don't have access to the DOM currently; there is a project to enable DOM-splitting with some defined CSS property to indicate that a part of the DOM needs to be composited differently
... similar to cross- vs same-origin iframes
... this would allow to hand off part of the DOM to a specific worker
... this would enable to render different part of the pages at different frame rates

Ricardo: OffscreenCanvas has indeed that feature with "transferControl"

David: the constraint right now is how workers get started
... to start a worker, I have to re-import all my libraries in a worker
... it would be great to be able to spawn a worker with the same set of loaded libraries
... (not mentioning that all of these need to be managed as different files, which isn't good wrt webpack-ing)

Myles: let's say there was way to tell this function runs into a separate worker
... how does this work with the global scope?

Luke: Jonas Sicking looked into this a few years ago; it's pretty difficult, would need to convince quite a few people
... we had a separate project called parallel JS that investigated this, was presented to TC39 but not well received and created challenges in implementations
... the closest thing was "FunctionPromise" (?)
... which made it easy to create cheap-to-instantiate string-defined functions that could be transfered to a worker

David: I could see that working, but that leaves the problem of imported scripts - we would need an API to include these in the pack

@@@: how would that work with the various optimization processes?

Luke: there would be opportunities for sharing machine code across these threads

David: the lack of multithreading in JS is unique in the world of programming languages

Myles: one fundamental difference is that this prevents multi-thread race crashes

Ryan: being able to cache pre-compiled or JITed code would be useful for game engines

Andrew: the one hope for that is the cache API with ServiceWorker - cloned copies of responses can make use of pre-JITed code

Dom: is that feature of the Cache API a well-known aspect?

Andrew: not sure - there will probably be an info box in the spec
... in any case, it won't be a guaranteed behavior
... we would also want to avoid heuristics being abused

Luke: parsed-but-not-instianted code I was alluding to is what WASM provide

Ryan: that's interesting, but will we lose this ability in JS?

David: +1 - let's not make JS a poor child of the Web
... let's make sure all these new cool features remain available to JS devs

Dave: from our experience, running WASM on low-end android is impossible from a performance perspective
... because the code is too big

Nick_Hulu: embedded devices, low-end devices don't have access to JIT; it would be great to be able to parallelize
... threads would be our only way to keep performance in this constrained environment

Ryan: there are more or more use cases that don't have JIT
... e.g. in cars

David: I hear consensus on the user-side - threading is needed
... but I hear lots of frinction from browser devs

Andrew: you mentioned there was 2 separate execution environments which creates deployment challenges
... do ES6 module help?
... it doesn't solve all the packaging issues

David: the challenge is as a framework developer, asking my users to deal with that complexity is something I would prefer avoid
... I also can't tell which modules my users have imported before I start a new worker

Ryan: I'm curious about this duplicate concept - what would that cover?

David: just the code

@@@: but what about e.g. dynamically generated functions?

Dave: this can be solved by a nice import function

David: but that still creates complexity

Myles: do you have a helper library that deals with this importing? how do you manage it?

David: right now I have to manually import the scripts, finding the right paths
... I have to create a URL object to manage my workers

Myles: maybe we should start by optimizing your current approach instead of doing a broader redesign

Dave: the issues that were summarized yesterday: the split file issue is one (but there is a workaround), shared memory will be helped by SharedArrayBuffer but doesn't solve the cases where you need object that get serialized/deserialized
... not sure how your read-only model would help with e.g. Physics Engine

Luke: a challenge with JS objects is that they're very complex
... there is a proposal for a TypedObject in JS that may help - it brings an immutable structure
... part of the idea would be be able to mark them as shareable - in which case they can only point at shareable objects
... this could help with multi-threading
... this would also help with gc-support in WASM
... TypedObject would be the reflection of gc-objects

Dom: this is in TC 39?

Luke: yes, at Stage 0 (there is an explainer)

Andrew: would it have similar shared semantics as sharedarraybuffer?

Luke: roughly

Ryan: what are structural limitations?

Luke: it has a weak memory model

Rafael: even in a mono thread this would go a long way to help with David's problem

David: it would indeed solve some (but not all) use cases

Dom: in summary?

David: worker initiialization and scripts imports could be simplified
... TypedObject provide nice opportunities for memory management for Games

Nick: I liked Luke's idea about cached-JITed code

Luke: see FunctionPromise in TC 39

@@@_King: coming from a C++ background - will emscripten manage this directly?

Luke: WASM takes care of it