Saylua-Archive / SayluaLegacy

Browser Game In Development
saylua.com
GNU Affero General Public License v3.0
2 stars 0 forks source link

Multithreading #38

Closed NoiSek closed 7 years ago

NoiSek commented 7 years ago

Bumping this to the top of the queue, as the outcome of this affects the execution of a number of other things.

Webworkers have wide enough support (92-97%) on a number of major browsers that I think it's worth pursuing for the performance benefits. When unavailable, the engine can fall back to single threaded execution.

Important that we always write around single threading as a best case scenario, regardless, however. It would be bad practice to depend on the multi-threaded performance as the baseline.

State of things:

~5ms average when rendering a change in game state, give or take. Leaves us with 12.6ms, but it could be faster, and there are occasional dips to 20-30ms in certain situations (mostly when Redux performs a form of garbage collection as far as I can tell).

Current Execution Model (For reference)

Engine Level

  1. The current event type is parsed
  2. A copy of the current entity and tile layers is created
  3. All entities are processed one at a time, and passed a copy of those layers to modify
  4. All tiles are processed one at a time, and passed a copy of those layers to modify
  5. The updated copies are returned as new layers for the GameReducer to consume

Script Level

  1. The scriptResolver checks if the actor has a script matching a given event in it's instance metadata
  2. The scriptResolver checks if the actor's parent entity / tile has a script matching a given event
  3. If either of the above is true, request a script and a list of required arguments from the scriptCompiler
  4. scriptCompiler checks if a script has already been compiled, and returns it
  5. scriptResolver asks variableResolver for an argument instance to match every argument requirement
  6. The final list of compiled arguments and the compiled script are applied in tandem, mutating layers and freeing the main execution thread to move on to another actor

Primary problem with this approach is that depends on a single actor not stalling the entire execution thread, and secondarily that it is quite unpredictable in rendering speed (mostly due to A* pathing).

It would be nice to try a system where actor scripts are spawned in Worker processes and present the main thread with a list of changes to be made when complete. This gives the additional benefit of being able to timeout lagging processes by timing their execution.

Most likely, this probably means trading our middle-of-the-road unpredictable speed for a consistently slower but predictable one. Due to the nature of the conflict resolution that will have to occur, there will likely be some speed costs incurred, but it could be that the tradeoff will either be worth it or faster.

We'll see.

NoiSek commented 7 years ago

Closed indefinitely, it is not currently possible to introduce multithreading in a way that would actually improve performance.

Webworkers can only accept copies of items, meaning serializing all functions into strings, which must then be evaluated, and then performing deep clone JSON -> string -> JSON conversions of all arguments into those evaluated functions.

The amount of latency that we would have to start with for there to be a net performance increase with this method is quite high. (Providing a copy of tiles alone is a 4000 item array of objects, for example)

There's (possibly) still hope for using it just for A* pathing, but as a whole this is unlikely to be a worthwhile approach to consider for scripting as a whole.