GameJs/gamejs

Worker misconception

Incubatio opened this issue · 3 comments

Gamejs worker is for me a set bad practice and misguiding for people not familiar with worker.

Intro:

Gamejs worker is basically create another instance of gamejs in a javascript Web Worker.
When you look at gamejs source code, you can see in several reference at different places to gamejs worker. This behavior is similar to application environment. Still a worker is not an environment, and neither an application clone.

Heavy and useless loading

Instantiating a "heavy" worker with lots of features is not an issue, but when cloning the application in a worker the user has access to lot of component that:

  • he will not use (why load it, if you don't need it)
  • he should not use (e.g. Display, mixer, ...)

Computation performance

A simple worker calculate prime number (ref: gamejs/example/worker) ~4 time faster than gamejs worker implementation.

Below is a simple bench:

  • time counter starts at the beginning of the worker file,
  • time is logged every time a prime number is found.

// Simple worker

  1. 181ms
  2. 281ms
  3. 387ms
  4. 495ms
  5. 608ms

// GameJs worker

  1. 557ms
  2. 968ms
  3. 1386ms
  4. 1832ms
  5. 2291ms

(Instanciation of simple worker is also faster because in actual gamejs Worker implementation, gamejs worker re-instanciate the whole application !)

Coupled and not reusable:

GameJs worker creates a blob that use yabble require implementation and thus is not reusable by people who don't wanna use yabble or this precise implementation.

Misguiding for new-comers:

One of gamejs purpose is to be light and simple to use.
For people who never used advanced concept such as worker and don't go read library source code, they could easily imagine that performance are already optimized and consider that gamejs implementation as their standard.
This is like teaching bad practice.

(A general point regarding your accusation of teaching bad practice: gamejs/worker is not teaching the w3c worker model. So it's not a misconception but it's an intentional deviation. Maybe we should rename the module, I now see value in that. Probably something like gamejs/backgroundthread or similar.)

The need to expose the event queue instead of providing async methods (as the workers do natively) and the fact that I want to provide a commonjs environment within the worker has lead to the current design (which I'm sure can be improved but I disagree that it's fundamentaly flawed).

So there's two must haves:

  • commonjs modules
  • worker must expose his event queue (no async method calls)

The only other pattern I can think of, which would fit gamejs equally well and still provide those two must-haves, would be a Worker as a single class whose methods return deferred results. This would also avoid the native, async callback-style but it would be more complex (devs would have to understand Deferred/Promise values) and less powerful then the current 'full blown applicaton' model. This WorkerPromise approach would be similar to this: https://github.com/BigIroh/Ozai but all the methods would return a Promise, for example we could use this library: https://github.com/kriskowal/q

Regarding your other points:

I have significantly cut down on the coupling with the rest of gamejs in the current simpleworker branch and I don't see how we can avoid the coupling which is still there if we still want to provide an eventqueue and commonjs (but again... i'm sure there are improvements possible.. i just don't yet see them).

Your benchmark is a bit misleading: the code inside the worker runs equally fast in native workers as it does in gamejs workers. But the events have to be grabbed out of the worker which only happens at the speed of the main application.... so it is capped to whatever speed reqAniFrame dictates.

People who want to use raw W3C Workers can still do that. And devs who do not want to deal with the w3c worker model can still use the normal gamejs mode of thinking: an exposed event queue, a commonjs module system, etc..

Implementation

So there's two must haves:

  • commonjs modules
  • worker must expose his event queue (no async method calls)

I totally agree with the two practice above.

Here is a simple example of a simple worker respecting those practice:
https://gist.github.com/Incubatio/5030855
Note: it's a gamecs example (which use requirejs), I've adapted this example to gamejs (and thus yabble) in next paragraph about the cause of performance loss.

Performance loss

the code inside the worker runs equally fast in native workers as it does in gamejs workers. But the events have to be grabbed out of the worker which only happens at the speed of the main application.... so it is capped to whatever speed reqAniFrame dictates.

Wrong, the events queue or the reqAniFrame are not the cause of the worker running slower, your implementation is the cause.
To prove the affirmation above I made this example (especially for you):
https://gist.github.com/Incubatio/5031361
The example is fully compatible with last gamejs version, Please update main.js:13 to point a valid prime.js.

Following my tests:

  • Using your implementation is the main performance loss
  • Initializing gamejs (gamejs.ready([..])) add 200 ms at initialization (not relevant if worker started at the beginning of the game)

i'm closing this now, since i changed the way workers work and i think you implemented your suggestion in gamecs